Compare commits

..

268 Commits

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

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

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

There are still some issues of the height randomly flickering in the
scenarios, which would previously result in the text no longer being
visible.
2023-12-29 19:31:45 +01:00
WarmUpTill
4f0c8c4ab5 Move audio fade helpers to audio action 2023-12-29 19:31:45 +01:00
WarmUpTill
946c90a749 Move tracking of cursor position changes to cursor condition 2023-12-29 19:31:45 +01:00
WarmUpTill
7ba4eaab9c Fix replay buffer save event only triggering in a single condition 2023-12-29 19:31:45 +01:00
WarmUpTill
74553a82fc Remove deprecated "on change" toggle of media condition 2023-12-29 19:31:45 +01:00
WarmUpTill
39fcb2d743 Fix typos 2023-12-29 19:31:45 +01:00
WarmUpTill
7041feaea3 Move handling of stream start / stop time to stream condition 2023-12-29 19:31:45 +01:00
WarmUpTill
9b1ffaeded Remove switcher-data dependency from transition-selection 2023-12-29 19:31:45 +01:00
WarmUpTill
07d397bdac Remove switcher-data dependency from scene-selection 2023-12-29 19:31:45 +01:00
WarmUpTill
a86ff4f1f0 Remove switcher-data dependency from status-control 2023-12-29 19:31:45 +01:00
WarmUpTill
953eca2732 Remove switcher-data dependency from variables 2023-12-29 19:31:45 +01:00
WarmUpTill
d6c5fb149a Fix warning 2023-12-29 19:31:45 +01:00
WarmUpTill
12982708cf Remove curl-helper dependency from switcher-data 2023-12-29 19:31:45 +01:00
WarmUpTill
0cf9e1dd81 Reduce dependencies of macros to switcher data 2023-12-29 19:31:45 +01:00
WarmUpTill
71e20c4983 Move loading / saving of global macro properties 2023-12-29 19:31:45 +01:00
WarmUpTill
2147f6d3bf Rename files 2023-12-29 19:31:45 +01:00
WarmUpTill
23df42d2d3 Add more plugin state helpers 2023-12-29 19:31:45 +01:00
WarmUpTill
bcf0a0b7fd Move functionality to scene-switch-helpers 2023-12-29 19:31:45 +01:00
WarmUpTill
dd78b03a28 Add additional sync-helpers to reduce dependencies to switcher-data 2023-12-29 19:31:45 +01:00
WarmUpTill
84dd5e6930 Add macro-helpers
Added to reduce dependencies to switcher-data
2023-12-29 19:31:45 +01:00
WarmUpTill
dcee98fea7 Move macro segment factory functinos to seperate files 2023-12-29 19:31:45 +01:00
WarmUpTill
9ffb982ae5 Reset date condition type only if necessary 2023-12-29 19:31:45 +01:00
WarmUpTill
428850306f Add option to wait for spawned process to exit 2023-12-25 21:24:48 +01:00
WarmUpTill
abfd6798e3 Adapt to new process start helpers 2023-12-25 21:24:48 +01:00
WarmUpTill
fffadd94de Add helpers to launch process with given config 2023-12-25 21:24:48 +01:00
WarmUpTill
d0919776f3 Add option to set variable to scene item name at given index 2023-12-19 22:59:32 +01:00
WarmUpTill
267855fded Cleanup
* Use OBSSourceAutoRelease
* Do not return true if source selection is invalid
2023-12-17 10:40:29 +01:00
WarmUpTill
4bdebb7feb Fix crash when asking for settings backup
This dialog cannot be modal since other plugins might attempt to display
content which might result in a deadlock.
2023-12-16 19:02:38 +01:00
Michael Kirsch
ab5f69d2ef Decrease the minimal interval time to 10ms 2023-12-13 19:29:14 +01:00
Michael Kirsch
63ee36ddfe Enable Area Selection in ScreenshotHelper class 2023-12-13 14:19:17 +01:00
WarmUpTill
43967f5ded Add temp variable support for video condition 2023-12-10 14:26:30 +01:00
WarmUpTill
c043053b2b Remove unused variable 2023-12-10 14:26:30 +01:00
WarmUpTill
220f27bd0a Move plugin dependencies to Aptfile 2023-12-10 13:42:39 +01:00
WarmUpTill
83914d5378 Linux: resolve procps symbols at runtime
This is done to be able to support both libprocps and libproc2.
Either of them or neither might be available on all platforms and this
should not prevent the plugin from being used at all.
2023-12-10 13:42:39 +01:00
WarmUpTill
57e4b1a3f1 Fix warning 2023-12-10 13:42:39 +01:00
WarmUpTill
c85e556924 Add support for "random()" expression
Generates a random number between [0,1)
2023-12-10 13:33:18 +01:00
WarmUpTill
8c0be8011d Enable use of obs-module-helper.hpp in tests 2023-12-10 13:33:18 +01:00
WarmUpTill
7914f75876 Fix macro references not being resolved when importing macros 2023-12-09 09:52:12 +01:00
WarmUpTill
7d1253e14e Use RAII wrappers for OBS data 2023-12-09 09:52:12 +01:00
WarmUpTill
d7c58b3eb3 Remove websocket dependency to switcher-data internals 2023-12-09 09:52:12 +01:00
WarmUpTill
fb664b6f08 Reduce dependencies to switcher-data.hpp implementation details
This is necessary for the switch to libremidi version 4 due to
incompatabilities  with websocketpp, but also might save some headaches
later on.
2023-12-09 09:52:12 +01:00
WarmUpTill
258a6d076e Fix "or not" logic type check being ignored 2023-12-09 09:52:12 +01:00
WarmUpTill
32ab060153 Ease configuration of token grabber port 2023-12-09 09:52:12 +01:00
WarmUpTill
91d3784f9b Don't wait for transition to complete if already on target scene 2023-12-09 09:52:12 +01:00
WarmUpTill
8a5aac3d31 Add option to check the recording duration 2023-12-09 09:52:12 +01:00
WarmUpTill
06c6effb7d Fix duration value not being updated correctly when changing units 2023-12-09 09:52:12 +01:00
WarmUpTill
f3789c05bd Move Duration and DurationSelection into separate files 2023-12-09 09:52:12 +01:00
WarmUpTill
c64e1f5e51 Rework Process condition
* Add option to en-/disable regex matching
* Add tempvar support for process name
2023-12-09 09:52:12 +01:00
WarmUpTill
5d8e24c19a Add temp variable support to Macro condition 2023-12-09 09:52:12 +01:00
WarmUpTill
f756de05c3 Add option to check if macro is paused 2023-12-09 09:52:12 +01:00
WarmUpTill
574fdb496f Add error logs for token grabber server 2023-12-09 09:52:12 +01:00
WarmUpTill
5580da69eb Fix window condition not clearing tempvars of previous condition type 2023-12-09 09:52:12 +01:00
WarmUpTill
35274863c9 Use available layout stretch space for settings input field 2023-12-09 09:52:12 +01:00
Przemek Pawlas
1f31b81f0d Add else actions toggle button 2023-12-09 09:42:10 +01:00
WarmUpTill
cde111ddeb Ignore settings which don't have a description or name
This fixes a crash when attempting to populate the setttings selection
of a Window Capture or Game Capture source
2023-12-07 07:04:50 +01:00
Przemek Pawlas
845c12c01c Add tooltip for run button explaining else (#931)
* Add tooltip for run button explaining else
* Switch run button modifier to Shift
* Toggle button label only when hovering over it
2023-12-06 10:40:14 +01:00
Przemek Pawlas
99a0118d31 Add clipboard text/image copy actions 2023-11-27 21:07:38 +01:00
Przemek Pawlas
bb7ee17362 Prepare Twitch action for introduction of new action types 2023-11-26 17:48:45 +01:00
Przemek Pawlas
70854ef012 Reorder Twitch condition enum value definitions 2023-11-26 17:48:45 +01:00
Przemek Pawlas
053c14056d Add more token options and reorder existing ones 2023-11-26 17:48:45 +01:00
Przemek Pawlas
9397d50764 Refactor twitch-helpers
Added more http helpers, reordered parameters, and reduced code
duplication
2023-11-26 17:48:45 +01:00
Przemek Pawlas
473e24cc42 Use consistent function naming for setting widget visibility 2023-11-26 17:48:45 +01:00
WarmUpTill
d9394605f4 Set tempvar values for raid events 2023-11-26 15:29:54 +01:00
WarmUpTill
d2b9074852 Fix build for Twitch mock 2023-11-26 15:29:54 +01:00
WarmUpTill
b525eb7a0e Add temp variable support to MIDI condition 2023-11-26 13:05:44 +01:00
WarmUpTill
3805aaeeec Add Twitch chat support
* Enable sending chat messages via the Twitch action
* Enable checking for chat messages using the Twitch condition
2023-11-26 00:17:38 +01:00
WarmUpTill
50dc404689 Add temp variable support to date condition 2023-11-20 20:08:09 +01:00
WarmUpTill
975eacbc7e Add temp variable support to timer condition 2023-11-20 20:08:09 +01:00
WarmUpTill
7e56dc35b8 Enable use of variables in find and replace options of variable action 2023-11-20 20:08:09 +01:00
WarmUpTill
b3566d98bf Add condition to check temporary variable values / macro properties 2023-11-20 20:08:09 +01:00
WarmUpTill
15379a8e20 Clean up locale and variable condition 2023-11-20 20:08:09 +01:00
WarmUpTill
1e178f4223 Add temp variable support to scene condition 2023-11-20 20:08:09 +01:00
WarmUpTill
c16a7b93ea Add temp variable support to window condition 2023-11-20 20:08:09 +01:00
WarmUpTill
f89bb1a750 Add temp variable support for audio condition 2023-11-20 20:08:09 +01:00
WarmUpTill
79961aabe4 Use SourceSetting helpers to enable modification of single setting 2023-11-20 20:08:09 +01:00
WarmUpTill
4686fc2b01 Add SourceSetting helpers
They enable easy modification of single setting value of sources
compared to working with the raw json string
2023-11-20 20:08:09 +01:00
WarmUpTill
569b915c07 Set temporary variables in Twitch condition 2023-11-20 20:08:09 +01:00
WarmUpTill
324d3fe7a5 Fix content content_classification_labels not being set 2023-11-20 20:08:09 +01:00
WarmUpTill
ae8d0bf5cb Add support for setting variable to value of temporary variable 2023-11-20 20:08:09 +01:00
WarmUpTill
17efa82057 Add temporary variables
Enables easier use of values returned by macro segments.
For example, this enables the user to extract the user name of a new
Twitch follower and change a text sources settings accordingly
2023-11-20 20:08:09 +01:00
WarmUpTill
c17523a476 Enable extraction of array values from channel info 2023-11-20 20:08:09 +01:00
WarmUpTill
33635d6991 Add action to enable extraction of values from json string 2023-11-20 20:08:09 +01:00
WarmUpTill
d290dbe86b Add nlohmann json dependency 2023-11-20 20:08:09 +01:00
WarmUpTill
c1f5c49eda Run PostLoad() for "else actions" as well 2023-11-20 20:08:09 +01:00
WarmUpTill
c3f4caa6f3 Add AddPostLoadStep()
Enables registering additional functions, which are to be executed after
the loading steps are complete
2023-11-20 20:08:09 +01:00
WarmUpTill
d4c7b0e455 Fix crash while running macro actions
Users could invalidate the actions vector while the actions were
executed resulting in a crash.
2023-11-20 20:08:09 +01:00
WarmUpTill
b0cb52fe90 Fix warning 2023-11-20 20:08:09 +01:00
WarmUpTill
386f831cd4 Fix typos 2023-11-20 20:08:09 +01:00
Przemek Pawlas
449daa80cc Add media seek to percentage action + cleanup 2023-11-17 20:11:00 +01:00
Przemek Pawlas
3003547b83 Rework Twitch condition
* Fix channel changes in condition not resetting subscription ID
* Simplified Twitch condition logic to match the subscription ID rather
  than broadcaster ID and some other data - worked well for me, as I
  mentioned not sure why it wasn't done this way in the 1st place (but
  if there's a reason let me know)
* Further made ordering more consistent so that Twitch macros/conditions
  are easier to read (more or less samethematical order in hpp and cpp)
* Removed some unused code
* Added some param names to some header files to functions
2023-11-08 19:59:59 +01:00
Przemek Pawlas
1664eb672f Verify if channel given channel name is valid 2023-11-08 19:59:59 +01:00
Przemek Pawlas
0939dcacb6 Cleanup 2023-11-08 19:59:59 +01:00
Przemek Pawlas
54b81d1a42 Add AnyOptionIsEnabled() 2023-11-08 19:59:59 +01:00
Przemek Pawlas
626606100f Add helper classes to handle Twitch channel point rewards 2023-11-08 19:59:59 +01:00
Przemek Pawlas
53a0b76e8c Add empty() 2023-11-08 19:59:59 +01:00
WarmUpTill
e16ac9400a Add option to set variable value to length of string
* Also use SringVariable for _strValue input field to enable getting the
  length of variable values
2023-10-25 15:06:11 +02:00
WarmUpTill
94060c236b Add option to set ResizingPlainTextEdit parameters 2023-10-25 15:06:11 +02:00
Przemek Pawlas
890792c270 Add more Twitch condition types
Also simplifies some code and make it more consistent
2023-10-25 14:47:34 +02:00
WarmUpTill
7bc1fc3d0d Fix crash if previous condition / action was invalid 2023-10-25 14:47:34 +02:00
WarmUpTill
9ea90ed61b Add Twitch condition
It supports:
* Checking if a channel is live (polling)
* Checking if a channel went live and it's stream type (event)
* Checking if a channel weng offline (event)
* Checking the stream title of a channel (polling)
* Checking the stream category of a channel (polling)
* Checking if the channel information was changed (event)
* Checking for new followers (event)
* Checking for subsciptions (new / end / gift / resub message) (event)
* Checking for cheers (event)
2023-10-14 21:22:53 +02:00
WarmUpTill
ce40b80d90 Clean up token selection
* Adjust locale
* Hide token and name fields if no account is connected
* Enable all token options by default
* Validate token every hour as required by Twitch
* Retrigger event subscriptions on token change

token
2023-10-14 21:22:53 +02:00
WarmUpTill
53fcd94c5a Add Twitch EventSub helpers
EventSub subscriptions let your application listen for events that
happen on Twitch.
2023-10-14 21:22:53 +02:00
WarmUpTill
ecb6247959 Combine manual category search button and combobox into a single widget
To avoid code duplication for Twitch condition and action
2023-10-14 21:22:53 +02:00
WarmUpTill
3b4cb6934d Prevent crashes when downgrading to older version of Twitch action 2023-10-14 21:22:53 +02:00
WarmUpTill
babe93a6ce Clean up Twitch action
* Allow filtering in action selection by typing
* Add spacing between action type definitions to allow reordering in
  future
2023-10-14 21:22:53 +02:00
WarmUpTill
1e9173699c Add option to start a channel raid 2023-10-14 21:22:53 +02:00
WarmUpTill
37ba7aa2ca Only change text of segment value preview if value changed
This is done to prevent the selection being lost whenever the text filed
was updated.
2023-10-14 21:22:53 +02:00
WarmUpTill
a83d2d9910 Add channel selection 2023-10-14 21:22:53 +02:00
WarmUpTill
0449064f5d Enable empty name warning to be hidden
Used for the twitch token selection for which the whole name selection
will be hidden if no token is connected.
2023-10-14 21:22:53 +02:00
WarmUpTill
6cf1073a3c Add option to use cached results
The cache will remain valid for 10s before it is refreshed with a "real"
Get / Post / Patch call.
2023-10-14 21:22:53 +02:00
WarmUpTill
9ac9171206 Fix windows being created when constructing ProgressDialog 2023-10-14 21:22:53 +02:00
WarmUpTill
25ce8cd2e4 Also ex-/import variables when ex-/importing macros
Manually creating necessary variables and reconfiguring the macros to
use them can be unintuitive so instead just create the missing variables
included in the export.
2023-10-11 21:42:05 +02:00
WarmUpTill
e50522ab89 Compress export / import macro text 2023-10-11 21:42:05 +02:00
WarmUpTill
4801502f32 Add option to open properties and filter windows of sources 2023-10-05 18:54:22 +02:00
Przemek Pawlas
e13e4b5ddd Add max length validation support for TextEdit 2023-10-05 18:53:46 +02:00
Michael Kirsch
de8da553f1 Implement source item transform changed condition (#885) 2023-10-03 19:55:45 +02:00
Przemek Pawlas
6558792e73 Add Twitch send chat announcement and emote-only mode actions (#890) 2023-10-03 14:09:29 +02:00
WarmUpTill
ea5e92017e Add "all" option in filter selection 2023-10-03 12:34:11 +02:00
Przemek Pawlas
46199efb58 Add tooltips for variable text inputs (#888) 2023-10-03 11:42:41 +02:00
WarmUpTill
8a6eae6c97 CI: Fix Windows build script appending new lines to CMakeLists.txt 2023-10-03 00:09:29 +02:00
WarmUpTill
67eef8a004 CI: Fix MacOS builds
This is just a temporary fix.
The real solution would be to switch to the new OBS plugin template
version and no longer rely on the packages app
2023-10-03 00:09:29 +02:00
WarmUpTill
b5341e5cde Hide option to set master volume (deprecated) 2023-09-21 23:52:14 +02:00
WarmUpTill
ac6140b17a Fix warnings 2023-09-21 23:52:14 +02:00
WarmUpTill
b1489d98f0 Update websocketpp to 0.8.2 2023-09-17 19:59:30 +02:00
WarmUpTill
3b7119ec58 Update Asio to 1.28.0 2023-09-17 19:59:30 +02:00
WarmUpTill
efa34d080e CI: Use dependency build cache 2023-09-17 17:50:25 +02:00
WarmUpTill
19a6277841 Enable use of throttling for "has changed" checks. 2023-09-17 17:08:15 +02:00
Przemek Pawlas
107e1413ed Add Twitch clip and marker actions
* Refactor stream title variable name
* Limit commerical duration to 180s
* Set maximum stream title size to 140 chars
2023-09-16 22:23:34 +02:00
Przemek Pawlas
94416dd881 Add .vscode to .gitignore 2023-09-16 22:23:34 +02:00
WarmUpTill
8efc39958f Add action to assign scene item count to variable
To be able to iterate through the scene items of a scene
2023-09-16 19:49:54 +02:00
WarmUpTill
569f794e33 Move GetSceneItemCount() to utility 2023-09-16 19:49:54 +02:00
WarmUpTill
fc1460e592 Fix crash on shutdown related to macro docks
It could happen that the macro was already deleted while the macro dock
was still attempting to update its displayed information.
2023-09-13 19:49:55 +02:00
WarmUpTill
d6e4ee0203 Add option to run "else" actions using the "Run macros" button
If there are else actions to run holding control and clicking the "Run
macros" button will allow you to test them.
2023-09-13 19:49:55 +02:00
WarmUpTill
0c8135078d Rework PerformActions() to enable choosing which action type to run 2023-09-13 19:49:55 +02:00
WarmUpTill
24f4b864c4 Fix macro splitter position not saving when switching to empty selection 2023-09-13 19:49:55 +02:00
WarmUpTill
41e47f32b6 Display warning if selected token does not support selected action 2023-09-11 18:27:47 +02:00
WarmUpTill
a73ccb15a1 Enable MacOS support for Twitch action 2023-09-11 18:27:47 +02:00
WarmUpTill
36a78f49a5 Add openssl to MacOS build scripts to enable Twitch support 2023-09-11 18:27:47 +02:00
WarmUpTill
cbd459b0a5 Improve error logging 2023-09-11 18:27:47 +02:00
WarmUpTill
03bb1fd089 Stop token server if token was received / timeout was reached 2023-09-11 18:27:47 +02:00
WarmUpTill
4e2c254717 Set commercial duration suffix to "s" to inidicate unit is in seconds 2023-09-11 18:27:47 +02:00
WarmUpTill
fffb6c5f29 Adaptions for "else" support
* Fix "on change" option always being highlighted
* Fix "run macro" button performing actions based on condition state
* Fix "run" option of macro action performing action based on condition
  stated of selected macro
2023-09-11 18:25:46 +02:00
WarmUpTill
81f669e226 CI: Separate dependency build from plugin build
* Goal is to enable caching of dependency builds to speed up CI build
  times significantly
* Should ease transition to new plugin build template version
2023-09-10 12:26:41 +02:00
Denis KARUPOVIC
6484ae54c0 Create fr-FR.ini (#863)
* Create fr-FR.ini

add french language

* Update fr-FR.ini (fix white spaces)

* Update fr-FR.ini (remove some useless trads + fix others)

* Update fr-FR.ini (correct some trads)

* Update fr-FR.ini (fix some trads)

* Update fr-FR.ini (fix some trads)

* Update fr-FR.ini (fix errors)

---------

Co-authored-by: WarmUpTill <WarmUpTill@web.de>
2023-09-10 01:41:01 +02:00
WarmUpTill
1c52caf5e9 Move legacy tab entries to end of file 2023-09-09 11:17:29 +02:00
WarmUpTill
ce1d4cce57 Add support for "else" action block
* Changed the "on change" match behaviour
  Previously macros with true conditions but "on change" enabled would
  be considered to be evaluating to false.
  Now they are considered to be evaluating to true and the "on change"
  option only has an effect on if the actions are performed or not.

  This could have effects on user which use the "Macro" condition type
  to check what other macros' conditions evaluate to.

* The splitter position separating the conditions, actions, and
  else-actions are not saved and restored based on which macro is being
  selected.
2023-09-09 11:16:50 +02:00
WarmUpTill
8246197ae6 Add "else" action block 2023-09-09 11:16:50 +02:00
WarmUpTill
8f90bf8846 Move splitter save/load helpers to utility 2023-09-09 11:16:50 +02:00
WarmUpTill
b955e8a0ea Move action and condition macro segement list to Ui_AdvSceneSwitcher 2023-09-09 11:16:50 +02:00
WarmUpTill
81da32f7a2 Only show slide show condition type if OBS supports necessary features 2023-09-09 09:56:29 +02:00
WarmUpTill
5324fa9350 Reset monitor selection in projector action when monitor setup changed 2023-09-06 21:30:55 +02:00
WarmUpTill
c375daa258 Add option to start commercial 2023-09-03 14:29:14 +02:00
WarmUpTill
4f3bd699c8 Fix tooltip not being translated 2023-09-03 14:29:14 +02:00
WarmUpTill
f5024621c0 Add option to assign environment variable value to variable 2023-09-01 20:37:02 +02:00
WarmUpTill
8308506d71 Rely on Qt when using GetCurrentWindowTitle() and OBS is in focus 2023-09-01 19:22:02 +02:00
WarmUpTill
6605c19202 Add OBS windows to GetWindowList() 2023-09-01 19:22:02 +02:00
WarmUpTill
fdf1a94f97 Add option to set placeholder text when asking for input 2023-08-29 20:46:33 +02:00
WarmUpTill
6b6e37da92 Hide preview scene options from studio mode action and condition
The options will only be hidden if the it is not being used.
Thus the functionality of existing macros will not be affected.
2023-08-28 21:19:38 +02:00
WarmUpTill
0f0fc8ae4e Add option to switch preview scene to scene switch action
Same functionality of the "Studio mode" action will be hidden and
eventually be removed.
2023-08-28 21:19:38 +02:00
WarmUpTill
4d7e2992e5 Add preview scene support to scene condition
Existing functionality of the "Studio mode" condition will be hidden and
eventually be removed.
2023-08-28 21:19:38 +02:00
WarmUpTill
a5050d4810 Add Twitch action
Allows you to set the stream title and stream category
2023-08-26 17:32:36 +02:00
WarmUpTill
9df6963f08 Add cpp-httplib 2023-08-26 17:32:36 +02:00
WarmUpTill
b833dd4576 Export FilterComboBox widget symbols
To be used for Twitch action
2023-08-26 17:32:36 +02:00
WarmUpTill
39378ae20f Enusre that placeholder text is visible for FilterComboBox 2023-08-26 17:32:36 +02:00
WarmUpTill
5a2cb943f7 Add option to open interaction dialog of source 2023-08-25 18:19:23 +02:00
WarmUpTill
b88209f63d Add option to check for settings change to source and filter condition 2023-08-19 19:21:19 +02:00
WarmUpTill
9c848938f8 Add support for registering additional save and load steps
Also renamed the reset steps to make it more clear what their use is.
2023-08-15 18:24:03 +02:00
WarmUpTill
07b86dda41 Cleanup ItemSettingsDialog and ItemSelection
* Added option to configure name conflict locale
 * Fixed some locale options not being displayed properly due to missing
   obs_module_text() calls and name conflicts not being handled
   properly.
 * No longer force a fixed width
 * Added option to hide the rename context menu entry
 * Use locale for invalid connection and variable selection string
2023-08-15 18:24:03 +02:00
WarmUpTill
49e8bf7639 Export ItemSelection and ItemSettingsDialog widget symbols
This is done to be able to support use of these widgets in plugins.
2023-08-15 18:24:03 +02:00
WarmUpTill
a2d0a7f544 Fix OCR value not being assigned to variables with enabled regex 2023-08-15 18:24:03 +02:00
WarmUpTill
1a8c894ddf Cleanup
* Fix warnings
 * Remove unused includes
 * Fix build issue if libremidi CMakeLists.txt does not exist
2023-08-15 18:24:03 +02:00
WarmUpTill
3f51f63298 Cleanup locale
* Remove unnecessary widget spacing
* Remove unused values
2023-08-15 18:24:03 +02:00
WarmUpTill
a0b4df574b Add option to disable filtering by typing in FilterComboBox 2023-08-14 15:47:38 +02:00
WarmUpTill
68c6492c3f Revert "Add OCL support to improve performance"
This reverts commit 284c8020b2.
2023-08-09 17:04:00 +02:00
WarmUpTill
0ba7ba77d8 Fix potential crash related to OCR 2023-08-09 17:04:00 +02:00
WarmUpTill
04b2b3474d Fix crash in output change check with pattern matching enabled 2023-08-09 17:04:00 +02:00
WarmUpTill
ecbb5ebbd7 Update issue template 2023-08-09 17:04:00 +02:00
WarmUpTill
b62757b65d Rely on output flag to identify media sources
Using OBS_SOURCE_CONTROLLABLE_MEDIA instead of a hardcoded list of
source names is much more reliable and has the upside of also supporting
plugin sources
2023-08-09 14:01:19 +02:00
WarmUpTill
b1a3ab5493 Fix window condition ignoring title matching if regex is disabled
If the only enabled option was window title matching and regular
expressions were not used any window title would match regardless if it
existed or not
2023-08-09 13:50:32 +02:00
WarmUpTill
abc3357180 Make macro properties dialog resizable 2023-08-07 19:17:41 +02:00
WarmUpTill
29f9cba236 Add option to skip execution of given macro on OBS startup 2023-08-07 19:17:41 +02:00
WarmUpTill
9a62522140 Fix DisplayMessage() not being visible when OBS is always on top
The previous behaviour would cause the impression of OSB being frozen
due to dialogs windows being opened behind OBS while they take over the
input focus.
2023-08-07 18:03:12 +02:00
WarmUpTill
666c52556e Fix plugin state condition not functioning on OBS shutdown
Also added note indicating the limitations of running macros on OBS
shutdown
2023-08-05 15:43:44 +02:00
WarmUpTill
1c0734d1cb Add media_playlist_source_codeyan to supported media source types
Source at: https://github.com/CodeYan01/media-playlist-source/
2023-08-05 13:35:19 +02:00
WarmUpTill
1304ac7336 Add tooltip to regex configure widget 2023-08-03 21:44:17 +02:00
WarmUpTill
884fb1b637 Adjust to SceneItemSelection rework
And do minor cleanup
2023-08-03 21:44:17 +02:00
WarmUpTill
a82bce947f Rework scene item selection
Added support for:
* Index based scelection
* Type based selection
* Name pattern based selection
2023-08-03 21:44:17 +02:00
WarmUpTill
d5241f0700 Add tooltip for variable and connection selection 2023-08-03 21:44:17 +02:00
WarmUpTill
f24f258c9c Add option to hide enable button and set default enable value 2023-08-03 21:44:17 +02:00
WarmUpTill
eddd9fa8ab Add helper to check if value is a valid number 2023-08-03 21:44:17 +02:00
WarmUpTill
6cc224aeec Add helper to get int from variable 2023-08-03 21:44:17 +02:00
WarmUpTill
4b0a631987 Add export / import functionality to macro tab
This enables the easy sharing of single / multiple macros across scene
collections.
Previously either all settings had to be copied via the export / import
functionality of the General tab or none at all.
2023-08-02 20:55:59 +02:00
WarmUpTill
dcae0e8e8b Reset scene switcher state when new scene collection is created
This avoids copying all macros of the previous scene collection to the
newly created one.
2023-08-02 20:55:59 +02:00
WarmUpTill
29f810a2a0 Add buttons to move macro segments to top / bottom of segment list 2023-08-02 17:00:34 +02:00
WarmUpTill
719dfa7b72 Fix segment selection being lost when using up / down button 2023-08-02 17:00:34 +02:00
WarmUpTill
259aac8fcd Perform actions only on condition change by default 2023-08-02 17:00:34 +02:00
WarmUpTill
5be2cd278f Allow empty string as argument and increase max length of argument 2023-08-02 17:00:34 +02:00
WarmUpTill
211eac5313 Add slide show condition
It allows you to check ...
* if the slide changed
* the current slide index
* the current slide path

Important limitation:
Its internal state is only updated whenever the "slide_changed" signal
is sent by the particular source.
2023-08-01 23:52:44 +02:00
WarmUpTill
f92ed3139d Add more transform actions
* Reset
* Rotation
* Flip (horizontal / vertical)
* Fit to screen
* Stretch to screen
* Center (horizontal / vertical / to screen)
2023-07-28 18:51:30 +02:00
WarmUpTill
bdff84c605 Use MacroSegmentSelection for index selection in "Variable" action 2023-07-27 21:06:54 +02:00
WarmUpTill
08e430396a Add option to check action enable state to "Macro" condition 2023-07-27 21:06:54 +02:00
WarmUpTill
df7fc7ec01 Add option to enable / disable actions of macros via "Macro" action 2023-07-27 21:06:54 +02:00
WarmUpTill
9eef581161 Add MacroSegmentSelection
Utility class to enable selection of index of a macro segments.
Add description of selected segment.
Highlights segment if selected macro is current macro.
2023-07-27 21:06:54 +02:00
WarmUpTill
52811e44f4 Add option to disable variable selection 2023-07-27 21:06:54 +02:00
WarmUpTill
7b1c56877e Add GetMacroSegmentDescription() and IsValidMacroSegmentIndex() 2023-07-27 21:06:54 +02:00
WarmUpTill
78d73af589 Adjust to FilterComboBox 2023-07-27 21:06:54 +02:00
WarmUpTill
d9c1ef4c22 Only emit signals if valid entry was found 2023-07-27 21:06:54 +02:00
WarmUpTill
93f96ee660 Add support for custom URIs 2023-07-27 19:53:25 +02:00
WarmUpTill
d25a8d4efb Remove unnecessary include 2023-07-27 19:53:25 +02:00
WarmUpTill
1f22278496 Move SetGridLayoutRowVisible() to utility 2023-07-27 19:53:25 +02:00
WarmUpTill
70b705e0fd Add variable support to timer condition 2023-07-26 13:34:47 +02:00
WarmUpTill
9e1dfdc3f0 Enable use of duration modifiers for hotkey condition 2023-07-26 08:00:16 +02:00
WarmUpTill
47d6d4ac06 Handle potential horizontal scrollbar when resizing StringListEdit 2023-07-23 00:29:14 +02:00
WarmUpTill
fe3586b6f4 Set maximum header size to 4096 2023-07-23 00:29:14 +02:00
WarmUpTill
650881204f Adjust capitalization of HTTP action 2023-07-23 00:29:14 +02:00
WarmUpTill
8c0ba112e2 Add SetMaxStringSize()
A fixed size of 170 chars caused issues with the http action
2023-07-23 00:29:14 +02:00
WarmUpTill
a17d8f7e09 Add support for sending generic OBS websocket messages
This should allow to send OBS websocket vendor requests to other plugins
which offer an API via this method (e.g. vertical OBS)
2023-07-15 14:48:45 +02:00
WarmUpTill
371b4ae05d Add option to disable individual actions of macros 2023-07-15 11:05:19 +02:00
WarmUpTill
1d7ce510f7 Add SwitchButton
Simple switch toogle button widget based on
https://github.com/KDAB/kdabtv/blob/master/Styling-Qt-Widgets/toggleswitch.h
2023-07-15 11:05:19 +02:00
WarmUpTill
d759ded64d Use FilterComboBox instead of regular QComboBox
This required the following adjustments:

Instead of having a dedicated entry indicating the empty selection the
setPlaceholderText() mechanism is used.
Thus the locations where the 1st entry was assumed to be the empty
selection would have to be adjusted.

Additional checks for the empty string / index -1 have been added.

FindIdxInRagne() was adjusted to return -1 instead of 0 in case the
given string was not found.

Switched to index based singal instead of text based signal to be
notified about selection changes.
2023-07-15 11:05:19 +02:00
WarmUpTill
0b2e1b88cc Add FilterComboBox
This helper class is based on QComboBox, which has filtering of items
enabled by default.
It is intended to be used in places where QComboBoxes have a lot of
entries.
For example, the condition, action, or macro selection widgets.
2023-07-15 11:05:19 +02:00
WarmUpTill
72a11eedfc Make selection entry optional for transition list
Needed for FilterComboBox
2023-07-15 11:05:19 +02:00
WarmUpTill
1f02226620 Add option to set flags in FindIdxInRagne() 2023-07-15 11:05:19 +02:00
WarmUpTill
ad8c29997c Fix group creation starting at incorrect index 2023-07-15 04:22:08 +02:00
WarmUpTill
2c7c40c3f0 Fix reordering issues in macro tree 2023-07-15 04:22:08 +02:00
WarmUpTill
c9c0e4db2e Improve OCR option of video condition
* Improve preprocessing of image by separating text color from the rest
  of the image properly
* Add option to choose how similar the matched color can be to other
  colors for it to still be counted as a match
* Show the prepreprocessed image in the "show match" preview dialog
2023-07-09 17:58:38 +02:00
WarmUpTill
dd880b13c0 Cleanup invertPatternMatchResult() 2023-07-09 17:58:38 +02:00
WarmUpTill
76c759c8f4 Display different message if whole image matches pattern
The "pattern is highlighted in red" message does not make sense in this
case as there is no room to display the red border around the match.
2023-07-09 17:58:38 +02:00
WarmUpTill
284c8020b2 Add OCL support to improve performance 2023-07-09 17:58:38 +02:00
WarmUpTill
f506600b8c Enable use of other OCR language models 2023-07-09 17:58:38 +02:00
WarmUpTill
339418b5f3 Add color matching 2023-07-09 17:58:38 +02:00
WarmUpTill
2eca2cc2e9 Cleanup video condition
* Style changes
* Separate functionality into different widgets
* Fix typos
2023-07-09 17:58:38 +02:00
WarmUpTill
4315d309f2 Add option to show description as tooltip 2023-07-09 17:58:38 +02:00
WarmUpTill
8d18bb363a Add options to disable plugins 2023-07-07 21:35:51 +02:00
WarmUpTill
592f31f059 Use the correct build type for dependencies (Windows)
Always building with the Release configuration caused issues (e.g.
linking) when attempting to build non-release configurations when using
the CI build scripts.
This is not done for OpenCV as it does not support all build types.
2023-07-07 21:35:51 +02:00
WarmUpTill
300ad15ad0 Reduce size of audio action widget
Only show audio fade options when they are needed.
2023-07-07 21:35:51 +02:00
WarmUpTill
91f93169eb Add option to set deinterlace mode and deinterlace field order 2023-07-07 19:19:40 +02:00
WarmUpTill
5d95069291 Cleanup
* Move run count increment back to PerformActions()
 * Rename variable
2023-07-06 19:59:46 +02:00
WarmUpTill
e5bb9162ef Prevent flicker when creating MacroConditionMacroEdit
Seems to be caused by the fact that some widgets were not part of any
layout while setLayout() was already called
2023-07-06 19:59:46 +02:00
WarmUpTill
6869cc8a01 Add option to highlight macro dock if conditions of macro are true 2023-07-06 19:59:46 +02:00
WarmUpTill
55c7ac8a32 Rework WasExecutedRecenty()
Added ExecutedSince() instead as WasExecutedRecenty() did not support
checking if a macro was executed recently when using it multiple times
in quick succession.

This could be the case if the MacroTree and a MacroDock would both
attempt to use this function at the same time.
2023-07-06 19:59:46 +02:00
WarmUpTill
101a85b063 Enable use of variables in macro dock widgets 2023-07-06 19:59:46 +02:00
WarmUpTill
e7c6afbc6a Fix potential crash when resolving variables during shutdown 2023-07-06 19:59:46 +02:00
WarmUpTill
18e19fb6c8 Add support for adding status labels to macro docks 2023-07-06 19:59:46 +02:00
WarmUpTill
a7ccfc3282 Add action to toggle lock of scene items 2023-07-05 00:53:03 +02:00
Joao Eriberto Mota Filho
85eb2a4235 Fix spelling errors 2023-07-01 20:51:31 +02:00
WarmUpTill
3e3805b542 Ignore libremidi warnings 2023-06-17 13:57:35 +02:00
WarmUpTill
c59acd836a Reduce size of audio action widget
Only show audio fade options when they are needed.
2023-06-17 13:57:35 +02:00
WarmUpTill
1e81181312 Add option to toggle filter 2023-06-09 20:28:01 +02:00
WarmUpTill
8a796efdb3 Allow specifying custom prompts for variable input 2023-06-07 12:36:39 +02:00
WarmUpTill
1a07a93552 Add spacer if not modifying strValue and remove it otherwise 2023-06-07 12:36:39 +02:00
WarmUpTill
bd74629420 Add helpers to add and remove trailing spacer items in QBoxLayouts 2023-06-07 12:36:39 +02:00
WarmUpTill
dabb293cc0 Mark locale maps as const
There is no reason to have these not be const as new locale entries will
not be added at runtime.
2023-06-03 22:38:42 +02:00
WarmUpTill
b6031bd29a Fix typo in locale name 2023-06-03 22:38:42 +02:00
WarmUpTill
05fa3af2ac Fix crash while waiting for transition and no transition is selected 2023-05-31 13:39:47 +02:00
WarmUpTill
c52166bfec Add option to trigger OBS hotkeys directly
Main use case would be to interact with other plugins which offer
hotkeys.
The benefit of this method is that it does not require a key to be bound
to the particular hotkey to trigger the functionality.
2023-05-30 13:14:58 +02:00
WarmUpTill
6d3964fa5d Fix non-modal-dialog answer being ignored 2023-05-30 13:14:58 +02:00
WarmUpTill
65545d85ba Let OBS handle dock restoration on first startup and cleanup on shutdown
The "manual" attempt at restoring the docks should only be performed on
scene collection change as OBS will already take care of this on
starutp.

Do not delete the macro dock widgets on shutdown so OBS is able to save
their location and size properly.
2023-05-30 12:58:23 +02:00
WarmUpTill
dea4ff47cd Add option to prompt user for value to be assigned to variable 2023-05-28 19:59:12 +02:00
WarmUpTill
258a5f6ab7 Move NonModalMessageDialog to separate file 2023-05-28 19:59:12 +02:00
WarmUpTill
2048ca2816 Add scenes to possible selection of "Source" condition type
It might be useful to check if a given scene is visible outside of it
being the currently active scene, as scenes can also be used as sources
within other scenes.
2023-05-26 21:45:45 +02:00
WarmUpTill
eb9e2e8062 Const correctness 2023-05-26 21:45:45 +02:00
WarmUpTill
3229bf0150 Clean up source condition and action
* Add hints directing to the scene item visibility action / condition as
  they frequently get mixed up
* Clean up layout of the source condition (removed spaces in locale as
  this unnecessary spacing in the layout when the settings check was not
  selected
2023-05-26 21:45:45 +02:00
WarmUpTill
dfa12f9807 Add support for configuring additional streaming options
* Set server
* Set stream key
* Set username
* Set password
2023-05-26 20:42:16 +02:00
284 changed files with 24718 additions and 5086 deletions

View File

@@ -22,6 +22,7 @@ A clear and concise description of what you expected to happen.
**Logs**
Please provide a log of your issue with verbose logging enabled (See General tab of the plugin).
In case of a crash, please also include the corresponding crash log.
See [here](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) for a description where to find the log files and how to share them.
Please share the currently used plugin settings by exporting them them to a file (See General tab of the plugin).
If applicable, add screenshots to help explain your problem.

View File

@@ -0,0 +1,79 @@
name: 'Setup plugin build dependencies'
description: 'Builds the plugin build dependencies'
inputs:
target:
description: 'Build target for dependencies'
required: true
config:
description: 'Build configuration'
required: false
default: 'Release'
visualStudio:
description: 'Visual Studio version (Windows only)'
required: false
default: 'Visual Studio 16 2019'
workingDirectory:
description: 'Working directory for packaging'
required: false
default: ${{ github.workspace }}
runs:
using: 'composite'
steps:
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.24.x'
- name: Restore cached dependencies
id: restore-cache
uses: actions/cache@v3
with:
path: ${{ env.DEP_DIR }}
key: ${{ env.DEP_DIR }}-${{ runner.os }}-${{ inputs.target }}
- name: Run macOS Build
if: ${{ runner.os == 'macOS' && steps.restore-cache.outputs.cache-hit != 'true' }}
shell: zsh {0}
run: |
build_args=(
-c ${{ inputs.config }}
-t macos-${{ inputs.target }}
)
if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug)
${{ inputs.workingDirectory }}/.github/scripts/build-deps-macos.zsh -o ${{ env.DEP_DIR }} ${build_args}
- name: Run Linux Build
if: ${{ runner.os == 'Linux' && steps.restore-cache.outputs.cache-hit != 'true' }}
shell: bash
run: |
build_args=(
-c ${{ inputs.config }}
-t linux-${{ inputs.target }}
)
if [[ -n "${CI}" && -n "${RUNNER_DEBUG}" ]]; then
build_args+=(--debug)
fi
${{ inputs.workingDirectory }}/.github/scripts/build-deps-linux.sh -o ${{ env.DEP_DIR }} "${build_args[@]}"
- name: Run Windows Build
if: ${{ runner.os == 'Windows' && steps.restore-cache.outputs.cache-hit != 'true' }}
shell: pwsh
run: |
$BuildArgs = @{
Target = '${{ inputs.target }}'
Configuration = '${{ inputs.config }}'
CMakeGenerator = '${{ inputs.visualStudio }}'
}
if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) {
$BuildArgs += @{
Debug = $true
}
}
${{ inputs.workingDirectory }}/.github/scripts/Build-Deps-Windows.ps1 -OutDirName ${{ env.DEP_DIR }} @BuildArgs

View File

@@ -49,7 +49,7 @@ runs:
if [[ '${{ inputs.codesign }}' == 'true' ]] build_args+=(-s)
if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug)
${{ inputs.workingDirectory }}/.github/scripts/build-macos.zsh ${build_args}
${{ inputs.workingDirectory }}/.github/scripts/build-macos.zsh -d ${{ env.DEP_DIR }} ${build_args}
- name: Run Linux Build
if: ${{ runner.os == 'Linux' }}
@@ -68,7 +68,7 @@ runs:
build_args+=(-p)
fi
${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh "${build_args[@]}"
${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh -d ${{ env.DEP_DIR }} "${build_args[@]}"
- name: Run Windows Build
if: ${{ runner.os == 'Windows' }}
@@ -86,4 +86,4 @@ runs:
}
}
${{ inputs.workingDirectory }}/.github/scripts/Build-Windows.ps1 @BuildArgs
${{ inputs.workingDirectory }}/.github/scripts/Build-Windows.ps1 -ADVSSDepName ${{ env.DEP_DIR }} @BuildArgs

View File

@@ -7,3 +7,8 @@ package 'ninja-build', bin: 'ninja'
package 'pkg-config'
package 'clang'
package 'clang-format-13'
package 'libxtst-dev'
package 'libxss-dev'
package 'libopencv-dev'
package 'libtesseract-dev'
package 'libprocps-dev'

View File

@@ -1,3 +1,4 @@
package '7zip.7zip', path: '7-zip', bin: '7z'
package 'cmake', path: 'Cmake\bin', bin: 'cmake'
package 'innosetup', path: 'Inno Setup 6', bin: 'iscc'
package 'OpenSSL', path: 'OpenSSL', bin: 'openssl'

324
.github/scripts/.build-deps.zsh vendored Executable file
View File

@@ -0,0 +1,324 @@
#!/usr/bin/env zsh
builtin emulate -L zsh
setopt EXTENDED_GLOB
setopt PUSHD_SILENT
setopt ERR_EXIT
setopt ERR_RETURN
setopt NO_UNSET
setopt PIPE_FAIL
setopt NO_AUTO_PUSHD
setopt NO_PUSHD_IGNORE_DUPS
setopt FUNCTION_ARGZERO
## Enable for script debugging
# setopt WARN_CREATE_GLOBAL
# setopt WARN_NESTED_VAR
# setopt XTRACE
autoload -Uz is-at-least && if ! is-at-least 5.2; then
print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade Zsh to fix this issue."
exit 1
fi
_trap_error() {
print -u2 -PR '%F{1} ✖︎ script execution error%f'
print -PR -e "
Callstack:
${(j:\n :)funcfiletrace}
"
exit 2
}
build() {
if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[3]}
local target="${host_os}-${CPUTYPE}"
local project_root=${SCRIPT_HOME:A:h:h}
local buildspec_file="${project_root}/buildspec.json"
trap '_trap_error' ZERR
fpath=("${SCRIPT_HOME}/utils.zsh" ${fpath})
autoload -Uz log_info log_error log_output set_loglevel check_${host_os} setup_${host_os} setup_obs setup_ccache
if [[ ! -r ${buildspec_file} ]] {
log_error \
'No buildspec.json found. Please create a build specification for your project.' \
'A buildspec.json.template file is provided in the repository to get you started.'
return 2
}
typeset -g -a skips=()
local -i _verbosity=1
local -r _version='1.0.0'
local -r -a _valid_targets=(
macos-x86_64
macos-arm64
macos-universal
linux-x86_64
)
local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
if [[ ${host_os} == 'macos' ]] {
local -r -a _valid_generators=(Xcode Ninja 'Unix Makefiles')
local generator="${${CI:+Ninja}:-Xcode}"
} else {
local -r -a _valid_generators=(Ninja 'Unix Makefiles')
local generator='Ninja'
}
local -r _usage="
Usage: %B${functrace[1]%:*}%b <option> [<options>]
%BOptions%b:
%F{yellow} Build configuration options%f
-----------------------------------------------------------------------------
%B-t | --target%b Specify target - default: %B%F{green}${host_os}-${CPUTYPE}%f%b
%B-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
%B-o | --out%b Output directory - default: %B%F{green}RelWithDebInfo%f%b
%B--generator%b Specify build system to generate - default: %B%F{green}Ninja%f%b
Available generators:
- Ninja
- Unix Makefiles
- Xcode (macOS only)
%F{yellow} Output options%f
-----------------------------------------------------------------------------
%B-q | --quiet%b Quiet (error output only)
%B-v | --verbose%b Verbose (more detailed output)
%B--skip-[all|build|deps|unpack]%b Skip all|building OBS|checking for dependencies|unpacking dependencies
%B--debug%b Debug (very detailed and added output)
%F{yellow} General options%f
-----------------------------------------------------------------------------
%B-h | --help%b Print this usage help
%B-V | --version%b Print script version information"
local -a args
while (( # )) {
case ${1} {
-t|--target|-c|--config|--generator)
if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
log_error "Missing value for option %B${1}%b"
log_output ${_usage}
exit 2
}
;;
}
case ${1} {
--)
shift
args+=($@)
break
;;
-t|--target)
if (( ! ${_valid_targets[(Ie)${2}]} )) {
log_error "Invalid value %B${2}%b for option %B${1}%b"
log_output ${_usage}
exit 2
}
target=${2}
shift 2
;;
-c|--config)
if (( ! ${_valid_configs[(Ie)${2}]} )) {
log_error "Invalid value %B${2}%b for option %B${1}%b"
log_output ${_usage}
exit 2
}
BUILD_CONFIG=${2}
shift 2
;;
-o|--out)
OUT_DIR="${2}"
shift 2
;;
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
-v|--verbose) (( _verbosity += 1 )); shift ;;
-h|--help) log_output ${_usage}; exit 0 ;;
-V|--version) print -Pr "${_version}"; exit 0 ;;
--debug) _verbosity=3; shift ;;
--generator)
if (( ! ${_valid_generators[(Ie)${2}]} )) {
log_error "Invalid value %B${2}%b for option %B${1}%b"
log_output ${_usage}
exit 2
}
generator=${2}
shift 2
;;
--skip-*)
local _skip="${${(s:-:)1}[-1]}"
local _check=(all deps unpack build)
(( ${_check[(Ie)${_skip}]} )) || log_warning "Invalid skip mode %B${_skip}%b supplied"
typeset -g -a skips=(${skips} ${_skip})
shift
;;
*) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
}
}
set -- ${(@)args}
set_loglevel ${_verbosity}
check_${host_os}
setup_ccache
typeset -g QT_VERSION
typeset -g DEPLOYMENT_TARGET
typeset -g OBS_DEPS_VERSION
setup_${host_os}
local product_name
local product_version
local git_tag="$(git describe --tags)"
read -r product_name product_version <<< \
"$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
if [[ "${git_tag}" =~ '^([0-9]+\.){0,2}(\*|[0-9]+)$' ]] {
log_info "Using git tag as version identifier '${git_tag}'"
product_version="${git_tag}"
} else {
log_info "Using buildspec.json version identifier '${product_version}'"
}
if [[ -z "${OUT_DIR}" ]] {
OUT_DIR="advss-build-dependencies"
}
mkdir -p "${project_root}/../${OUT_DIR}"
local advss_dep_path="$(realpath ${project_root}/../${OUT_DIR})"
local _plugin_deps="${project_root:h}/obs-build-dependencies/plugin-deps-${OBS_DEPS_VERSION}-qt${QT_VERSION}-${target##*-}"
case ${host_os} {
macos)
local opencv_dir="${project_root}/deps/opencv"
local opencv_build_dir="${opencv_dir}/build_${target##*-}"
local -a opencv_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DBUILD_LIST=core,imgproc,objdetect
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
)
if [ "${target}" != "macos-x86_64" ]; then
opencv_cmake_args+=(-DWITH_IPP=OFF)
fi
pushd ${opencv_dir}
log_info "Configure OpenCV ..."
cmake -S . -B ${opencv_build_dir} -G ${generator} ${opencv_cmake_args}
log_info "Building OpenCV ..."
cmake --build ${opencv_build_dir} --config Release
log_info "Installing OpenCV..."
cmake --install ${opencv_build_dir} --prefix "${advss_dep_path}" --config Release || true
popd
local leptonica_dir="${project_root}/deps/leptonica"
local leptonica_build_dir="${leptonica_dir}/build_${target##*-}"
local -a leptonica_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DSW_BUILD=OFF
-DOPENJPEG_SUPPORT=OFF
-DLIBWEBP_SUPPORT=OFF
-DCMAKE_DISABLE_FIND_PACKAGE_GIF=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_JPEG=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_TIFF=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_PNG=TRUE
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
)
pushd ${leptonica_dir}
log_info "Configure Leptonica ..."
cmake -S . -B ${leptonica_build_dir} -G ${generator} ${leptonica_cmake_args}
log_info "Building Leptonica ..."
cmake --build ${leptonica_build_dir} --config Release
log_info "Installing Leptonica..."
# Workaround for "unknown file attribute: H" errors when running install
cmake --install ${leptonica_build_dir} --prefix "${advss_dep_path}" --config Release || :
popd
local tesseract_dir="${project_root}/deps/tesseract"
local tesseract_build_dir="${tesseract_dir}/build_${target##*-}"
local -a tesseract_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DSW_BUILD=OFF
-DBUILD_TRAINING_TOOLS=OFF
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
)
if [ "${target}" != "macos-x86_64" ]; then
tesseract_cmake_args+=(
-DCMAKE_SYSTEM_PROCESSOR=aarch64
-DHAVE_AVX=FALSE
-DHAVE_AVX2=FALSE
-DHAVE_AVX512F=FALSE
-DHAVE_FMA=FALSE
-DHAVE_SSE4_1=FALSE
-DHAVE_NEON=TRUE
)
sed -i'.original' 's/HAVE_NEON FALSE/HAVE_NEON TRUE/g' "${tesseract_dir}/CMakeLists.txt"
fi
pushd ${tesseract_dir}
log_info "Configure Tesseract ..."
cmake -S . -B ${tesseract_build_dir} -G ${generator} ${tesseract_cmake_args}
log_info "Building Tesseract ..."
cmake --build ${tesseract_build_dir} --config Release
log_info "Installing Tesseract..."
cmake --install ${tesseract_build_dir} --prefix "${advss_dep_path}" --config Release
popd
pushd ${advss_dep_path}
log_info "Prepare openssl ..."
rm -rf openssl
git clone git://git.openssl.org/openssl.git --branch openssl-3.1.2 --depth 1
mv openssl openssl_x86
cp -r openssl_x86 openssl_arm
log_info "Building openssl x86 ..."
export MACOSX_DEPLOYMENT_TARGET=10.9
cd openssl_x86
./Configure darwin64-x86_64-cc shared
make
log_info "Building openssl arm ..."
export MACOSX_DEPLOYMENT_TARGET=10.15
cd ../openssl_arm
./Configure enable-rc5 zlib darwin64-arm64-cc no-asm
make
log_info "Combine arm and x86 openssl binaries ..."
cd ..
mkdir openssl-combined
lipo -create openssl_x86/libcrypto.a openssl_arm/libcrypto.a -output openssl-combined/libcrypto.a
lipo -create openssl_x86/libssl.a openssl_arm/libssl.a -output openssl-combined/libssl.a
log_info "Clean up openssl dir..."
mv openssl_x86 openssl
rm -rf openssl_arm
;;
linux)
# Nothing to do for now
;;
}
}
build ${@}

View File

@@ -36,6 +36,7 @@ build() {
local target="${host_os}-${CPUTYPE}"
local project_root=${SCRIPT_HOME:A:h:h}
local buildspec_file="${project_root}/buildspec.json"
local dep_dir=""
trap '_trap_error' ZERR
@@ -77,6 +78,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
%B-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
%B-s | --codesign%b Enable codesigning (macOS only)
%B-p | --portable%b Enable portable mode (Linux only)
%B-d | --dep%b Dependency directory name - default: %B%F{green}advss-build-dependencies%f%b
%B--generator%b Specify build system to generate - default: %B%F{green}Ninja%f%b
Available generators:
- Ninja
@@ -130,6 +132,10 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
BUILD_CONFIG=${2}
shift 2
;;
-d|--dep)
dep_dir="${2}"
shift 2
;;
-s|--codesign) CODESIGN=1; shift ;;
-p|--portable) typeset -g PORTABLE=1; shift ;;
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
@@ -168,6 +174,15 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
typeset -g OBS_DEPS_VERSION
setup_${host_os}
local advss_deps_path
if [[ -z "${dep_dir}" ]] {
log_info "Building advss deps ..."
dep_dir="advss-build-dependencies"
${SCRIPT_HOME}/build-deps-${host_os}.zsh -c "${BUILD_CONFIG:-RelWithDebInfo}" -t "${target}" --generator "${generator}" -o "${dep_dir}" --skip-deps --skip-unpack
}
advss_deps_path=$(realpath ${project_root}/../${dep_dir})
log_info "Using advss deps at $advss_deps_path ..."
local product_name
local product_version
local git_tag="$(git describe --tags)"
@@ -187,96 +202,6 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
sed -i '' \
"s/project(\(.*\) VERSION \(.*\))/project(${product_name} VERSION ${product_version})/" \
"${project_root}/CMakeLists.txt"
local opencv_dir="${project_root}/deps/opencv"
local opencv_build_dir="${opencv_dir}/build_${target##*-}"
local -a opencv_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DBUILD_LIST=core,imgproc,objdetect
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
)
if [ "${target}" != "macos-x86_64" ]; then
opencv_cmake_args+=(-DWITH_IPP=OFF)
fi
pushd ${opencv_dir}
log_info "Configure OpenCV ..."
cmake -S . -B ${opencv_build_dir} -G ${generator} ${opencv_cmake_args}
log_info "Building OpenCV ..."
cmake --build ${opencv_build_dir} --config Release
log_info "Installing OpenCV..."
cmake --install ${opencv_build_dir} --config Release || true
popd
local leptonica_dir="${project_root}/deps/leptonica"
local leptonica_build_dir="${leptonica_dir}/build_${target##*-}"
local -a leptonica_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DSW_BUILD=OFF
-DOPENJPEG_SUPPORT=OFF
-DLIBWEBP_SUPPORT=OFF
-DCMAKE_DISABLE_FIND_PACKAGE_GIF=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_JPEG=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_TIFF=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_PNG=TRUE
)
pushd ${leptonica_dir}
log_info "Configure Leptonica ..."
cmake -S . -B ${leptonica_build_dir} -G ${generator} ${leptonica_cmake_args}
log_info "Building Leptonica ..."
cmake --build ${leptonica_build_dir} --config Release
log_info "Installing Leptonica..."
# Workaround for "unknown file attribute: H" errors when running install
cmake --install ${leptonica_build_dir} --config Release || :
popd
local tesseract_dir="${project_root}/deps/tesseract"
local tesseract_build_dir="${tesseract_dir}/build_${target##*-}"
local -a tesseract_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DSW_BUILD=OFF
-DBUILD_TRAINING_TOOLS=OFF
)
if [ "${target}" != "macos-x86_64" ]; then
tesseract_cmake_args+=(
-DCMAKE_SYSTEM_PROCESSOR=aarch64
-DHAVE_AVX=FALSE
-DHAVE_AVX2=FALSE
-DHAVE_AVX512F=FALSE
-DHAVE_FMA=FALSE
-DHAVE_SSE4_1=FALSE
-DHAVE_NEON=TRUE
)
sed -i'.original' 's/HAVE_NEON FALSE/HAVE_NEON TRUE/g' "${tesseract_dir}/CMakeLists.txt"
fi
pushd ${tesseract_dir}
log_info "Configure Tesseract ..."
cmake -S . -B ${tesseract_build_dir} -G ${generator} ${tesseract_cmake_args}
log_info "Building Tesseract ..."
#cmake --build ${tesseract_build_dir} --config Release --target libtesseract
cmake --build ${tesseract_build_dir} --config Release
log_info "Installing Tesseract..."
#cmake --install ${tesseract_build_dir} --config Release --component libtesseract
cmake --install ${tesseract_build_dir} --config Release
popd
;;
linux)
sed -i'' \
@@ -295,7 +220,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
local -a cmake_args=(
-DCMAKE_BUILD_TYPE=${BUILD_CONFIG:-RelWithDebInfo}
-DQT_VERSION=${QT_VERSION}
-DCMAKE_PREFIX_PATH="${_plugin_deps}"
-DCMAKE_PREFIX_PATH="${_plugin_deps};${advss_deps_path}"
)
if (( _loglevel == 0 )) cmake_args+=(-Wno_deprecated -Wno-dev --log-level=ERROR)
@@ -312,12 +237,17 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
num_procs=$(( $(sysctl -n hw.ncpu) + 1 ))
local openssl_lib_dir="${advss_deps_path}/openssl-combined/"
local openssl_include_dir="${advss_deps_path}/openssl/include"
cmake_args+=(
-DCMAKE_FRAMEWORK_PATH="${_plugin_deps}/Frameworks"
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DOBS_CODESIGN_LINKER=ON
-DOBS_BUNDLE_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}"
-DOPENSSL_INCLUDE_DIR="${openssl_include_dir}"
-DOPENSSL_LIBRARIES="${openssl_lib_dir}/libcrypto.a;${openssl_lib_dir}/libssl.a"
)
;;

172
.github/scripts/Build-Deps-Windows.ps1 vendored Normal file
View File

@@ -0,0 +1,172 @@
[CmdletBinding()]
param(
[ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]
[string] $Configuration = 'RelWithDebInfo',
[ValidateSet('x86', 'x64')]
[string] $Target,
[ValidateSet('Visual Studio 17 2022', 'Visual Studio 16 2019')]
[string] $CMakeGenerator,
[string] $OutDirName,
[switch] $SkipDeps,
[switch] $SkipUnpack
)
$ErrorActionPreference = 'Stop'
if ( $DebugPreference -eq 'Continue' ) {
$VerbosePreference = 'Continue'
$InformationPreference = 'Continue'
}
if ( $PSVersionTable.PSVersion -lt '7.0.0' ) {
Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'
exit 2
}
function Build {
trap {
Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue'
Write-Error $_
exit 2
}
$ScriptHome = $PSScriptRoot
$ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.."
$BuildSpecFile = "${ProjectRoot}/buildspec.json"
$UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse
foreach ($Utility in $UtilityFunctions) {
Write-Debug "Loading $($Utility.FullName)"
. $Utility.FullName
}
$BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json
$ProductName = $BuildSpec.name
$ProductVersion = $BuildSpec.version
$script:VisualStudioVersion = ''
$script:PlatformSDK = '10.0.18363.657'
Setup-Host
if ( $CmakeGenerator -eq '' ) {
$CmakeGenerator = $script:VisualStudioVersion
}
$DepsPath = "plugin-deps-${script:DepsVersion}-*-${script:Target}"
$OBSDepPath = "$(Resolve-Path -Path ${ProjectRoot}/../obs-build-dependencies/${DepsPath})"
if ( $OutDirName -eq '' ) {
$OutDirName = "advss-build-dependencies"
}
New-Item -ItemType Directory -Force -Path ${ProjectRoot}/../${OutDirName}
$ADVSSDepPath = "$(Resolve-Path -Path ${ProjectRoot}/../${OutDirName})"
$OpenCVPath = "${ProjectRoot}/deps/opencv"
$OpenCVBuildPath = "${OpenCVPath}/build"
Push-Location -Stack BuildOpenCVTemp
Ensure-Location $ProjectRoot
$OpenCVCmakeArgs = @(
'-G', $CmakeGenerator
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_PREFIX_PATH:PATH=${OBSDepPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${ADVSSDepPath}"
"-DBUILD_LIST=core,imgproc,objdetect"
)
Log-Information "Configuring OpenCV..."
Invoke-External cmake -S ${OpenCVPath} -B ${OpenCVBuildPath} @OpenCVCmakeArgs
$OpenCVCmakeArgs = @(
'--config', "Release"
)
if ( $VerbosePreference -eq 'Continue' ) {
$OpenCVCmakeArgs += ('--verbose')
}
Log-Information "Building OpenCV..."
Invoke-External cmake --build "${OpenCVBuildPath}" @OpenCVCmakeArgs
Log-Information "Install OpenCV}..."
Invoke-External cmake --install "${OpenCVBuildPath}" --prefix "${ADVSSDepPath}" @OpenCVCmakeArgs
$LeptonicaPath = "${ProjectRoot}/deps/leptonica"
$LeptonicaBuildPath = "${LeptonicaPath}/build"
Push-Location -Stack BuildLeptonicaTemp
Ensure-Location $ProjectRoot
$LeptonicaCmakeArgs = @(
'-G', $CmakeGenerator
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=${Configuration}"
"-DCMAKE_PREFIX_PATH:PATH=${OBSDepPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${ADVSSDepPath}"
"-DSW_BUILD=OFF"
"-DLIBWEBP_SUPPORT=OFF"
)
Log-Information "Configuring leptonica..."
Invoke-External cmake -S ${LeptonicaPath} -B ${LeptonicaBuildPath} @LeptonicaCmakeArgs
$LeptonicaCmakeArgs = @(
'--config', "${Configuration}"
)
if ( $VerbosePreference -eq 'Continue' ) {
$LeptonicaCmakeArgs += ('--verbose')
}
Log-Information "Building leptonica..."
Invoke-External cmake --build "${LeptonicaBuildPath}" @LeptonicaCmakeArgs
Log-Information "Install leptonica..."
Invoke-External cmake --install "${LeptonicaBuildPath}" --prefix "${ADVSSDepPath}" @LeptonicaCmakeArgs
Push-Location -Stack BuildTesseractTemp
Ensure-Location $ProjectRoot
$TesseractPath = "${ProjectRoot}/deps/tesseract"
$TesseractBuildPath = "${TesseractPath}/build"
# Explicitly disable PkgConfig and tiff as it will lead build errors
$TesseractCmakeArgs = @(
'-G', $CmakeGenerator
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=${Configuration}"
"-DCMAKE_PREFIX_PATH:PATH=${OBSDepPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${ADVSSDepPath}"
"-DSW_BUILD=OFF"
"-DDISABLE_CURL=ON"
"-DBUILD_TRAINING_TOOLS=OFF"
"-DCMAKE_DISABLE_FIND_PACKAGE_TIFF=TRUE"
"-DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=TRUE"
)
Log-Information "Configuring tesseract..."
Invoke-External cmake -S ${TesseractPath} -B ${TesseractBuildPath} @TesseractCmakeArgs
$TesseractCmakeArgs = @(
'--config', "${Configuration}"
)
if ( $VerbosePreference -eq 'Continue' ) {
$TesseractCmakeArgs += ('--verbose')
}
Log-Information "Building tesseract..."
Invoke-External cmake --build "${TesseractBuildPath}" @TesseractCmakeArgs
Log-Information "Install tesseract..."
Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${ADVSSDepPath}" @TesseractCmakeArgs
}
Build

View File

@@ -6,6 +6,7 @@ param(
[string] $Target,
[ValidateSet('Visual Studio 17 2022', 'Visual Studio 16 2019')]
[string] $CMakeGenerator,
[string] $ADVSSDepName,
[switch] $SkipAll,
[switch] $SkipBuild,
[switch] $SkipDeps,
@@ -60,116 +61,17 @@ function Build {
$DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}"
$DepInstallPath = "$(Resolve-Path -Path ${ProjectRoot}/../obs-build-dependencies/${DepsPath})"
$OpenCVPath = "${ProjectRoot}/deps/opencv"
$OpenCVBuildPath = "${OpenCVPath}/build"
Push-Location -Stack BuildOpenCVTemp
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
Ensure-Location $ProjectRoot
$OpenCVCmakeArgs = @(
'-G', $CmakeGenerator
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
"-DBUILD_LIST=core,imgproc,objdetect"
)
Log-Information "Configuring OpenCV..."
Invoke-External cmake -S ${OpenCVPath} -B ${OpenCVBuildPath} @OpenCVCmakeArgs
$OpenCVCmakeArgs = @(
'--config', "Release"
)
if ( $VerbosePreference -eq 'Continue' ) {
$OpenCVCmakeArgs += ('--verbose')
}
Log-Information "Building OpenCV..."
Invoke-External cmake --build "${OpenCVBuildPath}" @OpenCVCmakeArgs
if ( $ADVSSDepName -eq '' ) {
Log-Information "Building advss deps ..."
$ADVSSDepName = "advss-build-dependencies"
invoke-expression -Command "$PSScriptRoot/Build-Deps-Windows.ps1 -Configuration $Configuration -Target $Target -CMakeGenerator `"$CMakeGenerator`" -OutDirName $ADVSSDepName -SkipDeps -SkipUnpack"
}
Log-Information "Install OpenCV}..."
Invoke-External cmake --install "${OpenCVBuildPath}" --prefix "${DepInstallPath}" @OpenCVCmakeArgs
$LeptonicaPath = "${ProjectRoot}/deps/leptonica"
$LeptonicaBuildPath = "${LeptonicaPath}/build"
Push-Location -Stack BuildLeptonicaTemp
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
Ensure-Location $ProjectRoot
$LeptonicaCmakeArgs = @(
'-G', $CmakeGenerator
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${DepInstallPath}"
"-DSW_BUILD=OFF"
"-DLIBWEBP_SUPPORT=OFF"
)
Log-Information "Configuring leptonica..."
Invoke-External cmake -S ${LeptonicaPath} -B ${LeptonicaBuildPath} @LeptonicaCmakeArgs
$LeptonicaCmakeArgs = @(
'--config', "Release"
)
if ( $VerbosePreference -eq 'Continue' ) {
$LeptonicaCmakeArgs += ('--verbose')
}
Log-Information "Building leptonica..."
Invoke-External cmake --build "${LeptonicaBuildPath}" @LeptonicaCmakeArgs
}
Log-Information "Install leptonica..."
Invoke-External cmake --install "${LeptonicaBuildPath}" --prefix "${DepInstallPath}" @LeptonicaCmakeArgs
Push-Location -Stack BuildTesseractTemp
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
Ensure-Location $ProjectRoot
$TesseractPath = "${ProjectRoot}/deps/tesseract"
$TesseractBuildPath = "${TesseractPath}/build"
# Explicitly disable PkgConfig and tiff as it will lead build errors
$TesseractCmakeArgs = @(
'-G', $CmakeGenerator
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${DepInstallPath}"
"-DSW_BUILD=OFF"
"-DDISABLE_CURL=ON"
"-DBUILD_TRAINING_TOOLS=OFF"
"-DCMAKE_DISABLE_FIND_PACKAGE_TIFF=TRUE"
"-DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=TRUE"
)
Log-Information "Configuring tesseract..."
Invoke-External cmake -S ${TesseractPath} -B ${TesseractBuildPath} @TesseractCmakeArgs
$TesseractCmakeArgs = @(
'--config', "Release"
)
if ( $VerbosePreference -eq 'Continue' ) {
$TesseractCmakeArgs += ('--verbose')
}
Log-Information "Building tesseract..."
Invoke-External cmake --build "${TesseractBuildPath}" @TesseractCmakeArgs
}
Log-Information "Install tesseract..."
Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${DepInstallPath}" @TesseractCmakeArgs
$ADVSSDepPath = "$(Resolve-Path -Path ${ProjectRoot}/../${script:ADVSSDepName})"
Log-Information "Using advss deps at $ADVSSDepPath ..."
(Get-Content -Path ${ProjectRoot}/CMakeLists.txt -Raw) `
-replace "project\((.*) VERSION (.*)\)", "project(${ProductName} VERSION ${ProductVersion})" `
| Out-File -Path ${ProjectRoot}/CMakeLists.txt
| Out-File -Path ${ProjectRoot}/CMakeLists.txt -NoNewline
Setup-Obs
@@ -182,7 +84,7 @@ function Build {
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
"-DCMAKE_BUILD_TYPE=${Configuration}"
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath};${ADVSSDepPath}"
"-DQT_VERSION=${script:QtVersion}"
)

13
.github/scripts/build-deps-linux.sh vendored Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
if ! type zsh > /dev/null 2>&1; then
echo ' => Installing script dependency Zsh.'
sudo apt-get -y update
sudo apt-get -y install zsh
fi
SCRIPT=$(readlink -f "${0}")
SCRIPT_DIR=$(dirname "${SCRIPT}")
zsh ${SCRIPT_DIR}/build-deps-linux.zsh "${@}"

1
.github/scripts/build-deps-linux.zsh vendored Symbolic link
View File

@@ -0,0 +1 @@
.build-deps.zsh

1
.github/scripts/build-deps-macos.zsh vendored Symbolic link
View File

@@ -0,0 +1 @@
.build-deps.zsh

View File

@@ -24,7 +24,7 @@ if (( ! ${+commands[packagesbuild]} )) {
pushd
mkcd ${project_root:h}/obs-build-dependencies
local packages_url='http://s.sudre.free.fr/Software/files/Packages.dmg'
local packages_url='https://web.archive.org/web/20230727054218/http://s.sudre.free.fr/Software/files/Packages.dmg'
local packages_hash='6afdd25386295974dad8f078b8f1e41cabebd08e72d970bf92f707c7e48b16c9'
if [[ ! -f Packages.dmg ]] {

View File

@@ -34,13 +34,6 @@ if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) {
log_info 'Installing obs plugin dependencies...'
sudo apt-get install -y \
libxtst-dev \
libxss-dev \
libopencv-dev \
libtesseract-dev \
libprocps-dev
local -a _qt_packages=()
if (( QT_VERSION == 5 )) {

View File

@@ -17,6 +17,7 @@ on:
env:
PLUGIN_NAME: SceneSwitcher
LIB_NAME: advanced-scene-switcher
DEP_DIR: advss-build-dependencies-1
jobs:
clang_check:
@@ -115,9 +116,6 @@ jobs:
with:
path: |
${{ github.workspace }}/.ccache
${{ github.workspace }}/plugin/deps/opencv/build_*
${{ github.workspace }}/plugin/deps/leptonica/build_*
${{ github.workspace }}/plugin/deps/tesseract/build*
key: macos-${{ matrix.arch }}-ccache-plugin-${{ steps.setup.outputs.ccacheDate }}
restore-keys: |
macos-${{ matrix.arch }}-ccache-plugin-
@@ -146,6 +144,13 @@ jobs:
print "CODESIGN_IDENT=${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}" >> $GITHUB_ENV
print "CODESIGN_IDENT_INSTALLER=${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}" >> $GITHUB_ENV
- name: Build Dependencies
uses: ./plugin/.github/actions/build-dependencies
with:
workingDirectory: ${{ github.workspace }}/plugin
target: ${{ matrix.arch }}
config: RelWithDebInfo
- name: Build Plugin
uses: ./plugin/.github/actions/build-plugin
with:
@@ -248,6 +253,13 @@ jobs:
echo 'found=false' >> $GITHUB_OUTPUT
fi
- name: Build Dependencies
uses: ./plugin/.github/actions/build-dependencies
with:
workingDirectory: ${{ github.workspace }}/plugin
target: ${{ matrix.arch }}
config: RelWithDebInfo
- name: Build Plugin
uses: ./plugin/.github/actions/build-plugin
with:
@@ -329,9 +341,6 @@ jobs:
with:
path: |
${{ github.workspace }}/.ccache
${{ github.workspace }}/plugin/deps/opencv/build_*
${{ github.workspace }}/plugin/deps/leptonica/build_*
${{ github.workspace }}/plugin/deps/tesseract/build*
key: windows-${{ matrix.arch }}-ccache-plugin-${{ steps.setup.outputs.ccacheDate }}
restore-keys: |
windows-${{ matrix.arch }}-ccache-plugin-
@@ -357,6 +366,14 @@ jobs:
"found=$(([string]${LabelFound}).ToLower())" >> $env:GITHUB_OUTPUT
- name: Build Dependencies
uses: ./plugin/.github/actions/build-dependencies
with:
workingDirectory: ${{ github.workspace }}/plugin
target: ${{ matrix.arch }}
config: RelWithDebInfo
visualStudio: 'Visual Studio 17 2022'
- name: Build Plugin
uses: ./plugin/.github/actions/build-plugin
with:

1
.gitignore vendored
View File

@@ -91,6 +91,7 @@ Thumbs.db
.settings
.idea
.metadata
.vscode
*.iml
*.ipr
*.sublime*

6
.gitmodules vendored
View File

@@ -19,3 +19,9 @@
[submodule "deps/libremidi"]
path = deps/libremidi
url = https://github.com/jcelerier/libremidi.git
[submodule "deps/cpp-httplib"]
path = deps/cpp-httplib
url = https://github.com/yhirose/cpp-httplib.git
[submodule "deps/json"]
path = deps/json
url = https://github.com/nlohmann/json.git

View File

@@ -93,8 +93,12 @@ target_sources(
${LIB_NAME}
PRIVATE src/macro-core/macro-action-audio.cpp
src/macro-core/macro-action-audio.hpp
src/macro-core/macro-action-clipboard.cpp
src/macro-core/macro-action-clipboard.hpp
src/macro-core/macro-action-edit.cpp
src/macro-core/macro-action-edit.hpp
src/macro-core/macro-action-factory.cpp
src/macro-core/macro-action-factory.hpp
src/macro-core/macro-action-file.cpp
src/macro-core/macro-action-file.hpp
src/macro-core/macro-action-filter.cpp
@@ -125,6 +129,8 @@ target_sources(
src/macro-core/macro-action-run.hpp
src/macro-core/macro-action-scene-collection.cpp
src/macro-core/macro-action-scene-collection.hpp
src/macro-core/macro-action-scene-lock.cpp
src/macro-core/macro-action-scene-lock.hpp
src/macro-core/macro-action-scene-order.cpp
src/macro-core/macro-action-scene-order.hpp
src/macro-core/macro-action-scene-switch.cpp
@@ -169,6 +175,8 @@ target_sources(
src/macro-core/macro-condition-display.hpp
src/macro-core/macro-condition-edit.cpp
src/macro-core/macro-condition-edit.hpp
src/macro-core/macro-condition-factory.cpp
src/macro-core/macro-condition-factory.hpp
src/macro-core/macro-condition-file.cpp
src/macro-core/macro-condition-file.hpp
src/macro-core/macro-condition-filter.cpp
@@ -201,6 +209,8 @@ target_sources(
src/macro-core/macro-condition-scene-transform.hpp
src/macro-core/macro-condition-scene-visibility.cpp
src/macro-core/macro-condition-scene-visibility.hpp
src/macro-core/macro-condition-slideshow.cpp
src/macro-core/macro-condition-slideshow.hpp
src/macro-core/macro-condition-scene.cpp
src/macro-core/macro-condition-scene.hpp
src/macro-core/macro-condition-source.cpp
@@ -209,6 +219,8 @@ target_sources(
src/macro-core/macro-condition-streaming.hpp
src/macro-core/macro-condition-studio-mode.cpp
src/macro-core/macro-condition-studio-mode.hpp
src/macro-core/macro-condition-tempvar.cpp
src/macro-core/macro-condition-tempvar.hpp
src/macro-core/macro-condition-timer.cpp
src/macro-core/macro-condition-timer.hpp
src/macro-core/macro-condition-transition.cpp
@@ -225,6 +237,8 @@ target_sources(
src/macro-core/macro-condition.hpp
src/macro-core/macro-dock.cpp
src/macro-core/macro-dock.hpp
src/macro-core/macro-helpers.cpp
src/macro-core/macro-helpers.hpp
src/macro-core/macro-properties.cpp
src/macro-core/macro-properties.hpp
src/macro-core/macro-ref.cpp
@@ -248,28 +262,43 @@ target_sources(
src/utils/connection-manager.hpp
src/utils/curl-helper.cpp
src/utils/curl-helper.hpp
src/utils/duration.cpp
src/utils/duration.hpp
src/utils/duration-control.cpp
src/utils/duration-control.hpp
src/utils/export-symbol-helper.hpp
src/utils/item-selection-helpers.cpp
src/utils/item-selection-helpers.hpp
src/utils/log-helper.hpp
src/utils/file-selection.cpp
src/utils/file-selection.hpp
src/utils/filter-combo-box.cpp
src/utils/filter-combo-box.hpp
src/utils/filter-selection.cpp
src/utils/filter-selection.hpp
src/utils/macro-export-import-dialog.cpp
src/utils/macro-export-import-dialog.hpp
src/utils/macro-list.cpp
src/utils/macro-list.hpp
src/utils/macro-run-button.cpp
src/utils/macro-run-button.hpp
src/utils/macro-segment-selection.cpp
src/utils/macro-segment-selection.hpp
src/utils/math-helpers.cpp
src/utils/math-helpers.hpp
src/utils/mouse-wheel-guard.cpp
src/utils/mouse-wheel-guard.hpp
src/utils/name-dialog.cpp
src/utils/name-dialog.hpp
src/utils/non-modal-dialog.cpp
src/utils/non-modal-dialog.hpp
src/utils/obs-dock.hpp
src/utils/obs-module-helper.cpp
src/utils/obs-module-helper.hpp
src/utils/osc-helpers.cpp
src/utils/osc-helpers.hpp
src/utils/plugin-state-helpers.cpp
src/utils/plugin-state-helpers.hpp
src/utils/priority-helper.cpp
src/utils/priority-helper.hpp
src/utils/process-config.cpp
@@ -292,12 +321,18 @@ target_sources(
src/utils/slider-spinbox.hpp
src/utils/source-selection.cpp
src/utils/source-selection.hpp
src/utils/source-setting.cpp
src/utils/source-setting.hpp
src/utils/string-list.cpp
src/utils/string-list.hpp
src/utils/striped-frame.cpp
src/utils/striped-frame.hpp
src/utils/sync-helper.cpp
src/utils/sync-helper.hpp
src/utils/switch-button.cpp
src/utils/switch-button.hpp
src/utils/sync-helpers.cpp
src/utils/sync-helpers.hpp
src/utils/temp-variable.cpp
src/utils/temp-variable.hpp
src/utils/transition-selection.cpp
src/utils/transition-selection.hpp
src/utils/utility.cpp
@@ -361,6 +396,14 @@ target_include_directories(
"${CMAKE_CURRENT_SOURCE_DIR}/deps/obs-websocket/lib"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/exprtk")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deps/json/CMakeLists.txt")
add_subdirectory(deps/json)
else()
find_package(nlohmann_json REQUIRED)
endif()
target_link_libraries(${LIB_NAME} PUBLIC nlohmann_json::nlohmann_json)
target_compile_definitions(${LIB_NAME} PRIVATE ADVSS_EXPORT_SYMBOLS=1)
# --- End of section ---
# --- Windows-specific build settings and tasks ---
@@ -427,27 +470,24 @@ else()
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
find_path(PROCPS2_INCLUDE_DIR NAMES libproc2/pids.h)
find_library(PROCPS_LIBRARY NAMES procps)
find_library(PROCPS2_LIBRARY NAMES proc2)
if(PROCPS_INCLUDE_DIR AND PROCPS_LIBRARY)
if(PROCPS_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using procps")
set(PROC_INCLUDE_DIR "${PROCPS_INCLUDE_DIR}")
set(PROC_LIBRARY "${PROCPS_LIBRARY}")
target_compile_definitions(${LIB_NAME} PRIVATE USE_PROCPS)
elseif(PROCPS2_INCLUDE_DIR AND PROCPS2_LIBRARY)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1)
endif()
if(PROCPS2_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using libproc2")
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
set(PROC_LIBRARY "${PROCPS2_LIBRARY}")
else()
message(
FATAL_ERROR
"found neither procps nor libproc2!
please either set ...
PROCPS_INCLUDE_DIR and PROCPS_LIBRARY
... or ...
PROCPS2_INCLUDE_DIR and PROCPS2_LIBRARY")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1)
endif()
if(NOT DEFINED PROCESS_CONDITION_SUPPORTED)
message(
WARNING
"found neither procps nor libproc2! Process condition will not be functional!"
)
endif()
target_link_libraries(${LIB_NAME} PRIVATE "${PROC_LIBRARY}")
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
find_package(CURL)

View File

@@ -146,23 +146,19 @@ AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Während des
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Fenster"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existiert und ..."
AdvSceneSwitcher.condition.window.entry.line2="... ist {{fullscreen}} Vollbild {{maximized}} Maximiert {{focused}} Fokusiert {{windowFocusChanged}} Vordergrundfenster geändert"
AdvSceneSwitcher.condition.window.entry.line3="Aktuelles Vordergrundfenster: {{focusWindow}}"
AdvSceneSwitcher.condition.file="Datei"
AdvSceneSwitcher.condition.file.type.match="entspricht"
AdvSceneSwitcher.condition.file.type.contentChange="Inhalt geändert"
AdvSceneSwitcher.condition.file.type.dateChange="Änderungsdatum geändert"
AdvSceneSwitcher.condition.file.remote="Entfernte Datei"
AdvSceneSwitcher.condition.file.local="Lokale Datei"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medien"
AdvSceneSwitcher.condition.media.source="Quelle"
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
AdvSceneSwitcher.condition.media.matchOnChange="Nur bei Änderung übereinstimmen (Hinweis: Diese Option wird in einer zukünftigen Version entfernt werden - bitte stattdessen Dauer-Modifikatoren verwenden)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
@@ -224,9 +220,8 @@ AdvSceneSwitcher.condition.record="Aufnahme"
AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
AdvSceneSwitcher.condition.record.state.stop="Aufnahme gestoppt"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Prozess"
AdvSceneSwitcher.condition.process.entry="{{processes}} läuft {{focused}} und ist fokusiert"
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}läuft{{focused}}und ist fokusiert"
AdvSceneSwitcher.condition.process.entry.focus="Aktueller Vordergrundprozess: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="Leerlauf"
AdvSceneSwitcher.condition.idle.entry="Keine Tastatur- oder Mauseingaben für {{duration}}"
@@ -328,7 +323,9 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Nächster Treffer bei: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Auf{{scenes}}{{sources}}passt zur Transformation"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Auf{{scenes}}{{sources}}{{types}}"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="entspricht Transformation"
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="hat sich geändert"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Übergang"
@@ -392,12 +389,11 @@ AdvSceneSwitcher.condition.variable.type.valueChanged="Wert verändert"
AdvSceneSwitcher.condition.variable.type.equalsVariable="entspricht Variable"
AdvSceneSwitcher.condition.variable.type.lessThanVariable="ist kleiner als die Variable"
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="ist größer als die Variable"
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Szene wechseln"
AdvSceneSwitcher.action.scene.entry="Wechsle zu Szene{{scenes}}mittels{{transitions}}mit einer Dauer von{{duration}}Sekunden"
AdvSceneSwitcher.action.scene.entry.noDuration="Wechsle zu Szene{{scenes}}mittels{{transitions}}"
AdvSceneSwitcher.action.scene="Szene wechseln"
AdvSceneSwitcher.action.scene.entry="Wechsle{{sceneTypes}}Szene zu{{scenes}}mittels{{transitions}}mit einer Dauer von{{duration}}Sekunden"
AdvSceneSwitcher.action.scene.entry.noDuration="Wechsle{{sceneTypes}}Szene zu{{scenes}}mittels{{transitions}}"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Warten, bis der Übergang zur Zielszene abgeschlossen ist"
AdvSceneSwitcher.action.wait="Warten"
AdvSceneSwitcher.action.wait.type.fixed="fixe"
@@ -438,20 +434,15 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Stream"
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
AdvSceneSwitcher.action.streaming.type.start="Stream starten"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Ausführen"
AdvSceneSwitcher.action.run.arguments="Argumente:"
AdvSceneSwitcher.action.run.addArgument="Argument hinzufügen"
AdvSceneSwitcher.action.run.addArgumentDescription="Neues Argument hinzufügen:"
AdvSceneSwitcher.action.run.entry="Ausführen {{filePath}}"
AdvSceneSwitcher.action.run.entry.workingDirectory="Arbeitsverzeichnis:{{workingDirectory}}"
AdvSceneSwitcher.action.sceneVisibility="Sichtbarkeit von Szenenelementen"
AdvSceneSwitcher.action.sceneVisibility.type.show="Anzeigen"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Verstecken"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten"
AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig"
AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
@@ -466,7 +457,7 @@ AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quellenei
AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste"
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren"
AdvSceneSwitcher.action.source.noSettingsButtons="Keine Buttons gefunden!"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden"
AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen"
AdvSceneSwitcher.action.media="Medien"
@@ -476,15 +467,14 @@ AdvSceneSwitcher.action.media.type.stop="Stoppen"
AdvSceneSwitcher.action.media.type.restart="Neu starten"
AdvSceneSwitcher.action.media.type.next="Nächste"
AdvSceneSwitcher.action.media.type.previous="Vorherige"
AdvSceneSwitcher.action.media.type.seek="Suche nach"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.media.type.seek.duration="Suche nach"
AdvSceneSwitcher.action.macro="Makro"
AdvSceneSwitcher.action.macro.type.pause="Pausieren"
AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin-Status"
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"
@@ -506,7 +496,6 @@ AdvSceneSwitcher.action.hotkey.leftMeta="Linke Meta-Taste"
AdvSceneSwitcher.action.hotkey.rightMeta="Rechte Meta-Taste"
AdvSceneSwitcher.action.hotkey.onlyOBS="Tastendruck nur an OBS senden"
AdvSceneSwitcher.action.hotkey.disabled="Globale Tastendrücke können nicht simuliert werden - die Funktionalität beschränkt sich auf das Senden von Tastendrücken an OBS!"
AdvSceneSwitcher.action.hotkey.entry="Drücke {{keys}} für {{duration}}"
AdvSceneSwitcher.action.sceneOrder="Reihenfolge der Szenenelemente"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Nach oben verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Nach unten verschieben"
@@ -516,7 +505,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}transformieren{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="Datei"
AdvSceneSwitcher.action.file.type.write="Schreiben"
AdvSceneSwitcher.action.file.type.append="Anhängen"
@@ -567,8 +556,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="Weiter mit ausgewähltem Element"
AdvSceneSwitcher.action.websocket="Websocket"
AdvSceneSwitcher.action.websocket.type.request="Anfrage"
AdvSceneSwitcher.action.websocket.type.event="Ereignis"
AdvSceneSwitcher.action.websocket.entry.request="Szenenwechsler {{type}} via {{connection}} senden"
AdvSceneSwitcher.action.websocket.entry.event="Szenenwechsler {{type}} an verbundene Clients senden"
AdvSceneSwitcher.action.http="HTTP"
AdvSceneSwitcher.action.http.type.get="GET"
AdvSceneSwitcher.action.http.type.post="POST"
@@ -586,7 +573,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="Ungültige Auswahl!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Aktionen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}"
; Transition Tab
@@ -646,7 +632,7 @@ AdvSceneSwitcher.mediaTab.states.none="Keine Auswahl"
AdvSceneSwitcher.mediaTab.states.playing="Abspielen"
AdvSceneSwitcher.mediaTab.states.opening="Öffnen"
AdvSceneSwitcher.mediaTab.states.buffering="Buffern"
AdvSceneSwitcher.mediaTab.states.Paused="Pausiert"
AdvSceneSwitcher.mediaTab.states.paused="Pausiert"
AdvSceneSwitcher.mediaTab.states.stopped="Gestoppt"
AdvSceneSwitcher.mediaTab.states.ended="Beendet"
AdvSceneSwitcher.mediaTab.states.error="Fehler"
@@ -724,7 +710,7 @@ AdvSceneSwitcher.sceneSequenceTab.loadFail="Laden der Einstellungen ist fehlgesc
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Laden der Einstellungen war erfolgreich!"
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} mit {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} wechsle zu {{scenes}} mit {{transitions}}"

File diff suppressed because it is too large Load Diff

View File

@@ -41,17 +41,17 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportar los a
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Archivos de texto (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="La importacion la configuracion de escenas fallo"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configuración de escenas importada correctamente"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenido del archivo"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Secuencia de escenas"
AdvSceneSwitcher.generalTab.priority.idleDetection="Detección de inactivos"
AdvSceneSwitcher.generalTab.priority.executable="Ejecutable"
AdvSceneSwitcher.generalTab.priority.screenRegion="Región de pantalla"
AdvSceneSwitcher.generalTab.priority.windowTitle="Título de la ventana"
AdvSceneSwitcher.generalTab.priority.media="Medios"
AdvSceneSwitcher.generalTab.priority.time="Tiempo"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configuración de escenas importada correctamente"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenido del archivo"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Secuencia de escenas"
AdvSceneSwitcher.generalTab.priority.idleDetection="Detección de inactivos"
AdvSceneSwitcher.generalTab.priority.executable="Ejecutable"
AdvSceneSwitcher.generalTab.priority.screenRegion="Región de pantalla"
AdvSceneSwitcher.generalTab.priority.windowTitle="Título de la ventana"
AdvSceneSwitcher.generalTab.priority.media="Medios"
AdvSceneSwitcher.generalTab.priority.time="Tiempo"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.generalTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.generalTab.transitionOverride="Establecer anulaciones de transición"
@@ -61,38 +61,38 @@ AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Al menos una opci
; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.priorityWarning="Nota: Se recomienda configurar las macros para que sean la funcionalidad de máxima prioridad.\nEsta configuración se puede cambiar en la ficha General."
AdvSceneSwitcher.macroTab.help="Las macros le permiten ejecutar una cadena de acciones en función de varias condiciones.\n\nHaga clic en el símbolo más resaltado para agregar una nueva macro."
AdvSceneSwitcher.macroTab.editConditionHelp="Esta sección le permite definir las condiciones de la macro.\n\nSeleccione una macro existente o agregue una nueva a la izquierda.\nLuego haga clic en el botón más a continuación para agregar una nueva condición."
AdvSceneSwitcher.macroTab.priorityWarning="Nota: Se recomienda configurar las macros para que sean la funcionalidad de máxima prioridad.\nEsta configuración se puede cambiar en la ficha General."
AdvSceneSwitcher.macroTab.help="Las macros le permiten ejecutar una cadena de acciones en función de varias condiciones.\n\nHaga clic en el símbolo más resaltado para agregar una nueva macro."
AdvSceneSwitcher.macroTab.editConditionHelp="Esta sección le permite definir las condiciones de la macro.\n\nSeleccione una macro existente o agregue una nueva a la izquierda.\nLuego haga clic en el botón más a continuación para agregar una nueva condición."
AdvSceneSwitcher.macroTab.editActionHelp="Esta sección le permite definir acciones de la macro.\n\nSeleccione una macro existente o agregue una nueva a la izquierda.\nLuego haga clic en el botón más a continuación para agregar una nueva acción"
AdvSceneSwitcher.macroTab.edit="Editar macro"
AdvSceneSwitcher.macroTab.edit.logic="Tipo de lógica:"
AdvSceneSwitcher.macroTab.edit.condition="Tipo de condición:"
AdvSceneSwitcher.macroTab.edit.logic="Tipo de lógica:"
AdvSceneSwitcher.macroTab.edit.condition="Tipo de condición:"
AdvSceneSwitcher.macroTab.edit.action="Tipo de acción:"
AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
AdvSceneSwitcher.macroTab.name="Nombre:"
AdvSceneSwitcher.macroTab.run="Ejecutar macro"
AdvSceneSwitcher.macroTab.runFail="Error al ejecutar \"%1\" si se ha producido un error en una de las acciones o si la macro ya se está ejecutando."
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
AdvSceneSwitcher.macroTab.maximize="Maximizar"
AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
AdvSceneSwitcher.macroTab.name="Nombre:"
AdvSceneSwitcher.macroTab.run="Ejecutar macro"
AdvSceneSwitcher.macroTab.runFail="Error al ejecutar \"%1\" si se ha producido un error en una de las acciones o si la macro ya se está ejecutando."
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
AdvSceneSwitcher.macroTab.maximize="Maximizar"
AdvSceneSwitcher.macroTab.minimize="Minimizar"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Resaltar macros ejecutadas recientemente"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la macro seleccionada, que se evaluaron como verdaderas recientemente"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Resaltar macros ejecutadas recientemente"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la macro seleccionada, que se evaluaron como verdaderas recientemente"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
; Lógica de macros
AdvSceneSwitcher.logic.none="Omitir entrada"
AdvSceneSwitcher.logic.and="Y"
AdvSceneSwitcher.logic.or="O"
AdvSceneSwitcher.logic.andNot="Y no"
AdvSceneSwitcher.logic.orNot="O no"
AdvSceneSwitcher.logic.rootNone="Si"
; Lógica de macros
AdvSceneSwitcher.logic.none="Omitir entrada"
AdvSceneSwitcher.logic.and="Y"
AdvSceneSwitcher.logic.or="O"
AdvSceneSwitcher.logic.andNot="Y no"
AdvSceneSwitcher.logic.orNot="O no"
AdvSceneSwitcher.logic.rootNone="Si"
AdvSceneSwitcher.logic.not="Si no"
; Macro Conditions
@@ -122,16 +122,13 @@ AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Durante la tr
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Ventana"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existen y ..."
AdvSceneSwitcher.condition.window.entry.line2="... es {{fullscreen}} pantalla completa {{maximized}} maximizada {{focused}} enfocada {{windowFocusChanged}} ventana de primer plano cambiada"
AdvSceneSwitcher.condition.file="Archivo"
AdvSceneSwitcher.condition.file.entry.line1="Contenido de{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.entry.line1="Contenido de{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medios"
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
AdvSceneSwitcher.condition.media.matchOnChange="Solo coincidir con el cambio (Nota: esta opción se eliminará en una versión futura; use modificadores de duración en su lugar)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
@@ -184,9 +181,8 @@ AdvSceneSwitcher.condition.record="Grabación"
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
AdvSceneSwitcher.condition.record.state.stop="Grabación detenida"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Proceso"
AdvSceneSwitcher.condition.process.entry="{{processes}} se está ejecutando {{focused}} y está enfocado"
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}se está ejecutando{{focused}}y está enfocado"
AdvSceneSwitcher.condition.idle="Inactivo"
AdvSceneSwitcher.condition.idle.entry="No hay entradas de teclado o ratón durante {{duration}}"
AdvSceneSwitcher.condition.pluginState="Estado del complemento"
@@ -277,7 +273,8 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}coincide con la transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}{{types}}"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="coincide con la transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Transición"
@@ -324,8 +321,8 @@ AdvSceneSwitcher.condition.stats.dockHint="Puede abrir el panel de \"Estadístic
AdvSceneSwitcher.condition.stats.entry="{{stats}} esta {{condition}} {{value}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Cambiar escena"
AdvSceneSwitcher.action.scene.entry="Cambiar a la escena {{scenes}} usando {{transitions}} con una duración de {{duration}} segundos"
AdvSceneSwitcher.action.scene="Cambiar escena"
AdvSceneSwitcher.action.scene.entry="Cambiar a la{{sceneTypes}}escena{{scenes}}usando{{transitions}}con una duración de{{duration}} segundos"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Espere hasta que se complete la transición a la escena de destino"
AdvSceneSwitcher.action.wait="Esperar"
AdvSceneSwitcher.action.wait.type.fixed="fijo"
@@ -360,18 +357,14 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Transmisión"
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Ejecutar"
AdvSceneSwitcher.action.run.arguments="Argumentos:"
AdvSceneSwitcher.action.run.addArgument="Agregar argumento"
AdvSceneSwitcher.action.run.addArgumentDescription="Añadir nuevo argumento:"
AdvSceneSwitcher.action.run.entry="Ejecutar {{filePath}}"
AdvSceneSwitcher.action.sceneVisibility="Visibilidad del elemento de escena"
AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filtro"
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
@@ -382,7 +375,7 @@ AdvSceneSwitcher.action.source="Fuente"
AdvSceneSwitcher.action.source.type.enable="Habilitar"
AdvSceneSwitcher.action.source.type.disable="Deshabilitar"
AdvSceneSwitcher.action.source.type.settings="Establecer configuración"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
AdvSceneSwitcher.action.source.warning="Advertencia: la IU de OBS no puede controlar la habilitación y deshabilitación global de fuentes"
AdvSceneSwitcher.action.source.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.action.media="Medios"
@@ -392,15 +385,14 @@ AdvSceneSwitcher.action.media.type.stop="Detener"
AdvSceneSwitcher.action.media.type.restart="Reiniciar"
AdvSceneSwitcher.action.media.type.next="Siguiente"
AdvSceneSwitcher.action.media.type.previous="Anterior"
AdvSceneSwitcher.action.media.type.seek="Buscar"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.media.type.seek.duration="Buscar"
AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pausa"
AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
AdvSceneSwitcher.action.macro.type.stop="Detener"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Estado del complemento"
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
@@ -422,7 +414,6 @@ AdvSceneSwitcher.action.hotkey.leftMeta="Meta izquierda"
AdvSceneSwitcher.action.hotkey.rightMeta="Meta derecho"
AdvSceneSwitcher.action.hotkey.onlyOBS="Enviar pulsación de tecla solo a OBS"
AdvSceneSwitcher.action.hotkey.disabled="No se puede simular la pulsación de teclas: ¡funcionalidad desactivada!"
AdvSceneSwitcher.action.hotkey.entry="Presione {{keys}} durante {{duration}}"
AdvSceneSwitcher.action.sceneOrder="Orden de elementos de escena"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Mover hacia arriba"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
@@ -432,7 +423,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}transformar{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="Archivo"
AdvSceneSwitcher.action.file.type.write="Escribir"
AdvSceneSwitcher.action.file.type.append="Agregar"
@@ -531,7 +522,7 @@ AdvSceneSwitcher.mediaTab.states.none="Ninguno"
AdvSceneSwitcher.mediaTab.states.playing="Reproduciéndose"
AdvSceneSwitcher.mediaTab.states.opening="Abriendo"
AdvSceneSwitcher.mediaTab.states.buffering="Almacenando en búfer"
AdvSceneSwitcher.mediaTab.states.Paused="Pausado"
AdvSceneSwitcher.mediaTab.states.paused="Pausado"
AdvSceneSwitcher.mediaTab.states.stopped="Detenido"
AdvSceneSwitcher.mediaTab.states.ended="Finalizado"
AdvSceneSwitcher.mediaTab.states.error="Error"
@@ -573,7 +564,7 @@ AdvSceneSwitcher.fileTab.help="Esta pestaña te permitirá cambiar escenas autom
; Random Tab
AdvSceneSwitcher.randomTab.title="Aleatorio"
AdvSceneSwitcher.randomTab.randomDisabledWarning="Funcionalidad deshabilitada - para activarla, selecciona \"Si no se cumple ninguna condición de cambio, cambiar a cualquier escena en la pestaña Aleatorio\" en la pestaña General"
AdvSceneSwitcher.randomTab.entry="Si no se cumple ninguna condición de cambio, cambiar a {{scenes}} usando {{transitions}} después de {{delay}}"
AdvSceneSwitcher.randomTab.entry="Si no se cumple ninguna condición de cambio, cambiar a {{scenes}} usando {{transitions}} después de {{delay}}"
AdvSceneSwitcher.randomTab.help="El selector de escenas elegirá aleatoriamente una entrada en esta pestaña para cambiar durante el tiempo configurado.\nTen en cuenta que la misma entrada no se elegirá dos veces seguidas.\n\nHaz clic en el símbolo más resaltado para continuar."
; Time Tab

1199
data/locale/fr-FR.ini Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -86,10 +86,9 @@ AdvSceneSwitcher.condition.scene="Сцена"
AdvSceneSwitcher.condition.scene.type.current="Текущий"
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"
AdvSceneSwitcher.condition.window="Окно"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} существует и ..."
AdvSceneSwitcher.condition.file="Файл"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Медиа"
AdvSceneSwitcher.condition.video="Видео"
AdvSceneSwitcher.condition.video.condition.match="точно соответствует"
@@ -108,7 +107,7 @@ AdvSceneSwitcher.condition.record.state.start="Запись запущена"
AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена"
AdvSceneSwitcher.condition.record.state.stop="Запись остановлена"
AdvSceneSwitcher.condition.process="Процесс"
AdvSceneSwitcher.condition.process.entry="{{processes}} запущен {{focused}} и сфокусирован"
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}запущен{{focused}}и сфокусирован"
AdvSceneSwitcher.condition.idle="Простой"
AdvSceneSwitcher.condition.idle.entry="Не было ни клавиатуры, ни мыши в течении{{duration}}"
AdvSceneSwitcher.condition.pluginState="Состояние плагина"
@@ -116,8 +115,8 @@ AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Автоматиче
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Переключить сцену"
AdvSceneSwitcher.action.scene.entry="Перейти к сцене {{scenes}} используя {{transitions}} с продолжительностью {{duration}} секунд"
AdvSceneSwitcher.action.scene="Переключить сцену"
AdvSceneSwitcher.action.scene.entry="Перейти к сцене{{sceneTypes}}{{scenes}}используя{{transitions}}с продолжительностью{{duration}}секунд"
AdvSceneSwitcher.action.wait="Подождать"
AdvSceneSwitcher.action.wait.type.fixed="фиксированный"
AdvSceneSwitcher.action.wait.type.random="случайный"
@@ -144,7 +143,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Потоковое вещание"
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Запустить"
@@ -205,7 +204,7 @@ AdvSceneSwitcher.mediaTab.states.none="Нет"
AdvSceneSwitcher.mediaTab.states.playing="Играет"
AdvSceneSwitcher.mediaTab.states.opening="Открытие"
AdvSceneSwitcher.mediaTab.states.buffering="Буферизация"
AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
AdvSceneSwitcher.mediaTab.states.paused="Приостановлено"
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
AdvSceneSwitcher.mediaTab.states.error="Ошибка"

View File

@@ -112,16 +112,13 @@ AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Geçiş hedefi
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Pencere"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
AdvSceneSwitcher.condition.window.entry.line2="... {{fullscreen}} Tam ekran {{maximized}} büyütüldü {{focused}} odaklanıldı {{windowFocusChanged}} ön plan penceresi değişikliği"
AdvSceneSwitcher.condition.file="Dosya"
AdvSceneSwitcher.condition.file.entry.line1="İçerik{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.entry.line1="İçerik{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
@@ -161,9 +158,8 @@ AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
AdvSceneSwitcher.condition.record.state.stop="Kayıt durdu"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="İşlem"
AdvSceneSwitcher.condition.process.entry="{{processes}} çalışıyor {{focused}} ve odaklandı"
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}çalışıyor{{focused}}ve odaklandı"
AdvSceneSwitcher.condition.idle="Boşta"
AdvSceneSwitcher.condition.idle.entry="...için klavye veya fare girişi yok {{duration}}"
AdvSceneSwitcher.condition.pluginState="Eklenti durumu"
@@ -233,7 +229,8 @@ AdvSceneSwitcher.condition.date.state.before="Önce"
AdvSceneSwitcher.condition.date.state.between="Arasında"
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}} dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}}{{types}}"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Geçiş"
@@ -261,8 +258,8 @@ AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD mevcut {{xPos}} x {{yPos}} x {{zPos}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Sahne Degistirici"
AdvSceneSwitcher.action.scene.entry="Sahneyi {{scenes}} kullanarak {{transitions}} süresi olan {{duration}} saniye"
AdvSceneSwitcher.action.scene="Sahne Degistirici"
AdvSceneSwitcher.action.scene.entry="Sahneyi{{sceneTypes}}{{scenes}}kullanarak{{transitions}}süresi olan{{duration}}saniye"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Hedef sahneye geçiş tamamlanana kadar bekleyin"
AdvSceneSwitcher.action.wait="Bekle"
AdvSceneSwitcher.action.wait.type.fixed="sabit"
@@ -290,18 +287,14 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Yayın"
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Çalıştır"
AdvSceneSwitcher.action.run.arguments="Argümanlar:"
AdvSceneSwitcher.action.run.addArgument="Argüman ekle"
AdvSceneSwitcher.action.run.addArgumentDescription="Yeni Argüman ekle:"
AdvSceneSwitcher.action.run.entry="Çalıştır {{filePath}}"
AdvSceneSwitcher.action.sceneVisibility="Sahne öğesi görünürlüğü"
AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filtrele"
AdvSceneSwitcher.action.filter.type.enable="Etkin"
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
@@ -312,7 +305,7 @@ AdvSceneSwitcher.action.source="Kaynak"
AdvSceneSwitcher.action.source.type.enable="Etkin"
AdvSceneSwitcher.action.source.type.disable="Etkisiz"
AdvSceneSwitcher.action.source.type.settings="Ayarları yap"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
AdvSceneSwitcher.action.source.warning="Uyarı: Kaynakların global olarak etkinleştirilmesi ve devre dışı bırakılması, OBS UI tarafından kontrol edilemez"
AdvSceneSwitcher.action.source.getSettings="Mevcut Ayarları yap"
AdvSceneSwitcher.action.media="Medya"
@@ -322,13 +315,12 @@ AdvSceneSwitcher.action.media.type.stop="Dur"
AdvSceneSwitcher.action.media.type.restart="Yeniden Başlat"
AdvSceneSwitcher.action.media.type.next="Sonraki"
AdvSceneSwitcher.action.media.type.previous="Önceki"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.macro="Makro"
AdvSceneSwitcher.action.macro.type.pause="Duraklat"
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
@@ -350,7 +342,6 @@ AdvSceneSwitcher.action.hotkey.leftMeta="Sol Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="Sağ Meta"
AdvSceneSwitcher.action.hotkey.onlyOBS="Tuşa basımı yalnızca OBS'ye gönder"
AdvSceneSwitcher.action.hotkey.disabled="Tuşa basma simülasyonu yapılamıyor - işlevsellik devre dışı!"
AdvSceneSwitcher.action.hotkey.entry="Bas {{keys}} uygun {{duration}}"
AdvSceneSwitcher.action.sceneOrder="Sahne öğesi sırası"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Yukarı taşı"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
@@ -360,7 +351,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}transform{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="Dosya"
AdvSceneSwitcher.action.file.type.write="Yaz"
AdvSceneSwitcher.action.file.type.append="Ekle"
@@ -442,7 +433,7 @@ AdvSceneSwitcher.mediaTab.states.none="Yok"
AdvSceneSwitcher.mediaTab.states.playing="Oynamak"
AdvSceneSwitcher.mediaTab.states.opening="Açmak"
AdvSceneSwitcher.mediaTab.states.buffering="Arabelleğe alma"
AdvSceneSwitcher.mediaTab.states.Paused="Duraklatıldı"
AdvSceneSwitcher.mediaTab.states.paused="Duraklatıldı"
AdvSceneSwitcher.mediaTab.states.stopped="Durduruldu"
AdvSceneSwitcher.mediaTab.states.ended="Bitti"
AdvSceneSwitcher.mediaTab.states.error="Hata"

View File

@@ -178,14 +178,13 @@ AdvSceneSwitcher.condition.file.type.contentChange="内容已更改"
AdvSceneSwitcher.condition.file.type.dateChange="修改日期已更改"
AdvSceneSwitcher.condition.file.remote="远程文件"
AdvSceneSwitcher.condition.file.local="本地文件"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="媒体"
AdvSceneSwitcher.condition.media.source="源"
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
AdvSceneSwitcher.condition.media.matchOnChange="仅在更改时匹配(注意:此选项将在未来版本中删除-请改用时间限制)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="视频"
@@ -267,9 +266,8 @@ AdvSceneSwitcher.condition.record="录制"
AdvSceneSwitcher.condition.record.state.start="录制运行中"
AdvSceneSwitcher.condition.record.state.pause="录制暂停"
AdvSceneSwitcher.condition.record.state.stop="录制停止"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="进程"
AdvSceneSwitcher.condition.process.entry="{{processes}} 是正在运行 {{focused}} 并且是获得焦点"
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}是正在运行{{focused}}并且是获得焦点"
AdvSceneSwitcher.condition.process.entry.focus="当前焦点进程: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="闲置检测"
AdvSceneSwitcher.condition.idle.entry="没有键盘或鼠标输入{{duration}}"
@@ -371,7 +369,8 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期"
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} 匹配变换"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} {{types}}"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="匹配变换"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="转场"
@@ -435,7 +434,6 @@ AdvSceneSwitcher.condition.variable.type.valueChanged="值已更改"
AdvSceneSwitcher.condition.variable.type.equalsVariable="等于变量"
AdvSceneSwitcher.condition.variable.type.lessThanVariable="小于变量"
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="大于变量"
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
AdvSceneSwitcher.condition.run="运行"
AdvSceneSwitcher.condition.run.entry="进程在超时 {{timeout}} 秒之前退出"
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}检查退出代码{{exitCode}}"
@@ -448,9 +446,9 @@ AdvSceneSwitcher.condition.display.type.displayCount="连接的显示器的数
AdvSceneSwitcher.condition.display.entry="{{conditions}}{{displays}}{{regex}}{{displayCount}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="切换场景"
AdvSceneSwitcher.action.scene.entry="切换场景 {{scenes}} 使用 {{transitions}} 时长 {{duration}} 秒"
AdvSceneSwitcher.action.scene.entry.noDuration="切换到场景{{scenes}}使用{{transitions}}"
AdvSceneSwitcher.action.scene="切换场景"
AdvSceneSwitcher.action.scene.entry="切换场景{{sceneTypes}}{{scenes}}使用{{transitions}}时长{{duration}}秒"
AdvSceneSwitcher.action.scene.entry.noDuration="切换到场景{{sceneTypes}}{{scenes}}使用{{transitions}}"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="等待目标场景的过渡完成"
AdvSceneSwitcher.action.wait="等待"
AdvSceneSwitcher.action.wait.type.fixed="固定数值"
@@ -494,7 +492,7 @@ AdvSceneSwitcher.action.streaming="推流"
AdvSceneSwitcher.action.streaming.type.stop="停止推流"
AdvSceneSwitcher.action.streaming.type.start="开始推流"
AdvSceneSwitcher.action.streaming.type.keyFrameInterval="设置关键帧间隔"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="运行"
AdvSceneSwitcher.action.sceneVisibility="场景项目可见性"
AdvSceneSwitcher.action.sceneVisibility.type.show="显示"
@@ -502,7 +500,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换"
AdvSceneSwitcher.action.sceneVisibility.type.source="来源"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部"
AdvSceneSwitcher.action.sceneVisibility.entry="置{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.sceneVisibility.entry="置{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="滤镜"
AdvSceneSwitcher.action.filter.type.enable="开启"
AdvSceneSwitcher.action.filter.type.disable="关闭"
@@ -517,7 +515,7 @@ AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮"
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc"
AdvSceneSwitcher.action.source.noSettingsButtons="未找到按钮!"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
AdvSceneSwitcher.action.source.warning="警告OBS UI无法控制全局启用和禁用源注意通过这个设置禁用我测试你必须通过这个在启用才行不然点击小眼睛你无法显示"
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
AdvSceneSwitcher.action.media="媒体"
@@ -527,15 +525,14 @@ AdvSceneSwitcher.action.media.type.stop="停止"
AdvSceneSwitcher.action.media.type.restart="重新启动"
AdvSceneSwitcher.action.media.type.next="下一个"
AdvSceneSwitcher.action.media.type.previous="上一个"
AdvSceneSwitcher.action.media.type.seek="时间轴定位"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.media.type.seek.duration="时间轴定位"
AdvSceneSwitcher.action.macro="宏"
AdvSceneSwitcher.action.macro.type.pause="暂停"
AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
AdvSceneSwitcher.action.macro.type.run="运行操作"
AdvSceneSwitcher.action.macro.type.stop="停止操作"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="插件状态"
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"
@@ -559,7 +556,6 @@ AdvSceneSwitcher.action.hotkey.leftMeta="左 Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="右 Meta"
AdvSceneSwitcher.action.hotkey.onlyOBS="仅向OBS发送按键"
AdvSceneSwitcher.action.hotkey.disabled="无法模拟按键-功能已禁用!"
AdvSceneSwitcher.action.hotkey.entry="按下 {{keys}} 在 {{duration}} 秒"
AdvSceneSwitcher.action.sceneOrder="场景项目顺序"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="上移"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="下移"
@@ -569,7 +565,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}} 上转换 {{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="文件"
AdvSceneSwitcher.action.file.type.write="写入"
AdvSceneSwitcher.action.file.type.append="追加写入"
@@ -624,8 +620,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
AdvSceneSwitcher.action.websocket="websocket"
AdvSceneSwitcher.action.websocket.type.request="request"
AdvSceneSwitcher.action.websocket.type.event="event"
AdvSceneSwitcher.action.websocket.entry.request="通过{{connection}} 发送场景切换器 {{type}}"
AdvSceneSwitcher.action.websocket.entry.event="发送场景切换器 {{type}} 到连接的客户端"
AdvSceneSwitcher.action.http="Http"
AdvSceneSwitcher.action.http.setHeaders="设置头信息(SET headers)"
AdvSceneSwitcher.action.http.headers="头信息(headers):"
@@ -655,7 +649,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="无效选择!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="不支持从 %1 个操作获取变量值!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条件中获取变量值!"
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="子字符串开始:{{subStringStart}} 子字符串大小:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"
@@ -732,7 +725,7 @@ AdvSceneSwitcher.mediaTab.states.none="无"
AdvSceneSwitcher.mediaTab.states.playing="播放中"
AdvSceneSwitcher.mediaTab.states.opening="打开中"
AdvSceneSwitcher.mediaTab.states.buffering="缓冲中"
AdvSceneSwitcher.mediaTab.states.Paused="暂停"
AdvSceneSwitcher.mediaTab.states.paused="暂停"
AdvSceneSwitcher.mediaTab.states.stopped="停止"
AdvSceneSwitcher.mediaTab.states.ended="结束"
AdvSceneSwitcher.mediaTab.states.error="错误"

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
<path d="m1,2c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#fefefe" id="svg_1" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<path d="m1,8c0,-0.27 0.09,-0.52 0.28,-0.71c0.38,-0.39 1.03,-0.39 1.41,0l5.31,5.3l5.28,-5.3c0.38,-0.39 1.03,-0.39 1.41,0c0.19,0.19 0.31,0.44 0.31,0.71s-0.13,0.52 -0.31,0.71l-6,6c-0.38,0.39 -1.03,0.39 -1.41,0l-6,-6c-0.19,-0.19 -0.28,-0.44 -0.28,-0.71zm0,0" fill="#fefefe" id="svg_2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 791 B

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#fefefe" id="svg_1"/>
<path d="m1,14c0,-0.27 0.11,-0.52 0.28,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.19,0.19 0.3,0.44 0.3,0.71s-0.11,0.52 -0.3,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.28,-5.3l-5.3,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.17,-0.19 -0.28,-0.44 -0.28,-0.71zm0,0" fill="#fefefe" id="svg_2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 753 B

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" version="1.1">
<!-- Created with SVG-edit - https://github.com/SVG-Edit/svgedit-->
<g class="layer">
<title>Layer 1</title>
<rect fill="#fefefe" height="2.25" id="svg_1" stroke="#fefefe" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(1, 0, 0, 1, 0, 0)" width="13" x="1.5" y="4"/>
<rect fill="#fefefe" height="2.25" id="svg_2" stroke="#fefefe" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(1, 0, 0, 1, 0, 0)" width="13" x="1.5" y="10"/>
<rect fill="#fefefe" height="1" id="svg_4" stroke="#fefefe" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(0.341951, -0.926734, 0.939718, 0.337226, -1.948, 13.2448)" width="14" x="1" y="7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 931 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
<defs>
</defs>
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
<path d="M 87.803 77.194 L 68.212 57.602 c 9.5 -14.422 7.912 -34.054 -4.766 -46.732 c 0 0 -0.001 0 -0.001 0 c -14.495 -14.493 -38.08 -14.494 -52.574 0 c -14.494 14.495 -14.494 38.079 0 52.575 c 7.248 7.247 16.767 10.87 26.287 10.87 c 7.134 0 14.267 -2.035 20.445 -6.104 l 19.591 19.591 C 78.659 89.267 80.579 90 82.498 90 s 3.84 -0.733 5.305 -2.197 C 90.732 84.873 90.732 80.124 87.803 77.194 z M 21.48 52.837 c -8.645 -8.646 -8.645 -22.713 0 -31.358 c 4.323 -4.322 10 -6.483 15.679 -6.483 c 5.678 0 11.356 2.161 15.678 6.483 c 8.644 8.644 8.645 22.707 0.005 31.352 c -0.002 0.002 -0.004 0.003 -0.005 0.005 c -0.002 0.002 -0.003 0.003 -0.004 0.005 C 44.184 61.481 30.123 61.48 21.48 52.837 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: #fefefe; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
<path d="m1,2c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_1"/>
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_4" transform="matrix(1, 0, 0, 1, 0, 0)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 791 B

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_1"/>
<path d="m1,14c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 757 B

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" version="1.1">
<!-- Created with SVG-edit - https://github.com/SVG-Edit/svgedit-->
<g class="layer">
<title>Layer 1</title>
<rect fill="#202020" height="2.25" id="svg_1" stroke="#202020" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(1, 0, 0, 1, 0, 0)" width="13" x="1.5" y="4"/>
<rect fill="#202020" height="2.25" id="svg_2" stroke="#202020" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(1, 0, 0, 1, 0, 0)" width="13" x="1.5" y="10"/>
<rect fill="#202020" height="1" id="svg_4" stroke="#202020" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(0.341951, -0.926734, 0.939718, 0.337226, -1.948, 13.2448)" width="14" x="1" y="7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 931 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
<defs>
</defs>
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
<path d="M 87.803 77.194 L 68.212 57.602 c 9.5 -14.422 7.912 -34.054 -4.766 -46.732 c 0 0 -0.001 0 -0.001 0 c -14.495 -14.493 -38.08 -14.494 -52.574 0 c -14.494 14.495 -14.494 38.079 0 52.575 c 7.248 7.247 16.767 10.87 26.287 10.87 c 7.134 0 14.267 -2.035 20.445 -6.104 l 19.591 19.591 C 78.659 89.267 80.579 90 82.498 90 s 3.84 -0.733 5.305 -2.197 C 90.732 84.873 90.732 80.124 87.803 77.194 z M 21.48 52.837 c -8.645 -8.646 -8.645 -22.713 0 -31.358 c 4.323 -4.322 10 -6.483 15.679 -6.483 c 5.678 0 11.356 2.161 15.678 6.483 c 8.644 8.644 8.645 22.707 0.005 31.352 c -0.002 0.002 -0.004 0.003 -0.005 0.005 c -0.002 0.002 -0.003 0.003 -0.004 0.005 C 44.184 61.481 30.123 61.48 21.48 52.837 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: #202020; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

2
deps/asio vendored

Submodule deps/asio updated: b73dc1d2c0...c465349fa5

1
deps/cpp-httplib vendored Submodule

Submodule deps/cpp-httplib added at c7ed1796a7

1
deps/json vendored Submodule

Submodule deps/json added at bc889afb4c

View File

@@ -67,8 +67,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>957</width>
<height>905</height>
<width>962</width>
<height>1033</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
@@ -119,7 +119,7 @@
<string notr="true">ms</string>
</property>
<property name="minimum">
<number>50</number>
<number>10</number>
</property>
<property name="maximum">
<number>20000</number>
@@ -256,6 +256,30 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QCheckBox" name="disableComboBoxFilter">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable</string>
</property>
</widget>
</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>
<layout class="QHBoxLayout" name="horizontalLayout_26">
<item>
@@ -805,7 +829,7 @@
<property name="title">
<string>AdvSceneSwitcher.macroTab.edit</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33">
<layout class="QVBoxLayout" name="verticalLayout_38">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0,0,0">
<item>
@@ -819,7 +843,7 @@
<widget class="QLineEdit" name="macroName"/>
</item>
<item>
<widget class="QPushButton" name="runMacro">
<widget class="advss::MacroRunButton" name="runMacro">
<property name="text">
<string>AdvSceneSwitcher.macroTab.run</string>
</property>
@@ -912,6 +936,16 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="conditionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="leftMargin">
@@ -988,6 +1022,19 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="conditionTop">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionUp">
<property name="maximumSize">
@@ -1023,6 +1070,19 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionBottom">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
@@ -1040,144 +1100,376 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="macroActions" native="true">
<layout class="QVBoxLayout" name="macroActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QPushButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</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>16777215</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_133">
<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_47">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_132">
<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="actionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<widget class="QSplitter" name="macroElseActionSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="macroActions" native="true">
<layout class="QVBoxLayout" name="macroActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="actionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QPushButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</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>16777215</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_133">
<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_47">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_132">
<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="actionTop">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionBottom">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</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>
<item>
<widget class="QPushButton" name="toggleElseActions">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="macroElseActions" native="true">
<layout class="QVBoxLayout" name="macroElseActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="elseActionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_33">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QPushButton" name="elseActionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</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="elseActionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</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_138">
<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_50">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_139">
<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="elseActionTop">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="elseActionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="elseActionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="elseActionBottom">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_22">
<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>
@@ -4567,6 +4859,17 @@
<extends>QListView</extends>
<header>macro-tree.hpp</header>
</customwidget>
<customwidget>
<class>advss::MacroSegmentList</class>
<extends>QScrollArea</extends>
<header>macro-segment-list.hpp</header>
<container>1</container>
</customwidget>
<customwidget>
<class>advss::MacroRunButton</class>
<extends>QPushButton</extends>
<header>macro-run-button.hpp</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@@ -1,15 +1,6 @@
#include <QMainWindow>
#include <QAction>
#include <QFileDialog>
#include <QDirIterator>
#include <regex>
#include <filesystem>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "status-control.hpp"
#include "scene-switch-helpers.hpp"
#include "curl-helper.hpp"
@@ -17,6 +8,16 @@
#include "utility.hpp"
#include "version.h"
#include <QMainWindow>
#include <QAction>
#include <QFileDialog>
#include <QTextStream>
#include <QDirIterator>
#include <regex>
#include <filesystem>
#include <obs-module.h>
#include <obs-frontend-api.h>
namespace advss {
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
@@ -116,13 +117,34 @@ void AdvSceneSwitcher::LoadUI()
loading = false;
}
bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
{
auto eventType = event->type();
if (obj == ui->macroElseActions && eventType == QEvent::Resize) {
QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
if (resizeEvent->size().height() == 0) {
SetElseActionsStateToHidden();
return QDialog::eventFilter(obj, event);
}
SetElseActionsStateToVisible();
}
return QDialog::eventFilter(obj, event);
}
/******************************************************************************
* Saving and loading
******************************************************************************/
static void AskForBackup(obs_data_t *obj);
static void AskForBackup(const QString &json);
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{
if (!switcher) {
return;
}
if (saving) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->Prune();
@@ -136,16 +158,25 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->Stop();
switcher->m.lock();
obs_data_t *obj =
OBSDataAutoRelease obj =
obs_data_get_obj(save_data, "advanced-scene-switcher");
if (!obj) {
obj = obs_data_create();
}
if (switcher->VersionChanged(obj, g_GIT_SHA1)) {
AskForBackup(obj);
auto json = obs_data_get_json(obj);
static QString jsonQString = json ? json : "";
std::thread t([]() {
obs_queue_task(
OBS_TASK_UI,
[](void *) {
AskForBackup(jsonQString);
},
nullptr, false);
});
t.detach();
}
switcher->LoadSettings(obj);
obs_data_release(obj);
switcher->m.unlock();
if (!switcher->stop) {
@@ -154,12 +185,12 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
}
}
static void AskForBackup(obs_data_t *obj)
static void AskForBackup(const QString &json)
{
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
const bool backupWasConfirmed = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
if (!backupSettings) {
if (!backupWasConfirmed) {
return;
}
@@ -178,8 +209,8 @@ static void AskForBackup(obs_data_t *obj)
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
obs_data_save_json(obj, file.fileName().toUtf8().constData());
auto out = QTextStream(&file);
out << json;
}
/******************************************************************************
@@ -206,7 +237,6 @@ void SwitcherData::Thread()
// 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();
auto runTime =
std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -317,19 +347,14 @@ void SwitcherData::SetPreconditions()
// Process name
GetForegroundProcessName(currentForegroundProcess);
// Cursor
std::pair<int, int> cursorPos = GetCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
cursorPos.second != switcher->lastCursorPos.second;
lastCursorPos = GetCursorPos();
// Macro
InvalidateMacroTempVarValues();
}
void SwitcherData::ResetForNextInterval()
{
// Core reset functions
ClearWebsocketMessages();
// Plugin reset functions
for (const auto &func : resetForNextIntervalFuncs) {
for (const auto &func : resetIntervalSteps) {
func();
}
}
@@ -403,7 +428,7 @@ bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
static void ResetMacros()
{
for (auto &m : switcher->macros) {
for (auto &m : GetMacros()) {
m->ResetRunCount();
m->ResetTimers();
}
@@ -439,18 +464,33 @@ void SwitcherData::Start()
}
}
bool CloseAllInputDialogs();
void SwitcherData::Stop()
{
if (th && th->isRunning()) {
stop = true;
cv.notify_all();
abortMacroWait = true;
macroWaitCv.notify_all();
macroTransitionCv.notify_all();
th->wait();
delete th;
th = nullptr;
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
GetMacroTransitionCV().notify_all();
// Not waiting if a dialog was closed is a workaround to avoid
// deadlocks when a variable input dialog is opened while Stop()
// is called.
//
// The QEventLoop handling the dialog would not exit until Stop()
// would return, which itself would wait for the switcher thread
// to complete, which itself is blocked by the variable input
// dialog.
//
// Must be reworked in the future, as this will not go well when
// OBS is shutting down and a dialog is still opened.
if (!CloseAllInputDialogs()) {
th->wait();
delete th;
th = nullptr;
}
writeToStatusFile("Advanced Scene Switcher stopped");
}
@@ -506,9 +546,6 @@ extern "C" void FreeSceneSwitcher()
static void handleSceneChange()
{
switcher->lastSceneChangeTime =
std::chrono::high_resolution_clock::now();
// Stop waiting if scene was changed
if (switcher->SceneChangedDuringWait()) {
switcher->cv.notify_one();
@@ -575,42 +612,29 @@ static void handlePeviewSceneChange()
}
}
static void setReplayBufferSaved()
static void handleTransitionEnd()
{
switcher->replayBufferSaved = true;
GetMacroTransitionCV().notify_all();
}
static void setTranstionEnd()
{
switcher->lastTransitionEndTime =
std::chrono::high_resolution_clock::now();
switcher->macroTransitionCv.notify_all();
}
static void setStreamStarting()
{
switcher->lastStreamStartingTime =
std::chrono::high_resolution_clock::now();
}
static void setStreamStopping()
{
switcher->lastStreamStoppingTime =
std::chrono::high_resolution_clock::now();
}
static void handleExit()
static void handleShutdown()
{
if (!switcher) {
return;
}
switcher->obsIsShuttingDown = true;
if (switcher->shutdownConditionCount) {
if (ShutdownCheckIsNecessary()) {
switcher->Stop();
switcher->CheckMacros();
switcher->RunMacros();
CheckMacros();
RunMacros();
// Unfortunately this will not work as OBS will now allow saving
// the scene collection data at this point, So any OBS specific
// changes done during shutdown will be lost
//
// TODO: Look for a way to possibly resolve this
obs_frontend_save();
}
FreeSceneSwitcher();
}
static void handleSceneCollectionChanging()
@@ -634,8 +658,12 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
}
switch (event) {
case OBS_FRONTEND_EVENT_EXIT:
handleExit();
case OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN:
// Note: We are intentionally not listening for
// OBS_FRONTEND_EVENT_EXIT here as at that point all scene
// collection data will already have been cleared and thus all
// macros will have already been cleared
handleShutdown();
break;
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange();
@@ -655,24 +683,16 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
resetLiveTime();
break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
setReplayBufferSaved();
break;
#endif
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
setTranstionEnd();
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
setStreamStarting();
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
setStreamStopping();
handleTransitionEnd();
break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 2, 0)
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
handleSceneCollectionChanging();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
SaveSceneSwitcher(nullptr, false, nullptr);
break;
#endif
default:
break;
@@ -723,6 +743,9 @@ void OpenSettingsWindow()
}
}
void SetupConnectionManager();
void SetupWebsocketHelpers();
extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
{
blog(LOG_INFO, "version: %s", g_GIT_TAG);
@@ -733,6 +756,8 @@ extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
PlatformInit();
LoadPlugins();
SetupDock();
SetupConnectionManager();
SetupWebsocketHelpers();
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
obs_frontend_add_event_callback(OBSEvent, switcher);

View File

@@ -39,6 +39,9 @@ public:
void RestoreWindowGeo();
void CheckFirstTimeSetup();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
/* --- Begin of general tab section --- */
public:
void SetupGeneralTab();
@@ -63,6 +66,7 @@ public slots:
void on_saveWindowGeo_stateChanged(int state);
void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_disableComboBoxFilter_stateChanged(int state);
void on_warnPluginLoadFailure_stateChanged(int state);
void on_hideLegacyTabs_stateChanged(int state);
void on_priorityUp_clicked();
@@ -83,10 +87,13 @@ public:
void SetEditMacro(Macro &m);
void SetMacroEditAreaDisabled(bool);
void HighlightAction(int idx, QColor color = QColor(Qt::green));
void HighlightElseAction(int idx, QColor color = QColor(Qt::green));
void HighlightCondition(int idx, QColor color = QColor(Qt::green));
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void SetActionData(Macro &m);
void SetElseActionData(Macro &m);
void SetConditionData(Macro &m);
void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
@@ -97,39 +104,67 @@ public slots:
void on_macroUp_clicked();
void on_macroDown_clicked();
void on_macroName_editingFinished();
void on_runMacro_clicked();
void on_runMacroInParallel_stateChanged(int value);
void on_runMacroOnChange_stateChanged(int value);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_conditionTop_clicked();
void on_conditionUp_clicked();
void on_conditionDown_clicked();
void on_conditionBottom_clicked();
void on_actionAdd_clicked();
void on_actionRemove_clicked();
void on_actionTop_clicked();
void on_actionUp_clicked();
void on_actionDown_clicked();
void MacroSelectionChanged(const QItemSelection &,
const QItemSelection &);
void on_actionBottom_clicked();
void on_toggleElseActions_clicked();
void on_elseActionAdd_clicked();
void on_elseActionRemove_clicked();
void on_elseActionTop_clicked();
void on_elseActionUp_clicked();
void on_elseActionDown_clicked();
void on_elseActionBottom_clicked();
void MacroSelectionAboutToChange();
void MacroSelectionChanged();
void UpMacroSegementHotkey();
void DownMacroSegementHotkey();
void DeleteMacroSegementHotkey();
void ShowMacroContextMenu(const QPoint &);
void ShowMacroActionsContextMenu(const QPoint &);
void ShowMacroElseActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &);
void CopyMacro();
void RenameCurrentMacro();
void ExportMacros();
void ImportMacros();
void ExpandAllActions();
void ExpandAllElseActions();
void ExpandAllConditions();
void CollapseAllActions();
void CollapseAllElseActions();
void CollapseAllConditions();
void MinimizeActions();
void MaximizeActions();
void MinimizeElseActions();
void MaximizeElseActions();
void MinimizeConditions();
void MaximizeConditions();
void SetElseActionsStateToHidden();
void SetElseActionsStateToVisible();
void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target);
void AddMacroAction(int idx);
void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx);
void MacroElseActionSelectionChanged(int idx);
void MacroElseActionReorder(int to, int target);
void AddMacroElseAction(int idx);
void RemoveMacroElseAction(int idx);
void SwapElseActions(Macro *m, int pos1, int pos2);
void MoveMacroElseActionUp(int idx);
void MoveMacroElseActionDown(int idx);
void MacroConditionSelectionChanged(int idx);
void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx);
@@ -149,8 +184,10 @@ signals:
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString &newName);
void MacroSegmentOrderChanged();
void SegmentTempVarsChanged();
void HighlightMacrosChanged(bool value);
void HighlightActionsChanged(bool value);
void HighlightElseActionsChanged(bool value);
void HighlightConditionsChanged(bool value);
void ConnectionAdded(const QString &);
@@ -161,18 +198,16 @@ signals:
void VariableRemoved(const QString &);
private:
enum class MacroSection { CONDITIONS, ACTIONS, ELSE_ACTIONS };
void SetupMacroSegmentSelection(MacroSection type, int idx);
bool ResolveMacroImportNameConflict(std::shared_ptr<Macro> &);
bool MacroTabIsInFocus();
MacroSegmentList *conditionsList = nullptr;
MacroSegmentList *actionsList = nullptr;
enum class MacroSection {
CONDITIONS,
ACTIONS,
};
MacroSection lastInteracted = MacroSection::CONDITIONS;
int currentConditionIdx = -1;
int currentActionIdx = -1;
int currentElseActionIdx = -1;
/* --- End of macro tab section --- */
@@ -305,7 +340,7 @@ public slots:
void on_timeUp_clicked();
void on_timeDown_clicked();
// Video tab
// Audio tab
public:
void SetupAudioTab();
public slots:

View File

@@ -2,6 +2,8 @@
#include "switcher-data.hpp"
#include "status-control.hpp"
#include "file-selection.hpp"
#include "filter-combo-box.hpp"
#include "variable.hpp"
#include "utility.hpp"
#include "version.h"
@@ -32,7 +34,7 @@ void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = SwitcherData::NoMatch::NO_SWITCH;
switcher->switchIfNotMatching = NoMatchBehavior::NO_SWITCH;
ui->noMatchSwitchScene->setEnabled(false);
ui->randomDisabledWarning->setVisible(true);
}
@@ -44,7 +46,7 @@ void AdvSceneSwitcher::on_noMatchSwitch_clicked()
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = SwitcherData::NoMatch::SWITCH;
switcher->switchIfNotMatching = NoMatchBehavior::SWITCH;
ui->noMatchSwitchScene->setEnabled(true);
UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());
ui->randomDisabledWarning->setVisible(true);
@@ -57,7 +59,7 @@ void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = SwitcherData::NoMatch::RANDOM_SWITCH;
switcher->switchIfNotMatching = NoMatchBehavior::RANDOM_SWITCH;
ui->noMatchSwitchScene->setEnabled(false);
ui->randomDisabledWarning->setVisible(false);
}
@@ -131,10 +133,9 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
}
switcher->windowPos = this->pos();
switcher->windowSize = this->size();
switcher->macroActionConditionSplitterPosition =
ui->macroActionConditionSplitter->sizes();
switcher->macroListMacroEditSplitterPosition =
ui->macroListMacroEditSplitter->sizes();
MacroSelectionAboutToChange(); // Trigger saving of splitter states
obs_frontend_save();
}
@@ -175,6 +176,16 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
switcher->disableHints = state;
}
void AdvSceneSwitcher::on_disableComboBoxFilter_stateChanged(int state)
{
if (loading) {
return;
}
switcher->disableFilterComboboxFilter = state;
FilterComboBox::SetFilterBehaviourEnabled(!state);
}
void AdvSceneSwitcher::on_warnPluginLoadFailure_stateChanged(int state)
{
if (loading) {
@@ -489,12 +500,20 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
return;
}
// New post load steps to be declared during load
postLoadSteps.clear();
// Needs to be loaded before any entries which might rely on scene group
// selections to be available.
loadSceneGroups(obj);
LoadVariables(obj);
LoadConnections(obj);
for (const auto &func : loadSteps) {
func(obj);
}
LoadMacros(obj);
LoadGlobalMacroProperties(obj);
loadWindowTitleSwitches(obj);
loadScreenRegionSwitches(obj);
loadPauseSwitches(obj);
@@ -514,8 +533,13 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
LoadHotkeys(obj);
LoadUISettings(obj);
for (const auto &func : postLoadSteps) {
func();
}
// Reset on startup and scene collection change
switcher->lastOpenedTab = -1;
startupLoadDone = true;
}
void SwitcherData::SaveSettings(obs_data_t *obj)
@@ -526,7 +550,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveSceneGroups(obj);
SaveMacros(obj);
SaveConnections(obj);
SaveGlobalMacroProperties(obj);
SaveVariables(obj);
saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj);
@@ -547,6 +571,10 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
SaveHotkeys(obj);
SaveUISettings(obj);
SaveVersion(obj, g_GIT_SHA1);
for (const auto &func : saveSteps) {
func(obj);
}
}
void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
@@ -574,6 +602,8 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
obs_data_set_bool(obj, "showSystemTrayNotifications",
showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_bool(obj, "disableFilterComboboxFilter",
disableFilterComboboxFilter);
obs_data_set_bool(obj, "warnPluginLoadFailure", warnPluginLoadFailure);
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
@@ -595,9 +625,9 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
interval = obs_data_get_int(obj, "interval");
obs_data_set_default_int(obj, "switch_if_not_matching",
static_cast<int>(NoMatch::NO_SWITCH));
switchIfNotMatching =
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
static_cast<int>(NoMatchBehavior::NO_SWITCH));
switchIfNotMatching = static_cast<NoMatchBehavior>(
obs_data_get_int(obj, "switch_if_not_matching"));
std::string nonMatchingSceneName =
obs_data_get_string(obj, "non_matching_scene");
nonMatchingScene = GetWeakSourceByName(nonMatchingSceneName.c_str());
@@ -622,6 +652,8 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
showSystemTrayNotifications =
obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints");
disableFilterComboboxFilter =
obs_data_get_bool(obj, "disableFilterComboboxFilter");
obs_data_set_default_bool(obj, "warnPluginLoadFailure", true);
warnPluginLoadFailure = obs_data_get_bool(obj, "warnPluginLoadFailure");
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
@@ -651,34 +683,6 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
lastImportPath = obs_data_get_string(obj, "lastImportPath");
}
static void saveSplitterPos(QList<int> &sizes, obs_data_t *obj,
const std::string name)
{
auto array = obs_data_array_create();
for (int i = 0; i < sizes.count(); ++i) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_int(array_obj, "pos", sizes[i]);
obs_data_array_push_back(array, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, name.c_str(), array);
obs_data_array_release(array);
}
static void loadSplitterPos(QList<int> &sizes, obs_data_t *obj,
const std::string name)
{
sizes.clear();
obs_data_array_t *array = obs_data_get_array(obj, name.c_str());
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
obs_data_t *item = obs_data_array_item(array, i);
sizes << obs_data_get_int(item, "pos");
obs_data_release(item);
}
obs_data_array_release(array);
}
void SwitcherData::SaveUISettings(obs_data_t *obj)
{
obs_data_set_int(obj, "generalTabPos", tabOrder[0]);
@@ -706,9 +710,7 @@ void SwitcherData::SaveUISettings(obs_data_t *obj)
obs_data_set_int(obj, "windowWidth", windowSize.width());
obs_data_set_int(obj, "windowHeight", windowSize.height());
saveSplitterPos(macroActionConditionSplitterPosition, obj,
"macroActionConditionSplitterPosition");
saveSplitterPos(macroListMacroEditSplitterPosition, obj,
SaveSplitterPos(macroListMacroEditSplitterPosition, obj,
"macroListMacroEditSplitterPosition");
}
@@ -762,9 +764,8 @@ void SwitcherData::LoadUISettings(obs_data_t *obj)
(int)obs_data_get_int(obj, "windowPosY")};
windowSize = {(int)obs_data_get_int(obj, "windowWidth"),
(int)obs_data_get_int(obj, "windowHeight")};
loadSplitterPos(macroActionConditionSplitterPosition, obj,
"macroActionConditionSplitterPosition");
loadSplitterPos(macroListMacroEditSplitterPosition, obj,
LoadSplitterPos(macroListMacroEditSplitterPosition, obj,
"macroListMacroEditSplitterPosition");
}
@@ -800,12 +801,13 @@ void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
return;
}
if (switchIfNotMatching == NoMatch::SWITCH && nonMatchingScene) {
if (switchIfNotMatching == NoMatchBehavior::SWITCH &&
nonMatchingScene) {
match = true;
scene = nonMatchingScene;
transition = nullptr;
}
if (switchIfNotMatching == NoMatch::RANDOM_SWITCH) {
if (switchIfNotMatching == NoMatchBehavior::RANDOM_SWITCH) {
match = checkRandom(scene, transition, sleep);
}
}
@@ -956,11 +958,11 @@ void AdvSceneSwitcher::SetupGeneralTab()
{
PopulateSceneSelection(ui->noMatchSwitchScene, false);
if (switcher->switchIfNotMatching == SwitcherData::NoMatch::SWITCH) {
if (switcher->switchIfNotMatching == NoMatchBehavior::SWITCH) {
ui->noMatchSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(true);
} else if (switcher->switchIfNotMatching ==
SwitcherData::NoMatch::NO_SWITCH) {
NoMatchBehavior::NO_SWITCH) {
ui->noMatchDontSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(false);
} else {
@@ -996,6 +998,10 @@ void AdvSceneSwitcher::SetupGeneralTab()
ui->showTrayNotifications->setChecked(
switcher->showSystemTrayNotifications);
ui->uiHintsDisable->setChecked(switcher->disableHints);
ui->disableComboBoxFilter->setChecked(
switcher->disableFilterComboboxFilter);
FilterComboBox::SetFilterBehaviourEnabled(
!switcher->disableFilterComboboxFilter);
ui->warnPluginLoadFailure->setChecked(switcher->warnPluginLoadFailure);
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);

View File

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

View File

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

View File

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

View File

@@ -119,7 +119,7 @@ void SwitcherData::writeToStatusFile(const QString &msg)
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream << msg << endl;
stream << msg << "\n";
#else
stream << msg << Qt::endl;
#endif
@@ -177,17 +177,17 @@ static std::string getRemoteData(std::string &url)
{
std::string readBuffer;
switcher->curl.SetOpt(CURLOPT_URL, url.c_str());
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &readBuffer);
CurlHelper::SetOpt(CURLOPT_URL, url.c_str());
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
CurlHelper::SetOpt(CURLOPT_WRITEDATA, &readBuffer);
// Set timeout to at least one second
int timeout = switcher->interval / 1000;
if (timeout == 0) {
timeout = 1;
}
switcher->curl.SetOpt(CURLOPT_TIMEOUT, 1);
switcher->curl.Perform();
CurlHelper::SetOpt(CURLOPT_TIMEOUT, 1);
CurlHelper::Perform();
return readBuffer;
}
@@ -422,7 +422,7 @@ void AdvSceneSwitcher::SetupFileTab()
obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning2"));
ui->remoteFileWarningLabel->hide();
if (switcher->curl.Initialized()) {
if (CurlHelper::Initialized()) {
ui->libcurlWarning->setVisible(false);
}

View File

@@ -143,8 +143,7 @@ void AdvSceneSwitcher::SetupRandomTab()
border-radius: 7px; \
border-color: rgb(0,0,0,0) \
}");
if (switcher->switchIfNotMatching !=
SwitcherData::NoMatch::RANDOM_SWITCH) {
if (switcher->switchIfNotMatching != NoMatchBehavior::RANDOM_SWITCH) {
if (!switcher->disableHints) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0));

View File

@@ -1,5 +1,6 @@
#include "platform-funcs.hpp"
#include "hotkey.hpp"
#include "log-helper.hpp"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
@@ -25,9 +26,10 @@
#include <QStringList>
#include <QRegularExpression>
#include <QLibrary>
#ifdef USE_PROCPS
#ifdef PROCPS_AVAILABLE
#include <proc/readproc.h>
#else
#endif
#ifdef PROCPS2_AVAILABLE
#include <libproc2/pids.h>
#endif
#include <fstream>
@@ -53,6 +55,31 @@ std::chrono::high_resolution_clock::time_point lastMouseLeftClickTime{};
std::chrono::high_resolution_clock::time_point lastMouseMiddleClickTime{};
std::chrono::high_resolution_clock::time_point lastMouseRightClickTime{};
static QLibrary *libprocps = nullptr;
#ifdef PROCPS_AVAILABLE
typedef PROCTAB *(*openproc_func)(int flags);
typedef void (*closeproc_func)(PROCTAB *PT);
typedef proc_t *(*readproc_func)(PROCTAB *__restrict const PT,
proc_t *__restrict p);
static openproc_func openproc_ = nullptr;
static closeproc_func closeproc_ = nullptr;
static readproc_func readproc_ = nullptr;
#endif
static bool libprocpsSupported = false;
static QLibrary *libproc2 = nullptr;
#ifdef PROCPS2_AVAILABLE
typedef int (*procps_pids_new_func)(struct pids_info **info,
enum pids_item *items, int numitems);
typedef struct pids_stack *(*procps_pids_get_func)(struct pids_info *info,
enum pids_fetch_type which);
typedef int (*procps_pids_unref_func)(struct pids_info **info);
static procps_pids_new_func procps_pids_new_ = nullptr;
static procps_pids_get_func procps_pids_get_ = nullptr;
static procps_pids_unref_func procps_pids_unref_ = nullptr;
#endif
static bool libprocps2Supported = false;
Display *disp()
{
if (!xdisplay) {
@@ -332,52 +359,63 @@ bool IsFullscreen(const std::string &title)
return windowStatesAreSet(title, states);
}
std::optional<std::string> GetTextInWindow(const std::string &window)
std::optional<std::string> GetTextInWindow(const std::string &)
{
// Not implemented
return {};
}
#ifdef USE_PROCPS
void GetProcessList(QStringList &processes)
static void getProcessListProcps(QStringList &processes)
{
processes.clear();
PROCTAB *proc = openproc(PROC_FILLSTAT);
#ifdef PROCPS_AVAILABLE
PROCTAB *proc = openproc_(PROC_FILLSTAT);
proc_t proc_info;
memset(&proc_info, 0, sizeof(proc_info));
while (readproc(proc, &proc_info) != NULL) {
while (readproc_(proc, &proc_info) != NULL) {
QString procName(proc_info.cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;
}
}
closeproc(proc);
closeproc_(proc);
#endif
}
#else
void GetProcessList(QStringList &processes)
static void getProcessListProcps2(QStringList &processes)
{
processes.clear();
#ifdef PROCPS2_AVAILABLE
struct pids_info *info = NULL;
struct pids_stack *stack;
enum pids_item Items[] = {
PIDS_CMD,
};
if (procps_pids_new(&info, Items, sizeof(Items) / sizeof(Items[0])) <
if (procps_pids_new_(&info, Items, sizeof(Items) / sizeof(Items[0])) <
0) {
return;
}
while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
auto cmd = PIDS_VAL(0, str, stack, info);
QString procName(cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;
}
}
procps_pids_unref(&info);
}
procps_pids_unref_(&info);
#endif
}
void GetProcessList(QStringList &processes)
{
processes.clear();
if (libprocpsSupported) {
getProcessListProcps(processes);
return;
}
if (libprocps2Supported) {
getProcessListProcps2(processes);
}
}
long getForegroundProcessPid()
{
@@ -517,6 +555,9 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_8, XK_8},
{HotkeyType::Key_9, XK_9},
// Other
{HotkeyType::Key_Tilde, XK_asciitilde},
{HotkeyType::Key_F1, XK_F1},
{HotkeyType::Key_F2, XK_F2},
{HotkeyType::Key_F3, XK_F3},
@@ -633,6 +674,58 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
XFlush(display);
}
static void initXtst()
{
libXtstHandle = new QLibrary("libXtst", nullptr);
pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent");
int _;
canSimulateKeyPresses = pressFunc &&
XQueryExtension(disp(), "XTEST", &_, &_, &_);
}
static void initXss()
{
libXssHandle = new QLibrary("libXss", nullptr);
allocSSFunc = (XScreenSaverAllocInfoFunc)libXssHandle->resolve(
"XScreenSaverAllocInfo");
querySSFunc = (XScreenSaverQueryInfoFunc)libXssHandle->resolve(
"XScreenSaverQueryInfo");
int _;
canGetIdleTime = allocSSFunc && querySSFunc &&
XQueryExtension(disp(), ScreenSaverName, &_, &_, &_);
}
static void initProcps()
{
#ifdef PROCPS_AVAILABLE
libprocps = new QLibrary("libprocps", nullptr);
openproc_ = (openproc_func)libprocps->resolve("openproc");
closeproc_ = (closeproc_func)libprocps->resolve("closeproc");
readproc_ = (readproc_func)libprocps->resolve("readproc");
if (openproc_ && closeproc_ && readproc_) {
libprocpsSupported = true;
blog(LOG_INFO, "libprocps symbols resolved successfully!");
}
#endif
}
static void initProc2()
{
#ifdef PROCPS2_AVAILABLE
libproc2 = new QLibrary("libproc2", nullptr);
procps_pids_new_ =
(procps_pids_new_func)libproc2->resolve("procps_pids_new");
procps_pids_get_ =
(procps_pids_get_func)libproc2->resolve("procps_pids_get");
procps_pids_unref_ =
(procps_pids_unref_func)libproc2->resolve("procps_pids_unref");
if (procps_pids_new_ && procps_pids_get_ && procps_pids_unref_) {
libprocps2Supported = true;
blog(LOG_INFO, "libproc2 symbols resolved successfully!");
}
#endif
}
void PlatformInit()
{
auto display = disp();
@@ -640,31 +733,26 @@ void PlatformInit()
return;
}
libXtstHandle = new QLibrary("libXtst", nullptr);
pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent");
int _;
canSimulateKeyPresses = pressFunc &&
XQueryExtension(disp(), "XTEST", &_, &_, &_);
initXtst();
initXss();
initProcps();
initProc2();
}
libXssHandle = new QLibrary("libXss", nullptr);
allocSSFunc = (XScreenSaverAllocInfoFunc)libXssHandle->resolve(
"XScreenSaverAllocInfo");
querySSFunc = (XScreenSaverQueryInfoFunc)libXssHandle->resolve(
"XScreenSaverQueryInfo");
canGetIdleTime = allocSSFunc && querySSFunc &&
XQueryExtension(disp(), ScreenSaverName, &_, &_, &_);
static void cleanupHelper(QLibrary *lib)
{
if (lib) {
delete lib;
lib = nullptr;
}
}
void PlatformCleanup()
{
if (libXtstHandle) {
delete libXtstHandle;
libXtstHandle = nullptr;
}
if (libXssHandle) {
delete libXssHandle;
libXssHandle = nullptr;
}
cleanupHelper(libXtstHandle);
cleanupHelper(libXssHandle);
cleanupHelper(libprocps);
cleanupHelper(libproc2);
cleanupDisplay();
}

View File

@@ -1,5 +1,5 @@
#include "macro-action-audio.hpp"
#include "switcher-data.hpp"
#include "macro.hpp"
#include "utility.hpp"
namespace advss {
@@ -36,16 +36,41 @@ const static std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
"AdvSceneSwitcher.action.audio.fade.type.rate"},
};
namespace {
struct FadeInfo {
std::atomic_bool active = {false};
std::atomic_int id = {0};
};
}
static FadeInfo masterAudioFade;
static std::unordered_map<std::string, FadeInfo> audioFades;
constexpr auto fadeInterval = std::chrono::milliseconds(100);
constexpr float minFade = 0.000001f;
// For backwards compatibility
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(29, 0, 0)
static float get_master_volume()
{
return 1.f;
}
static void set_master_volume(float)
{
return;
}
#else
auto get_master_volume = obs_get_master_volume;
auto set_master_volume = obs_set_master_volume;
#endif
void MacroActionAudio::SetFadeActive(bool value)
{
if (_action == Action::SOURCE_VOLUME) {
switcher->activeAudioFades[_audioSource.ToString()].active =
value;
audioFades[_audioSource.ToString()].active = value;
} else {
switcher->masterAudioFade.active = value;
masterAudioFade.active = value;
}
}
@@ -53,14 +78,13 @@ bool MacroActionAudio::FadeActive()
{
bool active = true;
if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find(
_audioSource.ToString());
if (it == switcher->activeAudioFades.end()) {
auto it = audioFades.find(_audioSource.ToString());
if (it == audioFades.end()) {
return false;
}
active = it->second.active;
} else {
active = switcher->masterAudioFade.active;
active = masterAudioFade.active;
}
return active;
@@ -70,14 +94,13 @@ std::atomic_int *MacroActionAudio::GetFadeIdPtr()
{
if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find(
_audioSource.ToString());
if (it == switcher->activeAudioFades.end()) {
auto it = audioFades.find(_audioSource.ToString());
if (it == audioFades.end()) {
return nullptr;
}
return &it->second.id;
}
return &switcher->masterAudioFade.id;
return &masterAudioFade.id;
}
void MacroActionAudio::SetVolume(float vol)
@@ -87,7 +110,7 @@ void MacroActionAudio::SetVolume(float vol)
obs_source_set_volume(s, vol);
obs_source_release(s);
} else {
obs_set_master_volume(vol);
set_master_volume(vol);
}
}
@@ -102,7 +125,7 @@ float MacroActionAudio::GetVolume()
curVol = obs_source_get_volume(s);
obs_source_release(s);
} else {
curVol = obs_get_master_volume();
curVol = get_master_volume();
}
return curVol;
}
@@ -287,15 +310,13 @@ std::string MacroActionAudio::GetShortDesc() const
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[action, name] : actionTypes) {
if (action == MacroActionAudio::Action::MONITOR) {
if (obs_audio_monitoring_available()) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
} else {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
if (action == MacroActionAudio::Action::MONITOR &&
!obs_audio_monitoring_available()) {
continue;
}
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
}
@@ -437,12 +458,6 @@ void MacroActionAudioEdit::SetWidgetVisibility()
_balance->setVisible(_entryData->_action ==
MacroActionAudio::Action::BALANCE);
_fadeTypes->setDisabled(!_entryData->_fade);
_wait->setDisabled(!_entryData->_fade);
_abortActiveFade->setDisabled(!_entryData->_fade);
_duration->setDisabled(!_entryData->_fade);
_rate->setDisabled(!_entryData->_fade);
_fadeTypeLayout->removeWidget(_fade);
_fadeTypeLayout->removeWidget(_fadeTypes);
_fadeTypeLayout->removeWidget(_duration);
@@ -474,6 +489,20 @@ void MacroActionAudioEdit::SetWidgetVisibility()
hasVolumeControl(_entryData->_action));
SetLayoutVisible(_fadeOptionsLayout,
hasVolumeControl(_entryData->_action));
_abortActiveFade->setVisible(hasVolumeControl(_entryData->_action) &&
_entryData->_fade);
_wait->setVisible(hasVolumeControl(_entryData->_action) &&
_entryData->_fade);
// TODO: Remove this in a future version:
if (_entryData->_action != MacroActionAudio::Action::MASTER_VOLUME &&
obs_get_version() >= MAKE_SEMANTIC_VERSION(29, 0, 0)) {
_actions->removeItem(_actions->findText(obs_module_text(
actionTypes.at(MacroActionAudio::Action::MASTER_VOLUME)
.c_str())));
}
updateGeometry();
adjustSize();
}

View File

@@ -0,0 +1,256 @@
#include "macro-action-clipboard.hpp"
#include "curl-helper.hpp"
#include <QApplication>
#include <QClipboard>
#include <QImage>
#include <QMimeData>
namespace advss {
const std::string MacroActionClipboard::id = "clipboard";
bool MacroActionClipboard::_registered = MacroActionFactory::Register(
MacroActionClipboard::id,
{MacroActionClipboard::Create, MacroActionClipboardEdit::Create,
"AdvSceneSwitcher.action.clipboard"});
const static std::map<MacroActionClipboard::Action, std::string> actionTypes = {
{MacroActionClipboard::Action::COPY_TEXT,
"AdvSceneSwitcher.action.clipboard.type.copy.text"},
{MacroActionClipboard::Action::COPY_IMAGE,
"AdvSceneSwitcher.action.clipboard.type.copy.image"},
};
static size_t writeCallback(void *ptr, size_t size, size_t nmemb,
QByteArray *buffer)
{
buffer->append((char *)ptr, nmemb);
return size * nmemb;
}
static std::optional<QImage> getImageFromUrl(const char *url)
{
QByteArray response;
CurlHelper::SetOpt(CURLOPT_URL, url);
CurlHelper::SetOpt(CURLOPT_HTTPGET, 1L);
CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, 30000);
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, writeCallback);
CurlHelper::SetOpt(CURLOPT_WRITEDATA, &response);
auto code = CurlHelper::Perform();
if (code != CURLE_OK) {
blog(LOG_WARNING,
"Retrieving image failed in %s with error: %s", __func__,
CurlHelper::GetError(code));
return {};
}
return QImage::fromData(response);
}
static void setMimeTypeParams(ClipboardQueueParams *params,
QClipboard *clipboard)
{
auto mimeData = clipboard->mimeData();
if (!mimeData) {
return;
}
auto mimeTypeList = mimeData->formats();
if (mimeTypeList.empty()) {
return;
}
params->mimeTypePrimary = mimeTypeList.first().toStdString();
params->mimeTypeAll = mimeTypeList.join(" ").toStdString();
}
static void copyText(void *param)
{
auto params = static_cast<ClipboardTextQueueParams *>(param);
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(params->text.c_str());
setMimeTypeParams(params, clipboard);
}
static void copyImageFromUrl(void *param)
{
auto params = static_cast<ClipboardImageQueueParams *>(param);
auto url = params->url.c_str();
auto image = getImageFromUrl(url);
if (!image || (*image).isNull()) {
blog(LOG_WARNING, "Failed to convert %s URL to image!", url);
return;
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setImage(*image);
setMimeTypeParams(params, clipboard);
}
bool MacroActionClipboard::PerformAction()
{
switch (_action) {
case Action::COPY_TEXT: {
ClipboardTextQueueParams params{"", "", _text};
obs_queue_task(OBS_TASK_UI, copyText, &params, true);
SetTempVarValues(params);
break;
}
case Action::COPY_IMAGE: {
ClipboardImageQueueParams params{"", "", _url};
obs_queue_task(OBS_TASK_UI, copyImageFromUrl, &params, true);
SetTempVarValues(params);
break;
}
default:
break;
}
return true;
}
void MacroActionClipboard::SetupTempVars()
{
MacroAction::SetupTempVars();
AddTempvar(
"mimeType.primary",
obs_module_text(
"AdvSceneSwitcher.tempVar.clipboard.mimeType.primary"),
obs_module_text(
"AdvSceneSwitcher.tempVar.clipboard.mimeType.primary.description"));
AddTempvar(
"mimeType.all",
obs_module_text(
"AdvSceneSwitcher.tempVar.clipboard.mimeType.all"),
obs_module_text(
"AdvSceneSwitcher.tempVar.clipboard.mimeType.all.description"));
}
void MacroActionClipboard::SetTempVarValues(const ClipboardQueueParams &params)
{
SetTempVarValue("mimeType.primary", params.mimeTypePrimary);
SetTempVarValue("mimeType.all", params.mimeTypeAll);
}
bool MacroActionClipboard::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_text.Save(obj, "text");
_url.Save(obj, "url");
return true;
}
bool MacroActionClipboard::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_text.Load(obj, "text");
_url.Load(obj, "url");
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[action, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
}
MacroActionClipboardEdit::MacroActionClipboardEdit(
QWidget *parent, std::shared_ptr<MacroActionClipboard> entryData)
: QWidget(parent),
_actions(new FilterComboBox(this)),
_text(new VariableLineEdit(this)),
_url(new VariableLineEdit(this))
{
populateActionSelection(_actions);
_url->setToolTip(obs_module_text(
"AdvSceneSwitcher.action.clipboard.copy.image.url.tooltip"));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_text, SIGNAL(editingFinished()), this,
SLOT(TextChanged()));
QWidget::connect(_url, SIGNAL(editingFinished()), this,
SLOT(UrlChanged()));
auto mainLayout = new QHBoxLayout();
mainLayout->addWidget(_actions);
mainLayout->addWidget(_text);
mainLayout->addWidget(_url);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionClipboardEdit::ActionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionClipboard::Action>(
_actions->itemData(index).toInt());
SetWidgetVisibility();
}
void MacroActionClipboardEdit::TextChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_text = _text->text().toStdString();
}
void MacroActionClipboardEdit::UrlChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_url = _url->text().toStdString();
}
void MacroActionClipboardEdit::SetWidgetVisibility()
{
_text->setVisible(_entryData->_action ==
MacroActionClipboard::Action::COPY_TEXT);
_url->setVisible(_entryData->_action ==
MacroActionClipboard::Action::COPY_IMAGE);
adjustSize();
updateGeometry();
}
void MacroActionClipboardEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_action)));
_text->setText(_entryData->_text);
_url->setText(_entryData->_url);
SetWidgetVisibility();
}
} // namespace advss

View File

@@ -0,0 +1,89 @@
#pragma once
#include "macro-action-edit.hpp"
#include "filter-combo-box.hpp"
#include <variable-line-edit.hpp>
namespace advss {
struct ClipboardQueueParams {
std::string mimeTypePrimary;
std::string mimeTypeAll;
};
struct ClipboardTextQueueParams : ClipboardQueueParams {
StringVariable text;
};
struct ClipboardImageQueueParams : ClipboardQueueParams {
StringVariable url;
};
class MacroActionClipboard : public MacroAction {
public:
MacroActionClipboard(Macro *m) : MacroAction(m) {}
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionClipboard>(m);
}
std::string GetId() const { return id; };
bool PerformAction();
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
enum class Action {
COPY_TEXT,
COPY_IMAGE,
};
Action _action = Action::COPY_TEXT;
StringVariable _text = obs_module_text(
"AdvSceneSwitcher.action.clipboard.copy.text.text.placeholder");
StringVariable _url = obs_module_text(
"AdvSceneSwitcher.action.clipboard.copy.image.url.placeholder");
private:
void SetupTempVars();
void SetTempVarValues(const ClipboardQueueParams &params);
static bool _registered;
static const std::string id;
};
class MacroActionClipboardEdit : public QWidget {
Q_OBJECT
public:
MacroActionClipboardEdit(
QWidget *parent,
std::shared_ptr<MacroActionClipboard> entryData = nullptr);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionClipboardEdit(
parent, std::dynamic_pointer_cast<MacroActionClipboard>(
action));
}
void UpdateEntryData();
protected:
std::shared_ptr<MacroActionClipboard> _entryData;
private slots:
void ActionChanged(int index);
void TextChanged();
void UrlChanged();
private:
void SetWidgetVisibility();
bool _loading = true;
FilterComboBox *_actions;
VariableLineEdit *_text;
VariableLineEdit *_url;
};
} // namespace advss

View File

@@ -1,67 +1,20 @@
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-action-edit.hpp"
#include "macro-helpers.hpp"
#include "macro.hpp"
#include "macro-properties.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-action-scene-switch.hpp"
#include "section.hpp"
#include "switch-button.hpp"
#include "utility.hpp"
#include <QGraphicsOpacityEffect>
namespace advss {
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{
static std::map<std::string, MacroActionInfo> _methods;
return _methods;
}
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createFunc(m);
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createWidgetFunc(parent, action);
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto &[_, action] : MacroActionFactory::GetActionTypes()) {
for (const auto &[_, action] : MacroActionFactory::GetActionTypes()) {
QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
@@ -77,27 +30,34 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
const std::string &id)
: MacroSegmentEdit(switcher->macroProperties._highlightActions, parent),
_actionSelection(new QComboBox()),
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
parent),
_actionSelection(new FilterComboBox()),
_enable(new SwitchButton()),
_entryData(entryData)
{
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ActionEnableChanged(bool)));
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
SLOT(EnableHighlight(bool)));
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
SLOT(UpdateActionState()));
populateActionSelection(_actionSelection);
_section->AddHeaderWidget(_enable);
_section->AddHeaderWidget(_actionSelection);
_section->AddHeaderWidget(_headerInfo);
QVBoxLayout *actionLayout = new QVBoxLayout;
auto actionLayout = new QVBoxLayout;
actionLayout->setContentsMargins({7, 7, 7, 7});
actionLayout->addWidget(_section);
_contentLayout->addLayout(actionLayout);
QHBoxLayout *mainLayout = new QHBoxLayout;
auto mainLayout = new QHBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addWidget(_frame);
@@ -106,6 +66,7 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
_entryData = entryData;
UpdateEntryData(id);
_actionStateTimer.start(300);
_loading = false;
}
@@ -115,15 +76,20 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
return;
}
std::string id = MacroActionFactory::GetIdByName(text);
if (id.empty()) {
return;
}
HeaderInfoChanged("");
auto idx = _entryData->get()->GetIndex();
auto macro = _entryData->get()->GetMacro();
std::string id = MacroActionFactory::GetIdByName(text);
HeaderInfoChanged("");
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
_entryData->reset();
*_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
(*_entryData)->PostLoad();
}
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
@@ -136,6 +102,9 @@ void MacroActionEdit::UpdateEntryData(const std::string &id)
{
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
const bool enabled = (*_entryData)->Enabled();
_enable->setChecked(enabled);
SetDisableEffect(!enabled);
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
@@ -150,7 +119,44 @@ void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
_entryData = data;
}
std::shared_ptr<MacroSegment> MacroActionEdit::Data()
void MacroActionEdit::SetDisableEffect(bool value)
{
if (value) {
auto effect = new QGraphicsOpacityEffect(this);
effect->setOpacity(0.5);
_section->setGraphicsEffect(effect);
} else {
_section->setGraphicsEffect(nullptr);
}
}
void MacroActionEdit::ActionEnableChanged(bool value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
(*_entryData)->SetEnabled(value);
SetDisableEffect(!value);
}
void MacroActionEdit::UpdateActionState()
{
if (_loading || !_entryData) {
return;
}
SetEnableAppearance((*_entryData)->Enabled());
}
void MacroActionEdit::SetEnableAppearance(bool value)
{
_enable->setChecked(value);
SetDisableEffect(!value);
}
std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
{
return *_entryData;
}
@@ -174,7 +180,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
id = temp.GetId();
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
macro->Actions().emplace(
macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
@@ -182,10 +188,11 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data);
macro->Actions().at(idx)->PostLoad();
obs_data_release(data);
}
macro->UpdateActionIndices();
actionsList->Insert(
ui->actionsList->Insert(
idx,
new MacroActionEdit(this, &macro->Actions()[idx], id));
SetActionData(*macro);
@@ -209,7 +216,7 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
if (currentActionIdx != -1) {
MacroActionSelectionChanged(currentActionIdx + 1);
}
actionsList->SetHelpMsgVisible(false);
ui->actionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::RemoveMacroAction(int idx)
@@ -224,11 +231,11 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
}
{
std::lock_guard<std::mutex> lock(switcher->m);
actionsList->Remove(idx);
auto lock = LockContext();
ui->actionsList->Remove(idx);
macro->Actions().erase(macro->Actions().begin() + idx);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateActionIndices();
SetActionData(*macro);
}
@@ -251,23 +258,115 @@ void AdvSceneSwitcher::on_actionRemove_clicked()
MacroActionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_actionUp_clicked()
void AdvSceneSwitcher::on_actionTop_clicked()
{
if (currentActionIdx == -1) {
return;
}
MacroActionReorder(0, currentActionIdx);
MacroActionSelectionChanged(0);
}
void AdvSceneSwitcher::on_actionUp_clicked()
{
if (currentActionIdx == -1 || currentActionIdx == 0) {
return;
}
MoveMacroActionUp(currentActionIdx);
MacroActionSelectionChanged(currentActionIdx - 1);
}
void AdvSceneSwitcher::on_actionDown_clicked()
{
if (currentActionIdx == -1) {
if (currentActionIdx == -1 ||
currentActionIdx == ui->actionsList->ContentLayout()->count() - 1) {
return;
}
MoveMacroActionDown(currentActionIdx);
MacroActionSelectionChanged(currentActionIdx + 1);
}
void AdvSceneSwitcher::on_actionBottom_clicked()
{
if (currentActionIdx == -1) {
return;
}
const int newIdx = ui->actionsList->ContentLayout()->count() - 1;
MacroActionReorder(newIdx, currentActionIdx);
MacroActionSelectionChanged(newIdx);
}
void AdvSceneSwitcher::on_elseActionAdd_clicked()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (currentElseActionIdx == -1) {
AddMacroElseAction((int)macro->ElseActions().size());
} else {
AddMacroElseAction(currentElseActionIdx + 1);
}
if (currentElseActionIdx != -1) {
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
}
ui->elseActionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::on_elseActionRemove_clicked()
{
if (currentElseActionIdx == -1) {
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
RemoveMacroElseAction((int)macro->Actions().size() - 1);
} else {
RemoveMacroElseAction(currentElseActionIdx);
}
MacroElseActionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_elseActionTop_clicked()
{
if (currentElseActionIdx == -1) {
return;
}
MacroElseActionReorder(0, currentElseActionIdx);
MacroElseActionSelectionChanged(0);
}
void AdvSceneSwitcher::on_elseActionUp_clicked()
{
if (currentElseActionIdx == -1 || currentElseActionIdx == 0) {
return;
}
MoveMacroElseActionUp(currentElseActionIdx);
MacroElseActionSelectionChanged(currentElseActionIdx - 1);
}
void AdvSceneSwitcher::on_elseActionDown_clicked()
{
if (currentElseActionIdx == -1 ||
currentElseActionIdx ==
ui->elseActionsList->ContentLayout()->count() - 1) {
return;
}
MoveMacroElseActionDown(currentElseActionIdx);
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
}
void AdvSceneSwitcher::on_elseActionBottom_clicked()
{
if (currentElseActionIdx == -1) {
return;
}
const int newIdx = ui->elseActionsList->ContentLayout()->count() - 1;
MacroElseActionReorder(newIdx, currentElseActionIdx);
MacroElseActionSelectionChanged(newIdx);
}
void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
@@ -277,15 +376,15 @@ void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
std::swap(pos1, pos2);
}
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
iter_swap(m->Actions().begin() + pos1, m->Actions().begin() + pos2);
m->UpdateActionIndices();
auto widget1 = static_cast<MacroActionEdit *>(
actionsList->ContentLayout()->takeAt(pos1)->widget());
ui->actionsList->ContentLayout()->takeAt(pos1)->widget());
auto widget2 = static_cast<MacroActionEdit *>(
actionsList->ContentLayout()->takeAt(pos2 - 1)->widget());
actionsList->Insert(pos1, widget2);
actionsList->Insert(pos2, widget1);
ui->actionsList->ContentLayout()->takeAt(pos2 - 1)->widget());
ui->actionsList->Insert(pos1, widget2);
ui->actionsList->Insert(pos2, widget1);
SetActionData(*m);
emit(MacroSegmentOrderChanged());
}
@@ -320,24 +419,159 @@ void AdvSceneSwitcher::MoveMacroActionDown(int idx)
HighlightAction(idx + 1);
}
void AdvSceneSwitcher::MacroActionSelectionChanged(int idx)
void AdvSceneSwitcher::MacroElseActionSelectionChanged(int idx)
{
SetupMacroSegmentSelection(MacroSection::ELSE_ACTIONS, idx);
}
void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
actionsList->SetSelection(idx);
conditionsList->SetSelection(-1);
if (idx < 0 || (unsigned)idx >= macro->Actions().size()) {
currentActionIdx = -1;
} else {
currentActionIdx = idx;
lastInteracted = MacroSection::ACTIONS;
if (to == from || from < 0 || from > (int)macro->ElseActions().size() ||
to < 0 || to > (int)macro->ElseActions().size()) {
return;
}
currentConditionIdx = -1;
HighlightControls();
{
auto lock = LockContext();
auto action = macro->ElseActions().at(from);
macro->ElseActions().erase(macro->ElseActions().begin() + from);
macro->ElseActions().insert(macro->ElseActions().begin() + to,
action);
macro->UpdateElseActionIndices();
ui->elseActionsList->ContentLayout()->insertItem(
to, ui->elseActionsList->ContentLayout()->takeAt(from));
SetElseActionData(*macro);
}
HighlightElseAction(to);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroElseAction(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
return;
}
std::string id;
if (idx - 1 >= 0) {
id = macro->ElseActions().at(idx - 1)->GetId();
} else {
MacroActionSwitchScene temp(nullptr);
id = temp.GetId();
}
{
auto lock = LockContext();
macro->ElseActions().emplace(
macro->ElseActions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
OBSDataAutoRelease data = obs_data_create();
macro->ElseActions().at(idx - 1)->Save(data);
macro->ElseActions().at(idx)->Load(data);
}
macro->ElseActions().at(idx)->PostLoad();
macro->UpdateElseActionIndices();
ui->elseActionsList->Insert(
idx, new MacroActionEdit(
this, &macro->ElseActions()[idx], id));
SetElseActionData(*macro);
}
HighlightElseAction(idx);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->ElseActions().size()) {
return;
}
{
auto lock = LockContext();
ui->elseActionsList->Remove(idx);
macro->ElseActions().erase(macro->ElseActions().begin() + idx);
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices();
SetActionData(*macro);
}
MacroElseActionSelectionChanged(-1);
lastInteracted = MacroSection::ELSE_ACTIONS;
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::SwapElseActions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
return;
}
if (pos1 > pos2) {
std::swap(pos1, pos2);
}
auto lock = LockContext();
iter_swap(m->ElseActions().begin() + pos1,
m->ElseActions().begin() + pos2);
m->UpdateElseActionIndices();
auto widget1 = static_cast<MacroActionEdit *>(
ui->elseActionsList->ContentLayout()->takeAt(pos1)->widget());
auto widget2 = static_cast<MacroActionEdit *>(
ui->elseActionsList->ContentLayout()
->takeAt(pos2 - 1)
->widget());
ui->elseActionsList->Insert(pos1, widget2);
ui->elseActionsList->Insert(pos2, widget1);
SetElseActionData(*m);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::MoveMacroElseActionUp(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 1 || idx >= (int)macro->ElseActions().size()) {
return;
}
SwapElseActions(macro.get(), idx, idx - 1);
HighlightElseAction(idx - 1);
}
void AdvSceneSwitcher::MoveMacroElseActionDown(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->ElseActions().size() - 1) {
return;
}
SwapElseActions(macro.get(), idx, idx + 1);
HighlightElseAction(idx + 1);
}
void AdvSceneSwitcher::MacroActionSelectionChanged(int idx)
{
SetupMacroSegmentSelection(MacroSection::ACTIONS, idx);
}
void AdvSceneSwitcher::MacroActionReorder(int to, int from)
@@ -347,18 +581,18 @@ void AdvSceneSwitcher::MacroActionReorder(int to, int from)
return;
}
if (from < 0 || from > (int)macro->Actions().size() || to < 0 ||
to > (int)macro->Actions().size()) {
if (to == from || from < 0 || from > (int)macro->Actions().size() ||
to < 0 || to > (int)macro->Actions().size()) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto action = macro->Actions().at(from);
macro->Actions().erase(macro->Actions().begin() + from);
macro->Actions().insert(macro->Actions().begin() + to, action);
macro->UpdateActionIndices();
actionsList->ContentLayout()->insertItem(
to, actionsList->ContentLayout()->takeAt(from));
ui->actionsList->ContentLayout()->insertItem(
to, ui->actionsList->ContentLayout()->takeAt(from));
SetActionData(*macro);
}
HighlightAction(to);

View File

@@ -1,36 +1,13 @@
#pragma once
#include "macro-action.hpp"
#include "macro-action-factory.hpp"
#include "filter-combo-box.hpp"
#include <QComboBox>
#include <memory>
namespace advss {
struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>);
TCreateMethod _createFunc = nullptr;
TCreateWidgetMethod _createWidgetFunc = nullptr;
std::string _name;
};
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return GetMap(); }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<std::string, MacroActionInfo> &GetMap();
};
class SwitchButton;
class MacroActionEdit : public MacroSegmentEdit {
Q_OBJECT
@@ -44,13 +21,19 @@ public:
private slots:
void ActionSelectionChanged(const QString &text);
void ActionEnableChanged(bool);
void UpdateActionState();
private:
std::shared_ptr<MacroSegment> Data();
std::shared_ptr<MacroSegment> Data() const;
void SetDisableEffect(bool);
void SetEnableAppearance(bool);
QComboBox *_actionSelection;
FilterComboBox *_actionSelection;
SwitchButton *_enable;
std::shared_ptr<MacroAction> *_entryData;
QTimer _actionStateTimer;
bool _loading = true;
};

View File

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

View File

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

View File

@@ -15,27 +15,75 @@ const static std::map<MacroActionFilter::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.filter.type.enable"},
{MacroActionFilter::Action::DISABLE,
"AdvSceneSwitcher.action.filter.type.disable"},
{MacroActionFilter::Action::TOGGLE,
"AdvSceneSwitcher.action.filter.type.toggle"},
{MacroActionFilter::Action::SETTINGS,
"AdvSceneSwitcher.action.filter.type.settings"},
};
bool MacroActionFilter::PerformAction()
const static std::map<MacroActionFilter::SettingsInputMethod, std::string>
inputMethods = {
{MacroActionFilter::SettingsInputMethod::INDIVIDUAL_MANUAL,
"AdvSceneSwitcher.action.filter.inputMethod.individualManual"},
{MacroActionFilter::SettingsInputMethod::INDIVIDUAL_TEMPVAR,
"AdvSceneSwitcher.action.filter.inputMethod.individualTempvar"},
{MacroActionFilter::SettingsInputMethod::JSON_STRING,
"AdvSceneSwitcher.action.filter.inputMethod.json"},
};
static void performActionHelper(
MacroActionFilter::Action action, const OBSWeakSource &filter,
MacroActionFilter::SettingsInputMethod settingsInputMethod,
const SourceSetting &setting, const std::string &manualSettingValue,
const TempVariableRef &tempVar, Macro *macro,
const StringVariable &settingsString)
{
auto s = obs_weak_source_get_source(_filter.GetFilter(_source));
switch (_action) {
case Action::ENABLE:
obs_source_set_enabled(s, true);
OBSSourceAutoRelease source = obs_weak_source_get_source(filter);
switch (action) {
case MacroActionFilter::Action::ENABLE:
obs_source_set_enabled(source, true);
break;
case Action::DISABLE:
obs_source_set_enabled(s, false);
case MacroActionFilter::Action::DISABLE:
obs_source_set_enabled(source, false);
break;
case Action::SETTINGS:
SetSourceSettings(s, _settings);
case MacroActionFilter::Action::TOGGLE:
obs_source_set_enabled(source, !obs_source_enabled(source));
break;
case MacroActionFilter::Action::SETTINGS:
switch (settingsInputMethod) {
case MacroActionFilter::SettingsInputMethod::INDIVIDUAL_MANUAL:
SetSourceSetting(source, setting, manualSettingValue);
break;
case MacroActionFilter::SettingsInputMethod::INDIVIDUAL_TEMPVAR: {
auto var = tempVar.GetTempVariable(macro);
if (!var) {
break;
}
auto value = var->Value();
if (!value) {
break;
}
SetSourceSetting(source, setting, *value);
break;
}
case MacroActionFilter::SettingsInputMethod::JSON_STRING:
SetSourceSettings(source, settingsString);
break;
}
break;
default:
break;
}
obs_source_release(s);
}
bool MacroActionFilter::PerformAction()
{
auto filters = _filter.GetFilters(_source);
for (const auto &filter : filters) {
performActionHelper(_action, filter, _settingsInputMethod,
_setting, _manualSettingValue, _tempVar,
GetMacro(), _settingsString);
}
return true;
}
@@ -59,7 +107,13 @@ bool MacroActionFilter::Save(obs_data_t *obj) const
_source.Save(obj);
_filter.Save(obj, "filter");
obs_data_set_int(obj, "action", static_cast<int>(_action));
_settings.Save(obj, "settings");
obs_data_set_int(obj, "inputMethod",
static_cast<int>(_settingsInputMethod));
_setting.Save(obj);
_manualSettingValue.Save(obj, "manualSettingValue");
_tempVar.Save(obj);
_settingsString.Save(obj, "settings");
obs_data_set_int(obj, "version", 1);
return true;
}
@@ -68,8 +122,28 @@ bool MacroActionFilter::Load(obs_data_t *obj)
MacroAction::Load(obj);
_source.Load(obj);
_filter.Load(obj, _source, "filter");
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_settings.Load(obj, "settings");
// TODO: Remove this fallback in future version
if (!obs_data_has_user_value(obj, "version")) {
const auto value = obs_data_get_int(obj, "action");
if (value == 2) {
_action = Action::SETTINGS;
} else {
_action = static_cast<Action>(value);
}
} else {
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
}
// TODO: Remove fallback in future version
if (obs_data_has_user_value(obj, "inputMethod")) {
_settingsInputMethod = static_cast<SettingsInputMethod>(
obs_data_get_int(obj, "inputMethod"));
} else {
_settingsInputMethod = SettingsInputMethod::JSON_STRING;
}
_setting.Load(obj);
_settingsString.Load(obj, "settings");
_manualSettingValue.Load(obj, "manualSettingValue");
_tempVar.Load(obj, GetMacro());
return true;
}
@@ -88,6 +162,15 @@ static inline void populateActionSelection(QComboBox *list)
}
}
static inline void populateSettingsInputMethods(QComboBox *list)
{
list->clear();
for (const auto &[value, name] : inputMethods) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
MacroActionFilterEdit::MacroActionFilterEdit(
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
: QWidget(parent),
@@ -96,7 +179,12 @@ MacroActionFilterEdit::MacroActionFilterEdit(
_actions(new QComboBox()),
_getSettings(new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.filter.getSettings"))),
_settings(new VariableTextEdit(this))
_settingsLayout(new QHBoxLayout()),
_settingsInputMethods(new QComboBox(this)),
_manualSettingValue(new VariableTextEdit(this, 5, 1, 1)),
_tempVars(new TempVariableSelection(this)),
_filterSettings(new SourceSettingSelection(this)),
_settingsString(new VariableTextEdit(this))
{
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
@@ -105,6 +193,8 @@ MacroActionFilterEdit::MacroActionFilterEdit(
sources.sort();
_sources->SetSourceNameList(sources);
populateSettingsInputMethods(_settingsInputMethods);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources,
@@ -115,27 +205,48 @@ MacroActionFilterEdit::MacroActionFilterEdit(
SLOT(FilterChanged(const FilterSelection &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QWidget::connect(_settingsString, SIGNAL(textChanged()), this,
SLOT(SettingsStringChanged()));
QWidget::connect(_tempVars,
SIGNAL(SelectionChanged(const TempVariableRef &)),
this, SLOT(SelectionChanged(const TempVariableRef &)));
QWidget::connect(_settingsInputMethods,
SIGNAL(currentIndexChanged(int)), this,
SLOT(SettingsInputMethodChanged(int)));
QWidget::connect(_manualSettingValue, SIGNAL(textChanged()), this,
SLOT(ManualSettingsValueChanged()));
QWidget::connect(_filterSettings,
SIGNAL(SelectionChanged(const SourceSetting &)), this,
SLOT(SelectionChanged(const SourceSetting &)));
QHBoxLayout *entryLayout = new QHBoxLayout;
auto entrylayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources}, {"{{filters}}", _filters},
{"{{actions}}", _actions}, {"{{settings}}", _settings},
{"{{sources}}", _sources},
{"{{filters}}", _filters},
{"{{actions}}", _actions},
{"{{settings}}", _filterSettings},
{"{{settingsInputMethod}}", _settingsInputMethods},
{"{{settingValue}}", _manualSettingValue},
{"{{tempVar}}", _tempVars},
{"{{getSettings}}", _getSettings},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
entryLayout, widgetPlaceholders);
entrylayout, widgetPlaceholders);
_settingsLayout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.filter.entry.settings"),
_settingsLayout, widgetPlaceholders);
QHBoxLayout *buttonLayout = new QHBoxLayout;
auto buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
buttonLayout->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_settings);
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(entrylayout);
mainLayout->addLayout(_settingsLayout);
mainLayout->addWidget(_settingsString);
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
@@ -153,12 +264,16 @@ void MacroActionFilterEdit::UpdateEntryData()
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->SetSource(_entryData->_source);
_filters->SetFilter(_entryData->_source, _entryData->_filter);
_settings->setPlainText(_entryData->_settings);
SetWidgetVisibility(_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
adjustSize();
updateGeometry();
_settingsString->setPlainText(_entryData->_settingsString);
const auto filters =
_entryData->_filter.GetFilters(_entryData->_source);
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
_filterSettings->SetSetting(_entryData->_setting);
_settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData(
static_cast<int>(_entryData->_settingsInputMethod)));
_tempVars->SetVariable(_entryData->_tempVar);
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
SetWidgetVisibility();
}
void MacroActionFilterEdit::SourceChanged(const SourceSelection &source)
@@ -177,8 +292,13 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
return;
}
auto lock = LockContext();
_entryData->_filter = filter;
{
auto lock = LockContext();
_entryData->_filter = filter;
}
const auto filters =
_entryData->_filter.GetFilters(_entryData->_source);
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -191,38 +311,130 @@ void MacroActionFilterEdit::ActionChanged(int value)
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionFilter::Action>(value);
SetWidgetVisibility(_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
SetWidgetVisibility();
}
void MacroActionFilterEdit::GetSettingsClicked()
{
if (_loading || !_entryData ||
!_entryData->_filter.GetFilter(_entryData->_source)) {
_entryData->_filter.GetFilters(_entryData->_source).empty()) {
return;
}
_settings->setPlainText(FormatJsonString(GetSourceSettings(
_entryData->_filter.GetFilter(_entryData->_source))));
switch (_entryData->_settingsInputMethod) {
case MacroActionFilter::SettingsInputMethod::INDIVIDUAL_MANUAL: {
const auto filters =
_entryData->_filter.GetFilters(_entryData->_source);
_manualSettingValue->setPlainText(GetSourceSettingValue(
filters.empty() ? nullptr : filters.at(0),
_entryData->_setting));
break;
}
case MacroActionFilter::SettingsInputMethod::INDIVIDUAL_TEMPVAR:
break;
case MacroActionFilter::SettingsInputMethod::JSON_STRING: {
const auto filters =
_entryData->_filter.GetFilters(_entryData->_source);
_settingsString->setPlainText(
FormatJsonString(GetSourceSettings(
filters.empty() ? nullptr : filters.at(0))));
break;
}
}
}
void MacroActionFilterEdit::SettingsChanged()
void MacroActionFilterEdit::SettingsStringChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_settings = _settings->toPlainText().toStdString();
_entryData->_settingsString =
_settingsString->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionFilterEdit::SetWidgetVisibility(bool showSettings)
void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
{
_settings->setVisible(showSettings);
_getSettings->setVisible(showSettings);
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_settingsInputMethod =
static_cast<MacroActionFilter::SettingsInputMethod>(
_settingsInputMethods->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionFilterEdit::SelectionChanged(const TempVariableRef &var)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_tempVar = var;
}
void MacroActionFilterEdit::SelectionChanged(const SourceSetting &setting)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_setting = setting;
}
void MacroActionFilterEdit::ManualSettingsValueChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_manualSettingValue =
_manualSettingValue->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionFilterEdit::SetWidgetVisibility()
{
SetLayoutVisible(_settingsLayout,
_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
_filterSettings->setVisible(
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod !=
MacroActionFilter::SettingsInputMethod::JSON_STRING);
_settingsString->setVisible(
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionFilter::SettingsInputMethod::JSON_STRING);
_getSettings->setVisible(_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
_tempVars->setVisible(_entryData->_action ==
MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionFilter::SettingsInputMethod::
INDIVIDUAL_TEMPVAR);
if (_entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionFilter::SettingsInputMethod::INDIVIDUAL_MANUAL) {
RemoveStretchIfPresent(_settingsLayout);
_manualSettingValue->show();
} else {
AddStretchIfNecessary(_settingsLayout);
_manualSettingValue->hide();
}
adjustSize();
updateGeometry();
}

View File

@@ -3,6 +3,7 @@
#include "variable-text-edit.hpp"
#include "source-selection.hpp"
#include "filter-selection.hpp"
#include "source-setting.hpp"
#include <QComboBox>
#include <QSpinBox>
@@ -27,13 +28,25 @@ public:
enum class Action {
ENABLE,
DISABLE,
TOGGLE,
SETTINGS,
};
enum class SettingsInputMethod {
INDIVIDUAL_MANUAL,
INDIVIDUAL_TEMPVAR,
JSON_STRING,
};
SettingsInputMethod _settingsInputMethod =
SettingsInputMethod::INDIVIDUAL_MANUAL;
SourceSelection _source;
FilterSelection _filter;
Action _action = Action::ENABLE;
StringVariable _settings = "";
StringVariable _settingsString = "";
TempVariableRef _tempVar;
StringVariable _manualSettingValue = "";
SourceSetting _setting;
private:
static bool _registered;
@@ -61,7 +74,11 @@ private slots:
void FilterChanged(const FilterSelection &);
void ActionChanged(int value);
void GetSettingsClicked();
void SettingsChanged();
void SettingsStringChanged();
void SettingsInputMethodChanged(int);
void SelectionChanged(const TempVariableRef &);
void SelectionChanged(const SourceSetting &);
void ManualSettingsValueChanged();
signals:
void HeaderInfoChanged(const QString &);
@@ -70,11 +87,17 @@ protected:
FilterSelectionWidget *_filters;
QComboBox *_actions;
QPushButton *_getSettings;
VariableTextEdit *_settings;
QHBoxLayout *_settingsLayout;
QComboBox *_settingsInputMethods;
VariableTextEdit *_manualSettingValue;
TempVariableSelection *_tempVars;
SourceSettingSelection *_filterSettings;
VariableTextEdit *_settingsString;
std::shared_ptr<MacroActionFilter> _entryData;
private:
void SetWidgetVisibility(bool);
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -2,6 +2,7 @@
#include "platform-funcs.hpp"
#include "utility.hpp"
#include <thread>
#include <obs-interaction.h>
namespace advss {
@@ -13,7 +14,27 @@ bool MacroActionHotkey::_registered = MacroActionFactory::Register(
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
"AdvSceneSwitcher.action.hotkey"});
static std::unordered_map<HotkeyType, long> keyTable = {
static const std::map<MacroActionHotkey::Action, std::string> actionTypes = {
{MacroActionHotkey::Action::OBS_HOTKEY,
"AdvSceneSwitcher.action.hotkey.type.obs"},
{MacroActionHotkey::Action::CUSTOM,
"AdvSceneSwitcher.action.hotkey.type.custom"},
};
static const std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
{OBS_HOTKEY_REGISTERER_FRONTEND,
"AdvSceneSwitcher.action.hotkey.type.obs.type.frontend"},
{OBS_HOTKEY_REGISTERER_SOURCE,
"AdvSceneSwitcher.action.hotkey.type.obs.type.soruce"},
{OBS_HOTKEY_REGISTERER_OUTPUT,
"AdvSceneSwitcher.action.hotkey.type.obs.type.output"},
{OBS_HOTKEY_REGISTERER_ENCODER,
"AdvSceneSwitcher.action.hotkey.type.obs.type.encoder"},
{OBS_HOTKEY_REGISTERER_SERVICE,
"AdvSceneSwitcher.action.hotkey.type.obs.type.service"},
};
static const std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, OBS_KEY_A},
{HotkeyType::Key_B, OBS_KEY_B},
@@ -54,6 +75,9 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_8, OBS_KEY_8},
{HotkeyType::Key_9, OBS_KEY_9},
// Other
{HotkeyType::Key_Tilde, OBS_KEY_ASCIITILDE},
{HotkeyType::Key_F1, OBS_KEY_F1},
{HotkeyType::Key_F2, OBS_KEY_F2},
{HotkeyType::Key_F3, OBS_KEY_F3},
@@ -196,7 +220,92 @@ static void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
obs_hotkey_inject_event(combo, false);
}
bool MacroActionHotkey::PerformAction()
static void addNamePrefix(std::string &name, obs_hotkey_t *hotkey)
{
const auto type = obs_hotkey_get_registerer_type(hotkey);
std::string prefix;
if (type == OBS_HOTKEY_REGISTERER_SOURCE) {
auto source = reinterpret_cast<obs_weak_source_t *>(
obs_hotkey_get_registerer(hotkey));
prefix = "[" + GetWeakSourceName(source) + "] ";
} else if (type == OBS_HOTKEY_REGISTERER_OUTPUT) {
auto weakOutput = reinterpret_cast<obs_weak_output_t *>(
obs_hotkey_get_registerer(hotkey));
std::string name;
auto output = obs_weak_output_get_output(weakOutput);
if (output) {
name = obs_output_get_name(output);
obs_output_release(output);
}
prefix = "[" + name + "] ";
} else if (type == OBS_HOTKEY_REGISTERER_ENCODER) {
auto weakEncoder = reinterpret_cast<obs_weak_encoder_t *>(
obs_hotkey_get_registerer(hotkey));
std::string name;
auto encoder = obs_weak_encoder_get_encoder(weakEncoder);
if (encoder) {
name = obs_encoder_get_name(encoder);
obs_encoder_release(encoder);
}
prefix = "[" + name + "] ";
} else if (type == OBS_HOTKEY_REGISTERER_SERVICE) {
auto weakService = reinterpret_cast<obs_weak_service_t *>(
obs_hotkey_get_registerer(hotkey));
std::string name;
auto service = obs_weak_service_get_service(weakService);
if (service) {
name = obs_service_get_name(service);
obs_service_release(service);
}
prefix = "[" + name + "] ";
}
name = prefix + name;
}
static obs_hotkey_id getHotkeyIdByName(const std::string &name)
{
struct Parameters {
std::string name;
obs_hotkey_id id = OBS_INVALID_HOTKEY_ID;
} params;
auto func = [](void *param, obs_hotkey_id id, obs_hotkey_t *hotkey) {
auto params = static_cast<Parameters *>(param);
std::string name = obs_hotkey_get_name(hotkey);
addNamePrefix(name, hotkey);
if (name == params->name) {
params->id = id;
return false;
}
return true;
};
params.name = name;
obs_enum_hotkeys(func, &params);
return params.id;
}
void MacroActionHotkey::SendOBSHotkey()
{
auto id = getHotkeyIdByName(_hotkeyName);
if (id == OBS_INVALID_HOTKEY_ID) {
blog(LOG_WARNING,
"failed to get hotkey id for \"%s\" - key will not be pressed",
_hotkeyName.c_str());
return;
}
obs_queue_task(
OBS_TASK_UI,
[](void *param) {
auto id = (size_t *)param;
obs_hotkey_trigger_routed_callback(*id, false);
obs_hotkey_trigger_routed_callback(*id, true);
obs_hotkey_trigger_routed_callback(*id, false);
},
&id, true);
}
void MacroActionHotkey::SendCustomHotkey()
{
std::vector<HotkeyType> keys;
if (_leftShift) {
@@ -237,18 +346,35 @@ bool MacroActionHotkey::PerformAction()
t.detach();
}
}
}
bool MacroActionHotkey::PerformAction()
{
switch (_action) {
case advss::MacroActionHotkey::Action::OBS_HOTKEY:
SendOBSHotkey();
break;
case advss::MacroActionHotkey::Action::CUSTOM:
SendCustomHotkey();
break;
default:
break;
}
return true;
}
void MacroActionHotkey::LogAction() const
{
vblog(LOG_INFO, "sent hotkey");
vblog(LOG_INFO, "sent hotkey type %d", static_cast<int>(_action));
}
bool MacroActionHotkey::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "hotkeyType", _hotkeyType);
obs_data_set_string(obj, "hotkeyName", _hotkeyName.c_str());
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);
@@ -260,13 +386,22 @@ bool MacroActionHotkey::Save(obs_data_t *obj) const
obs_data_set_bool(obj, "right_meta", _rightMeta);
_duration.Save(obj);
obs_data_set_bool(obj, "onlyOBS", _onlySendToObs);
obs_data_set_int(obj, "version", 1);
obs_data_set_int(obj, "version", 2);
return true;
}
bool MacroActionHotkey::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
int version = obs_data_get_int(obj, "version");
if (version != 2) {
_action = Action::CUSTOM;
} else {
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
}
_hotkeyType = static_cast<obs_hotkey_registerer_type>(
obs_data_get_int(obj, "hotkeyType"));
_hotkeyName = obs_data_get_string(obj, "hotkeyName");
_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");
@@ -276,7 +411,7 @@ bool MacroActionHotkey::Load(obs_data_t *obj)
_rightAlt = obs_data_get_bool(obj, "right_alt");
_leftMeta = obs_data_get_bool(obj, "left_meta");
_rightMeta = obs_data_get_bool(obj, "right_meta");
if (!obs_data_has_user_value(obj, "version")) {
if (version == 0) {
_duration = obs_data_get_int(obj, "duration") / 1000.0;
} else {
_duration.Load(obj);
@@ -393,11 +528,68 @@ static inline void populateKeySelection(QComboBox *list)
list->addItem("NumpadDivide");
list->addItem("NumpadDecimal");
list->addItem("NumpadEnter");
list->addItem("Tilde");
}
static void populateActionSelection(QComboBox *list)
{
for (const auto &[_, value] : actionTypes) {
list->addItem(obs_module_text(value.c_str()));
}
}
static void populateHotkeyTypeSelection(QComboBox *list)
{
for (const auto &[_, value] : hotkeyTypes) {
list->addItem(obs_module_text(value.c_str()));
}
}
static void addNamePrefix(QString &name, obs_hotkey_t *hotkey)
{
auto temp = name.toStdString();
addNamePrefix(temp, hotkey);
name = QString::fromStdString(temp);
}
static void populateHotkeyNames(QComboBox *list,
obs_hotkey_registerer_type type)
{
struct Parameters {
QStringList names;
QStringList descriptions;
obs_hotkey_registerer_type type;
} params;
auto func = [](void *param, obs_hotkey_id, obs_hotkey_t *hotkey) {
auto params = static_cast<Parameters *>(param);
if (obs_hotkey_get_registerer_type(hotkey) == params->type) {
QString description(obs_hotkey_get_description(hotkey));
addNamePrefix(description, hotkey);
params->descriptions.append(description);
QString name(obs_hotkey_get_name(hotkey));
addNamePrefix(name, hotkey);
params->names.append(name);
}
return true;
};
params.type = type;
obs_enum_hotkeys(func, &params);
list->clear();
for (int i = 0; i < params.names.size(); i++) {
list->addItem(params.descriptions.at(i), params.names.at(i));
}
AddSelectionEntry(list,
obs_module_text("AdvSceneSwitcher.selectHotkey"));
}
MacroActionHotkeyEdit::MacroActionHotkeyEdit(
QWidget *parent, std::shared_ptr<MacroActionHotkey> entryData)
: QWidget(parent),
_actionType(new QComboBox()),
_hotkeyType(new QComboBox()),
_obsHotkeys(new QComboBox()),
_keys(new QComboBox()),
_leftShift(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"))),
@@ -419,10 +611,20 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
_onlySendToOBS(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.onlyOBS"))),
_noKeyPressSimulationWarning(new QLabel(
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled")))
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled"))),
_entryLayout(new QHBoxLayout()),
_keyConfigLayout(new QHBoxLayout())
{
populateKeySelection(_keys);
populateActionSelection(_actionType);
populateHotkeyTypeSelection(_hotkeyType);
QWidget::connect(_actionType, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_hotkeyType, SIGNAL(currentIndexChanged(int)), this,
SLOT(HotkeyTypeChanged(int)));
QWidget::connect(_obsHotkeys, SIGNAL(currentIndexChanged(int)), this,
SLOT(OBSHotkeyChanged(int)));
QWidget::connect(_keys, SIGNAL(currentIndexChanged(int)), this,
SLOT(KeyChanged(int)));
QWidget::connect(_leftShift, SIGNAL(stateChanged(int)), this,
@@ -446,28 +648,21 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
QWidget::connect(_onlySendToOBS, SIGNAL(stateChanged(int)), this,
SLOT(OnlySendToOBSChanged(int)));
QHBoxLayout *line1Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{keys}}", _keys},
{"{{duration}}", _duration},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
line1Layout, widgetPlaceholders);
_entryLayout->setContentsMargins(0, 0, 0, 0);
_keyConfigLayout->setContentsMargins(0, 0, 0, 0);
_keyConfigLayout->addWidget(_leftShift);
_keyConfigLayout->addWidget(_rightShift);
_keyConfigLayout->addWidget(_leftCtrl);
_keyConfigLayout->addWidget(_rightCtrl);
_keyConfigLayout->addWidget(_leftAlt);
_keyConfigLayout->addWidget(_rightAlt);
_keyConfigLayout->addWidget(_leftMeta);
_keyConfigLayout->addWidget(_rightMeta);
_keyConfigLayout->addStretch();
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);
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(_entryLayout);
mainLayout->addLayout(_keyConfigLayout);
mainLayout->addWidget(_onlySendToOBS);
mainLayout->addWidget(_noKeyPressSimulationWarning);
setLayout(mainLayout);
@@ -479,10 +674,76 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
_loading = false;
}
void MacroActionHotkeyEdit::SetWarningVisibility()
void MacroActionHotkeyEdit::RepopulateOBSHotkeySelection()
{
populateHotkeyNames(_obsHotkeys, _entryData->_hotkeyType);
}
void MacroActionHotkeyEdit::SetWidgetVisibility()
{
_entryLayout->removeWidget(_actionType);
_entryLayout->removeWidget(_hotkeyType);
_entryLayout->removeWidget(_obsHotkeys);
_entryLayout->removeWidget(_keys);
_entryLayout->removeWidget(_duration);
ClearLayout(_entryLayout);
PlaceWidgets(
obs_module_text(
_entryData->_action ==
MacroActionHotkey::Action::OBS_HOTKEY
? "AdvSceneSwitcher.action.hotkey.entry.obs"
: "AdvSceneSwitcher.action.hotkey.entry.custom"),
_entryLayout,
{
{"{{actionType}}", _actionType},
{"{{hotkeyType}}", _hotkeyType},
{"{{obsHotkeys}}", _obsHotkeys},
{"{{keys}}", _keys},
{"{{duration}}", _duration},
});
_noKeyPressSimulationWarning->setVisible(!_entryData->_onlySendToObs &&
!canSimulateKeyPresses);
SetLayoutVisible(_keyConfigLayout,
_entryData->_action ==
MacroActionHotkey::Action::CUSTOM);
_duration->setVisible(_entryData->_action ==
MacroActionHotkey::Action::CUSTOM);
_keys->setVisible(_entryData->_action ==
MacroActionHotkey::Action::CUSTOM);
_onlySendToOBS->setVisible(_entryData->_action ==
MacroActionHotkey::Action::CUSTOM);
_hotkeyType->setVisible(_entryData->_action ==
MacroActionHotkey::Action::OBS_HOTKEY);
_obsHotkeys->setVisible(_entryData->_action ==
MacroActionHotkey::Action::OBS_HOTKEY);
adjustSize();
updateGeometry();
}
static QString getHotkeyDescriptionByName(const std::string &name)
{
struct Parameters {
std::string name;
QString description = "";
} params;
auto func = [](void *param, obs_hotkey_id, obs_hotkey_t *hotkey) {
auto params = static_cast<Parameters *>(param);
std::string name = obs_hotkey_get_name(hotkey);
addNamePrefix(name, hotkey);
if (name == params->name) {
params->description =
obs_hotkey_get_description(hotkey);
addNamePrefix(params->description, hotkey);
return false;
}
return true;
};
params.name = name;
obs_enum_hotkeys(func, &params);
return params.description;
}
void MacroActionHotkeyEdit::UpdateEntryData()
@@ -491,6 +752,11 @@ void MacroActionHotkeyEdit::UpdateEntryData()
return;
}
_actionType->setCurrentIndex(static_cast<int>(_entryData->_action));
_hotkeyType->setCurrentIndex(static_cast<int>(_entryData->_hotkeyType));
RepopulateOBSHotkeySelection();
_obsHotkeys->setCurrentText(
getHotkeyDescriptionByName(_entryData->_hotkeyName));
_keys->setCurrentIndex(static_cast<int>(_entryData->_key));
_leftShift->setChecked(_entryData->_leftShift);
_rightShift->setChecked(_entryData->_rightShift);
@@ -503,7 +769,7 @@ void MacroActionHotkeyEdit::UpdateEntryData()
_duration->SetDuration(_entryData->_duration);
_onlySendToOBS->setChecked(_entryData->_onlySendToObs ||
!canSimulateKeyPresses);
SetWarningVisibility();
SetWidgetVisibility();
}
void MacroActionHotkeyEdit::LShiftChanged(int state)
@@ -604,7 +870,7 @@ void MacroActionHotkeyEdit::OnlySendToOBSChanged(int state)
auto lock = LockContext();
_entryData->_onlySendToObs = state;
SetWarningVisibility();
SetWidgetVisibility();
}
void MacroActionHotkeyEdit::KeyChanged(int key)
@@ -617,4 +883,43 @@ void MacroActionHotkeyEdit::KeyChanged(int key)
_entryData->_key = static_cast<HotkeyType>(key);
}
void MacroActionHotkeyEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionHotkey::Action>(value);
SetWidgetVisibility();
}
void MacroActionHotkeyEdit::HotkeyTypeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
{
auto lock = LockContext();
_entryData->_hotkeyType =
static_cast<obs_hotkey_registerer_type>(value);
}
RepopulateOBSHotkeySelection();
}
void MacroActionHotkeyEdit::OBSHotkeyChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
if (idx == -1) {
_entryData->_hotkeyName = "";
return;
}
_entryData->_hotkeyName =
_obsHotkeys->itemData(idx).toString().toStdString();
}
} // namespace advss

View File

@@ -23,7 +23,15 @@ public:
return std::make_shared<MacroActionHotkey>(m);
}
OBSWeakSource _HotkeySource;
enum class Action {
OBS_HOTKEY,
CUSTOM,
};
Action _action = Action::OBS_HOTKEY;
obs_hotkey_registerer_type _hotkeyType = OBS_HOTKEY_REGISTERER_FRONTEND;
std::string _hotkeyName;
HotkeyType _key = HotkeyType::Key_NoKey;
bool _leftShift = false;
bool _rightShift = false;
@@ -41,6 +49,9 @@ public:
#endif
private:
void SendOBSHotkey();
void SendCustomHotkey();
static bool _registered;
static const std::string id;
};
@@ -62,6 +73,9 @@ public:
}
private slots:
void ActionChanged(int key);
void HotkeyTypeChanged(int key);
void OBSHotkeyChanged(int);
void KeyChanged(int key);
void LShiftChanged(int state);
void RShiftChanged(int state);
@@ -75,6 +89,9 @@ private slots:
void OnlySendToOBSChanged(int state);
protected:
QComboBox *_actionType;
QComboBox *_hotkeyType;
QComboBox *_obsHotkeys;
QComboBox *_keys;
QCheckBox *_leftShift;
QCheckBox *_rightShift;
@@ -91,8 +108,11 @@ protected:
std::shared_ptr<MacroActionHotkey> _entryData;
private:
void SetWarningVisibility();
QHBoxLayout *_mainLayout;
void RepopulateOBSHotkeySelection();
void SetWidgetVisibility();
QHBoxLayout *_entryLayout;
QHBoxLayout *_keyConfigLayout;
bool _loading = true;
};

View File

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

View File

@@ -11,14 +11,23 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register(
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
"AdvSceneSwitcher.action.macro"});
const static std::map<PerformMacroAction, std::string> actionTypes = {
{PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"},
{PerformMacroAction::UNPAUSE,
const static std::map<MacroActionMacro::Action, std::string> actionTypes = {
{MacroActionMacro::Action::PAUSE,
"AdvSceneSwitcher.action.macro.type.pause"},
{MacroActionMacro::Action::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{PerformMacroAction::RESET_COUNTER,
{MacroActionMacro::Action::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
{PerformMacroAction::RUN, "AdvSceneSwitcher.action.macro.type.run"},
{PerformMacroAction::STOP, "AdvSceneSwitcher.action.macro.type.stop"},
{MacroActionMacro::Action::RUN,
"AdvSceneSwitcher.action.macro.type.run"},
{MacroActionMacro::Action::STOP,
"AdvSceneSwitcher.action.macro.type.stop"},
{MacroActionMacro::Action::DISABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.disableAction"},
{MacroActionMacro::Action::ENABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.enableAction"},
{MacroActionMacro::Action::TOGGLE_ACTION,
"AdvSceneSwitcher.action.macro.type.toggleAction"},
};
bool MacroActionMacro::PerformAction()
@@ -29,23 +38,42 @@ bool MacroActionMacro::PerformAction()
}
switch (_action) {
case PerformMacroAction::PAUSE:
case Action::PAUSE:
macro->SetPaused();
break;
case PerformMacroAction::UNPAUSE:
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case PerformMacroAction::RESET_COUNTER:
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
case PerformMacroAction::RUN:
case Action::RUN:
if (!macro->Paused()) {
macro->PerformActions();
macro->PerformActions(true, false, true);
}
break;
case PerformMacroAction::STOP:
case Action::STOP:
macro->Stop();
break;
case Action::DISABLE_ACTION:
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
false)) {
macro->Actions().at(_actionIndex - 1)->SetEnabled(false);
}
break;
case Action::ENABLE_ACTION:
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
false)) {
macro->Actions().at(_actionIndex - 1)->SetEnabled(true);
}
break;
case Action::TOGGLE_ACTION:
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
false)) {
auto action = macro->Actions().at(_actionIndex - 1);
action->SetEnabled(!action->Enabled());
}
break;
default:
break;
}
@@ -59,23 +87,35 @@ void MacroActionMacro::LogAction() const
return;
}
switch (_action) {
case PerformMacroAction::PAUSE:
case Action::PAUSE:
vblog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
break;
case PerformMacroAction::UNPAUSE:
case Action::UNPAUSE:
vblog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
break;
case PerformMacroAction::RESET_COUNTER:
case Action::RESET_COUNTER:
vblog(LOG_INFO, "reset counter for \"%s\"",
macro->Name().c_str());
break;
case PerformMacroAction::RUN:
case Action::RUN:
vblog(LOG_INFO, "run nested macro \"%s\"",
macro->Name().c_str());
break;
case PerformMacroAction::STOP:
case Action::STOP:
vblog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
break;
case Action::DISABLE_ACTION:
vblog(LOG_INFO, "disabled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str());
break;
case Action::ENABLE_ACTION:
vblog(LOG_INFO, "enabled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str());
break;
case Action::TOGGLE_ACTION:
vblog(LOG_INFO, "toggled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str());
break;
default:
break;
}
@@ -85,6 +125,7 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_macro.Save(obj);
_actionIndex.Save(obj, "actionIndex");
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
@@ -93,7 +134,8 @@ bool MacroActionMacro::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_macro.Load(obj);
_action = static_cast<PerformMacroAction>(
_actionIndex.Load(obj, "actionIndex");
_action = static_cast<MacroActionMacro::Action>(
obs_data_get_int(obj, "action"));
return true;
}
@@ -112,25 +154,28 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionMacroEdit::MacroActionMacroEdit(
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
: QWidget(parent)
: QWidget(parent),
_macros(new MacroSelection(parent)),
_actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION)),
_actions(new QComboBox())
{
_macros = new MacroSelection(parent);
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_actionIndex,
SIGNAL(SelectionChanged(const IntVariable &)), this,
SLOT(ActionIndexChanged(const IntVariable &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{macros}}", _macros},
};
auto mainLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
mainLayout, widgetPlaceholders);
mainLayout,
{{"{{actions}}", _actions},
{"{{actionIndex}}", _actionIndex},
{"{{macros}}", _macros}});
setLayout(mainLayout);
_entryData = entryData;
@@ -144,11 +189,10 @@ void MacroActionMacroEdit::UpdateEntryData()
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_actionIndex->SetValue(_entryData->_actionIndex);
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
_macros->SetCurrentMacro(_entryData->_macro);
if (_entryData->_action == PerformMacroAction::RUN ||
_entryData->_action == PerformMacroAction::STOP) {
_macros->HideSelectedMacro();
}
SetWidgetVisibility();
}
void MacroActionMacroEdit::MacroChanged(const QString &text)
@@ -159,6 +203,7 @@ void MacroActionMacroEdit::MacroChanged(const QString &text)
auto lock = LockContext();
_entryData->_macro = text;
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -170,14 +215,36 @@ void MacroActionMacroEdit::ActionChanged(int value)
}
auto lock = LockContext();
_entryData->_action = static_cast<PerformMacroAction>(value);
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
SetWidgetVisibility();
}
if (_entryData->_action == PerformMacroAction::RUN ||
_entryData->_action == PerformMacroAction::STOP) {
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_actionIndex = value;
}
void MacroActionMacroEdit::SetWidgetVisibility()
{
if (_entryData->_action == MacroActionMacro::Action::RUN ||
_entryData->_action == MacroActionMacro::Action::STOP) {
_macros->HideSelectedMacro();
} else {
_macros->ShowAllMacros();
}
const bool isModifyingActionState =
_entryData->_action ==
MacroActionMacro::Action::DISABLE_ACTION ||
_entryData->_action ==
MacroActionMacro::Action::ENABLE_ACTION ||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
_actionIndex->setVisible(isModifyingActionState);
}
} // namespace advss

View File

@@ -1,19 +1,12 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro-selection.hpp"
#include "macro-segment-selection.hpp"
#include <QHBoxLayout>
namespace advss {
enum class PerformMacroAction {
PAUSE,
UNPAUSE,
RESET_COUNTER,
RUN,
STOP,
};
class MacroActionMacro : public MacroRefAction {
public:
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
@@ -28,7 +21,18 @@ public:
return std::make_shared<MacroActionMacro>(m);
}
PerformMacroAction _action = PerformMacroAction::PAUSE;
enum class Action {
PAUSE,
UNPAUSE,
RESET_COUNTER,
RUN,
STOP,
DISABLE_ACTION,
ENABLE_ACTION,
TOGGLE_ACTION,
};
Action _action = Action::PAUSE;
IntVariable _actionIndex = 1;
private:
static bool _registered;
@@ -54,16 +58,19 @@ public:
private slots:
void MacroChanged(const QString &text);
void ActionChanged(int value);
void ActionIndexChanged(const IntVariable &value);
signals:
void HeaderInfoChanged(const QString &);
protected:
MacroSelection *_macros;
MacroSegmentSelection *_actionIndex;
QComboBox *_actions;
std::shared_ptr<MacroActionMacro> _entryData;
private:
QHBoxLayout *_mainLayout;
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -10,22 +10,45 @@ bool MacroActionMedia::_registered = MacroActionFactory::Register(
{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"},
{MediaAction::SEEK, "AdvSceneSwitcher.action.media.type.seek"},
const static std::map<MacroActionMedia::Action, std::string> actionTypes = {
{MacroActionMedia::Action::PLAY,
"AdvSceneSwitcher.action.media.type.play"},
{MacroActionMedia::Action::PAUSE,
"AdvSceneSwitcher.action.media.type.pause"},
{MacroActionMedia::Action::STOP,
"AdvSceneSwitcher.action.media.type.stop"},
{MacroActionMedia::Action::RESTART,
"AdvSceneSwitcher.action.media.type.restart"},
{MacroActionMedia::Action::NEXT,
"AdvSceneSwitcher.action.media.type.next"},
{MacroActionMedia::Action::PREVIOUS,
"AdvSceneSwitcher.action.media.type.previous"},
{MacroActionMedia::Action::SEEK_DURATION,
"AdvSceneSwitcher.action.media.type.seek.duration"},
{MacroActionMedia::Action::SEEK_PERCENTAGE,
"AdvSceneSwitcher.action.media.type.seek.percentage"},
};
std::string MacroActionMedia::GetShortDesc() const
{
return _mediaSource.ToString();
}
void MacroActionMedia::SeekToPercentage(obs_source_t *source) const
{
auto totalTimeMs = obs_source_media_get_duration(source);
auto percentageTimeMs = round(totalTimeMs * _seekPercentage / 100);
obs_source_media_set_time(source, percentageTimeMs);
}
bool MacroActionMedia::PerformAction()
{
auto source = obs_weak_source_get_source(_mediaSource.GetSource());
obs_media_state state = obs_source_media_get_state(source);
switch (_action) {
case MediaAction::PLAY:
case Action::PLAY:
if (state == OBS_MEDIA_STATE_STOPPED ||
state == OBS_MEDIA_STATE_ENDED) {
obs_source_media_restart(source);
@@ -33,27 +56,31 @@ bool MacroActionMedia::PerformAction()
obs_source_media_play_pause(source, false);
}
break;
case MediaAction::PAUSE:
case Action::PAUSE:
obs_source_media_play_pause(source, true);
break;
case MediaAction::STOP:
case Action::STOP:
obs_source_media_stop(source);
break;
case MediaAction::RESTART:
case Action::RESTART:
obs_source_media_restart(source);
break;
case MediaAction::NEXT:
case Action::NEXT:
obs_source_media_next(source);
break;
case MediaAction::PREVIOUS:
case Action::PREVIOUS:
obs_source_media_previous(source);
break;
case MediaAction::SEEK:
obs_source_media_set_time(source, _seek.Milliseconds());
case Action::SEEK_DURATION:
obs_source_media_set_time(source, _seekDuration.Milliseconds());
break;
case Action::SEEK_PERCENTAGE:
SeekToPercentage(source);
break;
default:
break;
}
obs_source_release(source);
return true;
}
@@ -73,39 +100,42 @@ void MacroActionMedia::LogAction() const
bool MacroActionMedia::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_mediaSource.Save(obj, "mediaSource");
obs_data_set_int(obj, "action", static_cast<int>(_action));
_seek.Save(obj);
_seekDuration.Save(obj);
_seekPercentage.Save(obj, "seekPercentage");
_mediaSource.Save(obj, "mediaSource");
return true;
}
bool MacroActionMedia::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_seekDuration.Load(obj);
_seekPercentage.Load(obj, "seekPercentage");
_mediaSource.Load(obj, "mediaSource");
_action = static_cast<MediaAction>(obs_data_get_int(obj, "action"));
_seek.Load(obj);
return true;
}
std::string MacroActionMedia::GetShortDesc() const
{
return _mediaSource.ToString();
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionMediaEdit::MacroActionMediaEdit(
QWidget *parent, std::shared_ptr<MacroActionMedia> entryData)
: QWidget(parent),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_actions(new QComboBox()),
_seek(new DurationSelection())
_seekDuration(new DurationSelection()),
_seekPercentage(new SliderSpinBox(
0, 100,
obs_module_text(
"AdvSceneSwitcher.action.media.seek.percentage.label"))),
_sources(new SourceSelectionWidget(this, QStringList(), true))
{
populateActionSelection(_actions);
auto sources = GetMediaSourceNames();
@@ -114,39 +144,67 @@ MacroActionMediaEdit::MacroActionMediaEdit(
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_seekDuration,
SIGNAL(DurationChanged(const Duration &)), this,
SLOT(SeekDurationChanged(const Duration &)));
QWidget::connect(
_seekPercentage,
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
this,
SLOT(SeekPercentageChanged(const NumberVariable<double> &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_seek, SIGNAL(DurationChanged(const Duration &)), this,
SLOT(DurationChanged(const Duration &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
auto layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{mediaSources}}", _sources},
{"{{actions}}", _actions},
{"{{duration}}", _seek},
{"{{seekDuration}}", _seekDuration},
{"{{seekPercentage}}", _seekPercentage},
{"{{mediaSources}}", _sources},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.media.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
layout, widgetPlaceholders);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionMediaEdit::UpdateEntryData()
void MacroActionMediaEdit::ActionChanged(int value)
{
if (!_entryData) {
if (_loading || !_entryData) {
return;
}
_sources->SetSource(_entryData->_mediaSource);
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_seek->SetDuration(_entryData->_seek);
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionMedia::Action>(value);
SetWidgetVisibility();
}
void MacroActionMediaEdit::SeekDurationChanged(const Duration &seekDuration)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_seekDuration = seekDuration;
}
void MacroActionMediaEdit::SeekPercentageChanged(
const NumberVariable<double> &seekPercentage)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_seekPercentage = seekPercentage;
}
void MacroActionMediaEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
@@ -159,34 +217,32 @@ void MacroActionMediaEdit::SourceChanged(const SourceSelection &source)
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionMediaEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MediaAction>(value);
SetWidgetVisibility();
}
void MacroActionMediaEdit::DurationChanged(const Duration &dur)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_seek = dur;
}
void MacroActionMediaEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
_seek->setVisible(_entryData->_action == MediaAction::SEEK);
_seekDuration->setVisible(_entryData->_action ==
MacroActionMedia::Action::SEEK_DURATION);
_seekPercentage->setVisible(_entryData->_action ==
MacroActionMedia::Action::SEEK_PERCENTAGE);
adjustSize();
}
void MacroActionMediaEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_seekDuration->SetDuration(_entryData->_seekDuration);
_seekPercentage->SetDoubleValue(_entryData->_seekPercentage);
_sources->SetSource(_entryData->_mediaSource);
SetWidgetVisibility();
}
} // namespace advss

View File

@@ -1,42 +1,45 @@
#pragma once
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include "slider-spinbox.hpp"
#include "source-selection.hpp"
#include <QSpinBox>
#include <QHBoxLayout>
namespace advss {
enum class MediaAction {
PLAY,
PAUSE,
STOP,
RESTART,
NEXT,
PREVIOUS,
SEEK,
};
class MacroActionMedia : public MacroAction {
public:
MacroActionMedia(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionMedia>(m);
}
std::string GetId() const { return id; };
std::string GetShortDesc() const;
bool PerformAction();
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
enum class Action {
PLAY,
PAUSE,
STOP,
RESTART,
NEXT,
PREVIOUS,
SEEK_DURATION,
SEEK_PERCENTAGE,
};
Action _action = Action::PLAY;
Duration _seekDuration;
DoubleVariable _seekPercentage = 50;
SourceSelection _mediaSource;
MediaAction _action = MediaAction::PLAY;
Duration _seek;
private:
void SeekToPercentage(obs_source_t *source) const;
static bool _registered;
static const std::string id;
};
@@ -48,7 +51,6 @@ public:
MacroActionMediaEdit(
QWidget *parent,
std::shared_ptr<MacroActionMedia> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
@@ -56,24 +58,27 @@ public:
parent,
std::dynamic_pointer_cast<MacroActionMedia>(action));
}
void UpdateEntryData();
private slots:
void SourceChanged(const SourceSelection &);
void ActionChanged(int value);
void DurationChanged(const Duration &value);
void SeekDurationChanged(const Duration &seekDuration);
void
SeekPercentageChanged(const NumberVariable<double> &seekPercentage);
void SourceChanged(const SourceSelection &source);
signals:
void HeaderInfoChanged(const QString &);
protected:
SourceSelectionWidget *_sources;
QComboBox *_actions;
DurationSelection *_seek;
std::shared_ptr<MacroActionMedia> _entryData;
private:
void SetWidgetVisibility();
QHBoxLayout *_mainLayout;
QComboBox *_actions;
DurationSelection *_seekDuration;
SliderSpinBox *_seekPercentage;
SourceSelectionWidget *_sources;
std::shared_ptr<MacroActionMedia> _entryData;
bool _loading = true;
};

View File

@@ -1,8 +1,9 @@
#include "macro-action-plugin-state.hpp"
#include "switcher-data.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
#include <thread>
#include <condition_variable>
#include <QMainWindow>
using namespace std::chrono_literals;
@@ -27,40 +28,38 @@ const static std::map<PluginStateAction, std::string> actionTypes = {
"AdvSceneSwitcher.action.pluginState.type.terminate"},
};
const static std::map<SwitcherData::NoMatch, std::string> noMatchValues = {
{SwitcherData::NoMatch::NO_SWITCH,
const static std::map<NoMatchBehavior, std::string> noMatchValues = {
{NoMatchBehavior::NO_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch"},
{SwitcherData::NoMatch::SWITCH,
{NoMatchBehavior::SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo"},
{SwitcherData::NoMatch::RANDOM_SWITCH,
{NoMatchBehavior::RANDOM_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom"},
};
static void stopPlugin()
{
std::thread t([]() { switcher->Stop(); });
std::thread t([]() { StopPlugin(); });
t.detach();
}
static void importSettings(const std::string &path)
{
if (switcher->settingsWindowOpened) {
if (SettingsWindowIsOpened()) {
return;
}
obs_data_t *obj = obs_data_create_from_json_file(path.c_str());
OBSDataAutoRelease obj = obs_data_create_from_json_file(path.c_str());
if (!obj) {
return;
}
switcher->LoadSettings(obj);
obs_data_release(obj);
LoadPluginSettings(obj);
}
static void setNoMatchBehaviour(int value, OBSWeakSource &scene)
{
switcher->switchIfNotMatching =
static_cast<SwitcherData::NoMatch>(value);
if (switcher->switchIfNotMatching == SwitcherData::NoMatch::SWITCH) {
switcher->nonMatchingScene = scene;
SetPluginNoMatchBehavior(static_cast<NoMatchBehavior>(value));
if (GetPluginNoMatchBehavior() == NoMatchBehavior::SWITCH) {
SetNoMatchScene(scene);
}
}
@@ -342,8 +341,8 @@ void MacroActionPluginStateEdit::SetWidgetVisibility()
break;
case PluginStateAction::NO_MATCH_BEHAVIOUR:
_values->show();
if (static_cast<SwitcherData::NoMatch>(_entryData->_value) ==
SwitcherData::NoMatch::SWITCH) {
if (static_cast<NoMatchBehavior>(_entryData->_value) ==
NoMatchBehavior::SWITCH) {
_scenes->show();
}
break;

View File

@@ -57,6 +57,12 @@ bool MacroActionProjector::PerformAction()
break;
}
if (_fullscreen && _monitor == -1) {
blog(LOG_INFO, "refusing to open fullscreen projector"
" with invalid display selection");
return true;
}
obs_frontend_open_projector(type, _fullscreen ? _monitor : -1, "",
name.c_str());
@@ -85,6 +91,7 @@ bool MacroActionProjector::Save(obs_data_t *obj) const
MacroAction::Save(obj);
obs_data_set_int(obj, "type", static_cast<int>(_type));
obs_data_set_int(obj, "monitor", _monitor);
obs_data_set_string(obj, "monitorName", _monitorName.c_str());
obs_data_set_bool(obj, "fullscreen", _fullscreen);
_scene.Save(obj);
_source.Save(obj);
@@ -96,12 +103,50 @@ bool MacroActionProjector::Load(obs_data_t *obj)
MacroAction::Load(obj);
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
_monitor = obs_data_get_int(obj, "monitor");
_monitorName = obs_data_get_string(obj, "monitorName");
_fullscreen = obs_data_get_bool(obj, "fullscreen");
_scene.Load(obj);
_source.Load(obj);
if (MonitorSetupChanged()) {
blog(LOG_INFO, "monitor setup seems to have changed! "
"resetting projector action monitor selection!");
_monitor = -1;
}
return true;
}
void MacroActionProjector::SetMonitor(int idx)
{
_monitor = idx;
auto monitorNames = GetMonitorNames();
if (_monitor < 0 || _monitor >= monitorNames.size()) {
// Monitor setup changed while settings were selected?
_monitorName = "";
return;
}
_monitorName = monitorNames.at(_monitor).toStdString();
}
int MacroActionProjector::GetMonitor() const
{
return _monitor;
}
bool MacroActionProjector::MonitorSetupChanged()
{
if (_monitorName.empty()) {
return false;
}
auto monitorNames = GetMonitorNames();
if (_monitor < 0 || _monitor >= monitorNames.size()) {
return true;
}
return monitorNames.at(_monitor) !=
QString::fromStdString(_monitorName);
}
static inline void populateSelectionTypes(QComboBox *list)
{
for (auto entry : selectionTypes) {
@@ -134,6 +179,8 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
sources.sort();
_sources->SetSourceNameList(sources);
_monitors->addItems(GetMonitorNames());
_monitors->setPlaceholderText(
obs_module_text("AdvSceneSwitcher.selectDisplay"));
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(WindowTypeChanged(int)));
@@ -177,7 +224,7 @@ void MacroActionProjectorEdit::UpdateEntryData()
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
_scenes->SetScene(_entryData->_scene);
_sources->SetSource(_entryData->_source);
_monitors->setCurrentIndex(_entryData->_monitor);
_monitors->setCurrentIndex(_entryData->GetMonitor());
SetWidgetVisibility();
}
@@ -208,7 +255,7 @@ void MacroActionProjectorEdit::MonitorChanged(int value)
}
auto lock = LockContext();
_entryData->_monitor = value;
_entryData->SetMonitor(value);
}
void MacroActionProjectorEdit::WindowTypeChanged(int)

View File

@@ -17,6 +17,8 @@ public:
{
return std::make_shared<MacroActionProjector>(m);
}
void SetMonitor(int);
int GetMonitor() const;
enum class Type {
SOURCE,
@@ -29,10 +31,15 @@ public:
Type _type = Type::SCENE;
SourceSelection _source;
SceneSelection _scene;
int _monitor = 0;
bool _fullscreen = true;
private:
bool MonitorSetupChanged();
int _monitor = -1;
// Only used to detect display setup changes
std::string _monitorName = "";
static bool _registered;
static const std::string id;
};

View File

@@ -54,12 +54,12 @@ bool MacroActionRandom::PerformAction()
}
if (macros.size() == 1) {
lastRandomMacro = macros[0];
return macros[0]->PerformActions();
return macros[0]->PerformActions(true);
}
srand((unsigned int)time(0));
size_t idx = std::rand() % (macros.size());
lastRandomMacro = macros[idx];
return macros[idx]->PerformActions();
return macros[idx]->PerformActions(true);
}
void MacroActionRandom::LogAction() const

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
#include "macro-action-scene-collection.hpp"
#include "switcher-data.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -17,7 +17,7 @@ bool MacroActionSceneCollection::PerformAction()
// Changing the scene collection will also reload the settings of the
// scene switcher, so to avoid issues of not being able to change
// settings ignore this action if the settings dialog is opened.
if (switcher->settingsWindowOpened) {
if (SettingsWindowIsOpened()) {
return false;
}
obs_frontend_set_current_scene_collection(_sceneCollection.c_str());

View File

@@ -0,0 +1,176 @@
#include "macro-action-scene-lock.hpp"
#include "utility.hpp"
namespace advss {
const std::string MacroActionSceneLock::id = "scene_lock";
bool MacroActionSceneLock::_registered = MacroActionFactory::Register(
MacroActionSceneLock::id,
{MacroActionSceneLock::Create, MacroActionSceneLockEdit::Create,
"AdvSceneSwitcher.action.sceneLock"});
const static std::map<MacroActionSceneLock::Action, std::string> actionTypes = {
{MacroActionSceneLock::Action::LOCK,
"AdvSceneSwitcher.action.sceneLock.type.lock"},
{MacroActionSceneLock::Action::UNLOCK,
"AdvSceneSwitcher.action.sceneLock.type.unlock"},
{MacroActionSceneLock::Action::TOGGLE,
"AdvSceneSwitcher.action.sceneLock.type.toggle"},
};
static void setSceneItemLock(obs_sceneitem_t *item,
MacroActionSceneLock::Action action)
{
switch (action) {
case MacroActionSceneLock::Action::LOCK:
obs_sceneitem_set_locked(item, true);
break;
case MacroActionSceneLock::Action::UNLOCK:
obs_sceneitem_set_locked(item, false);
break;
case MacroActionSceneLock::Action::TOGGLE:
obs_sceneitem_set_locked(item, !obs_sceneitem_locked(item));
break;
}
}
bool MacroActionSceneLock::PerformAction()
{
auto items = _source.GetSceneItems(_scene);
for (const auto &item : items) {
setSceneItemLock(item, _action);
}
return true;
}
void MacroActionSceneLock::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), _source.ToString(true).c_str(),
_scene.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown scene lock action %d",
static_cast<int>(_action));
}
}
bool MacroActionSceneLock::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
_source.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionSceneLock::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_scene.Load(obj);
_source.Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
return true;
}
std::string MacroActionSceneLock::GetShortDesc() const
{
if (!_source.ToString().empty()) {
return _scene.ToString() + " - " + _source.ToString();
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionSceneLockEdit::MacroActionSceneLockEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneLock> entryData)
: QWidget(parent),
_scenes(new SceneSelectionWidget(window(), true, false, true, true)),
_sources(new SceneItemSelectionWidget(parent)),
_actions(new QComboBox())
{
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(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &)));
auto layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.sceneLock.entry"),
layout, widgetPlaceholders);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSceneLockEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem((_entryData->_source));
}
void MacroActionSceneLockEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_scene = s;
}
void MacroActionSceneLockEdit::SourceChanged(const SceneItemSelection &item)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
adjustSize();
updateGeometry();
}
void MacroActionSceneLockEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionSceneLock::Action>(value);
}
} // namespace advss

View File

@@ -0,0 +1,69 @@
#pragma once
#include "macro-action-edit.hpp"
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
namespace advss {
class MacroActionSceneLock : public MacroAction {
public:
MacroActionSceneLock(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSceneLock>(m);
}
enum class Action {
LOCK,
UNLOCK,
TOGGLE,
};
Action _action = Action::LOCK;
SceneSelection _scene;
SceneItemSelection _source;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSceneLockEdit : public QWidget {
Q_OBJECT
public:
MacroActionSceneLockEdit(
QWidget *parent,
std::shared_ptr<MacroActionSceneLock> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSceneLockEdit(
parent, std::dynamic_pointer_cast<MacroActionSceneLock>(
action));
}
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const SceneItemSelection &);
void ActionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
QComboBox *_actions;
std::shared_ptr<MacroActionSceneLock> _entryData;
private:
bool _loading = true;
};
} // namespace advss

View File

@@ -23,7 +23,7 @@ const static std::map<SceneOrderAction, std::string> actionTypes = {
"AdvSceneSwitcher.action.sceneOrder.type.movePosition"},
};
static void moveSceneItemsUp(std::vector<obs_scene_item *> &items)
static void moveSceneItemsUp(std::vector<OBSSceneItem> &items)
{
// In the case of the same source being in two sequential positions
// moving the sources up will cause the sources to swap positions due to
@@ -33,41 +33,36 @@ static void moveSceneItemsUp(std::vector<obs_scene_item *> &items)
// will be moved up.
std::reverse(items.begin(), items.end());
for (auto &i : items) {
for (const auto &i : items) {
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_UP);
obs_sceneitem_release(i);
}
}
static void moveSceneItemsDown(std::vector<obs_scene_item *> &items)
static void moveSceneItemsDown(std::vector<OBSSceneItem> &items)
{
for (auto &i : items) {
for (const auto &i : items) {
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_DOWN);
obs_sceneitem_release(i);
}
}
static void moveSceneItemsTop(std::vector<obs_scene_item *> &items)
static void moveSceneItemsTop(std::vector<OBSSceneItem> &items)
{
for (auto &i : items) {
for (const auto &i : items) {
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_TOP);
obs_sceneitem_release(i);
}
}
static void moveSceneItemsBottom(std::vector<obs_scene_item *> &items)
static void moveSceneItemsBottom(std::vector<OBSSceneItem> &items)
{
for (auto &i : items) {
for (const auto &i : items) {
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_BOTTOM);
obs_sceneitem_release(i);
}
}
static void moveSceneItemsPos(std::vector<obs_scene_item *> &items, int pos)
static void moveSceneItemsPos(std::vector<OBSSceneItem> &items, int pos)
{
for (auto &i : items) {
for (const auto &i : items) {
obs_sceneitem_set_order_position(i, pos);
obs_sceneitem_release(i);
}
}
@@ -149,7 +144,7 @@ std::string MacroActionSceneOrder::GetShortDesc() const
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
for (const auto &entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
@@ -228,6 +223,8 @@ void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
adjustSize();
updateGeometry();
}
void MacroActionSceneOrderEdit::ActionChanged(int value)

View File

@@ -1,5 +1,7 @@
#include "macro-action-scene-switch.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "macro.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp"
@@ -7,12 +9,20 @@ namespace advss {
using namespace std::chrono_literals;
const std::string MacroActionSwitchScene::id = "scene_switch";
const std::string MacroActionSwitchScene::id = GetSceneSwitchActionId().data();
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
MacroActionSwitchScene::id,
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
"AdvSceneSwitcher.action.switchScene"});
"AdvSceneSwitcher.action.scene"});
const static std::map<MacroActionSwitchScene::SceneType, std::string>
sceneTypes = {
{MacroActionSwitchScene::SceneType::PROGRAM,
"AdvSceneSwitcher.action.scene.type.program"},
{MacroActionSwitchScene::SceneType::PREVIEW,
"AdvSceneSwitcher.action.scene.type.preview"},
};
static void waitForTransitionChange(OBSWeakSource &transition,
std::unique_lock<std::mutex> *lock,
@@ -20,11 +30,14 @@ static void waitForTransitionChange(OBSWeakSource &transition,
{
const auto time = 100ms;
obs_source_t *source = obs_weak_source_get_source(transition);
if (!source) {
return;
}
bool stillTransitioning = true;
while (stillTransitioning && !switcher->abortMacroWait &&
while (stillTransitioning && !MacroWaitShouldAbort() &&
!macro->GetStop()) {
switcher->macroTransitionCv.wait_for(*lock, time);
GetMacroTransitionCV().wait_for(*lock, time);
float t = obs_transition_get_time(source);
stillTransitioning = t < 1.0f && t > 0.0f;
}
@@ -38,8 +51,8 @@ static void waitForTransitionChangeFixedDuration(
auto time = std::chrono::high_resolution_clock::now() +
std::chrono::milliseconds(duration);
while (!switcher->abortMacroWait && !macro->GetStop()) {
if (switcher->macroTransitionCv.wait_until(*lock, time) ==
while (!MacroWaitShouldAbort() && !macro->GetStop()) {
if (GetMacroTransitionCV().wait_until(*lock, time) ==
std::cv_status::timeout) {
break;
}
@@ -80,11 +93,16 @@ static OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
return transition;
}
static int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t,
static int getExpectedTransitionDuration(OBSWeakSource &scene,
OBSWeakSource &transiton_,
double duration)
{
OBSWeakSource transition = t;
if (!switcher->transitionOverrideOverride) {
OBSWeakSource transition = transiton_;
// If we are not modifying the transition override, we will have to
// check, if a transition override is set by the user and use its
// duration instead
if (!ShouldModifyTransitionOverrides()) {
auto overrideTransition = getOverrideTransition(scene);
if (overrideTransition) {
transition = overrideTransition;
@@ -93,6 +111,7 @@ static int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t,
}
}
}
if (isUsingFixedLengthTransition(transition)) {
return -1; // no API is available to access the fixed duration
}
@@ -107,9 +126,9 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
{
const int expectedTransitionDuration = getExpectedTransitionDuration(
scene, transition, _duration.Seconds());
switcher->abortMacroWait = false;
SetMacroAbortWait(false);
std::unique_lock<std::mutex> lock(switcher->m);
std::unique_lock<std::mutex> lock(*GetMutex());
if (expectedTransitionDuration < 0) {
waitForTransitionChange(transition, &lock, GetMacro());
} else {
@@ -117,16 +136,24 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
&lock, GetMacro());
}
return !switcher->abortMacroWait;
return !MacroWaitShouldAbort();
}
bool MacroActionSwitchScene::PerformAction()
{
auto scene = _scene.GetScene();
if (_sceneType == SceneType::PREVIEW) {
OBSSourceAutoRelease previewScneSource =
obs_weak_source_get_source(scene);
obs_frontend_set_current_preview_scene(previewScneSource);
return true;
}
auto transition = _transition.GetTransition();
SwitchScene({scene, transition, (int)(_duration.Milliseconds())},
obs_frontend_preview_program_mode_active());
if (_blockUntilTransitionDone && scene) {
if (_blockUntilTransitionDone && scene && scene != GetCurrentScene()) {
return WaitForTransition(scene, transition);
}
return true;
@@ -134,24 +161,9 @@ bool MacroActionSwitchScene::PerformAction()
void MacroActionSwitchScene::LogAction() const
{
auto t = _scene.GetType();
auto sceneName = GetWeakSourceName(_scene.GetScene(false));
switch (t) {
case SceneSelection::Type::SCENE:
vblog(LOG_INFO, "switch to scene '%s'",
_scene.ToString(true).c_str());
break;
case SceneSelection::Type::GROUP:
vblog(LOG_INFO, "switch to scene '%s' (scene group '%s')",
sceneName.c_str(), _scene.ToString(true).c_str());
break;
case SceneSelection::Type::PREVIOUS:
vblog(LOG_INFO, "switch to previous scene '%s'",
sceneName.c_str());
break;
default:
break;
}
vblog(LOG_INFO, "switch%s scene to '%s'",
_sceneType == SceneType::PREVIEW ? " preview" : "",
_scene.ToString(true).c_str());
}
bool MacroActionSwitchScene::Save(obs_data_t *obj) const
@@ -162,6 +174,7 @@ bool MacroActionSwitchScene::Save(obs_data_t *obj) const
_duration.Save(obj);
obs_data_set_bool(obj, "blockUntilTransitionDone",
_blockUntilTransitionDone);
obs_data_set_int(obj, "sceneType", static_cast<int>(_sceneType));
return true;
}
@@ -173,6 +186,7 @@ bool MacroActionSwitchScene::Load(obs_data_t *obj)
_duration.Load(obj);
_blockUntilTransitionDone =
obs_data_get_bool(obj, "blockUntilTransitionDone");
_sceneType = static_cast<SceneType>(obs_data_get_int(obj, "sceneType"));
return true;
}
@@ -181,6 +195,13 @@ std::string MacroActionSwitchScene::GetShortDesc() const
return _scene.ToString();
}
static inline void populateTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : sceneTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
: QWidget(parent),
@@ -189,9 +210,11 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
_duration(new DurationSelection(parent, false)),
_blockUntilTransitionDone(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.scene.blockUntilTransitionDone"))),
_sceneTypes(new QComboBox()),
_entryLayout(new QHBoxLayout())
{
_duration->SpinBox()->setSpecialValueText("-");
populateTypeSelection(_sceneTypes);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
@@ -203,28 +226,29 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
this, SLOT(DurationChanged(const Duration &)));
QWidget::connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)),
this, SLOT(BlockUntilTransitionDoneChanged(int)));
QWidget::connect(_sceneTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(SceneTypeChanged(int)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{transitions}}", _transitions},
{"{{duration}}", _duration},
{"{{blockUntilTransitionDone}}", _blockUntilTransitionDone},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
_entryLayout, widgetPlaceholders);
_entryLayout,
{{"{{scenes}}", _scenes},
{"{{transitions}}", _transitions},
{"{{duration}}", _duration},
{"{{sceneTypes}}", _sceneTypes}});
QVBoxLayout *mainLayout = new QVBoxLayout;
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(_entryLayout);
mainLayout->addWidget(_blockUntilTransitionDone);
setLayout(mainLayout);
_entryData = entryData;
_sceneTypes->setCurrentIndex(static_cast<int>(_entryData->_sceneType));
_scenes->SetScene(_entryData->_scene);
_transitions->SetTransition(_entryData->_transition);
_duration->SetDuration(_entryData->_duration);
_blockUntilTransitionDone->setChecked(
_entryData->_blockUntilTransitionDone);
SetDurationVisibility();
SetWidgetVisibility();
_loading = false;
}
@@ -248,8 +272,44 @@ void MacroActionSwitchSceneEdit::BlockUntilTransitionDoneChanged(int state)
_entryData->_blockUntilTransitionDone = state;
}
void MacroActionSwitchSceneEdit::SetDurationVisibility()
void MacroActionSwitchSceneEdit::SceneTypeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_sceneType =
static_cast<MacroActionSwitchScene::SceneType>(value);
SetWidgetVisibility();
}
void MacroActionSwitchSceneEdit::SetWidgetVisibility()
{
_entryLayout->removeWidget(_scenes);
_entryLayout->removeWidget(_transitions);
_entryLayout->removeWidget(_duration);
_entryLayout->removeWidget(_sceneTypes);
ClearLayout(_entryLayout);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{transitions}}", _transitions},
{"{{duration}}", _duration},
{"{{sceneTypes}}", _sceneTypes},
};
if (_entryData->_sceneType ==
MacroActionSwitchScene::SceneType::PREVIEW) {
_transitions->hide();
_duration->hide();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.scene.entry.preview"),
_entryLayout, widgetPlaceholders);
return;
}
_transitions->show();
if (_entryData->_transition.GetType() !=
TransitionSelection::Type::TRANSITION) {
_duration->show();
@@ -258,15 +318,6 @@ void MacroActionSwitchSceneEdit::SetDurationVisibility()
_entryData->_transition.GetTransition());
_duration->setVisible(!fixedDuration);
_entryLayout->removeWidget(_scenes);
_entryLayout->removeWidget(_transitions);
_entryLayout->removeWidget(_duration);
ClearLayout(_entryLayout);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{transitions}}", _transitions},
{"{{duration}}", _duration},
};
if (fixedDuration) {
PlaceWidgets(
obs_module_text(
@@ -299,7 +350,7 @@ void MacroActionSwitchSceneEdit::TransitionChanged(const TransitionSelection &t)
auto lock = LockContext();
_entryData->_transition = t;
SetDurationVisibility();
SetWidgetVisibility();
}
} // namespace advss

View File

@@ -22,6 +22,9 @@ public:
{
return std::make_shared<MacroActionSwitchScene>(m);
}
enum class SceneType { PROGRAM, PREVIEW };
SceneType _sceneType = SceneType::PROGRAM;
SceneSelection _scene;
TransitionSelection _transition;
Duration _duration;
@@ -55,20 +58,21 @@ private slots:
void TransitionChanged(const TransitionSelection &);
void DurationChanged(const Duration &seconds);
void BlockUntilTransitionDoneChanged(int state);
void SceneTypeChanged(int);
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
SceneSelectionWidget *_scenes;
TransitionSelectionWidget *_transitions;
DurationSelection *_duration;
QCheckBox *_blockUntilTransitionDone;
QComboBox *_sceneTypes;
QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionSwitchScene> _entryData;
private:
void SetDurationVisibility();
bool _loading = true;
};

View File

@@ -1,6 +1,8 @@
#include "macro-action-scene-transform.hpp"
#include "utility.hpp"
#include <graphics/matrix4.h>
namespace advss {
const std::string MacroActionSceneTransform::id = "scene_transform";
@@ -11,18 +13,242 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
MacroActionSceneTransformEdit::Create,
"AdvSceneSwitcher.action.sceneTransform"});
bool MacroActionSceneTransform::PerformAction()
{
ApplySettings(_settings); // Resolves variables
const static std::map<MacroActionSceneTransform::Action, std::string> actions = {
{MacroActionSceneTransform::Action::MANUAL_TRANSFORM,
"AdvSceneSwitcher.action.sceneTransform.type.manual"},
{MacroActionSceneTransform::Action::RESET,
"AdvSceneSwitcher.action.sceneTransform.type.reset"},
{MacroActionSceneTransform::Action::ROTATE,
"AdvSceneSwitcher.action.sceneTransform.type.rotate"},
{MacroActionSceneTransform::Action::FLIP_HORIZONTAL,
"AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal"},
{MacroActionSceneTransform::Action::FLIP_VERTICAL,
"AdvSceneSwitcher.action.sceneTransform.type.flipVertical"},
{MacroActionSceneTransform::Action::FIT_TO_SCREEN,
"AdvSceneSwitcher.action.sceneTransform.type.fitToScreen"},
{MacroActionSceneTransform::Action::STRETCH_TO_SCREEN,
"AdvSceneSwitcher.action.sceneTransform.type.stretchToScreen"},
{MacroActionSceneTransform::Action::CENTER_TO_SCREEN,
"AdvSceneSwitcher.action.sceneTransform.type.centerToScreen"},
{MacroActionSceneTransform::Action::CENTER_VERTICALLY,
"AdvSceneSwitcher.action.sceneTransform.type.centerVertically"},
{MacroActionSceneTransform::Action::CENTER_HORIZONTALLY,
"AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally"},
};
auto items = _source.GetSceneItems(_scene);
for (auto &item : items) {
/* --------------- Helpers based on OBS window-basic-main.cpp --------------- */
static void getItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br)
{
matrix4 boxTransform;
obs_sceneitem_get_box_transform(item, &boxTransform);
vec3_set(&tl, M_INFINITE, M_INFINITE, 0.0f);
vec3_set(&br, -M_INFINITE, -M_INFINITE, 0.0f);
auto GetMinPos = [&](float x, float y) {
vec3 pos;
vec3_set(&pos, x, y, 0.0f);
vec3_transform(&pos, &pos, &boxTransform);
vec3_min(&tl, &tl, &pos);
vec3_max(&br, &br, &pos);
};
GetMinPos(0.0f, 0.0f);
GetMinPos(1.0f, 0.0f);
GetMinPos(0.0f, 1.0f);
GetMinPos(1.0f, 1.0f);
}
static vec3 getItemTL(obs_sceneitem_t *item)
{
vec3 tl, br;
getItemBox(item, tl, br);
return tl;
}
static void setItemTL(obs_sceneitem_t *item, const vec3 &tl)
{
vec3 newTL;
vec2 pos;
obs_sceneitem_get_pos(item, &pos);
newTL = getItemTL(item);
pos.x += tl.x - newTL.x;
pos.y += tl.y - newTL.y;
obs_sceneitem_set_pos(item, &pos);
}
static void reset(obs_sceneitem_t *item)
{
obs_transform_info info;
vec2_set(&info.pos, 0.0f, 0.0f);
vec2_set(&info.scale, 1.0f, 1.0f);
info.rot = 0.0f;
info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
info.bounds_type = OBS_BOUNDS_NONE;
info.bounds_alignment = OBS_ALIGN_CENTER;
vec2_set(&info.bounds, 0.0f, 0.0f);
obs_sceneitem_set_info(item, &info);
obs_sceneitem_crop crop = {};
obs_sceneitem_set_crop(item, &crop);
}
static void rotate(obs_sceneitem_t *item, float rot)
{
vec3 tl = getItemTL(item);
rot += obs_sceneitem_get_rot(item);
if (rot >= 360.0f) {
rot -= 360.0f;
} else if (rot <= -360.0f) {
rot += 360.0f;
}
obs_sceneitem_set_rot(item, rot);
obs_sceneitem_force_update_transform(item);
setItemTL(item, tl);
};
static void flip(obs_sceneitem_t *item, const vec2 &mul)
{
vec3 tl = getItemTL(item);
vec2 scale;
obs_sceneitem_get_scale(item, &scale);
vec2_mul(&scale, &scale, &mul);
obs_sceneitem_set_scale(item, &scale);
obs_sceneitem_force_update_transform(item);
setItemTL(item, tl);
}
static void centerAlign(obs_sceneitem_t *item, obs_bounds_type boundsType)
{
obs_video_info ovi;
obs_get_video_info(&ovi);
obs_transform_info itemInfo;
vec2_set(&itemInfo.pos, 0.0f, 0.0f);
vec2_set(&itemInfo.scale, 1.0f, 1.0f);
itemInfo.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
itemInfo.rot = 0.0f;
vec2_set(&itemInfo.bounds, float(ovi.base_width),
float(ovi.base_height));
itemInfo.bounds_type = boundsType;
itemInfo.bounds_alignment = OBS_ALIGN_CENTER;
obs_sceneitem_set_info(item, &itemInfo);
}
enum class CenterType {
SCREEN,
VERTICAL,
HORIZONTAL,
};
static void center(obs_sceneitem_t *item, const CenterType &centerType)
{
// Get center x, y coordinates of items
vec3 tl, br;
getItemBox(item, tl, br);
float left = tl.x;
float top = tl.y;
float right = br.x;
float bottom = br.y;
vec3 center;
center.x = (right + left) / 2.0f;
center.y = (top + bottom) / 2.0f;
center.z = 0.0f;
// Get coordinates of screen center
obs_video_info ovi;
obs_get_video_info(&ovi);
vec3 screenCenter;
vec3_set(&screenCenter, float(ovi.base_width), float(ovi.base_height),
0.0f);
vec3_mulf(&screenCenter, &screenCenter, 0.5f);
// Calculate difference between screen center and item center
vec3 offset;
vec3_sub(&offset, &screenCenter, &center);
// Shift item by offset
vec3_add(&tl, &tl, &offset);
vec3 itemTL = getItemTL(item);
if (centerType == CenterType::VERTICAL) {
tl.x = itemTL.x;
} else if (centerType == CenterType::HORIZONTAL) {
tl.y = itemTL.y;
}
setItemTL(item, tl);
}
/* ------------------------------------------------------------------------- */
void MacroActionSceneTransform::Transform(obs_scene_item *item)
{
if (!item) {
return;
}
switch (_action) {
case MacroActionSceneTransform::Action::RESET:
obs_sceneitem_defer_update_begin(item);
reset(item);
obs_sceneitem_defer_update_end(item);
break;
case MacroActionSceneTransform::Action::ROTATE:
rotate(item, _rotation);
break;
case MacroActionSceneTransform::Action::FLIP_HORIZONTAL: {
vec2 scale;
vec2_set(&scale, -1.0f, 1.0f);
flip(item, scale);
break;
}
case MacroActionSceneTransform::Action::FLIP_VERTICAL: {
vec2 scale;
vec2_set(&scale, 1.0f, -1.0f);
flip(item, scale);
break;
}
case MacroActionSceneTransform::Action::FIT_TO_SCREEN:
centerAlign(item, OBS_BOUNDS_SCALE_INNER);
break;
case MacroActionSceneTransform::Action::STRETCH_TO_SCREEN:
centerAlign(item, OBS_BOUNDS_STRETCH);
break;
case MacroActionSceneTransform::Action::CENTER_TO_SCREEN:
center(item, CenterType::SCREEN);
break;
case MacroActionSceneTransform::Action::CENTER_VERTICALLY:
center(item, CenterType::VERTICAL);
break;
case MacroActionSceneTransform::Action::CENTER_HORIZONTALLY:
center(item, CenterType::HORIZONTAL);
break;
case MacroActionSceneTransform::Action::MANUAL_TRANSFORM:
ApplySettings(_settings); // Resolves variables
obs_sceneitem_defer_update_begin(item);
obs_sceneitem_set_info(item, &_info);
obs_sceneitem_set_crop(item, &_crop);
obs_sceneitem_defer_update_end(item);
break;
}
}
obs_sceneitem_release(item);
bool MacroActionSceneTransform::PerformAction()
{
auto items = _source.GetSceneItems(_scene);
for (const auto &item : items) {
Transform(item);
}
return true;
}
@@ -30,15 +256,18 @@ bool MacroActionSceneTransform::PerformAction()
void MacroActionSceneTransform::LogAction() const
{
vblog(LOG_INFO,
"performed transform action for source \"%s\" on scene \"%s\"",
_source.ToString(true).c_str(), _scene.ToString(true).c_str());
"performed transform action %d for source \"%s\" on scene \"%s\"",
static_cast<int>(_action), _source.ToString(true).c_str(),
_scene.ToString(true).c_str());
}
bool MacroActionSceneTransform::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_scene.Save(obj);
_source.Save(obj);
_rotation.Save(obj, "rotation");
_settings.Save(obj, "settings");
return true;
}
@@ -53,8 +282,15 @@ bool MacroActionSceneTransform::Load(obs_data_t *obj)
}
MacroAction::Load(obj);
if (!obs_data_has_user_value(obj, "action")) {
_action =
Action::MANUAL_TRANSFORM; // TODO: Remove fallback in future
} else {
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
}
_scene.Load(obj);
_source.Load(obj);
_rotation.Load(obj, "rotation");
_settings.Load(obj, "settings");
// Convert old data format
@@ -107,21 +343,34 @@ void MacroActionSceneTransform::ApplySettings(const std::string &settings)
}
obs_data_release(obj);
}
for (auto item : items) {
obs_sceneitem_release(item);
}
obs_data_release(data);
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[type, name] : actions) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(type));
}
}
MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> entryData)
: QWidget(parent)
: QWidget(parent),
_scenes(new SceneSelectionWidget(window(), true, false, false, true)),
_sources(new SceneItemSelectionWidget(parent)),
_action(new QComboBox()),
_rotation(new VariableDoubleSpinBox()),
_getSettings(new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sceneTransform.getTransform"))),
_settings(new VariableTextEdit(this)),
_buttonLayout(new QHBoxLayout())
{
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
_sources = new SceneItemSelectionWidget(parent);
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
_settings = new VariableTextEdit(this);
_rotation->setMinimum(-360);
_rotation->setMaximum(360);
_rotation->setSuffix("°");
populateActionSelection(_action);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
@@ -130,6 +379,12 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_action, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(
_rotation,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(RotationChanged(const NumberVariable<double> &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
@@ -138,23 +393,21 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QHBoxLayout *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{settings}}", _settings},
{"{{getSettings}}", _getSettings},
{"{{scenes}}", _scenes}, {"{{rotation}}", _rotation},
{"{{sources}}", _sources}, {"{{action}}", _action},
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
};
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.sceneTransform.entry"),
entryLayout, widgetPlaceholders);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
_buttonLayout->addWidget(_getSettings);
_buttonLayout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_settings);
mainLayout->addLayout(buttonLayout);
mainLayout->addLayout(_buttonLayout);
setLayout(mainLayout);
_entryData = entryData;
@@ -170,10 +423,11 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source);
_action->setCurrentIndex(
_action->findData(static_cast<int>(_entryData->_action)));
_rotation->SetValue(_entryData->_rotation);
_settings->setPlainText(_entryData->_settings);
adjustSize();
updateGeometry();
SetWidgetVisibility();
}
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
@@ -196,6 +450,30 @@ void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
adjustSize();
updateGeometry();
}
void MacroActionSceneTransformEdit::ActionChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionSceneTransform::Action>(
_action->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionSceneTransformEdit::RotationChanged(const DoubleVariable &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_rotation = value;
}
void MacroActionSceneTransformEdit::GetSettingsClicked()
@@ -211,9 +489,6 @@ void MacroActionSceneTransformEdit::GetSettingsClicked()
auto settings = GetSceneItemTransform(items[0]);
_settings->setPlainText(FormatJsonString(settings));
for (auto item : items) {
obs_sceneitem_release(item);
}
}
void MacroActionSceneTransformEdit::SettingsChanged()
@@ -229,4 +504,19 @@ void MacroActionSceneTransformEdit::SettingsChanged()
updateGeometry();
}
void MacroActionSceneTransformEdit::SetWidgetVisibility()
{
SetLayoutVisible(
_buttonLayout,
_entryData->_action ==
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
_settings->setVisible(
_entryData->_action ==
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
_rotation->setVisible(_entryData->_action ==
MacroActionSceneTransform::Action::ROTATE);
adjustSize();
updateGeometry();
}
} // namespace advss

View File

@@ -3,6 +3,7 @@
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
#include "variable-text-edit.hpp"
#include "variable-spinbox.hpp"
#include <QSpinBox>
@@ -22,11 +23,27 @@ public:
return std::make_shared<MacroActionSceneTransform>(m);
}
enum class Action {
RESET,
ROTATE,
FLIP_HORIZONTAL,
FLIP_VERTICAL,
FIT_TO_SCREEN,
STRETCH_TO_SCREEN,
CENTER_TO_SCREEN,
CENTER_VERTICALLY,
CENTER_HORIZONTALLY,
MANUAL_TRANSFORM = 100,
};
Action _action = Action::RESET;
SceneSelection _scene;
SceneItemSelection _source;
StringVariable _settings = "";
DoubleVariable _rotation = 90.0;
private:
void Transform(obs_scene_item *);
void ApplySettings(const std::string &);
std::string ConvertSettings();
@@ -57,19 +74,25 @@ public:
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const SceneItemSelection &);
void ActionChanged(int);
void RotationChanged(const NumberVariable<double> &value);
void GetSettingsClicked();
void SettingsChanged();
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
QComboBox *_action;
VariableDoubleSpinBox *_rotation;
QPushButton *_getSettings;
VariableTextEdit *_settings;
std::shared_ptr<MacroActionSceneTransform> _entryData;
QHBoxLayout *_buttonLayout;
private:
std::shared_ptr<MacroActionSceneTransform> _entryData;
bool _loading = true;
};

View File

@@ -11,85 +11,38 @@ bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register(
MacroActionSceneVisibilityEdit::Create,
"AdvSceneSwitcher.action.sceneVisibility"});
const static std::map<SceneVisibilityAction, std::string> actionTypes = {
{SceneVisibilityAction::SHOW,
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
{SceneVisibilityAction::HIDE,
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
{SceneVisibilityAction::TOGGLE,
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
};
const static std::map<SceneItemSourceType, std::string> sourceItemSourceTypes = {
{SceneItemSourceType::SOURCE,
"AdvSceneSwitcher.action.sceneVisibility.type.source"},
{SceneItemSourceType::SOURCE_GROUP,
"AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup"},
};
struct VisibilityData {
std::string name;
SceneVisibilityAction action;
const static std::map<MacroActionSceneVisibility::Action, std::string>
actionTypes = {
{MacroActionSceneVisibility::Action::SHOW,
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
{MacroActionSceneVisibility::Action::HIDE,
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
{MacroActionSceneVisibility::Action::TOGGLE,
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
};
static void setSceneItemVisibility(obs_sceneitem_t *item,
SceneVisibilityAction action)
MacroActionSceneVisibility::Action action)
{
switch (action) {
case SceneVisibilityAction::SHOW:
case MacroActionSceneVisibility::Action::SHOW:
obs_sceneitem_set_visible(item, true);
break;
case SceneVisibilityAction::HIDE:
case MacroActionSceneVisibility::Action::HIDE:
obs_sceneitem_set_visible(item, false);
break;
case SceneVisibilityAction::TOGGLE:
case MacroActionSceneVisibility::Action::TOGGLE:
obs_sceneitem_set_visible(item, !obs_sceneitem_visible(item));
break;
}
}
static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
void *ptr)
{
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(ptr);
auto sourceTypeName = obs_source_get_display_name(
obs_source_get_id(obs_sceneitem_get_source(item)));
if (sourceTypeName && vInfo->name == sourceTypeName) {
setSceneItemVisibility(item, vInfo->action);
}
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, visibilitySourceTypeEnum, ptr);
}
return true;
}
bool MacroActionSceneVisibility::PerformAction()
{
switch (_sourceType) {
case SceneItemSourceType::SOURCE: {
auto items = _source.GetSceneItems(_scene);
for (auto item : items) {
setSceneItemVisibility(item, _action);
obs_sceneitem_release(item);
}
break;
auto items = _source.GetSceneItems(_scene);
for (const auto &item : items) {
setSceneItemVisibility(item, _action);
}
case SceneItemSourceType::SOURCE_GROUP: {
auto s = obs_weak_source_get_source(_scene.GetScene());
auto scene = obs_scene_from_source(s);
VisibilityData vInfo = {_sourceGroup, _action};
obs_scene_enum_items(scene, visibilitySourceTypeEnum, &vInfo);
obs_source_release(s);
break;
}
default:
break;
}
return true;
}
@@ -97,18 +50,11 @@ void MacroActionSceneVisibility::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
if (_sourceType == SceneItemSourceType::SOURCE) {
vblog(LOG_INFO,
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(),
_source.ToString(true).c_str(),
_scene.ToString(true).c_str());
} else {
vblog(LOG_INFO,
"performed visibility action \"%s\" for any source type \"%s\" on scene \"%s\"",
it->second.c_str(), _sourceGroup.c_str(),
_scene.ToString(true).c_str());
}
vblog(LOG_INFO,
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), _source.ToString(true).c_str(),
_scene.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
static_cast<int>(_action));
@@ -119,13 +65,8 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
if (_sourceType == SceneItemSourceType::SOURCE) {
_source.Save(obj);
} else {
obs_data_set_string(obj, "sourceGroup", _sourceGroup.c_str());
}
_source.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
return true;
}
@@ -136,89 +77,64 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
if (obs_data_has_user_value(obj, "source")) {
auto sourceName = obs_data_get_string(obj, "source");
obs_data_set_string(obj, "sceneItem", sourceName);
obs_data_set_string(obj, "sourceGroup", sourceName);
}
MacroAction::Load(obj);
_scene.Load(obj);
_source.Load(obj);
_sourceType = static_cast<SceneItemSourceType>(
obs_data_get_int(obj, "sourceType"));
_sourceGroup = obs_data_get_string(obj, "sourceGroup");
_action = static_cast<SceneVisibilityAction>(
_action = static_cast<MacroActionSceneVisibility::Action>(
obs_data_get_int(obj, "action"));
// TODO: Remove in future version
if (obs_data_get_int(obj, "sourceType") != 0) {
auto sourceGroup = obs_data_get_string(obj, "sourceGroup");
_source.SetSourceTypeSelection(sourceGroup);
}
return true;
}
std::string MacroActionSceneVisibility::GetShortDesc() const
{
if (_sourceType == SceneItemSourceType::SOURCE &&
!_source.ToString().empty()) {
if (!_source.ToString().empty()) {
return _scene.ToString() + " - " + _source.ToString();
}
if (_sourceType == SceneItemSourceType::SOURCE_GROUP &&
!_sourceGroup.empty()) {
return _scene.ToString() + " - " +
obs_module_text(
"AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup") +
" " + _sourceGroup;
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static inline void populateSourceItemTypeSelection(QComboBox *list)
{
for (auto entry : sourceItemSourceTypes) {
for (const auto &entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
: QWidget(parent)
: QWidget(parent),
_scenes(new SceneSelectionWidget(window(), true, false, true, true)),
_sources(new SceneItemSelectionWidget(parent)),
_actions(new QComboBox())
{
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
_sourceTypes = new QComboBox();
_sources = new SceneItemSelectionWidget(parent);
_sourceGroups = new QComboBox();
_actions = new QComboBox();
populateSourceItemTypeSelection(_sourceTypes);
PopulateSourceGroupSelection(_sourceGroups);
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(_sourceTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(SourceTypeChanged(int)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_sourceGroups,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceGroupChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes}, {"{{sourceTypes}}", _sourceTypes},
{"{{sources}}", _sources}, {"{{sourceGroups}}", _sourceGroups},
{"{{actions}}", _actions},
};
auto layout = new QHBoxLayout;
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.sceneVisibility.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
layout,
{{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions}});
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
@@ -232,13 +148,8 @@ void MacroActionSceneVisibilityEdit::UpdateEntryData()
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sourceTypes->setCurrentIndex(
static_cast<int>(_entryData->_sourceType));
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem((_entryData->_source));
_sourceGroups->setCurrentText(
QString::fromStdString(_entryData->_sourceGroup));
SetWidgetVisibility();
}
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
@@ -251,17 +162,6 @@ void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
_entryData->_scene = s;
}
void MacroActionSceneVisibilityEdit::SourceTypeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_sourceType = static_cast<SceneItemSourceType>(value);
SetWidgetVisibility();
}
void MacroActionSceneVisibilityEdit::SourceChanged(
const SceneItemSelection &item)
{
@@ -273,22 +173,8 @@ void MacroActionSceneVisibilityEdit::SourceChanged(
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionSceneVisibilityEdit::SourceGroupChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
if (text == obs_module_text("AdvSceneSwitcher.selectItem")) {
_entryData->_sourceGroup = "";
} else {
_entryData->_sourceGroup = text.toStdString();
}
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
adjustSize();
updateGeometry();
}
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
@@ -298,19 +184,8 @@ void MacroActionSceneVisibilityEdit::ActionChanged(int value)
}
auto lock = LockContext();
_entryData->_action = static_cast<SceneVisibilityAction>(value);
}
void MacroActionSceneVisibilityEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
_sources->setVisible(_entryData->_sourceType ==
SceneItemSourceType::SOURCE);
_sourceGroups->setVisible(_entryData->_sourceType ==
SceneItemSourceType::SOURCE_GROUP);
adjustSize();
_entryData->_action =
static_cast<MacroActionSceneVisibility::Action>(value);
}
} // namespace advss

View File

@@ -7,17 +7,6 @@
namespace advss {
enum class SceneVisibilityAction {
SHOW,
HIDE,
TOGGLE,
};
enum class SceneItemSourceType {
SOURCE,
SOURCE_GROUP,
};
class MacroActionSceneVisibility : public MacroAction {
public:
MacroActionSceneVisibility(Macro *m) : MacroAction(m) {}
@@ -33,10 +22,14 @@ public:
}
SceneSelection _scene;
SceneItemSourceType _sourceType = SceneItemSourceType::SOURCE;
SceneItemSelection _source;
std::string _sourceGroup = "";
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
enum class Action {
SHOW,
HIDE,
TOGGLE,
};
Action _action = Action::SHOW;
private:
static bool _registered;
@@ -62,25 +55,18 @@ public:
private slots:
void SceneChanged(const SceneSelection &);
void SourceTypeChanged(int value);
void SourceChanged(const SceneItemSelection &);
void SourceGroupChanged(const QString &text);
void ActionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
QComboBox *_sourceTypes;
SceneItemSelectionWidget *_sources;
QComboBox *_sourceGroups;
QComboBox *_actions;
std::shared_ptr<MacroActionSceneVisibility> _entryData;
private:
void SetWidgetVisibility();
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -32,7 +32,7 @@ void MacroActionScreenshot::CustomScreenshot(OBSWeakSource &source)
}
auto s = obs_weak_source_get_source(source);
_screenshot.~ScreenshotHelper();
new (&_screenshot) ScreenshotHelper(s, false, 0, true, _path);
new (&_screenshot) ScreenshotHelper(s, QRect(), false, 0, true, _path);
obs_source_release(s);
}

View File

@@ -66,7 +66,7 @@ bool MacroActionSequence::PerformAction()
return true;
}
return macro->PerformActions();
return macro->PerformActions(true);
}
void MacroActionSequence::LogAction() const

View File

@@ -12,15 +12,66 @@ bool MacroActionSource::_registered = MacroActionFactory::Register(
{MacroActionSource::Create, MacroActionSourceEdit::Create,
"AdvSceneSwitcher.action.source"});
const static std::map<SourceAction, std::string> actionTypes = {
{SourceAction::ENABLE, "AdvSceneSwitcher.action.source.type.enable"},
{SourceAction::DISABLE, "AdvSceneSwitcher.action.source.type.disable"},
{SourceAction::SETTINGS,
const static std::map<MacroActionSource::Action, std::string> actionTypes = {
{MacroActionSource::Action::ENABLE,
"AdvSceneSwitcher.action.source.type.enable"},
{MacroActionSource::Action::DISABLE,
"AdvSceneSwitcher.action.source.type.disable"},
{MacroActionSource::Action::SETTINGS,
"AdvSceneSwitcher.action.source.type.settings"},
{SourceAction::REFRESH_SETTINGS,
{MacroActionSource::Action::REFRESH_SETTINGS,
"AdvSceneSwitcher.action.source.type.refreshSettings"},
{SourceAction::SETTINGS_BUTTON,
{MacroActionSource::Action::SETTINGS_BUTTON,
"AdvSceneSwitcher.action.source.type.pressSettingsButton"},
{MacroActionSource::Action::DEINTERLACE_MODE,
"AdvSceneSwitcher.action.source.type.deinterlaceMode"},
{MacroActionSource::Action::DEINTERLACE_FIELD_ORDER,
"AdvSceneSwitcher.action.source.type.deinterlaceOrder"},
{MacroActionSource::Action::OPEN_INTERACTION_DIALOG,
"AdvSceneSwitcher.action.source.type.openInteractionDialog"},
{MacroActionSource::Action::OPEN_FILTER_DIALOG,
"AdvSceneSwitcher.action.source.type.openFilterDialog"},
{MacroActionSource::Action::OPEN_PROPERTIES_DIALOG,
"AdvSceneSwitcher.action.source.type.openPropertiesDialog"},
};
const static std::map<obs_deinterlace_mode, std::string> deinterlaceModes = {
{OBS_DEINTERLACE_MODE_DISABLE,
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
{OBS_DEINTERLACE_MODE_DISCARD,
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
{OBS_DEINTERLACE_MODE_RETRO,
"AdvSceneSwitcher.action.source.deinterlaceMode.retro"},
{OBS_DEINTERLACE_MODE_BLEND,
"AdvSceneSwitcher.action.source.deinterlaceMode.blend"},
{OBS_DEINTERLACE_MODE_BLEND_2X,
"AdvSceneSwitcher.action.source.deinterlaceMode.blend2x"},
{OBS_DEINTERLACE_MODE_LINEAR,
"AdvSceneSwitcher.action.source.deinterlaceMode.linear"},
{OBS_DEINTERLACE_MODE_LINEAR_2X,
"AdvSceneSwitcher.action.source.deinterlaceMode.linear2x"},
{OBS_DEINTERLACE_MODE_YADIF,
"AdvSceneSwitcher.action.source.deinterlaceMode.yadif"},
{OBS_DEINTERLACE_MODE_YADIF_2X,
"AdvSceneSwitcher.action.source.deinterlaceMode.yadif2x"},
};
const static std::map<obs_deinterlace_field_order, std::string>
deinterlaceFieldOrders = {
{OBS_DEINTERLACE_FIELD_ORDER_TOP,
"AdvSceneSwitcher.action.source.deinterlaceOrder.topFieldFirst"},
{OBS_DEINTERLACE_FIELD_ORDER_BOTTOM,
"AdvSceneSwitcher.action.source.deinterlaceOrder.bottomFieldFirst"},
};
const static std::map<MacroActionSource::SettingsInputMethod, std::string>
inputMethods = {
{MacroActionSource::SettingsInputMethod::INDIVIDUAL_MANUAL,
"AdvSceneSwitcher.action.source.inputMethod.individualManual"},
{MacroActionSource::SettingsInputMethod::INDIVIDUAL_TEMPVAR,
"AdvSceneSwitcher.action.source.inputMethod.individualTempvar"},
{MacroActionSource::SettingsInputMethod::JSON_STRING,
"AdvSceneSwitcher.action.source.inputMethod.json"},
};
static std::vector<SourceSettingButton> getSourceButtons(OBSWeakSource source)
@@ -38,6 +89,7 @@ static std::vector<SourceSettingButton> getSourceButtons(OBSWeakSource source)
buttons.emplace_back(button);
} while (obs_property_next(&it));
obs_source_release(s);
obs_properties_destroy(sourceProperties);
return buttons;
}
@@ -75,25 +127,72 @@ static void refreshSourceSettings(obs_source_t *s)
}
}
static bool isInteractable(obs_source_t *source)
{
uint32_t flags = obs_source_get_output_flags(source);
return (flags & OBS_SOURCE_INTERACTION) != 0;
}
bool MacroActionSource::PerformAction()
{
auto s = obs_weak_source_get_source(_source.GetSource());
switch (_action) {
case SourceAction::ENABLE:
case Action::ENABLE:
obs_source_set_enabled(s, true);
break;
case SourceAction::DISABLE:
case Action::DISABLE:
obs_source_set_enabled(s, false);
break;
case SourceAction::SETTINGS:
SetSourceSettings(s, _settings);
case Action::SETTINGS:
switch (_settingsInputMethod) {
case SettingsInputMethod::INDIVIDUAL_MANUAL:
SetSourceSetting(s, _setting, _manualSettingValue);
break;
case SettingsInputMethod::INDIVIDUAL_TEMPVAR: {
auto var = _tempVar.GetTempVariable(GetMacro());
if (!var) {
break;
}
auto value = var->Value();
if (!value) {
break;
}
SetSourceSetting(s, _setting, *value);
break;
}
case SettingsInputMethod::JSON_STRING:
SetSourceSettings(s, _settingsString);
break;
}
break;
case SourceAction::REFRESH_SETTINGS:
case Action::REFRESH_SETTINGS:
refreshSourceSettings(s);
break;
case SourceAction::SETTINGS_BUTTON:
case Action::SETTINGS_BUTTON:
pressSourceButton(_button, s);
break;
case Action::DEINTERLACE_MODE:
obs_source_set_deinterlace_mode(s, _deinterlaceMode);
break;
case Action::DEINTERLACE_FIELD_ORDER:
obs_source_set_deinterlace_field_order(s, _deinterlaceOrder);
break;
case Action::OPEN_INTERACTION_DIALOG:
if (isInteractable(s)) {
obs_frontend_open_source_interaction(s);
} else {
blog(LOG_INFO,
"refusing to open interaction dialog "
"for non intractable source \"%s\"",
_source.ToString().c_str());
}
break;
case Action::OPEN_FILTER_DIALOG:
obs_frontend_open_source_filters(s);
break;
case Action::OPEN_PROPERTIES_DIALOG:
obs_frontend_open_source_properties(s);
break;
default:
break;
}
@@ -119,7 +218,16 @@ bool MacroActionSource::Save(obs_data_t *obj) const
_source.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_button.Save(obj);
_settings.Save(obj, "settings");
obs_data_set_int(obj, "inputMethod",
static_cast<int>(_settingsInputMethod));
_setting.Save(obj);
_manualSettingValue.Save(obj, "manualSettingValue");
_tempVar.Save(obj);
_settingsString.Save(obj, "settings");
obs_data_set_int(obj, "deinterlaceMode",
static_cast<int>(_deinterlaceMode));
obs_data_set_int(obj, "deinterlaceOrder",
static_cast<int>(_deinterlaceOrder));
return true;
}
@@ -127,9 +235,23 @@ bool MacroActionSource::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_source.Load(obj);
_action = static_cast<SourceAction>(obs_data_get_int(obj, "action"));
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_button.Load(obj);
_settings.Load(obj, "settings");
// TODO: Remove fallback in future version
if (obs_data_has_user_value(obj, "inputMethod")) {
_settingsInputMethod = static_cast<SettingsInputMethod>(
obs_data_get_int(obj, "inputMethod"));
} else {
_settingsInputMethod = SettingsInputMethod::JSON_STRING;
}
_setting.Load(obj);
_settingsString.Load(obj, "settings");
_manualSettingValue.Load(obj, "manualSettingValue");
_tempVar.Load(obj, GetMacro());
_deinterlaceMode = static_cast<obs_deinterlace_mode>(
obs_data_get_int(obj, "deinterlaceMode"));
_deinterlaceOrder = static_cast<obs_deinterlace_field_order>(
obs_data_get_int(obj, "deinterlaceOrder"));
return true;
}
@@ -143,7 +265,7 @@ static inline void populateActionSelection(QComboBox *list)
{
for (auto &[actionType, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
if (actionType == SourceAction::REFRESH_SETTINGS) {
if (actionType == MacroActionSource::Action::REFRESH_SETTINGS) {
list->setItemData(
list->count() - 1,
obs_module_text(
@@ -170,23 +292,60 @@ static inline void populateSourceButtonSelection(QComboBox *list,
}
}
static inline void populateDeinterlaceModeSelection(QComboBox *list)
{
list->clear();
for (const auto &[value, name] : deinterlaceModes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
static inline void populateDeinterlaceFieldOrderSelection(QComboBox *list)
{
list->clear();
for (const auto &[value, name] : deinterlaceFieldOrders) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
static inline void populateSettingsInputMethods(QComboBox *list)
{
list->clear();
for (const auto &[value, name] : inputMethods) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
MacroActionSourceEdit::MacroActionSourceEdit(
QWidget *parent, std::shared_ptr<MacroActionSource> entryData)
: QWidget(parent)
{
_sources = new SourceSelectionWidget(this, QStringList(), true);
_actions = new QComboBox();
_settingsButtons = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
_settings = new VariableTextEdit(this);
_warning = new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning"));
: QWidget(parent),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_actions(new QComboBox()),
_settingsButtons(new QComboBox()),
_settingsLayout(new QHBoxLayout()),
_settingsInputMethods(new QComboBox(this)),
_manualSettingValue(new VariableTextEdit(this, 5, 1, 1)),
_tempVars(new TempVariableSelection(this)),
_sourceSettings(new SourceSettingSelection(this)),
_settingsString(new VariableTextEdit(this)),
_getSettings(new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.source.getSettings"))),
_deinterlaceMode(new QComboBox()),
_deinterlaceOrder(new QComboBox()),
_warning(new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning")))
{
populateActionSelection(_actions);
auto sources = GetSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
populateDeinterlaceModeSelection(_deinterlaceMode);
populateDeinterlaceFieldOrderSelection(_deinterlaceOrder);
populateSettingsInputMethods(_settingsInputMethods);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
@@ -197,24 +356,51 @@ MacroActionSourceEdit::MacroActionSourceEdit(
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QWidget::connect(_settingsString, SIGNAL(textChanged()), this,
SLOT(SettingsStringChanged()));
QWidget::connect(_deinterlaceMode, SIGNAL(currentIndexChanged(int)),
this, SLOT(DeinterlaceModeChanged(int)));
QWidget::connect(_deinterlaceOrder, SIGNAL(currentIndexChanged(int)),
this, SLOT(DeinterlaceOrderChanged(int)));
QWidget::connect(_tempVars,
SIGNAL(SelectionChanged(const TempVariableRef &)),
this, SLOT(SelectionChanged(const TempVariableRef &)));
QWidget::connect(_settingsInputMethods,
SIGNAL(currentIndexChanged(int)), this,
SLOT(SettingsInputMethodChanged(int)));
QWidget::connect(_manualSettingValue, SIGNAL(textChanged()), this,
SLOT(ManualSettingsValueChanged()));
QWidget::connect(_sourceSettings,
SIGNAL(SelectionChanged(const SourceSetting &)), this,
SLOT(SelectionChanged(const SourceSetting &)));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *entryLayout = new QHBoxLayout;
QHBoxLayout *buttonLayout = new QHBoxLayout;
auto entryLayout = new QHBoxLayout;
entryLayout->setContentsMargins(0, 0, 0, 0);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources},
{"{{actions}}", _actions},
{"{{settings}}", _settings},
{"{{settings}}", _sourceSettings},
{"{{settingsInputMethod}}", _settingsInputMethods},
{"{{settingValue}}", _manualSettingValue},
{"{{tempVar}}", _tempVars},
{"{{getSettings}}", _getSettings},
{"{{settingsButtons}}", _settingsButtons},
{"{{deinterlaceMode}}", _deinterlaceMode},
{"{{deinterlaceOrder}}", _deinterlaceOrder},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"),
entryLayout, widgetPlaceholders);
_settingsLayout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.source.entry.settings"),
_settingsLayout, widgetPlaceholders);
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addLayout(_settingsLayout);
mainLayout->addWidget(_warning);
mainLayout->addWidget(_settings);
mainLayout->addWidget(_settingsString);
auto buttonLayout = new QHBoxLayout;
buttonLayout->setContentsMargins(0, 0, 0, 0);
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
mainLayout->addLayout(buttonLayout);
@@ -235,13 +421,21 @@ void MacroActionSourceEdit::UpdateEntryData()
_entryData->_source.GetSource());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->SetSource(_entryData->_source);
_sourceSettings->SetSource(_entryData->_source.GetSource());
_sourceSettings->SetSetting(_entryData->_setting);
_settingsButtons->setCurrentText(
QString::fromStdString(_entryData->_button.ToString()));
_settings->setPlainText(_entryData->_settings);
SetWidgetVisibility();
_settingsString->setPlainText(_entryData->_settingsString);
_deinterlaceMode->setCurrentIndex(_deinterlaceMode->findData(
static_cast<int>(_entryData->_deinterlaceMode)));
_deinterlaceOrder->setCurrentIndex(_deinterlaceOrder->findData(
static_cast<int>(_entryData->_deinterlaceOrder)));
_settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData(
static_cast<int>(_entryData->_settingsInputMethod)));
_tempVars->SetVariable(_entryData->_tempVar);
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
adjustSize();
updateGeometry();
SetWidgetVisibility();
}
void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
@@ -256,6 +450,7 @@ void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
}
populateSourceButtonSelection(_settingsButtons,
_entryData->_source.GetSource());
_sourceSettings->SetSource(_entryData->_source.GetSource());
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -267,7 +462,7 @@ void MacroActionSourceEdit::ActionChanged(int value)
}
auto lock = LockContext();
_entryData->_action = static_cast<SourceAction>(value);
_entryData->_action = static_cast<MacroActionSource::Action>(value);
SetWidgetVisibility();
}
@@ -288,18 +483,99 @@ void MacroActionSourceEdit::GetSettingsClicked()
return;
}
_settings->setPlainText(FormatJsonString(
GetSourceSettings(_entryData->_source.GetSource())));
switch (_entryData->_settingsInputMethod) {
case MacroActionSource::SettingsInputMethod::INDIVIDUAL_MANUAL:
_manualSettingValue->setPlainText(GetSourceSettingValue(
_entryData->_source.GetSource(), _entryData->_setting));
break;
case MacroActionSource::SettingsInputMethod::INDIVIDUAL_TEMPVAR:
break;
case MacroActionSource::SettingsInputMethod::JSON_STRING:
_settingsString->setPlainText(FormatJsonString(
GetSourceSettings(_entryData->_source.GetSource())));
break;
}
}
void MacroActionSourceEdit::SettingsChanged()
void MacroActionSourceEdit::SettingsStringChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_settings = _settings->toPlainText().toStdString();
_entryData->_settingsString =
_settingsString->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionSourceEdit::DeinterlaceModeChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_deinterlaceMode = static_cast<obs_deinterlace_mode>(
_deinterlaceMode->itemData(idx).toInt());
}
void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_deinterlaceOrder =
static_cast<obs_deinterlace_field_order>(
_deinterlaceOrder->itemData(idx).toInt());
}
void MacroActionSourceEdit::SelectionChanged(const TempVariableRef &var)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_tempVar = var;
}
void MacroActionSourceEdit::SettingsInputMethodChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_settingsInputMethod =
static_cast<MacroActionSource::SettingsInputMethod>(
_settingsInputMethods->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionSourceEdit::SelectionChanged(const SourceSetting &setting)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_setting = setting;
}
void MacroActionSourceEdit::ManualSettingsValueChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_manualSettingValue =
_manualSettingValue->toPlainText().toStdString();
adjustSize();
updateGeometry();
@@ -307,15 +583,50 @@ void MacroActionSourceEdit::SettingsChanged()
void MacroActionSourceEdit::SetWidgetVisibility()
{
const bool showSettings = _entryData->_action == SourceAction::SETTINGS;
const bool showWarning = _entryData->_action == SourceAction::ENABLE ||
_entryData->_action == SourceAction::DISABLE;
_settings->setVisible(showSettings);
_getSettings->setVisible(showSettings);
SetLayoutVisible(_settingsLayout,
_entryData->_action ==
MacroActionSource::Action::SETTINGS);
_sourceSettings->setVisible(
_entryData->_action == MacroActionSource::Action::SETTINGS &&
_entryData->_settingsInputMethod !=
MacroActionSource::SettingsInputMethod::JSON_STRING);
_settingsString->setVisible(
_entryData->_action == MacroActionSource::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionSource::SettingsInputMethod::JSON_STRING);
_getSettings->setVisible(_entryData->_action ==
MacroActionSource::Action::SETTINGS);
_tempVars->setVisible(_entryData->_action ==
MacroActionSource::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionSource::SettingsInputMethod::
INDIVIDUAL_TEMPVAR);
if (_entryData->_action == MacroActionSource::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionSource::SettingsInputMethod::INDIVIDUAL_MANUAL) {
RemoveStretchIfPresent(_settingsLayout);
_manualSettingValue->show();
} else {
AddStretchIfNecessary(_settingsLayout);
_manualSettingValue->hide();
}
const bool showWarning =
_entryData->_action == MacroActionSource::Action::ENABLE ||
_entryData->_action == MacroActionSource::Action::DISABLE;
_warning->setVisible(showWarning);
_settingsButtons->setVisible(_entryData->_action ==
SourceAction::SETTINGS_BUTTON);
_settingsButtons->setVisible(
_entryData->_action ==
MacroActionSource::Action::SETTINGS_BUTTON);
_deinterlaceMode->setVisible(
_entryData->_action ==
MacroActionSource::Action::DEINTERLACE_MODE);
_deinterlaceOrder->setVisible(
_entryData->_action ==
MacroActionSource::Action::DEINTERLACE_FIELD_ORDER);
adjustSize();
updateGeometry();
}
bool SourceSettingButton::Save(obs_data_t *obj) const

View File

@@ -2,6 +2,7 @@
#include "macro-action-edit.hpp"
#include "variable-text-edit.hpp"
#include "source-selection.hpp"
#include "source-setting.hpp"
#include <QSpinBox>
#include <QLabel>
@@ -10,14 +11,6 @@
namespace advss {
enum class SourceAction {
ENABLE,
DISABLE,
SETTINGS,
REFRESH_SETTINGS,
SETTINGS_BUTTON,
};
struct SourceSettingButton {
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
@@ -43,8 +36,35 @@ public:
SourceSelection _source;
SourceSettingButton _button;
StringVariable _settings = "";
SourceAction _action = SourceAction::ENABLE;
StringVariable _settingsString = "";
StringVariable _manualSettingValue = "";
obs_deinterlace_mode _deinterlaceMode = OBS_DEINTERLACE_MODE_DISABLE;
obs_deinterlace_field_order _deinterlaceOrder =
OBS_DEINTERLACE_FIELD_ORDER_TOP;
TempVariableRef _tempVar;
SourceSetting _setting;
enum class Action {
ENABLE,
DISABLE,
SETTINGS,
REFRESH_SETTINGS,
SETTINGS_BUTTON,
DEINTERLACE_MODE,
DEINTERLACE_FIELD_ORDER,
OPEN_INTERACTION_DIALOG,
OPEN_FILTER_DIALOG,
OPEN_PROPERTIES_DIALOG,
};
Action _action = Action::SETTINGS;
enum class SettingsInputMethod {
INDIVIDUAL_MANUAL,
INDIVIDUAL_TEMPVAR,
JSON_STRING,
};
SettingsInputMethod _settingsInputMethod =
SettingsInputMethod::INDIVIDUAL_MANUAL;
private:
static bool _registered;
@@ -72,7 +92,13 @@ private slots:
void ActionChanged(int value);
void ButtonChanged(int idx);
void GetSettingsClicked();
void SettingsChanged();
void SettingsStringChanged();
void DeinterlaceModeChanged(int);
void DeinterlaceOrderChanged(int);
void SelectionChanged(const TempVariableRef &);
void SettingsInputMethodChanged(int);
void SelectionChanged(const SourceSetting &);
void ManualSettingsValueChanged();
signals:
void HeaderInfoChanged(const QString &);
@@ -80,9 +106,17 @@ protected:
SourceSelectionWidget *_sources;
QComboBox *_actions;
QComboBox *_settingsButtons;
QHBoxLayout *_settingsLayout;
QComboBox *_settingsInputMethods;
VariableTextEdit *_manualSettingValue;
TempVariableSelection *_tempVars;
SourceSettingSelection *_sourceSettings;
VariableTextEdit *_settingsString;
QPushButton *_getSettings;
VariableTextEdit *_settings;
QComboBox *_deinterlaceMode;
QComboBox *_deinterlaceOrder;
QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData;
private:

View File

@@ -19,26 +19,65 @@ const static std::map<MacroActionStream::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.streaming.type.start"},
{MacroActionStream::Action::KEYFRAME_INTERVAL,
"AdvSceneSwitcher.action.streaming.type.keyFrameInterval"},
{MacroActionStream::Action::SERVER,
"AdvSceneSwitcher.action.streaming.type.server"},
{MacroActionStream::Action::STREAM_KEY,
"AdvSceneSwitcher.action.streaming.type.streamKey"},
{MacroActionStream::Action::USERNAME,
"AdvSceneSwitcher.action.streaming.type.username"},
{MacroActionStream::Action::PASSWORD,
"AdvSceneSwitcher.action.streaming.type.password"},
};
constexpr int streamStartCooldown = 5;
std::chrono::high_resolution_clock::time_point MacroActionStream::s_lastAttempt =
std::chrono::high_resolution_clock::now();
void MacroActionStream::SetKeyFrameInterval()
void MacroActionStream::SetKeyFrameInterval() const
{
const auto configPath = GetPathInProfileDir("streamEncoder.json");
obs_data_t *settings =
auto settings =
obs_data_create_from_json_file_safe(configPath.c_str(), "bak");
obs_data_set_int(settings, "keyint_sec", _keyFrameInterval);
if (settings) {
obs_data_save_json_safe(settings, configPath.c_str(), "tmp",
"bak");
obs_data_release(settings);
if (!settings) {
blog(LOG_WARNING, "failed to set key frame interval");
return;
}
obs_data_set_int(settings, "keyint_sec", _keyFrameInterval);
obs_data_save_json_safe(settings, configPath.c_str(), "tmp", "bak");
obs_data_release(settings);
}
bool MacroActionStream::CooldownDurationReached()
void MacroActionStream::SetStreamSettingsValue(const char *name,
const std::string &value,
bool enableAuth) const
{
const auto configPath = GetPathInProfileDir("service.json");
auto data =
obs_data_create_from_json_file_safe(configPath.c_str(), "bak");
if (!data) {
blog(LOG_WARNING, "failed to set %s", name);
return;
}
auto settings = obs_data_get_obj(data, "settings");
if (!settings) {
blog(LOG_WARNING, "failed to set %s", name);
obs_data_release(data);
return;
}
obs_data_set_string(settings, name, value.c_str());
if (enableAuth) {
obs_data_set_bool(settings, "use_auth", true);
}
obs_data_set_obj(data, "settings", settings);
auto service = obs_frontend_get_streaming_service();
obs_service_update(service, settings);
obs_frontend_save_streaming_service();
obs_frontend_set_streaming_service(service);
obs_data_release(settings);
obs_data_release(data);
}
bool MacroActionStream::CooldownDurationReached() const
{
auto timePassed = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::high_resolution_clock::now() - s_lastAttempt);
@@ -64,6 +103,18 @@ bool MacroActionStream::PerformAction()
case Action::KEYFRAME_INTERVAL:
SetKeyFrameInterval();
break;
case Action::SERVER:
SetStreamSettingsValue("server", _stringValue);
break;
case Action::STREAM_KEY:
SetStreamSettingsValue("key", _stringValue);
break;
case Action::USERNAME:
SetStreamSettingsValue("username", _stringValue, true);
break;
case Action::PASSWORD:
SetStreamSettingsValue("password", _stringValue, true);
break;
default:
break;
}
@@ -86,6 +137,7 @@ bool MacroActionStream::Save(obs_data_t *obj) const
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_keyFrameInterval.Save(obj, "keyFrameInterval");
_stringValue.Save(obj, "stringValue");
return true;
}
@@ -94,6 +146,7 @@ bool MacroActionStream::Load(obs_data_t *obj)
MacroAction::Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_keyFrameInterval.Load(obj, "keyFrameInterval");
_stringValue.Load(obj, "stringValue");
return true;
}
@@ -108,11 +161,19 @@ MacroActionStreamEdit::MacroActionStreamEdit(
QWidget *parent, std::shared_ptr<MacroActionStream> entryData)
: QWidget(parent),
_actions(new QComboBox()),
_keyFrameInterval(new VariableSpinBox())
_keyFrameInterval(new VariableSpinBox()),
_stringValue(new VariableLineEdit(this)),
_showPassword(new QPushButton()),
_layout(new QHBoxLayout())
{
_keyFrameInterval->setMinimum(0);
_keyFrameInterval->setMaximum(25);
_showPassword->setMaximumWidth(22);
_showPassword->setFlat(true);
_showPassword->setStyleSheet(
"QPushButton { background-color: transparent; border: 0px }");
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
@@ -122,13 +183,20 @@ MacroActionStreamEdit::MacroActionStreamEdit(
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this,
SLOT(KeyFrameIntervalChanged(const NumberVariable<int> &)));
QWidget::connect(_stringValue, SIGNAL(editingFinished()), this,
SLOT(StringValueChanged()));
QWidget::connect(_showPassword, SIGNAL(pressed()), this,
SLOT(ShowPassword()));
QWidget::connect(_showPassword, SIGNAL(released()), this,
SLOT(HidePassword()));
QHBoxLayout *mainLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.streaming.entry"),
mainLayout,
_layout,
{{"{{actions}}", _actions},
{"{{keyFrameInterval}}", _keyFrameInterval}});
setLayout(mainLayout);
{"{{keyFrameInterval}}", _keyFrameInterval},
{"{{stringValue}}", _stringValue},
{"{{showPassword}}", _showPassword}});
setLayout(_layout);
_entryData = entryData;
UpdateEntryData();
@@ -142,6 +210,7 @@ void MacroActionStreamEdit::UpdateEntryData()
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_keyFrameInterval->SetValue(_entryData->_keyFrameInterval);
_stringValue->setText(_entryData->_stringValue);
SetWidgetVisiblity();
}
@@ -156,15 +225,59 @@ void MacroActionStreamEdit::KeyFrameIntervalChanged(
_entryData->_keyFrameInterval = value;
}
void MacroActionStreamEdit::StringValueChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_stringValue = _stringValue->text().toStdString();
SetWidgetVisiblity();
}
void MacroActionStreamEdit::ShowPassword()
{
SetButtonIcon(_showPassword, ":res/images/visible.svg");
_stringValue->setEchoMode(QLineEdit::Normal);
}
void MacroActionStreamEdit::HidePassword()
{
SetButtonIcon(_showPassword, ":res/images/invisible.svg");
_stringValue->setEchoMode(QLineEdit::PasswordEchoOnEdit);
}
void MacroActionStreamEdit::SetWidgetVisiblity()
{
if (!_entryData) {
return;
}
const auto action = _entryData->_action;
_keyFrameInterval->setVisible(
_entryData->_action ==
MacroActionStream::Action::KEYFRAME_INTERVAL);
action == MacroActionStream::Action::KEYFRAME_INTERVAL);
const bool modifyingStringValue =
action == MacroActionStream::Action::SERVER ||
action == MacroActionStream::Action::STREAM_KEY ||
action == MacroActionStream::Action::USERNAME ||
action == MacroActionStream::Action::PASSWORD;
_stringValue->setVisible(modifyingStringValue);
if (modifyingStringValue) {
// Give as much space to string value line edit as posssible
RemoveStretchIfPresent(_layout);
} else {
AddStretchIfNecessary(_layout);
}
if (action == MacroActionStream::Action::PASSWORD ||
action == MacroActionStream::Action::STREAM_KEY) {
_stringValue->setEchoMode(QLineEdit::PasswordEchoOnEdit);
_showPassword->show();
HidePassword();
} else {
_stringValue->setEchoMode(QLineEdit::Normal);
_showPassword->hide();
}
}
void MacroActionStreamEdit::ActionChanged(int value)
@@ -172,9 +285,12 @@ void MacroActionStreamEdit::ActionChanged(int value)
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionStream::Action>(value);
{
auto lock = LockContext();
_entryData->_action =
static_cast<MacroActionStream::Action>(value);
}
_stringValue->setText(QString(""));
SetWidgetVisiblity();
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "macro-action-edit.hpp"
#include "variable-spinbox.hpp"
#include "variable-line-edit.hpp"
#include <QDoubleSpinBox>
#include <QComboBox>
@@ -26,13 +27,20 @@ public:
STOP,
START,
KEYFRAME_INTERVAL,
SERVER,
STREAM_KEY,
USERNAME,
PASSWORD,
};
Action _action = Action::STOP;
NumberVariable<int> _keyFrameInterval = 0;
IntVariable _keyFrameInterval = 0;
StringVariable _stringValue;
private:
void SetKeyFrameInterval();
bool CooldownDurationReached();
void SetKeyFrameInterval() const;
void SetStreamSettingsValue(const char *name, const std::string &value,
bool enableAuth = false) const;
bool CooldownDurationReached() const;
static std::chrono::high_resolution_clock::time_point s_lastAttempt;
static bool _registered;
@@ -58,16 +66,21 @@ public:
private slots:
void ActionChanged(int value);
void KeyFrameIntervalChanged(const NumberVariable<int> &);
void StringValueChanged();
void ShowPassword();
void HidePassword();
protected:
QComboBox *_actions;
VariableSpinBox *_keyFrameInterval;
VariableLineEdit *_stringValue;
QPushButton *_showPassword;
std::shared_ptr<MacroActionStream> _entryData;
private:
void SetWidgetVisiblity();
QHBoxLayout *_mainLayout;
QHBoxLayout *_layout;
bool _loading = true;
};

View File

@@ -10,14 +10,14 @@ bool MacroActionSudioMode::_registered = MacroActionFactory::Register(
{MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
"AdvSceneSwitcher.action.studioMode"});
const static std::map<StudioModeAction, std::string> actionTypes = {
{StudioModeAction::SWAP_SCENE,
const static std::map<MacroActionSudioMode::Action, std::string> actionTypes = {
{MacroActionSudioMode::Action::SWAP_SCENE,
"AdvSceneSwitcher.action.studioMode.type.swap"},
{StudioModeAction::SET_SCENE,
{MacroActionSudioMode::Action::SET_SCENE,
"AdvSceneSwitcher.action.studioMode.type.setScene"},
{StudioModeAction::ENABLE_STUDIO_MODE,
{MacroActionSudioMode::Action::ENABLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.enable"},
{StudioModeAction::DISABLE_STUDIO_MODE,
{MacroActionSudioMode::Action::DISABLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.disable"},
};
@@ -41,19 +41,19 @@ static void enableStudioMode(bool enable)
bool MacroActionSudioMode::PerformAction()
{
switch (_action) {
case StudioModeAction::SWAP_SCENE:
case Action::SWAP_SCENE:
obs_frontend_preview_program_trigger_transition();
break;
case StudioModeAction::SET_SCENE: {
case Action::SET_SCENE: {
auto s = obs_weak_source_get_source(_scene.GetScene());
obs_frontend_set_current_preview_scene(s);
obs_source_release(s);
break;
}
case StudioModeAction::ENABLE_STUDIO_MODE:
case Action::ENABLE_STUDIO_MODE:
enableStudioMode(true);
break;
case StudioModeAction::DISABLE_STUDIO_MODE:
case Action::DISABLE_STUDIO_MODE:
enableStudioMode(false);
break;
default:
@@ -86,15 +86,14 @@ bool MacroActionSudioMode::Save(obs_data_t *obj) const
bool MacroActionSudioMode::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action =
static_cast<StudioModeAction>(obs_data_get_int(obj, "action"));
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_scene.Load(obj);
return true;
}
std::string MacroActionSudioMode::GetShortDesc() const
{
if (_action == StudioModeAction::SET_SCENE) {
if (_action == Action::SET_SCENE) {
return _scene.ToString();
}
return "";
@@ -102,8 +101,9 @@ std::string MacroActionSudioMode::GetShortDesc() const
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
for (const auto &[id, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(id));
}
}
@@ -139,9 +139,10 @@ void MacroActionSudioModeEdit::UpdateEntryData()
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_action)));
_scenes->SetScene(_entryData->_scene);
_scenes->setVisible(_entryData->_action == StudioModeAction::SET_SCENE);
SetWidgetVisibility();
}
void MacroActionSudioModeEdit::SceneChanged(const SceneSelection &s)
@@ -156,15 +157,27 @@ void MacroActionSudioModeEdit::SceneChanged(const SceneSelection &s)
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionSudioModeEdit::ActionChanged(int value)
void MacroActionSudioModeEdit::SetWidgetVisibility()
{
_scenes->setVisible(_entryData->_action ==
MacroActionSudioMode::Action::SET_SCENE);
if (_entryData->_action != MacroActionSudioMode::Action::SET_SCENE) {
_actions->removeItem(_actions->findData(static_cast<int>(
MacroActionSudioMode::Action::SET_SCENE)));
}
}
void MacroActionSudioModeEdit::ActionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<StudioModeAction>(value);
_scenes->setVisible(_entryData->_action == StudioModeAction::SET_SCENE);
_entryData->_action = static_cast<MacroActionSudioMode::Action>(
_actions->itemData(index).toInt());
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}

View File

@@ -4,13 +4,6 @@
namespace advss {
enum class StudioModeAction {
SWAP_SCENE,
SET_SCENE,
ENABLE_STUDIO_MODE,
DISABLE_STUDIO_MODE,
};
class MacroActionSudioMode : public MacroAction {
public:
MacroActionSudioMode(Macro *m) : MacroAction(m) {}
@@ -25,7 +18,14 @@ public:
return std::make_shared<MacroActionSudioMode>(m);
}
StudioModeAction _action = StudioModeAction::SWAP_SCENE;
enum class Action {
SWAP_SCENE,
SET_SCENE, // TODO: Remove in future version as the
// functionality moved to the scene switch action
ENABLE_STUDIO_MODE,
DISABLE_STUDIO_MODE,
};
Action _action = Action::SWAP_SCENE;
SceneSelection _scene;
private:
@@ -55,12 +55,13 @@ private slots:
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
QComboBox *_actions;
SceneSelectionWidget *_scenes;
std::shared_ptr<MacroActionSudioMode> _entryData;
private:
bool _loading = true;
};

View File

@@ -14,7 +14,7 @@ bool MacroActionTimer::_registered = MacroActionFactory::Register(
{MacroActionTimer::Create, MacroActionTimerEdit::Create,
"AdvSceneSwitcher.action.timer"});
static std::map<TimerAction, std::string> timerActions = {
const static std::map<TimerAction, std::string> timerActions = {
{TimerAction::PAUSE, "AdvSceneSwitcher.action.timer.type.pause"},
{TimerAction::CONTINUE, "AdvSceneSwitcher.action.timer.type.continue"},
{TimerAction::RESET, "AdvSceneSwitcher.action.timer.type.reset"},

View File

@@ -88,7 +88,7 @@ void MacroActionTransition::SetSourceTransition(bool show)
obs_source_release(transition);
const auto items = _source.GetSceneItems(_scene);
for (auto &item : items) {
for (const auto &item : items) {
if (_setTransitionType) {
obs_sceneitem_set_transition(item, show, t);
}
@@ -96,7 +96,6 @@ void MacroActionTransition::SetSourceTransition(bool show)
obs_sceneitem_set_transition_duration(
item, show, _duration.Milliseconds());
}
obs_sceneitem_release(item);
}
obs_source_release(t);
@@ -200,7 +199,7 @@ std::string MacroActionTransition::GetShortDesc() const
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
for (const auto &entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
@@ -298,6 +297,8 @@ void MacroActionTransitionEdit::SourceChanged(const SceneItemSelection &item)
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
adjustSize();
updateGeometry();
}
void MacroActionTransitionEdit::ActionChanged(int value)

View File

@@ -1,9 +1,9 @@
#include "macro-action-variable.hpp"
#include "macro-condition-edit.hpp"
#include "switcher-data.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "math-helpers.hpp"
#include "non-modal-dialog.hpp"
#include "utility.hpp"
namespace advss {
@@ -15,7 +15,7 @@ bool MacroActionVariable::_registered = MacroActionFactory::Register(
{MacroActionVariable::Create, MacroActionVariableEdit::Create,
"AdvSceneSwitcher.action.variable"});
static std::map<MacroActionVariable::Type, std::string> actionTypes = {
const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
{MacroActionVariable::Type::SET_FIXED_VALUE,
"AdvSceneSwitcher.action.variable.type.set"},
{MacroActionVariable::Type::APPEND,
@@ -38,6 +38,20 @@ static std::map<MacroActionVariable::Type, std::string> actionTypes = {
"AdvSceneSwitcher.action.variable.type.findAndReplace"},
{MacroActionVariable::Type::MATH_EXPRESSION,
"AdvSceneSwitcher.action.variable.type.mathExpression"},
{MacroActionVariable::Type::USER_INPUT,
"AdvSceneSwitcher.action.variable.type.askForValue"},
{MacroActionVariable::Type::ENV_VARIABLE,
"AdvSceneSwitcher.action.variable.type.environmentVariable"},
{MacroActionVariable::Type::SCENE_ITEM_COUNT,
"AdvSceneSwitcher.action.variable.type.sceneItemCount"},
{MacroActionVariable::Type::STRING_LENGTH,
"AdvSceneSwitcher.action.variable.type.stringLength"},
{MacroActionVariable::Type::EXTRACT_JSON,
"AdvSceneSwitcher.action.variable.type.extractJson"},
{MacroActionVariable::Type::SET_TO_TEMPVAR,
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Type::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
};
static void apppend(Variable &var, const std::string &value)
@@ -121,6 +135,64 @@ void MacroActionVariable::HandleMathExpression(Variable *var)
var->SetValue(std::get<double>(result));
}
struct GetSceneItemNameHelper {
int curIdx = 0;
int targetIdx = 0;
std::string name = "";
};
static bool getSceneItemAtIdx(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
auto data = reinterpret_cast<GetSceneItemNameHelper *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, getSceneItemAtIdx, ptr);
}
if (data->curIdx == data->targetIdx) {
data->name =
obs_source_get_name(obs_sceneitem_get_source(item));
data->curIdx--;
return false;
}
data->curIdx--;
return true;
}
void MacroActionVariable::SetToSceneItemName(Variable *var)
{
auto weakSource = _scene.GetScene();
if (!weakSource) {
var->SetValue("");
return;
}
auto index = _sceneItemIndex.GetValue();
if (index < 1) {
var->SetValue("");
return;
}
auto sceneItemCount = GetSceneItemCount(weakSource);
OBSSourceAutoRelease source = obs_weak_source_get_source(weakSource);
auto scene = obs_scene_from_source(source);
GetSceneItemNameHelper data{sceneItemCount, index};
obs_scene_enum_items(scene, getSceneItemAtIdx, &data);
var->SetValue(data.name);
}
struct AskForInputParams {
QString prompt;
QString placeholder;
std::optional<std::string> result;
};
static void askForInput(void *param)
{
auto parameters = static_cast<AskForInputParams *>(param);
auto dialog = new NonModalMessageDialog(
parameters->prompt, NonModalMessageDialog::Type::INPUT);
dialog->SetInput(parameters->placeholder);
parameters->result = dialog->GetInput();
}
bool MacroActionVariable::PerformAction()
{
auto var = _variable.lock();
@@ -189,6 +261,60 @@ bool MacroActionVariable::PerformAction()
HandleMathExpression(var.get());
return true;
}
case Type::USER_INPUT: {
AskForInputParams params{
_useCustomPrompt
? QString::fromStdString(_inputPrompt)
: QString(obs_module_text(
"AdvSceneSwitcher.action.variable.askForValuePromptDefault"))
.arg(QString::fromStdString(
var->Name())),
_useCustomPrompt && _useInputPlaceholder
? QString::fromStdString(_inputPlaceholder)
: "",
{}};
obs_queue_task(OBS_TASK_UI, askForInput, &params, true);
if (!params.result.has_value()) {
return false;
}
var->SetValue(*params.result);
return true;
}
case Type::ENV_VARIABLE: {
var->SetValue(std::getenv(_envVariableName.c_str()));
return true;
}
case Type::SCENE_ITEM_COUNT: {
var->SetValue(GetSceneItemCount(_scene.GetScene(false)));
return true;
}
case Type::STRING_LENGTH: {
var->SetValue(std::string(_strValue).length());
return true;
}
case Type::EXTRACT_JSON: {
auto value = GetJsonField(var->Value(), _strValue);
if (!value.has_value()) {
return true;
}
var->SetValue(*value);
return true;
}
case Type::SET_TO_TEMPVAR: {
auto tempVar = _tempVar.GetTempVariable(GetMacro());
if (!tempVar) {
return true;
}
auto value = tempVar->Value();
if (!value) {
return true;
}
var->SetValue(*value);
return true;
}
case Type::SCENE_ITEM_NAME:
SetToSceneItemName(var.get());
return true;
}
return true;
@@ -201,7 +327,7 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
GetWeakVariableName(_variable).c_str());
obs_data_set_string(obj, "variable2Name",
GetWeakVariableName(_variable2).c_str());
obs_data_set_string(obj, "strValue", _strValue.c_str());
_strValue.Save(obj, "strValue");
obs_data_set_double(obj, "numValue", _numValue);
obs_data_set_int(obj, "condition", static_cast<int>(_type));
obs_data_set_int(obj, "segmentIdx", GetSegmentIndexValue());
@@ -209,10 +335,18 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
obs_data_set_int(obj, "subStringSize", _subStringSize);
obs_data_set_string(obj, "regexPattern", _regexPattern.c_str());
obs_data_set_int(obj, "regexMatchIdx", _regexMatchIdx);
obs_data_set_string(obj, "findStr", _findStr.c_str());
obs_data_set_string(obj, "replaceStr", _replaceStr.c_str());
_findStr.Save(obj, "findStr");
_replaceStr.Save(obj, "replaceStr");
_regex.Save(obj);
_mathExpression.Save(obj, "mathExpression");
obs_data_set_bool(obj, "useCustomPrompt", _useCustomPrompt);
_inputPrompt.Save(obj, "inputPrompt");
obs_data_set_bool(obj, "useInputPlaceholder", _useInputPlaceholder);
_inputPlaceholder.Save(obj, "inputPlaceholder");
_envVariableName.Save(obj, "environmentVariableName");
_scene.Save(obj);
_tempVar.Save(obj);
_sceneItemIndex.Save(obj, "sceneItemIndex");
return true;
}
@@ -223,7 +357,7 @@ bool MacroActionVariable::Load(obs_data_t *obj)
GetWeakVariableByName(obs_data_get_string(obj, "variableName"));
_variable2 = GetWeakVariableByName(
obs_data_get_string(obj, "variable2Name"));
_strValue = obs_data_get_string(obj, "strValue");
_strValue.Load(obj, "strValue");
_numValue = obs_data_get_double(obj, "numValue");
_type = static_cast<Type>(obs_data_get_int(obj, "condition"));
_segmentIdxLoadValue = obs_data_get_int(obj, "segmentIdx");
@@ -232,9 +366,17 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_regex.Load(obj);
_regexPattern = obs_data_get_string(obj, "regexPattern");
_regexMatchIdx = obs_data_get_int(obj, "regexMatchIdx");
_findStr = obs_data_get_string(obj, "findStr");
_replaceStr = obs_data_get_string(obj, "replaceStr");
_findStr.Load(obj, "findStr");
_replaceStr.Load(obj, "replaceStr");
_mathExpression.Load(obj, "mathExpression");
_useCustomPrompt = obs_data_get_bool(obj, "useCustomPrompt");
_inputPrompt.Load(obj, "inputPrompt");
_useInputPlaceholder = obs_data_get_bool(obj, "useInputPlaceholder");
_inputPlaceholder.Load(obj, "inputPlaceholder");
_envVariableName.Load(obj, "environmentVariableName");
_scene.Load(obj);
_tempVar.Load(obj, GetMacro());
_sceneItemIndex.Load(obj, "sceneItemIndex");
return true;
}
@@ -277,7 +419,7 @@ void MacroActionVariable::SetSegmentIndexValue(int value)
_macroSegment = segment;
if (segment) {
segment->IncrementVariableRef();
IncrementVariableRef(segment.get());
}
}
@@ -319,13 +461,14 @@ void MacroActionVariable::DecrementCurrentSegmentVariableRef()
return;
}
segment->DecrementVariableRef();
DecrementVariableRef(segment.get());
}
static inline void populateTypeSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
for (const auto &[action, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
}
@@ -334,10 +477,11 @@ MacroActionVariableEdit::MacroActionVariableEdit(
: QWidget(parent),
_variables(new VariableSelection(this)),
_variables2(new VariableSelection(this)),
_actions(new QComboBox()),
_strValue(new ResizingPlainTextEdit(this, 5, 1, 1)),
_actions(new FilterComboBox(this)),
_strValue(new VariableTextEdit(this, 5, 1, 1)),
_numValue(new QDoubleSpinBox()),
_segmentIdx(new QSpinBox()),
_segmentIdx(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::CONDITION, false)),
_segmentValueStatus(new QLabel()),
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)),
_substringLayout(new QVBoxLayout()),
@@ -349,16 +493,24 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)),
_regexMatchIdx(new QSpinBox()),
_findReplaceLayout(new QHBoxLayout()),
_findStr(new ResizingPlainTextEdit(this, 10, 1, 1)),
_replaceStr(new ResizingPlainTextEdit(this, 10, 1, 1)),
_findStr(new VariableTextEdit(this, 10, 1, 1)),
_replaceStr(new VariableTextEdit(this, 10, 1, 1)),
_mathExpression(new VariableLineEdit(this)),
_mathExpressionResult(new QLabel())
_mathExpressionResult(new QLabel()),
_promptLayout(new QHBoxLayout()),
_useCustomPrompt(new QCheckBox()),
_inputPrompt(new VariableLineEdit(this)),
_placeholderLayout(new QHBoxLayout()),
_useInputPlaceholder(new QCheckBox()),
_inputPlaceholder(new VariableLineEdit(this)),
_envVariable(new VariableLineEdit(this)),
_scenes(new SceneSelectionWidget(this, true, false, true, true,
true)),
_tempVars(new TempVariableSelection(this)),
_sceneItemIndex(new VariableSpinBox())
{
_numValue->setMinimum(-9999999999);
_numValue->setMaximum(9999999999);
_segmentIdx->setMinimum(0);
_segmentIdx->setMaximum(99);
_segmentIdx->setSpecialValueText("-");
_segmentValue->setReadOnly(true);
_subStringStart->setMinimum(1);
_subStringStart->setMaximum(99999);
@@ -371,6 +523,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_regexMatchIdx->setMinimum(1);
_regexMatchIdx->setMaximum(999);
_regexMatchIdx->setSuffix(".");
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_sceneItemIndex->setMinimum(1);
populateTypeSelection(_actions);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
@@ -383,8 +538,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
SLOT(StrValueChanged()));
QWidget::connect(_numValue, SIGNAL(valueChanged(double)), this,
SLOT(NumValueChanged(double)));
QWidget::connect(_segmentIdx, SIGNAL(valueChanged(int)), this,
SLOT(SegmentIndexChanged(int)));
QWidget::connect(_segmentIdx,
SIGNAL(SelectionChanged(const IntVariable &)), this,
SLOT(SegmentIndexChanged(const IntVariable &)));
QWidget::connect(window(), SIGNAL(MacroSegmentOrderChanged()), this,
SLOT(MacroSegmentOrderChanged()));
QWidget::connect(_subStringStart, SIGNAL(valueChanged(int)), this,
@@ -403,6 +559,25 @@ MacroActionVariableEdit::MacroActionVariableEdit(
SLOT(ReplaceStrValueChanged()));
QWidget::connect(_mathExpression, SIGNAL(editingFinished()), this,
SLOT(MathExpressionChanged()));
QWidget::connect(_useCustomPrompt, SIGNAL(stateChanged(int)), this,
SLOT(UseCustomPromptChanged(int)));
QWidget::connect(_inputPrompt, SIGNAL(editingFinished()), this,
SLOT(InputPromptChanged()));
QWidget::connect(_useInputPlaceholder, SIGNAL(stateChanged(int)), this,
SLOT(UseInputPlaceholderChanged(int)));
QWidget::connect(_inputPlaceholder, SIGNAL(editingFinished()), this,
SLOT(InputPlaceholderChanged()));
QWidget::connect(_envVariable, SIGNAL(editingFinished()), this,
SLOT(EnvVariableChanged()));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_tempVars,
SIGNAL(SelectionChanged(const TempVariableRef &)),
this, SLOT(SelectionChanged(const TempVariableRef &)));
QWidget::connect(
_sceneItemIndex,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables},
@@ -417,6 +592,14 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{findStr}}", _findStr},
{"{{replaceStr}}", _replaceStr},
{"{{mathExpression}}", _mathExpression},
{"{{useCustomPrompt}}", _useCustomPrompt},
{"{{inputPrompt}}", _inputPrompt},
{"{{useInputPlaceholder}}", _useInputPlaceholder},
{"{{inputPlaceholder}}", _inputPlaceholder},
{"{{envVariableName}}", _envVariable},
{"{{scenes}}", _scenes},
{"{{tempVars}}", _tempVars},
{"{{sceneItemIndex}}", _sceneItemIndex},
};
auto entryLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
@@ -437,6 +620,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
"AdvSceneSwitcher.action.variable.entry.findAndReplace"),
_findReplaceLayout, widgetPlaceholders, false);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.variable.entry.userInput.customPrompt"),
_promptLayout, widgetPlaceholders);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.variable.entry.userInput.placeholder"),
_placeholderLayout, widgetPlaceholders);
auto regexConfigLayout = new QHBoxLayout;
regexConfigLayout->addWidget(_regex);
regexConfigLayout->addStretch();
@@ -453,6 +645,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
layout->addWidget(_segmentValue);
layout->addLayout(_findReplaceLayout);
layout->addWidget(_mathExpressionResult);
layout->addLayout(_promptLayout);
layout->addLayout(_placeholderLayout);
setLayout(layout);
_entryData = entryData;
@@ -473,20 +667,34 @@ void MacroActionVariableEdit::UpdateEntryData()
_variables->SetVariable(_entryData->_variable);
_variables2->SetVariable(_entryData->_variable2);
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
_strValue->setPlainText(QString::fromStdString(_entryData->_strValue));
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_type)));
_strValue->setPlainText(_entryData->_strValue);
_numValue->setValue(_entryData->_numValue);
_segmentIdx->setValue(_entryData->GetSegmentIndexValue() + 1);
_segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1);
_segmentIdx->SetMacro(_entryData->GetMacro());
_segmentIdx->SetType(
_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE
? MacroSegmentSelection::Type::CONDITION
: MacroSegmentSelection::Type::ACTION);
_subStringStart->setValue(_entryData->_subStringStart + 1);
_subStringSize->setValue(_entryData->_subStringSize);
_regex->SetRegexConfig(_entryData->_regex);
_regexPattern->setPlainText(
QString::fromStdString(_entryData->_regexPattern));
_regexMatchIdx->setValue(_entryData->_regexMatchIdx + 1);
_findStr->setPlainText(QString::fromStdString(_entryData->_findStr));
_replaceStr->setPlainText(
QString::fromStdString(_entryData->_replaceStr));
_findStr->setPlainText(_entryData->_findStr);
_replaceStr->setPlainText(_entryData->_replaceStr);
_mathExpression->setText(_entryData->_mathExpression);
_useCustomPrompt->setChecked(_entryData->_useCustomPrompt);
_inputPrompt->setText(_entryData->_inputPrompt);
_useInputPlaceholder->setChecked(_entryData->_useInputPlaceholder);
_inputPlaceholder->setText(_entryData->_inputPlaceholder);
_envVariable->setText(_entryData->_envVariableName);
_scenes->SetScene(_entryData->_scene);
_tempVars->SetVariable(_entryData->_tempVar);
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
SetWidgetVisibility();
}
@@ -510,21 +718,23 @@ void MacroActionVariableEdit::Variable2Changed(const QString &text)
_entryData->_variable2 = GetWeakVariableByQString(text);
}
void MacroActionVariableEdit::ActionChanged(int value)
void MacroActionVariableEdit::ActionChanged(int idx)
{
if (_loading || !_entryData) {
if (_loading || !_entryData || idx == -1) {
return;
}
auto lock = LockContext();
_entryData->_type = static_cast<MacroActionVariable::Type>(value);
SetWidgetVisibility();
_entryData->_type = static_cast<MacroActionVariable::Type>(
_actions->itemData(idx).toInt());
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE ||
_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE) {
MarkSelectedSegment();
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) {
_segmentIdx->SetType(MacroSegmentSelection::Type::ACTION);
} else if (_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE) {
_segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION);
}
SetWidgetVisibility();
}
void MacroActionVariableEdit::StrValueChanged()
@@ -549,7 +759,7 @@ void MacroActionVariableEdit::NumValueChanged(double val)
_entryData->_numValue = val;
}
void MacroActionVariableEdit::SegmentIndexChanged(int val)
void MacroActionVariableEdit::SegmentIndexChanged(const IntVariable &val)
{
if (_loading || !_entryData) {
return;
@@ -557,7 +767,6 @@ void MacroActionVariableEdit::SegmentIndexChanged(int val)
auto lock = LockContext();
_entryData->SetSegmentIndexValue(val - 1);
MarkSelectedSegment();
}
void MacroActionVariableEdit::SetSegmentValueError(const QString &text)
@@ -590,7 +799,7 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
SetSegmentValueError(obs_module_text(
"AdvSceneSwitcher.action.variable.invalidSelection"));
const QSignalBlocker b(_segmentIdx);
_segmentIdx->setValue(index);
_segmentIdx->SetValue(0);
return;
}
@@ -614,7 +823,7 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
return;
}
if (!segment->SupportsVariableValue()) {
if (!SupportsVariableValue(segment.get())) {
std::string type;
QString fmt;
@@ -639,8 +848,14 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
_segmentValueStatus->setText(obs_module_text(
"AdvSceneSwitcher.action.variable.currentSegmentValue"));
_segmentValue->show();
_segmentValue->setPlainText(
QString::fromStdString(segment->GetVariableValue()));
// Only update the text if the value changed to prevent possible text
// selections being lost
auto previousText = _segmentValue->toPlainText();
auto newText = QString::fromStdString(segment->GetVariableValue());
if (newText != previousText) {
_segmentValue->setPlainText(newText);
}
adjustSize();
updateGeometry();
@@ -649,7 +864,7 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
void MacroActionVariableEdit::MacroSegmentOrderChanged()
{
const QSignalBlocker b(_segmentIdx);
_segmentIdx->setValue(_entryData->GetSegmentIndexValue() + 1);
_segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1);
}
void MacroActionVariableEdit::SubStringStartChanged(int val)
@@ -752,40 +967,87 @@ void MacroActionVariableEdit::MathExpressionChanged()
updateGeometry();
}
void MacroActionVariableEdit::MarkSelectedSegment()
void MacroActionVariableEdit::UseCustomPromptChanged(int value)
{
if (switcher->disableHints) {
if (_loading || !_entryData) {
return;
}
auto m = _entryData->GetMacro();
if (!m) {
auto lock = LockContext();
_entryData->_useCustomPrompt = value;
SetWidgetVisibility();
}
void MacroActionVariableEdit::InputPromptChanged()
{
if (_loading || !_entryData) {
return;
}
int index = _entryData->GetSegmentIndexValue();
if (index < 0) {
auto lock = LockContext();
_entryData->_inputPrompt = _inputPrompt->text().toStdString();
}
void MacroActionVariableEdit::UseInputPlaceholderChanged(int value)
{
if (_loading || !_entryData) {
return;
}
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) {
const auto &actions = m->Actions();
if (index >= (int)actions.size()) {
return;
}
AdvSceneSwitcher::window->HighlightAction(
index, QColor(Qt::lightGray));
} else {
const auto &conditions = m->Conditions();
if (index >= (int)conditions.size()) {
return;
}
AdvSceneSwitcher::window->HighlightCondition(
index, QColor(Qt::lightGray));
auto lock = LockContext();
_entryData->_useInputPlaceholder = value;
SetWidgetVisibility();
}
void MacroActionVariableEdit::InputPlaceholderChanged()
{
if (_loading || !_entryData) {
return;
}
PulseWidget(_segmentIdx, QColor(Qt::lightGray), QColor(0, 0, 0, 0),
true);
auto lock = LockContext();
_entryData->_inputPlaceholder = _inputPlaceholder->text().toStdString();
}
void MacroActionVariableEdit::EnvVariableChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_envVariableName = _envVariable->text().toStdString();
}
void MacroActionVariableEdit::SceneChanged(const SceneSelection &scene)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_scene = scene;
}
void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_tempVar = var;
}
void MacroActionVariableEdit::SceneItemIndexChanged(
const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_sceneItemIndex = value;
}
void MacroActionVariableEdit::SetWidgetVisibility()
@@ -799,7 +1061,9 @@ void MacroActionVariableEdit::SetWidgetVisibility()
_strValue->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_FIXED_VALUE ||
_entryData->_type == MacroActionVariable::Type::APPEND);
_entryData->_type == MacroActionVariable::Type::APPEND ||
_entryData->_type == MacroActionVariable::Type::STRING_LENGTH ||
_entryData->_type == MacroActionVariable::Type::EXTRACT_JSON);
_numValue->setVisible(
_entryData->_type == MacroActionVariable::Type::INCREMENT ||
_entryData->_type == MacroActionVariable::Type::DECREMENT);
@@ -833,7 +1097,44 @@ void MacroActionVariableEdit::SetWidgetVisibility()
_mathExpression->setVisible(_entryData->_type ==
MacroActionVariable::Type::MATH_EXPRESSION);
_mathExpressionResult->hide();
SetLayoutVisible(_promptLayout,
_entryData->_type ==
MacroActionVariable::Type::USER_INPUT);
_inputPrompt->setVisible(
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
_entryData->_useCustomPrompt);
if (_entryData->_useCustomPrompt) {
RemoveStretchIfPresent(_promptLayout);
} else {
AddStretchIfNecessary(_promptLayout);
}
SetLayoutVisible(
_placeholderLayout,
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
_entryData->_useCustomPrompt);
_useInputPlaceholder->setVisible(
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
_entryData->_useCustomPrompt);
_inputPlaceholder->setVisible(
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
_entryData->_useCustomPrompt &&
_entryData->_useInputPlaceholder);
if (_entryData->_useInputPlaceholder) {
RemoveStretchIfPresent(_placeholderLayout);
} else {
AddStretchIfNecessary(_placeholderLayout);
}
_envVariable->setVisible(_entryData->_type ==
MacroActionVariable::Type::ENV_VARIABLE);
_scenes->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_COUNT ||
_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME);
_tempVars->setVisible(_entryData->_type ==
MacroActionVariable::Type::SET_TO_TEMPVAR);
_sceneItemIndex->setVisible(_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME);
adjustSize();
updateGeometry();
}

View File

@@ -1,8 +1,12 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro-segment-selection.hpp"
#include "regex-config.hpp"
#include "resizing-text-edit.hpp"
#include "scene-selection.hpp"
#include "variable-line-edit.hpp"
#include "variable-text-edit.hpp"
#include "variable-spinbox.hpp"
namespace advss {
@@ -35,24 +39,45 @@ public:
SUBSTRING,
FIND_AND_REPLACE,
MATH_EXPRESSION,
USER_INPUT,
ENV_VARIABLE,
SCENE_ITEM_COUNT,
STRING_LENGTH,
EXTRACT_JSON,
SET_TO_TEMPVAR,
SCENE_ITEM_NAME,
};
Type _type = Type::SET_FIXED_VALUE;
std::weak_ptr<Variable> _variable;
std::weak_ptr<Variable> _variable2;
std::string _strValue = "";
StringVariable _strValue = "";
double _numValue = 0;
int _subStringStart = 0;
int _subStringSize = 0;
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig();
std::string _regexPattern = ".*";
int _regexMatchIdx = 0;
std::string _findStr = obs_module_text(
StringVariable _findStr = obs_module_text(
"AdvSceneSwitcher.action.variable.findAndReplace.find");
std::string _replaceStr = obs_module_text(
StringVariable _replaceStr = obs_module_text(
"AdvSceneSwitcher.action.variable.findAndReplace.replace");
StringVariable _mathExpression = obs_module_text(
"AdvSceneSwitcher.action.variable.mathExpression.example");
bool _useCustomPrompt = false;
StringVariable _inputPrompt = obs_module_text(
"AdvSceneSwitcher.action.variable.askForValuePrompt");
bool _useInputPlaceholder = false;
StringVariable _inputPlaceholder =
obs_module_text("AdvSceneSwitcher.enterText");
#ifdef _WIN32
StringVariable _envVariableName = "USERPROFILE";
#else
StringVariable _envVariableName = "HOME";
#endif
SceneSelection _scene;
TempVariableRef _tempVar;
IntVariable _sceneItemIndex = 1;
private:
void DecrementCurrentSegmentVariableRef();
@@ -60,6 +85,7 @@ private:
void HandleRegexSubString(Variable *);
void HandleFindAndReplace(Variable *);
void HandleMathExpression(Variable *);
void SetToSceneItemName(Variable *);
std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1;
@@ -89,7 +115,7 @@ private slots:
void ActionChanged(int);
void StrValueChanged();
void NumValueChanged(double);
void SegmentIndexChanged(int val);
void SegmentIndexChanged(const IntVariable &);
void UpdateSegmentVariableValue();
void MacroSegmentOrderChanged();
void SubStringStartChanged(int val);
@@ -100,17 +126,28 @@ private slots:
void FindStrValueChanged();
void ReplaceStrValueChanged();
void MathExpressionChanged();
void UseCustomPromptChanged(int);
void InputPromptChanged();
void UseInputPlaceholderChanged(int);
void InputPlaceholderChanged();
void EnvVariableChanged();
void SceneChanged(const SceneSelection &);
void SelectionChanged(const TempVariableRef &var);
void SceneItemIndexChanged(const NumberVariable<int> &);
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
void SetSegmentValueError(const QString &);
VariableSelection *_variables;
VariableSelection *_variables2;
QComboBox *_actions;
ResizingPlainTextEdit *_strValue;
FilterComboBox *_actions;
VariableTextEdit *_strValue;
QDoubleSpinBox *_numValue;
QSpinBox *_segmentIdx;
MacroSegmentSelection *_segmentIdx;
QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout;
@@ -122,17 +159,22 @@ protected:
ResizingPlainTextEdit *_regexPattern;
QSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout;
ResizingPlainTextEdit *_findStr;
ResizingPlainTextEdit *_replaceStr;
VariableTextEdit *_findStr;
VariableTextEdit *_replaceStr;
VariableLineEdit *_mathExpression;
QLabel *_mathExpressionResult;
QHBoxLayout *_promptLayout;
QCheckBox *_useCustomPrompt;
VariableLineEdit *_inputPrompt;
QHBoxLayout *_placeholderLayout;
QCheckBox *_useInputPlaceholder;
VariableLineEdit *_inputPlaceholder;
VariableLineEdit *_envVariable;
SceneSelectionWidget *_scenes;
TempVariableSelection *_tempVars;
VariableSpinBox *_sceneItemIndex;
std::shared_ptr<MacroActionVariable> _entryData;
private:
void MarkSelectedSegment();
void SetWidgetVisibility();
void SetSegmentValueError(const QString &);
QTimer _timer;
bool _loading = true;
};

View File

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

View File

@@ -10,47 +10,75 @@ bool MacroActionWebsocket::_registered = MacroActionFactory::Register(
{MacroActionWebsocket::Create, MacroActionWebsocketEdit::Create,
"AdvSceneSwitcher.action.websocket"});
static std::map<MacroActionWebsocket::Type, std::string> actionTypes = {
{MacroActionWebsocket::Type::REQUEST,
"AdvSceneSwitcher.action.websocket.type.request"},
{MacroActionWebsocket::Type::EVENT,
"AdvSceneSwitcher.action.websocket.type.event"},
const static std::map<MacroActionWebsocket::API, std::string> apiTypes = {
{MacroActionWebsocket::API::SCENE_SWITCHER,
"AdvSceneSwitcher.action.websocket.api.sceneSwitcher"},
{MacroActionWebsocket::API::OBS_WEBSOCKET,
"AdvSceneSwitcher.action.websocket.api.obsWebsocket"},
{MacroActionWebsocket::API::GENERIC_WEBSOCKET,
"AdvSceneSwitcher.action.websocket.api.genericWebsocket"},
};
void MacroActionWebsocket::SendRequest()
const static std::map<MacroActionWebsocket::MessageType, std::string>
messageTypes = {
{MacroActionWebsocket::MessageType::REQUEST,
"AdvSceneSwitcher.action.websocket.type.request"},
{MacroActionWebsocket::MessageType::EVENT,
"AdvSceneSwitcher.action.websocket.type.event"},
};
void MacroActionWebsocket::SendRequest(const std::string &msg)
{
auto connection = _connection.lock();
if (!connection) {
return;
}
connection->SendMsg(_message);
connection->SendMsg(msg);
}
bool MacroActionWebsocket::PerformAction()
{
switch (_type) {
case MacroActionWebsocket::Type::REQUEST:
SendRequest();
break;
case MacroActionWebsocket::Type::EVENT:
SendWebsocketEvent(_message);
break;
default:
break;
if (_api != MacroActionWebsocket::API::SCENE_SWITCHER) {
SendRequest(_message);
return true;
}
if (_type == MacroActionWebsocket::MessageType::REQUEST) {
auto vendorRequest = ConstructVendorRequestMessage(_message);
SendRequest(vendorRequest);
} else {
SendWebsocketEvent(_message);
}
return true;
}
void MacroActionWebsocket::LogAction() const
{
if (_api == API::GENERIC_WEBSOCKET) {
vblog(LOG_INFO,
"sent generic websocket message \"%s\" via \"%s\"",
_message.c_str(),
GetWeakConnectionName(_connection).c_str());
return;
}
if (_api == API::OBS_WEBSOCKET) {
vblog(LOG_INFO, "sent obs websocket message \"%s\" via \"%s\"",
_message.c_str(),
GetWeakConnectionName(_connection).c_str());
return;
}
switch (_type) {
case MacroActionWebsocket::Type::REQUEST:
vblog(LOG_INFO, "sent msg \"%s\" via \"%s\"", _message.c_str(),
case MacroActionWebsocket::MessageType::REQUEST:
vblog(LOG_INFO, "sent scene switcher message \"%s\" via \"%s\"",
_message.c_str(),
GetWeakConnectionName(_connection).c_str());
break;
case MacroActionWebsocket::Type::EVENT:
vblog(LOG_INFO, "sent event \"%s\" to connected clients",
case MacroActionWebsocket::MessageType::EVENT:
vblog(LOG_INFO,
"sent scene switcher event \"%s\" to connected clients",
_message.c_str());
break;
default:
@@ -61,6 +89,7 @@ void MacroActionWebsocket::LogAction() const
bool MacroActionWebsocket::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "api", static_cast<int>(_api));
obs_data_set_int(obj, "type", static_cast<int>(_type));
_message.Save(obj, "message");
obs_data_set_string(obj, "connection",
@@ -71,7 +100,8 @@ bool MacroActionWebsocket::Save(obs_data_t *obj) const
bool MacroActionWebsocket::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
_api = static_cast<API>(obs_data_get_int(obj, "api"));
_type = static_cast<MessageType>(obs_data_get_int(obj, "type"));
_message.Load(obj, "message");
_connection =
GetWeakConnectionByName(obs_data_get_string(obj, "connection"));
@@ -80,40 +110,54 @@ bool MacroActionWebsocket::Load(obs_data_t *obj)
std::string MacroActionWebsocket::GetShortDesc() const
{
if (_type == Type::REQUEST) {
if (_type == MessageType::REQUEST) {
return GetWeakConnectionName(_connection);
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
static inline void populateAPISelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
for (const auto &[_, name] : apiTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
static inline void populateMessageTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : messageTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionWebsocketEdit::MacroActionWebsocketEdit(
QWidget *parent, std::shared_ptr<MacroActionWebsocket> entryData)
: QWidget(parent),
_actions(new QComboBox(this)),
_apiType(new QComboBox(this)),
_messageType(new QComboBox(this)),
_message(new VariableTextEdit(this)),
_connection(new ConnectionSelection(this)),
_editLayout(new QHBoxLayout())
_editLayout(new QHBoxLayout()),
_settingsConflict(new QLabel())
{
populateActionSelection(_actions);
populateAPISelection(_apiType);
populateMessageTypeSelection(_messageType);
_settingsConflict->setWordWrap(true);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_apiType, SIGNAL(currentIndexChanged(int)), this,
SLOT(APITypeChanged(int)));
QWidget::connect(_messageType, SIGNAL(currentIndexChanged(int)), this,
SLOT(MessageTypeChanged(int)));
QWidget::connect(_message, SIGNAL(textChanged()), this,
SLOT(MessageChanged()));
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
this,
SLOT(ConnectionSelectionChanged(const QString &)));
QVBoxLayout *mainLayout = new QVBoxLayout;
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(_editLayout);
mainLayout->addWidget(_message);
mainLayout->addWidget(_settingsConflict);
setLayout(mainLayout);
_entryData = entryData;
@@ -121,34 +165,111 @@ MacroActionWebsocketEdit::MacroActionWebsocketEdit(
_loading = false;
}
void MacroActionWebsocketEdit::CheckForSettingsConflict()
{
auto connection = _entryData->_connection.lock();
if (!connection) {
_settingsConflict->hide();
return;
}
if (_entryData->_api == MacroActionWebsocket::API::GENERIC_WEBSOCKET &&
connection->IsUsingOBSProtocol()) {
_settingsConflict->show();
_settingsConflict->setText(obs_module_text(
"AdvSceneSwitcher.action.websocket.settingsConflictGeneric"));
} else if (_entryData->_api !=
MacroActionWebsocket::API::GENERIC_WEBSOCKET &&
!connection->IsUsingOBSProtocol()) {
_settingsConflict->show();
_settingsConflict->setText(obs_module_text(
"AdvSceneSwitcher.action.websocket.settingsConflictOBS"));
} else {
_settingsConflict->hide();
}
adjustSize();
updateGeometry();
}
void MacroActionWebsocketEdit::SetWidgetVisibility()
{
_messageType->setVisible(_entryData->_api ==
MacroActionWebsocket::API::SCENE_SWITCHER);
switch (_entryData->_api) {
case MacroActionWebsocket::API::SCENE_SWITCHER:
if (_entryData->_type ==
MacroActionWebsocket::MessageType::REQUEST) {
SetupRequestEdit();
} else {
SetupEventEdit();
}
break;
case MacroActionWebsocket::API::OBS_WEBSOCKET:
case MacroActionWebsocket::API::GENERIC_WEBSOCKET:
SetupGenericEdit();
break;
default:
break;
}
CheckForSettingsConflict();
adjustSize();
updateGeometry();
}
void MacroActionWebsocketEdit::SetupRequestEdit()
{
_editLayout->removeWidget(_actions);
_editLayout->removeWidget(_connection);
ClearLayout(_editLayout);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{type}}", _actions},
{"{{connection}}", _connection},
};
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.websocket.entry.request"),
_editLayout, widgetPlaceholders);
ClearWidgets();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request"),
_editLayout,
{{"{{api}}", _apiType},
{"{{type}}", _messageType},
{"{{connection}}", _connection}});
_connection->show();
}
void MacroActionWebsocketEdit::SetupEventEdit()
{
_editLayout->removeWidget(_actions);
ClearWidgets();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event"),
_editLayout,
{{"{{api}}", _apiType},
{"{{type}}", _messageType},
{"{{connection}}", _connection}});
// Add widget to layout but hide it
_editLayout->addWidget(_connection);
_connection->hide();
}
void MacroActionWebsocketEdit::SetupGenericEdit()
{
ClearWidgets();
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.websocket.entry.generic"),
_editLayout,
{{"{{api}}", _apiType},
{"{{type}}", _messageType},
{"{{connection}}", _connection}});
_connection->show();
// Add widget to layout but hide it
_editLayout->addWidget(_messageType);
_messageType->hide();
}
void MacroActionWebsocketEdit::ClearWidgets()
{
_editLayout->removeWidget(_apiType);
_editLayout->removeWidget(_messageType);
_editLayout->removeWidget(_connection);
ClearLayout(_editLayout);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{type}}", _actions},
{"{{connection}}", _connection},
};
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.websocket.entry.event"),
_editLayout, widgetPlaceholders);
_connection->hide();
}
void MacroActionWebsocketEdit::UpdateEntryData()
@@ -157,33 +278,37 @@ void MacroActionWebsocketEdit::UpdateEntryData()
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
_apiType->setCurrentIndex(static_cast<int>(_entryData->_api));
_messageType->setCurrentIndex(static_cast<int>(_entryData->_type));
_message->setPlainText(_entryData->_message);
_connection->SetConnection(_entryData->_connection);
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
SetupRequestEdit();
} else {
SetupEventEdit();
}
adjustSize();
updateGeometry();
SetWidgetVisibility();
}
void MacroActionWebsocketEdit::ActionChanged(int index)
void MacroActionWebsocketEdit::APITypeChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_type = static_cast<MacroActionWebsocket::Type>(index);
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
SetupRequestEdit();
} else {
SetupEventEdit();
_entryData->_api = static_cast<MacroActionWebsocket::API>(index);
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionWebsocketEdit::MessageTypeChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_type =
static_cast<MacroActionWebsocket::MessageType>(index);
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -210,6 +335,7 @@ void MacroActionWebsocketEdit::ConnectionSelectionChanged(
auto lock = LockContext();
_entryData->_connection = GetWeakConnectionByQString(connection);
CheckForSettingsConflict();
emit(HeaderInfoChanged(connection));
}

View File

@@ -24,17 +24,24 @@ public:
return std::make_shared<MacroActionWebsocket>(m);
}
enum class Type {
enum class API {
SCENE_SWITCHER,
OBS_WEBSOCKET,
GENERIC_WEBSOCKET,
};
enum class MessageType {
REQUEST,
EVENT,
};
Type _type = Type::REQUEST;
API _api = API::SCENE_SWITCHER;
MessageType _type = MessageType::REQUEST;
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
std::weak_ptr<Connection> _connection;
private:
void SendRequest();
void SendRequest(const std::string &msg);
static bool _registered;
static const std::string id;
@@ -57,7 +64,8 @@ public:
}
private slots:
void ActionChanged(int);
void APITypeChanged(int);
void MessageTypeChanged(int);
void MessageChanged();
void ConnectionSelectionChanged(const QString &);
signals:
@@ -67,13 +75,20 @@ protected:
std::shared_ptr<MacroActionWebsocket> _entryData;
private:
void CheckForSettingsConflict();
void SetWidgetVisibility();
void SetupRequestEdit();
void SetupEventEdit();
void SetupGenericEdit();
void ClearWidgets();
QComboBox *_actions;
QComboBox *_apiType;
QComboBox *_messageType;
VariableTextEdit *_message;
ConnectionSelection *_connection;
QHBoxLayout *_editLayout;
QLabel *_settingsConflict;
bool _loading = true;
};

View File

@@ -11,12 +11,15 @@ bool MacroAction::Save(obs_data_t *obj) const
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_bool(obj, "enabled", _enabled);
return true;
}
bool MacroAction::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
obs_data_set_default_bool(obj, "enabled", true);
_enabled = obs_data_get_bool(obj, "enabled");
return true;
}
@@ -25,6 +28,16 @@ void MacroAction::LogAction() const
vblog(LOG_INFO, "performed action %s", GetId().c_str());
}
void MacroAction::SetEnabled(bool value)
{
_enabled = value;
}
bool MacroAction::Enabled() const
{
return _enabled;
}
MacroRefAction::MacroRefAction(Macro *m, bool supportsVariableValue)
: MacroAction(m, supportsVariableValue)
{

View File

@@ -12,6 +12,11 @@ public:
virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual void LogAction() const;
void SetEnabled(bool);
bool Enabled() const;
private:
bool _enabled = true;
};
class MacroRefAction : virtual public MacroAction {

View File

@@ -12,7 +12,7 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
{MacroConditionAudio::Create, MacroConditionAudioEdit::Create,
"AdvSceneSwitcher.condition.audio"});
static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
const static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
{MacroConditionAudio::Type::OUTPUT_VOLUME,
"AdvSceneSwitcher.condition.audio.type.output"},
{MacroConditionAudio::Type::CONFIGURED_VOLUME,
@@ -25,7 +25,7 @@ static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
"AdvSceneSwitcher.condition.audio.type.balance"},
};
static std::map<MacroConditionAudio::OutputCondition, std::string>
const static std::map<MacroConditionAudio::OutputCondition, std::string>
audioOutputConditionTypes = {
{MacroConditionAudio::OutputCondition::ABOVE,
"AdvSceneSwitcher.condition.audio.state.above"},
@@ -33,7 +33,7 @@ static std::map<MacroConditionAudio::OutputCondition, std::string>
"AdvSceneSwitcher.condition.audio.state.below"},
};
static std::map<MacroConditionAudio::VolumeCondition, std::string>
const static std::map<MacroConditionAudio::VolumeCondition, std::string>
audioVolumeConditionTypes = {
{MacroConditionAudio::VolumeCondition::ABOVE,
"AdvSceneSwitcher.condition.audio.state.above"},
@@ -47,6 +47,12 @@ static std::map<MacroConditionAudio::VolumeCondition, std::string>
"AdvSceneSwitcher.condition.audio.state.unmute"},
};
void MacroConditionAudio::SetType(const Type &type)
{
_checkType = type;
SetupTempVars();
}
MacroConditionAudio::~MacroConditionAudio()
{
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
@@ -56,17 +62,17 @@ MacroConditionAudio::~MacroConditionAudio()
bool MacroConditionAudio::CheckOutputCondition()
{
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
// peak will have a value from -60 db to 0 db so we need to scale it
double curVolume = ((double)_peak + 60) * 1.7;
switch (_outputCondition) {
case OutputCondition::ABOVE:
// peak will have a value from -60 db to 0 db
ret = curVolume > _volume;
break;
case OutputCondition::BELOW:
// peak will have a value from -60 db to 0 db
ret = curVolume < _volume;
break;
default:
@@ -74,24 +80,25 @@ bool MacroConditionAudio::CheckOutputCondition()
}
SetVariableValue(std::to_string(curVolume));
SetTempVarValue("output_volume", std::to_string(curVolume));
// Reset for next check
_peak = -std::numeric_limits<float>::infinity();
obs_source_release(s);
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
ResetVolmeter();
}
return ret;
return ret && source;
}
bool MacroConditionAudio::CheckVolumeCondition()
{
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
float curVolume = obs_source_get_volume(s);
bool muted = obs_source_muted(s);
float curVolume = obs_source_get_volume(source);
bool muted = obs_source_muted(source);
switch (_volumeCondition) {
case VolumeCondition::ABOVE:
@@ -118,8 +125,10 @@ bool MacroConditionAudio::CheckVolumeCondition()
break;
}
obs_source_release(s);
return ret;
SetTempVarValue("configured_volume", std::to_string(curVolume));
SetTempVarValue("muted", (source && muted) ? "true" : "false");
return ret && source;
}
bool MacroConditionAudio::CheckSyncOffset()
@@ -129,16 +138,17 @@ bool MacroConditionAudio::CheckSyncOffset()
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
auto curOffset = obs_source_get_sync_offset(s) / nsPerMs;
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
auto curOffset = obs_source_get_sync_offset(source) / nsPerMs;
if (_outputCondition == OutputCondition::ABOVE) {
ret = curOffset > _syncOffset;
} else {
ret = curOffset < _syncOffset;
}
SetVariableValue(std::to_string(curOffset));
obs_source_release(s);
return ret;
SetTempVarValue("sync_offset", std::to_string(curOffset));
return ret && source;
}
bool MacroConditionAudio::CheckMonitor()
@@ -148,11 +158,13 @@ bool MacroConditionAudio::CheckMonitor()
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
ret = obs_source_get_monitoring_type(s) == _monitorType;
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
ret = obs_source_get_monitoring_type(source) == _monitorType;
SetVariableValue("");
obs_source_release(s);
return ret;
SetTempVarValue("monitor",
std::to_string(obs_source_get_monitoring_type(source)));
return ret && source;
}
bool MacroConditionAudio::CheckBalance()
@@ -162,16 +174,17 @@ bool MacroConditionAudio::CheckBalance()
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
auto curBalance = obs_source_get_balance_value(s);
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
auto curBalance = obs_source_get_balance_value(source);
if (_outputCondition == OutputCondition::ABOVE) {
ret = curBalance > _balance;
} else {
ret = curBalance < _balance;
}
SetVariableValue(std::to_string(curBalance));
obs_source_release(s);
return ret;
SetTempVarValue("balance", std::to_string(curBalance));
return ret && source;
}
bool MacroConditionAudio::CheckCondition()
@@ -225,13 +238,12 @@ obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel,
entry);
obs_source_t *as = obs_weak_source_get_source(source);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
OBSSourceAutoRelease audioSource = obs_weak_source_get_source(source);
if (!obs_volmeter_attach_source(volmeter, audioSource)) {
const char *name = obs_source_get_name(audioSource);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
return volmeter;
}
@@ -290,6 +302,50 @@ void MacroConditionAudio::ResetVolmeter()
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
}
void MacroConditionAudio::SetupTempVars()
{
MacroCondition::SetupTempVars();
switch (_checkType) {
case Type::OUTPUT_VOLUME:
AddTempvar(
"output_volume",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.output_volume"),
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.output_volume.description"));
break;
case Type::CONFIGURED_VOLUME:
AddTempvar(
"configured_volume",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.configured_volume"),
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.configured_volume.description"));
AddTempvar("muted",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.muted"));
break;
case Type::SYNC_OFFSET:
AddTempvar(
"sync_offset",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.sync_offset"));
break;
case Type::MONITOR:
AddTempvar("monitor",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.monitor"));
break;
case Type::BALANCE:
AddTempvar("balance",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.balance"));
break;
default:
break;
}
}
static inline void populateCheckTypes(QComboBox *list)
{
list->clear();
@@ -397,10 +453,9 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
void MacroConditionAudioEdit::UpdateVolmeterSource()
{
delete _volMeter;
obs_source_t *soruce = obs_weak_source_get_source(
OBSSourceAutoRelease soruce = obs_weak_source_get_source(
_entryData->_audioSource.GetSource());
_volMeter = new VolControl(soruce);
obs_source_release(soruce);
_volMeter = new VolControl(soruce.Get());
QLayout *layout = this->layout();
layout->addWidget(_volMeter);
@@ -479,10 +534,9 @@ void MacroConditionAudioEdit::ConditionChanged(int cond)
}
auto lock = LockContext();
if (_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType == MacroConditionAudio::Type::SYNC_OFFSET) {
if (_entryData->GetType() == MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
_entryData->GetType() == MacroConditionAudio::Type::SYNC_OFFSET) {
_entryData->_outputCondition =
static_cast<MacroConditionAudio::OutputCondition>(cond);
} else {
@@ -499,16 +553,15 @@ void MacroConditionAudioEdit::CheckTypeChanged(int idx)
}
auto lock = LockContext();
_entryData->_checkType = static_cast<MacroConditionAudio::Type>(
_checkTypes->itemData(idx).toInt());
_entryData->SetType(static_cast<MacroConditionAudio::Type>(
_checkTypes->itemData(idx).toInt()));
const QSignalBlocker b(_condition);
if (_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType == MacroConditionAudio::Type::SYNC_OFFSET) {
if (_entryData->GetType() == MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
_entryData->GetType() == MacroConditionAudio::Type::SYNC_OFFSET) {
populateOutputConditionSelection(_condition);
} else if (_entryData->_checkType ==
} else if (_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
populateVolumeConditionSelection(_condition);
}
@@ -526,17 +579,16 @@ void MacroConditionAudioEdit::UpdateEntryData()
_syncOffset->SetValue(_entryData->_syncOffset);
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
_balance->SetDoubleValue(_entryData->_balance);
_checkTypes->setCurrentIndex(_checkTypes->findData(
static_cast<int>(_entryData->_checkType)));
_checkTypes->setCurrentIndex(
_checkTypes->findData(static_cast<int>(_entryData->GetType())));
if (_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType == MacroConditionAudio::Type::SYNC_OFFSET) {
if (_entryData->GetType() == MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
_entryData->GetType() == MacroConditionAudio::Type::SYNC_OFFSET) {
populateOutputConditionSelection(_condition);
_condition->setCurrentIndex(
static_cast<int>(_entryData->_outputCondition));
} else if (_entryData->_checkType ==
} else if (_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
populateVolumeConditionSelection(_condition);
_condition->setCurrentIndex(
@@ -554,9 +606,9 @@ void MacroConditionAudioEdit::SetWidgetVisibility()
}
_volume->setVisible(
_entryData->_checkType ==
_entryData->GetType() ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
(_entryData->_checkType ==
(_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME &&
(_entryData->_volumeCondition ==
MacroConditionAudio::VolumeCondition::ABOVE ||
@@ -565,20 +617,20 @@ void MacroConditionAudioEdit::SetWidgetVisibility()
_entryData->_volumeCondition ==
MacroConditionAudio::VolumeCondition::BELOW)));
_condition->setVisible(
_entryData->_checkType ==
_entryData->GetType() ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType ==
_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType ==
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
_entryData->GetType() ==
MacroConditionAudio::Type::SYNC_OFFSET);
_syncOffset->setVisible(_entryData->_checkType ==
_syncOffset->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::SYNC_OFFSET);
_monitorTypes->setVisible(_entryData->_checkType ==
_monitorTypes->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::MONITOR);
_balance->setVisible(_entryData->_checkType ==
_balance->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::BALANCE);
_volMeter->setVisible(_entryData->_checkType ==
_volMeter->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::OUTPUT_VOLUME);
adjustSize();
}

View File

@@ -37,6 +37,8 @@ public:
MONITOR,
BALANCE,
};
void SetType(const Type &);
Type GetType() const { return _checkType; }
enum class OutputCondition {
ABOVE,
@@ -56,7 +58,6 @@ public:
NumberVariable<int> _syncOffset = 0;
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
NumberVariable<double> _balance = 0.5;
Type _checkType = Type::OUTPUT_VOLUME;
OutputCondition _outputCondition = OutputCondition::ABOVE;
VolumeCondition _volumeCondition = VolumeCondition::ABOVE;
obs_volmeter_t *_volmeter = nullptr;
@@ -67,7 +68,9 @@ private:
bool CheckSyncOffset();
bool CheckMonitor();
bool CheckBalance();
void SetupTempVars();
Type _checkType = Type::OUTPUT_VOLUME;
float _peak = -std::numeric_limits<float>::infinity();
static bool _registered;
static const std::string id;

View File

@@ -1,5 +1,4 @@
#include "macro-condition-cursor.hpp"
#include "switcher-data.hpp"
#include "platform-funcs.hpp"
#include "utility.hpp"
@@ -15,7 +14,7 @@ bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
{MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
"AdvSceneSwitcher.condition.cursor"});
static std::map<MacroConditionCursor::Condition, std::string>
const static std::map<MacroConditionCursor::Condition, std::string>
cursorConditionTypes = {
{MacroConditionCursor::Condition::REGION,
"AdvSceneSwitcher.condition.cursor.type.region"},
@@ -27,7 +26,7 @@ static std::map<MacroConditionCursor::Condition, std::string>
#endif
};
static std::map<MacroConditionCursor::Button, std::string> buttonTypes = {
const static std::map<MacroConditionCursor::Button, std::string> buttonTypes = {
{MacroConditionCursor::Button::LEFT,
"AdvSceneSwitcher.condition.cursor.button.left"},
{MacroConditionCursor::Button::MIDDLE,
@@ -61,13 +60,15 @@ bool MacroConditionCursor::CheckCondition()
std::to_string(cursorPos.second));
break;
case Condition::MOVING:
ret = switcher->cursorPosChanged;
ret = cursorPos.first != _lastCursorPosition.first ||
cursorPos.second != _lastCursorPosition.second;
break;
case Condition::CLICK:
ret = CheckClick();
break;
}
_lastCheckTime = std::chrono::high_resolution_clock::now();
_lastCursorPosition = cursorPos;
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");

View File

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

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