Compare commits

...

12 Commits

Author SHA1 Message Date
WarmUpTill
9bcced524b Move transition behaviour related settings to General tab 2022-11-09 09:57:55 -08:00
WarmUpTill
3bb2ddfaac Update GitHub Actions for set-output deprecation
GitHub Actions has deprecated set-output. Replace usages of set-output
in stdout with the new syntax to save the output to the new environment
variable.

See:
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-11-06 09:53:25 -08:00
WarmUpTill
50e26aba72 Improve focus handling
* Display current focus window / process
 * Match against app name instead of window name on MacOS when using
   Process condition
 * Clean up
2022-11-06 09:53:25 -08:00
WarmUpTill
267ab6a7c1 Set Windows installer version to git tag 2022-11-05 12:24:50 -07:00
WarmUpTill
65ea7987c7 Add special handling for the "between" case when ignoring the date
In this case the left time value will be treated as the start of the
time range and the right one as the end.

This now enables specifying ranges that span over the 24h boundary.
E.g. 23:00:00 to 01:00:00.
This would have previously been treated as 01:00:00 to 23:00:00 instead.
2022-11-02 07:57:01 -07:00
WarmUpTill
a6839666ce Fix scene item selection not saving index 2022-11-02 07:56:37 -07:00
WarmUpTill
e5843de4fc Add option to check for average brightness 2022-10-21 13:15:11 -07:00
WarmUpTill
a330378c16 Improve option to check multiple media sources
* Added the option to select current, previous, variables, ...
 * Refresh the source list on scene change
 * General clean up of media condition
2022-10-21 13:15:00 -07:00
WarmUpTill
ddb7888e61 Enable word wrap for "on change" and "parallel" options on macro tab 2022-10-21 13:14:35 -07:00
WarmUpTill
73aef4f0b2 Workaround for macro drag / drop not working as expected
See:
 * QTBUG-106395
 * https://github.com/obsproject/obs-studio/issues/7321
2022-10-13 10:29:39 -07:00
WarmUpTill
67c3b73e10 Fix date condition showing incorrect date
This is only affecting the displayed value when reopening a macro.
The entered values were stored / used correctly.
2022-10-08 15:59:15 -07:00
WarmUpTill
9a3d381cf0 Improve macro examples 2022-10-08 15:59:07 -07:00
31 changed files with 703 additions and 316 deletions

View File

@@ -92,21 +92,21 @@ jobs:
if [[ '${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}' != '' && \ if [[ '${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}' != '' && \
'${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}' != '' && \ '${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}' != '' && \
'${{ secrets.MACOS_SIGNING_CERT }}' != '' ]] { '${{ secrets.MACOS_SIGNING_CERT }}' != '' ]] {
print '::set-output name=haveCodesignIdent::true' print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT
} else { } else {
print '::set-output name=haveCodesignIdent::false' print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT
} }
if [[ '${{ secrets.MACOS_NOTARIZATION_USERNAME }}' != '' && \ if [[ '${{ secrets.MACOS_NOTARIZATION_USERNAME }}' != '' && \
'${{ secrets.MACOS_NOTARIZATION_PASSWORD }}' != '' ]] { '${{ secrets.MACOS_NOTARIZATION_PASSWORD }}' != '' ]] {
print '::set-output name=haveNotarizationUser::true' print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT
} else { } else {
print '::set-output name=haveNotarizationUser::false' print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT
} }
print '::endgroup::' print '::endgroup::'
print "::set-output name=ccacheDate::$(date +"%Y-%m-%d")" print "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
print "::set-output name=commitHash::${"$(git rev-parse HEAD)"[0,9]}" print "commitHash=${"$(git rev-parse HEAD)"[0,9]}" >> $GITHUB_OUTPUT
echo "$PWD/.github/scripts" >> $GITHUB_PATH echo "$PWD/.github/scripts" >> $GITHUB_PATH
- name: Restore Compilation Cache - name: Restore Compilation Cache
@@ -123,9 +123,9 @@ jobs:
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
run: | run: |
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]] { if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]] {
print '::set-output name=found::true' print 'found=true' >> $GITHUB_OUTPUT
} else { } else {
print '::set-output name=found::false' print 'found=false' >> $GITHUB_OUTPUT
} }
- name: Install Apple Developer Certificate - name: Install Apple Developer Certificate
@@ -206,8 +206,8 @@ jobs:
id: setup id: setup
run: | run: |
## SETUP ENVIRONMENT SCRIPT ## SETUP ENVIRONMENT SCRIPT
echo "::set-output name=ccacheDate::$(date +"%Y-%m-%d")" echo "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
echo "::set-output name=commitHash::$(git rev-parse HEAD | cut -c1-9)" echo "commitHash=$(git rev-parse HEAD | cut -c1-9)" >> $GITHUB_OUTPUT
echo "$PWD/.github/scripts" >> $GITHUB_PATH echo "$PWD/.github/scripts" >> $GITHUB_PATH
- name: Restore Compilation Cache - name: Restore Compilation Cache
@@ -225,9 +225,9 @@ jobs:
run: | run: |
## GITHUB LABEL SCRIPT ## GITHUB LABEL SCRIPT
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]]; then if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]]; then
echo '::set-output name=found::true' echo 'found=true' >> $GITHUB_OUTPUT
else else
echo '::set-output name=found::false' echo 'found=false' >> $GITHUB_OUTPUT
fi fi
- name: Build Plugin - name: Build Plugin
@@ -295,7 +295,7 @@ jobs:
run: | run: |
## SETUP ENVIRONMENT SCRIPT ## SETUP ENVIRONMENT SCRIPT
$CommitHash = (git rev-parse HEAD)[0..8] -join '' $CommitHash = (git rev-parse HEAD)[0..8] -join ''
Write-Output "::set-output name=commitHash::${CommitHash}" "commitHash=${CommitHash}" >> $env:GITHUB_OUTPUT
- name: Check for GitHub Labels - name: Check for GitHub Labels
id: seekingTesters id: seekingTesters
@@ -316,7 +316,7 @@ jobs:
$false $false
} }
Write-Output "::set-output name=found::$(([string]${LabelFound}).ToLower())" "found=$(([string]${LabelFound}).ToLower())" >> $env:GITHUB_OUTPUT
- name: Build Plugin - name: Build Plugin
uses: ./plugin/.github/actions/build-plugin uses: ./plugin/.github/actions/build-plugin
@@ -367,7 +367,7 @@ jobs:
id: metadata id: metadata
run: | run: |
## METADATA SCRIPT ## METADATA SCRIPT
echo "::set-output name=version::${GITHUB_REF/refs\/tags\//}" echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Download build artifacts - name: Download build artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3

View File

@@ -15,7 +15,7 @@ cd obs-studio/UI/frontend-plugins/
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
``` ```
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher: Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
``` ```
add_subdirectory(SceneSwitcher) add_subdirectory(SceneSwitcher)
``` ```
@@ -66,11 +66,123 @@ Finally, start the plugin build using your provided generator. (E.g. Ninja on Li
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out. Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out.
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
In general changes in the `src/legacy` folder should be avoided. In general changes in the `src/legacy` folder should be avoided.
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`. ## Macro condition
The key functions to add conditions or are the Register() functions. Macro conditions should inherit from the `MacroCondition` class and must implement the following functions:
```
class MacroConditionExample : public MacroCondition {
public:
MacroConditionExample(Macro *m) : MacroCondition(m) {}
// This function should perfrom the condition check
bool CheckCondition();
// This function should store the required condition data to "obj"
// For example called on OBS shutdown
bool Save(obs_data_t *obj);
// This function should load the condition data from "obj"
// For example called on OBS startup
bool Load(obs_data_t *obj);
// This function should return a unique id for this condition type
// The _id is defined below
std::string GetId() { return _id; };
// Helper function called when new conditions are created
// Will be used later when regeistering the new condition type
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionExample>(m);
}
private:
// Used to register new condition type
static bool _registered;
// Unique id identifying this condition type
static const std::string _id;
};
```
When defining the widget used to control the settings of the condition type, it is important to add a static `Create()` method.
It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`)
```
class MacroConditionExampleEdit : public QWidget {
Q_OBJECT
public:
// ...
MacroConditionExampleEdit(
QWidget *parent,
std::shared_ptr<MacroConditionExample> cond = nullptr);
// Function will be used to create the widget for editing the settings of the condition
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionExampleEdit(
parent,
std::dynamic_pointer_cast<MacroConditionExample>(cond));
}
// ...
};
```
To finally register the new condition type you will have to add the following call to `MacroConditionFactory::Register()`.
```
bool MacroConditionExample::_registered = MacroConditionFactory::Register(
MacroConditionExample::id, // Unique string identifying this condition type
{
MacroConditionExample::Create, // Function called to create the object performing the condition
MacroConditionExampleEdit::Create, // Function called to create the widget configure the condition
"AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated)
true // Condition type supports duration modifiers (default true)
}
);
```
## Macro action
The process of adding a new action type is very similar to adding a new condition.
The differences are highlighted in the comments below.
```
class MacroActionExample : public MacroAction {
public:
MacroActionExample(Macro *m) : MacroAction(m) {}
// This function should perfrom the action
// If false is returned the macro will aborted
bool PerformAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return _id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionExample>(m);
}
private:
static bool _registered;
static const std::string _id;
};
```
```
class MacroActionExampleEdit : public QWidget {
Q_OBJECT
public:
// ...
MacroActionExampleEdit(
QWidget *parent,
std::shared_ptr<MacroActionExample> entryData = nullptr);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionExampleEdit(
parent,
std::dynamic_pointer_cast<MacroActionExample>(action));
}
// ...
};
```
``` ```
MacroActionFactory::Register( MacroActionFactory::Register(
MacroActionExample::id, // Unique string identifying this action type MacroActionExample::id, // Unique string identifying this action type
@@ -81,6 +193,6 @@ MacroActionFactory::Register(
} }
); );
``` ```
## External dependencies
If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, try to follow the examples under `src/macro-external`. If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, try to follow the examples under `src/macro-external`.
These are basically plugins themselves that get attempted to be loaded on startup of the advanced scene switcher. These are basically plugins themselves that get attempted to be loaded on startup of the advanced scene switcher.

View File

@@ -1,5 +1,5 @@
#define MyAppName "@CMAKE_PROJECT_NAME@" #define MyAppName "@CMAKE_PROJECT_NAME@"
#define MyAppVersion "@CMAKE_PROJECT_VERSION@" #define MyAppVersion "@GIT_TAG@"
#define MyAppPublisher "@PLUGIN_AUTHOR@" #define MyAppPublisher "@PLUGIN_AUTHOR@"
#define MyAppURL "http://www.mywebsite.com" #define MyAppURL "http://www.mywebsite.com"

View File

@@ -51,13 +51,13 @@ AdvSceneSwitcher.generalTab.priority.media="Medien"
AdvSceneSwitcher.generalTab.priority.time="Zeit" AdvSceneSwitcher.generalTab.priority.time="Zeit"
AdvSceneSwitcher.generalTab.priority.audio="Audio" AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video" AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
AdvSceneSwitcher.generalTab.transitionOverride="Verwende Übergangsüberschreibungen"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Szenenübergänge" AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
AdvSceneSwitcher.transitionTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
AdvSceneSwitcher.transitionTab.transitionOverride="Verwende Übergangsüberschreibungen"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B" AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist" AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"

View File

@@ -55,6 +55,11 @@ AdvSceneSwitcher.generalTab.priority.time="Time"
AdvSceneSwitcher.generalTab.priority.audio="Audio" AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video" AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro" AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.generalTab.transition="Transitions"
AdvSceneSwitcher.generalTab.setTransitionBy="When changing transitions:"
AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.title="Macro"
@@ -128,6 +133,7 @@ AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Window" AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..." AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed" AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
AdvSceneSwitcher.condition.window.entry.line3="Current foreground window: {{focusWindow}}"
AdvSceneSwitcher.condition.file="File" AdvSceneSwitcher.condition.file="File"
AdvSceneSwitcher.condition.file.type.match="matches" AdvSceneSwitcher.condition.file.type.match="matches"
AdvSceneSwitcher.condition.file.type.contentChange="content changed" AdvSceneSwitcher.condition.file.type.contentChange="content changed"
@@ -149,6 +155,7 @@ AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
AdvSceneSwitcher.condition.video.condition.noImage="has no output" AdvSceneSwitcher.condition.video.condition.noImage="has no output"
AdvSceneSwitcher.condition.video.condition.pattern="matches pattern" AdvSceneSwitcher.condition.video.condition.pattern="matches pattern"
AdvSceneSwitcher.condition.video.condition.object="contains object" AdvSceneSwitcher.condition.video.condition.object="contains object"
AdvSceneSwitcher.condition.video.condition.brightness="brightness"
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?" AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file" AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot" AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
@@ -159,6 +166,9 @@ AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will al
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: " AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely." AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern." AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern."
AdvSceneSwitcher.condition.video.brightnessThreshold="Average brightness:"
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="A high value is indicating a bright image and a low one a darker one."
AdvSceneSwitcher.condition.video.currentBrightness="Current average brightness: %1"
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: " AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load." AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches." AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
@@ -196,6 +206,7 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
AdvSceneSwitcher.condition.record.entry="{{recordState}}" AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Process" AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused" AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="Idle" AdvSceneSwitcher.condition.idle="Idle"
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}" AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
AdvSceneSwitcher.condition.pluginState="Plugin state" AdvSceneSwitcher.condition.pluginState="Plugin state"
@@ -537,10 +548,6 @@ AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition" AdvSceneSwitcher.transitionTab.title="Transition"
AdvSceneSwitcher.transitionTab.setTransitionBy="When changing transitions:"
AdvSceneSwitcher.transitionTab.transitionOverride="Set transition overrides"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Change active transition type"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B" AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active" AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"

View File

@@ -54,6 +54,10 @@ AdvSceneSwitcher.generalTab.priority.time="Tiempo"
AdvSceneSwitcher.generalTab.priority.audio="Audio" AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video" AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro" AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.generalTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.generalTab.transitionOverride="Establecer anulaciones de transición"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.title="Macro"
@@ -478,10 +482,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="Continuar con el elemento selecci
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transición" AdvSceneSwitcher.transitionTab.title="Transición"
AdvSceneSwitcher.transitionTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.transitionTab.transitionOverride="Establecer anulaciones de transición"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliza la transición para el cambio de escena automatizado de la escena A a la escena B" AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliza la transición para el cambio de escena automatizado de la escena A a la escena B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body> <p> Estos ajustes <span style =\"font-style: italic; \"> solo </span> surgió a las transiciones causadas por el selector de escenas - Echa un vistazo a <a href =\"https://obsproject.com/forum/resources/transition-table.1174/\"> <span style=\" texto-decoración: subrayado; color: # 268bd2; \"> Tabla de transición </span> </a> si desea configurar esto para cambios de escena manuales. <br/> Los definiciones definidos aquí tienen prioridad sobre los ajustes de transición configurados en cualquier otro lugar del selector de escenas. < br/> <br/> Haz clic en el símbolo más a continuación para agregar una nueva entrada. </p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body> <p> Estos ajustes <span style =\"font-style: italic; \"> solo </span> surgió a las transiciones causadas por el selector de escenas - Echa un vistazo a <a href =\"https://obsproject.com/forum/resources/transition-table.1174/\"> <span style=\" texto-decoración: subrayado; color: # 268bd2; \"> Tabla de transición </span> </a> si desea configurar esto para cambios de escena manuales. <br/> Los definiciones definidos aquí tienen prioridad sobre los ajustes de transición configurados en cualquier otro lugar del selector de escenas. < br/> <br/> Haz clic en el símbolo más a continuación para agregar una nueva entrada. </p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Cambiar transición si la escena está activa" AdvSceneSwitcher.transitionTab.defaultTransition="Cambiar transición si la escena está activa"

View File

@@ -52,6 +52,10 @@ AdvSceneSwitcher.generalTab.priority.time="Время"
AdvSceneSwitcher.generalTab.priority.audio="Аудио" AdvSceneSwitcher.generalTab.priority.audio="Аудио"
AdvSceneSwitcher.generalTab.priority.video="Видео" AdvSceneSwitcher.generalTab.priority.video="Видео"
AdvSceneSwitcher.generalTab.priority.macro="Макрос" AdvSceneSwitcher.generalTab.priority.macro="Макрос"
AdvSceneSwitcher.generalTab.setTransitionBy="При изменении переходов:"
AdvSceneSwitcher.generalTab.transitionOverride="Установить переопределение переходов"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Изменить тип активного перехода"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="Макрос" AdvSceneSwitcher.macroTab.title="Макрос"
@@ -147,10 +151,6 @@ AdvSceneSwitcher.action.run="Запустить"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Переход" AdvSceneSwitcher.transitionTab.title="Переход"
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B" AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна" AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"

View File

@@ -53,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Zaman"
AdvSceneSwitcher.generalTab.priority.audio="Ses" AdvSceneSwitcher.generalTab.priority.audio="Ses"
AdvSceneSwitcher.generalTab.priority.video="Video" AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Makro" AdvSceneSwitcher.generalTab.priority.macro="Makro"
AdvSceneSwitcher.generalTab.setTransitionBy="Geçişleri değiştirirken:"
AdvSceneSwitcher.generalTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="Makro" AdvSceneSwitcher.macroTab.title="Makro"
@@ -391,10 +395,6 @@ AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyon
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Geçiş" AdvSceneSwitcher.transitionTab.title="Geçiş"
AdvSceneSwitcher.transitionTab.setTransitionBy="Geçişleri değiştirirken:"
AdvSceneSwitcher.transitionTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın" AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir" AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir"

View File

@@ -54,6 +54,9 @@ AdvSceneSwitcher.generalTab.priority.time="时间"
AdvSceneSwitcher.generalTab.priority.audio="音频" AdvSceneSwitcher.generalTab.priority.audio="音频"
AdvSceneSwitcher.generalTab.priority.video="视频" AdvSceneSwitcher.generalTab.priority.video="视频"
AdvSceneSwitcher.generalTab.priority.macro="宏" AdvSceneSwitcher.generalTab.priority.macro="宏"
AdvSceneSwitcher.generalTab.setTransitionBy="更改转场特效时:"
AdvSceneSwitcher.generalTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="更改激活转场特效类型"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="宏" AdvSceneSwitcher.macroTab.title="宏"
@@ -437,9 +440,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="转场特效" AdvSceneSwitcher.transitionTab.title="转场特效"
AdvSceneSwitcher.transitionTab.setTransitionBy="更改转场特效时:"
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特效类型"
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效" AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效" AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"

View File

@@ -68,7 +68,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>957</width> <width>957</width>
<height>817</height> <height>905</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_19"> <layout class="QVBoxLayout" name="verticalLayout_19">
@@ -419,6 +419,49 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="transitionBox">
<property name="title">
<string>AdvSceneSwitcher.generalTab.transition</string>
</property>
<layout class="QGridLayout" name="transitionLayout">
<item row="1" column="1">
<widget class="QCheckBox" name="adjustActiveTransitionType">
<property name="text">
<string>AdvSceneSwitcher.generalTab.adjustActiveTransitionType</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>AdvSceneSwitcher.generalTab.setTransitionBy</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="transitionOverridecheckBox">
<property name="text">
<string>AdvSceneSwitcher.generalTab.transitionOverride</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_9">
<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>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="priorityBox"> <widget class="QGroupBox" name="priorityBox">
<property name="title"> <property name="title">
@@ -610,7 +653,7 @@
<enum>Qt::MoveAction</enum> <enum>Qt::MoveAction</enum>
</property> </property>
<property name="spacing"> <property name="spacing">
<number>1</number> <number>0</number>
</property> </property>
<property name="sortingEnabled"> <property name="sortingEnabled">
<bool>false</bool> <bool>false</bool>
@@ -763,7 +806,7 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_33"> <layout class="QVBoxLayout" name="verticalLayout_33">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0"> <layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0,0,0">
<item> <item>
<widget class="QLabel" name="label_20"> <widget class="QLabel" name="label_20">
<property name="text"> <property name="text">
@@ -783,16 +826,36 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="runMacroInParallel"> <widget class="QCheckBox" name="runMacroInParallel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>AdvSceneSwitcher.macroTab.runInParallel</string> <string>AdvSceneSwitcher.macroTab.runInParallel</string>
</property> </property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="runMacroOnChange"> <widget class="QCheckBox" name="runMacroOnChange">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_18">
<property name="text"> <property name="text">
<string>AdvSceneSwitcher.macroTab.onChange</string> <string>AdvSceneSwitcher.macroTab.onChange</string>
</property> </property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -1710,44 +1773,6 @@
<string>AdvSceneSwitcher.transitionTab.title</string> <string>AdvSceneSwitcher.transitionTab.title</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_15"> <layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="1">
<widget class="QCheckBox" name="adjustActiveTransitionType">
<property name="text">
<string>AdvSceneSwitcher.transitionTab.adjustActiveTransitionType</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>AdvSceneSwitcher.transitionTab.setTransitionBy</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="transitionOverridecheckBox">
<property name="text">
<string>AdvSceneSwitcher.transitionTab.transitionOverride</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_9">
<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> <item>
<widget class="QGroupBox" name="transitionsGroup"> <widget class="QGroupBox" name="transitionsGroup">
<property name="title"> <property name="title">

View File

@@ -280,6 +280,9 @@ void SwitcherData::setPreconditions()
} }
currentTitle = title; currentTitle = title;
// Process name
GetForegroundProcessName(currentForegroundProcess);
// Cursor // Cursor
std::pair<int, int> cursorPos = getCursorPos(); std::pair<int, int> cursorPos = getCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first || cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||

View File

@@ -496,6 +496,39 @@ void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
SetShowFrames(); SetShowFrames();
} }
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading) {
return;
}
if (!state && !switcher->adjustActiveTransitionType) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
ui->adjustActiveTransitionType->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->transitionOverrideOverride = state;
}
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
{
if (loading) {
return;
}
// This option only makes sense if we are allowed to use transition overrides
if (!state && !switcher->transitionOverrideOverride) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
ui->transitionOverridecheckBox->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->adjustActiveTransitionType = state;
}
void SwitcherData::loadSettings(obs_data_t *obj) void SwitcherData::loadSettings(obs_data_t *obj)
{ {
if (!obj) { if (!obj) {
@@ -599,6 +632,13 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]); obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
obs_data_set_int(obj, "threadPriority", threadPriority); obs_data_set_int(obj, "threadPriority", threadPriority);
obs_data_set_bool(obj, "tansitionOverrideOverride",
transitionOverrideOverride);
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
} }
void SwitcherData::loadGeneralSettings(obs_data_t *obj) void SwitcherData::loadGeneralSettings(obs_data_t *obj)
@@ -674,6 +714,11 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_set_default_int(obj, "threadPriority", obs_data_set_default_int(obj, "threadPriority",
QThread::NormalPriority); QThread::NormalPriority);
threadPriority = obs_data_get_int(obj, "threadPriority"); threadPriority = obs_data_get_int(obj, "threadPriority");
transitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
adjustActiveTransitionType =
obs_data_get_bool(obj, "adjustActiveTransitionType");
} }
void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name) void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)

View File

@@ -169,39 +169,6 @@ void SwitcherData::checkDefaultSceneTransitions()
} }
} }
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading) {
return;
}
if (!state && !switcher->adjustActiveTransitionType) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
ui->adjustActiveTransitionType->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->transitionOverrideOverride = state;
}
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
{
if (loading) {
return;
}
// This option only makes sense if we are allowed to use transition overrides
if (!state && !switcher->transitionOverrideOverride) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
ui->transitionOverridecheckBox->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->adjustActiveTransitionType = state;
}
void AdvSceneSwitcher::defTransitionDelayValueChanged(int value) void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
{ {
if (loading) { if (loading) {
@@ -335,13 +302,6 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
} }
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray); obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
obs_data_array_release(defaultTransitionsArray); obs_data_array_release(defaultTransitionsArray);
obs_data_set_bool(obj, "tansitionOverrideOverride",
transitionOverrideOverride);
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_default_int(obj, "defTransitionDelay", obs_data_set_default_int(obj, "defTransitionDelay",
default_def_transition_dealy); default_def_transition_dealy);
obs_data_set_int(obj, "defTransitionDelay", obs_data_set_int(obj, "defTransitionDelay",
@@ -380,11 +340,6 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
} }
obs_data_array_release(defaultTransitionsArray); obs_data_array_release(defaultTransitionsArray);
transitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
adjustActiveTransitionType =
obs_data_get_bool(obj, "adjustActiveTransitionType");
// Check for invalid config // Check for invalid config
if (!transitionOverrideOverride && !adjustActiveTransitionType) { if (!transitionOverrideOverride && !adjustActiveTransitionType) {
adjustActiveTransitionType = true; adjustActiveTransitionType = true;

View File

@@ -434,10 +434,24 @@ std::string getProcNameFromPid(int pid)
return buffer.str(); return buffer.str();
} }
bool isInFocus(const QString &executable) void GetForegroundProcessName(QString &proc)
{ {
std::string temp;
GetForegroundProcessName(temp);
proc = QString::fromStdString(temp);
}
void GetForegroundProcessName(std::string &proc)
{
proc.resize(0);
auto pid = getForegroundProcessPid(); auto pid = getForegroundProcessPid();
std::string current = getProcNameFromPid(pid); std::string current = getProcNameFromPid(pid);
}
bool isInFocus(const QString &executable)
{
std::string current;
GetForegroundProcessName(current);
// True if executable switch equals current window // True if executable switch equals current window
bool equals = (executable.toStdString() == current); bool equals = (executable.toStdString() == current);

View File

@@ -67,6 +67,26 @@ bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
return false; return false;
} }
bool MacroConditionDate::CheckBetween(QDateTime &now)
{
// In the case of ignoring the date component treat the "left" value as
// the start of the time range and the "right" value as the end
if (_ignoreDate) {
if (_dateTime2 >= _dateTime) {
return now >= _dateTime && now <= _dateTime2;
}
// Assume that the end / start value should be shifted by 24h
// as otherwise the condition would never be true
return (now >= _dateTime && now <= _dateTime2.addDays(1)) ||
(now >= _dateTime.addDays(-1) && now <= _dateTime2);
}
if (_dateTime2 >= _dateTime) {
return now >= _dateTime && now <= _dateTime2;
}
return now >= _dateTime2 && now <= _dateTime;
}
bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck) bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
{ {
bool match = false; bool match = false;
@@ -92,11 +112,7 @@ bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
match = cur <= _dateTime; match = cur <= _dateTime;
break; break;
case DateCondition::BETWEEN: case DateCondition::BETWEEN:
if (_dateTime2 > _dateTime) { match = CheckBetween(cur);
match = cur >= _dateTime && cur <= _dateTime2;
} else {
match = cur >= _dateTime2 && cur <= _dateTime;
}
break; break;
default: default:
break; break;
@@ -232,12 +248,12 @@ void MacroConditionDate::SetTime2(const QTime &time)
QDateTime MacroConditionDate::GetDateTime1() QDateTime MacroConditionDate::GetDateTime1()
{ {
return _updateOnRepeat ? _dateTime : _origDateTime; return _repeat && _updateOnRepeat ? _dateTime : _origDateTime;
} }
QDateTime MacroConditionDate::GetDateTime2() QDateTime MacroConditionDate::GetDateTime2()
{ {
return _updateOnRepeat ? _dateTime2 : _origDateTime2; return _repeat && _updateOnRepeat ? _dateTime2 : _origDateTime2;
} }
QDateTime MacroConditionDate::GetNextMatchDateTime() QDateTime MacroConditionDate::GetNextMatchDateTime()
@@ -576,12 +592,12 @@ void MacroConditionDateEdit::UpdateEntryData()
_dayOfWeek->setCurrentIndex( _dayOfWeek->setCurrentIndex(
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek))); static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
_ignoreWeekTime->setChecked(!_entryData->_ignoreTime); _ignoreWeekTime->setChecked(!_entryData->_ignoreTime);
_weekTime->setDateTime(_entryData->GetDateTime1()); _weekTime->setTime(_entryData->GetDateTime1().time());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition)); _condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_date->setDateTime(_entryData->GetDateTime1()); _date->setDate(_entryData->GetDateTime1().date());
_time->setDateTime(_entryData->GetDateTime1()); _time->setTime(_entryData->GetDateTime1().time());
_date2->setDateTime(_entryData->GetDateTime2()); _date2->setDate(_entryData->GetDateTime2().date());
_time2->setDateTime(_entryData->GetDateTime2()); _time2->setTime(_entryData->GetDateTime2().time());
_ignoreDate->setChecked(!_entryData->_ignoreDate); _ignoreDate->setChecked(!_entryData->_ignoreDate);
_ignoreTime->setChecked(!_entryData->_ignoreTime); _ignoreTime->setChecked(!_entryData->_ignoreTime);
_repeat->setChecked(_entryData->_repeat); _repeat->setChecked(_entryData->_repeat);

View File

@@ -58,6 +58,7 @@ public:
private: private:
bool CheckDayOfWeek(int64_t); bool CheckDayOfWeek(int64_t);
bool CheckRegularDate(int64_t); bool CheckRegularDate(int64_t);
bool CheckBetween(QDateTime &now);
QDateTime _dateTime = QDateTime::currentDateTime(); QDateTime _dateTime = QDateTime::currentDateTime();
QDateTime _dateTime2 = QDateTime::currentDateTime(); QDateTime _dateTime2 = QDateTime::currentDateTime();

View File

@@ -10,39 +10,40 @@ bool MacroConditionMedia::_registered = MacroConditionFactory::Register(
{MacroConditionMedia::Create, MacroConditionMediaEdit::Create, {MacroConditionMedia::Create, MacroConditionMediaEdit::Create,
"AdvSceneSwitcher.condition.media"}); "AdvSceneSwitcher.condition.media"});
static std::map<MediaTimeRestriction, std::string> mediaTimeRestrictions = { static std::map<MacroConditionMedia::Time, std::string> mediaTimeRestrictions = {
{MediaTimeRestriction::TIME_RESTRICTION_NONE, {MacroConditionMedia::Time::TIME_RESTRICTION_NONE,
"AdvSceneSwitcher.mediaTab.timeRestriction.none"}, "AdvSceneSwitcher.mediaTab.timeRestriction.none"},
{MediaTimeRestriction::TIME_RESTRICTION_SHORTER, {MacroConditionMedia::Time::TIME_RESTRICTION_SHORTER,
"AdvSceneSwitcher.mediaTab.timeRestriction.shorter"}, "AdvSceneSwitcher.mediaTab.timeRestriction.shorter"},
{MediaTimeRestriction::TIME_RESTRICTION_LONGER, {MacroConditionMedia::Time::TIME_RESTRICTION_LONGER,
"AdvSceneSwitcher.mediaTab.timeRestriction.longer"}, "AdvSceneSwitcher.mediaTab.timeRestriction.longer"},
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER, {MacroConditionMedia::Time::TIME_RESTRICTION_REMAINING_SHORTER,
"AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter"}, "AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter"},
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER, {MacroConditionMedia::Time::TIME_RESTRICTION_REMAINING_LONGER,
"AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"}, "AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"},
}; };
static std::map<MediaState, std::string> mediaStates = { static std::map<MacroConditionMedia::State, std::string> mediaStates = {
{MediaState::OBS_MEDIA_STATE_NONE, {MacroConditionMedia::State::OBS_MEDIA_STATE_NONE,
"AdvSceneSwitcher.mediaTab.states.none"}, "AdvSceneSwitcher.mediaTab.states.none"},
{MediaState::OBS_MEDIA_STATE_PLAYING, {MacroConditionMedia::State::OBS_MEDIA_STATE_PLAYING,
"AdvSceneSwitcher.mediaTab.states.playing"}, "AdvSceneSwitcher.mediaTab.states.playing"},
{MediaState::OBS_MEDIA_STATE_OPENING, {MacroConditionMedia::State::OBS_MEDIA_STATE_OPENING,
"AdvSceneSwitcher.mediaTab.states.opening"}, "AdvSceneSwitcher.mediaTab.states.opening"},
{MediaState::OBS_MEDIA_STATE_BUFFERING, {MacroConditionMedia::State::OBS_MEDIA_STATE_BUFFERING,
"AdvSceneSwitcher.mediaTab.states.buffering"}, "AdvSceneSwitcher.mediaTab.states.buffering"},
{MediaState::OBS_MEDIA_STATE_PAUSED, {MacroConditionMedia::State::OBS_MEDIA_STATE_PAUSED,
"AdvSceneSwitcher.mediaTab.states.paused"}, "AdvSceneSwitcher.mediaTab.states.paused"},
{MediaState::OBS_MEDIA_STATE_STOPPED, {MacroConditionMedia::State::OBS_MEDIA_STATE_STOPPED,
"AdvSceneSwitcher.mediaTab.states.stopped"}, "AdvSceneSwitcher.mediaTab.states.stopped"},
{MediaState::OBS_MEDIA_STATE_ENDED, {MacroConditionMedia::State::OBS_MEDIA_STATE_ENDED,
"AdvSceneSwitcher.mediaTab.states.ended"}, "AdvSceneSwitcher.mediaTab.states.ended"},
{MediaState::OBS_MEDIA_STATE_ERROR, {MacroConditionMedia::State::OBS_MEDIA_STATE_ERROR,
"AdvSceneSwitcher.mediaTab.states.error"}, "AdvSceneSwitcher.mediaTab.states.error"},
{MediaState::PLAYLIST_ENDED, {MacroConditionMedia::State::PLAYLIST_ENDED,
"AdvSceneSwitcher.mediaTab.states.playlistEnd"}, "AdvSceneSwitcher.mediaTab.states.playlistEnd"},
{MediaState::ANY, "AdvSceneSwitcher.mediaTab.states.any"}, {MacroConditionMedia::State::ANY,
"AdvSceneSwitcher.mediaTab.states.any"},
}; };
MacroConditionMedia::~MacroConditionMedia() MacroConditionMedia::~MacroConditionMedia()
@@ -65,20 +66,20 @@ bool MacroConditionMedia::CheckTime()
bool match = false; bool match = false;
switch (_restriction) { switch (_restriction) {
case MediaTimeRestriction::TIME_RESTRICTION_NONE: case Time::TIME_RESTRICTION_NONE:
match = true; match = true;
break; break;
case MediaTimeRestriction::TIME_RESTRICTION_SHORTER: case Time::TIME_RESTRICTION_SHORTER:
match = currentTime < _time.seconds * 1000; match = currentTime < _time.seconds * 1000;
break; break;
case MediaTimeRestriction::TIME_RESTRICTION_LONGER: case Time::TIME_RESTRICTION_LONGER:
match = currentTime > _time.seconds * 1000; match = currentTime > _time.seconds * 1000;
break; break;
case MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER: case Time::TIME_RESTRICTION_REMAINING_SHORTER:
match = duration > currentTime && match = duration > currentTime &&
duration - currentTime < _time.seconds * 1000; duration - currentTime < _time.seconds * 1000;
break; break;
case MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER: case Time::TIME_RESTRICTION_REMAINING_LONGER:
match = duration > currentTime && match = duration > currentTime &&
duration - currentTime > _time.seconds * 1000; duration - currentTime > _time.seconds * 1000;
break; break;
@@ -100,24 +101,24 @@ bool MacroConditionMedia::CheckState()
int expectedState = static_cast<int>(_state); int expectedState = static_cast<int>(_state);
switch (_state) { switch (_state) {
case MediaState::OBS_MEDIA_STATE_STOPPED: case State::OBS_MEDIA_STATE_STOPPED:
match = _stopped || currentState == OBS_MEDIA_STATE_STOPPED; match = _stopped || currentState == OBS_MEDIA_STATE_STOPPED;
break; break;
case MediaState::OBS_MEDIA_STATE_ENDED: case State::OBS_MEDIA_STATE_ENDED:
match = _ended || currentState == OBS_MEDIA_STATE_ENDED; match = _ended || currentState == OBS_MEDIA_STATE_ENDED;
break; break;
case MediaState::PLAYLIST_ENDED: case State::PLAYLIST_ENDED:
match = CheckPlaylistEnd(currentState); match = CheckPlaylistEnd(currentState);
break; break;
case MediaState::ANY: case State::ANY:
match = true; match = true;
break; break;
case MediaState::OBS_MEDIA_STATE_NONE: case State::OBS_MEDIA_STATE_NONE:
case MediaState::OBS_MEDIA_STATE_PLAYING: case State::OBS_MEDIA_STATE_PLAYING:
case MediaState::OBS_MEDIA_STATE_OPENING: case State::OBS_MEDIA_STATE_OPENING:
case MediaState::OBS_MEDIA_STATE_BUFFERING: case State::OBS_MEDIA_STATE_BUFFERING:
case MediaState::OBS_MEDIA_STATE_PAUSED: case State::OBS_MEDIA_STATE_PAUSED:
case MediaState::OBS_MEDIA_STATE_ERROR: case State::OBS_MEDIA_STATE_ERROR:
match = currentState == expectedState; match = currentState == expectedState;
break; break;
default: default:
@@ -148,7 +149,7 @@ bool MacroConditionMedia::CheckMediaMatch()
bool match = false; bool match = false;
bool matched = CheckState() && CheckTime(); bool matched = CheckState() && CheckTime();
if (matched && !(_onlyMatchonChagne && _alreadyMatched)) { if (matched && !(_onlyMatchOnChagne && _alreadyMatched)) {
match = true; match = true;
} }
_alreadyMatched = matched; _alreadyMatched = matched;
@@ -161,16 +162,22 @@ bool MacroConditionMedia::CheckMediaMatch()
return match; return match;
} }
void MacroConditionMedia::HandleSceneChange()
{
UpdateMediaSourcesOfSceneList();
_lastConfigureScene = switcher->currentScene;
}
bool MacroConditionMedia::CheckCondition() bool MacroConditionMedia::CheckCondition()
{ {
bool match = false; bool match = false;
switch (_sourceType) { switch (_sourceType) {
case MediaSourceType::ANY: case Type::ANY:
for (auto &source : _sources) { for (auto &source : _sources) {
match = match || source.CheckCondition(); match = match || source.CheckCondition();
} }
break; break;
case MediaSourceType::ALL: { case Type::ALL: {
bool res = true; bool res = true;
for (auto &source : _sources) { for (auto &source : _sources) {
res = res && source.CheckCondition(); res = res && source.CheckCondition();
@@ -178,12 +185,17 @@ bool MacroConditionMedia::CheckCondition()
match = res; match = res;
break; break;
} }
case MediaSourceType::SOURCE: case Type::SOURCE:
match = CheckMediaMatch(); match = CheckMediaMatch();
break; break;
default: default:
break; break;
} }
if (_lastConfigureScene != switcher->currentScene) {
HandleSceneChange();
}
return match; return match;
} }
@@ -196,7 +208,7 @@ bool MacroConditionMedia::Save(obs_data_t *obj)
obs_data_set_int(obj, "state", static_cast<int>(_state)); obs_data_set_int(obj, "state", static_cast<int>(_state));
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction)); obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
_time.Save(obj); _time.Save(obj);
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchonChagne); obs_data_set_bool(obj, "matchOnChagne", _onlyMatchOnChagne);
obs_data_set_int(obj, "version", 0); obs_data_set_int(obj, "version", 0);
return true; return true;
} }
@@ -222,22 +234,24 @@ static bool enumSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
return true; return true;
} }
void forMediaSourceOnSceneAddMediaCondition( void MacroConditionMedia::UpdateMediaSourcesOfSceneList()
OBSWeakSource sceneWeakSource, MacroConditionMedia *origCond,
std::vector<MacroConditionMedia> &conditions)
{ {
conditions.clear(); _sources.clear();
if (!_scene.GetScene(false)) {
return;
}
std::vector<OBSWeakSource> mediaSources; std::vector<OBSWeakSource> mediaSources;
auto s = obs_weak_source_get_source(sceneWeakSource); auto s = obs_weak_source_get_source(_scene.GetScene(false));
auto scene = obs_scene_from_source(s); auto scene = obs_scene_from_source(s);
obs_scene_enum_items(scene, enumSceneItem, &mediaSources); obs_scene_enum_items(scene, enumSceneItem, &mediaSources);
obs_source_release(s); obs_source_release(s);
_sources.reserve(mediaSources.size());
for (auto &source : mediaSources) { for (auto &source : mediaSources) {
MacroConditionMedia cond(*origCond); MacroConditionMedia cond(*this);
cond._sourceType = MediaSourceType::SOURCE; cond._sourceType = Type::SOURCE;
cond._source = source; cond._source = source;
conditions.push_back(cond); _sources.push_back(cond);
} }
} }
@@ -247,15 +261,15 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
const char *sourceName = obs_data_get_string(obj, "source"); const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName); _source = GetWeakSourceByName(sourceName);
_scene.Load(obj); _scene.Load(obj);
_sourceType = static_cast<MediaSourceType>( _sourceType = static_cast<Type>(obs_data_get_int(obj, "sourceType"));
obs_data_get_int(obj, "sourceType")); _state = static_cast<MacroConditionMedia::State>(
_state = static_cast<MediaState>(obs_data_get_int(obj, "state")); obs_data_get_int(obj, "state"));
_restriction = static_cast<MediaTimeRestriction>( _restriction = static_cast<MacroConditionMedia::Time>(
obs_data_get_int(obj, "restriction")); obs_data_get_int(obj, "restriction"));
_time.Load(obj); _time.Load(obj);
_onlyMatchonChagne = obs_data_get_bool(obj, "matchOnChagne"); _onlyMatchOnChagne = obs_data_get_bool(obj, "matchOnChagne");
if (_sourceType == MediaSourceType::SOURCE) { if (_sourceType == Type::SOURCE) {
obs_source_t *mediasource = obs_weak_source_get_source(_source); obs_source_t *mediasource = obs_weak_source_get_source(_source);
signal_handler_t *sh = signal_handler_t *sh =
obs_source_get_signal_handler(mediasource); obs_source_get_signal_handler(mediasource);
@@ -265,11 +279,11 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
obs_source_release(mediasource); obs_source_release(mediasource);
} }
forMediaSourceOnSceneAddMediaCondition(_scene.GetScene(), this, UpdateMediaSourcesOfSceneList();
_sources);
if (!obs_data_has_user_value(obj, "version")) { if (!obs_data_has_user_value(obj, "version")) {
if (_state == MediaState::OBS_MEDIA_STATE_ENDED) { if (_state ==
_state = MediaState::PLAYLIST_ENDED; MacroConditionMedia::State::OBS_MEDIA_STATE_ENDED) {
_state = MacroConditionMedia::State::PLAYLIST_ENDED;
} }
} }
return true; return true;
@@ -278,20 +292,20 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
std::string MacroConditionMedia::GetShortDesc() std::string MacroConditionMedia::GetShortDesc()
{ {
switch (_sourceType) { switch (_sourceType) {
case MediaSourceType::SOURCE: case Type::SOURCE:
if (_source) { if (_source) {
return GetWeakSourceName(_source); return GetWeakSourceName(_source);
} }
break; break;
case MediaSourceType::ANY: case Type::ANY:
if (_scene.GetScene()) { if (_scene.GetScene(false)) {
return obs_module_text( return obs_module_text(
"AdvSceneSwitcher.condition.media.anyOnScene") + "AdvSceneSwitcher.condition.media.anyOnScene") +
std::string(" ") + _scene.ToString(); std::string(" ") + _scene.ToString();
} }
break; break;
case MediaSourceType::ALL: case Type::ALL:
if (_scene.GetScene()) { if (_scene.GetScene(false)) {
return obs_module_text( return obs_module_text(
"AdvSceneSwitcher.condition.media.allOnScene") + "AdvSceneSwitcher.condition.media.allOnScene") +
std::string(" ") + _scene.ToString(); std::string(" ") + _scene.ToString();
@@ -356,7 +370,7 @@ void MacroConditionMedia::MediaNext(void *data, calldata_t *)
media->_next = true; media->_next = true;
} }
static void populateMediaTimeRestrictions(QComboBox *list) static void populateMediaTimes(QComboBox *list)
{ {
for (auto entry : mediaTimeRestrictions) { for (auto entry : mediaTimeRestrictions) {
list->addItem(obs_module_text(entry.second.c_str())); list->addItem(obs_module_text(entry.second.c_str()));
@@ -383,7 +397,8 @@ static void addAnyAndAllStates(QComboBox *list)
MacroConditionMediaEdit::MacroConditionMediaEdit( MacroConditionMediaEdit::MacroConditionMediaEdit(
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData) QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
: QWidget(parent), : QWidget(parent),
_scenes(new SceneSelectionWidget(window())), _scenes(new SceneSelectionWidget(window(), true, true, true, true,
true)),
_mediaSources(new QComboBox()), _mediaSources(new QComboBox()),
_states(new QComboBox()), _states(new QComboBox()),
_timeRestrictions(new QComboBox()), _timeRestrictions(new QComboBox()),
@@ -414,7 +429,7 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
populateMediaSelection(_mediaSources); populateMediaSelection(_mediaSources);
addAnyAndAllStates(_mediaSources); addAnyAndAllStates(_mediaSources);
populateMediaStates(_states); populateMediaStates(_states);
populateMediaTimeRestrictions(_timeRestrictions); populateMediaTimes(_timeRestrictions);
QHBoxLayout *entryLayout = new QHBoxLayout; QHBoxLayout *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
@@ -446,14 +461,14 @@ void MacroConditionMediaEdit::SourceChanged(const QString &text)
if (text == if (text ==
obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene")) { obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene")) {
_entryData->_sourceType = MediaSourceType::ANY; _entryData->_sourceType = MacroConditionMedia::Type::ANY;
} else if (text == } else if (text ==
obs_module_text( obs_module_text(
"AdvSceneSwitcher.condition.media.allOnScene")) { "AdvSceneSwitcher.condition.media.allOnScene")) {
_entryData->_sourceType = MediaSourceType::ALL; _entryData->_sourceType = MacroConditionMedia::Type::ALL;
} else { } else {
_entryData->_sources.clear(); _entryData->_sources.clear();
_entryData->_sourceType = MediaSourceType::SOURCE; _entryData->_sourceType = MacroConditionMedia::Type::SOURCE;
} }
_entryData->ClearSignalHandler(); _entryData->ClearSignalHandler();
@@ -473,21 +488,21 @@ void MacroConditionMediaEdit::SceneChanged(const SceneSelection &s)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = s; _entryData->_scene = s;
forMediaSourceOnSceneAddMediaCondition(_entryData->_scene.GetScene(), _entryData->UpdateMediaSourcesOfSceneList();
_entryData.get(),
_entryData->_sources);
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
} }
MediaState getMediaStateFromIdx(int idx) MacroConditionMedia::State getMediaStateFromIdx(int idx)
{ {
if (idx < static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE)) { if (idx < static_cast<int>(
return static_cast<MediaState>(idx); MacroConditionMedia::State::LAST_OBS_MEDIA_STATE)) {
return static_cast<MacroConditionMedia::State>(idx);
} else { } else {
return static_cast<MediaState>( return static_cast<MacroConditionMedia::State>(
idx - idx -
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE) + static_cast<int>(
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE) +
custom_media_states_offset); custom_media_states_offset);
} }
} }
@@ -500,10 +515,8 @@ void MacroConditionMediaEdit::StateChanged(int index)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_state = getMediaStateFromIdx(index); _entryData->_state = getMediaStateFromIdx(index);
if (_entryData->_sourceType != MediaSourceType::SOURCE) { if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
forMediaSourceOnSceneAddMediaCondition( _entryData->UpdateMediaSourcesOfSceneList();
_entryData->_scene.GetScene(), _entryData.get(),
_entryData->_sources);
} }
} }
@@ -513,19 +526,18 @@ void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
return; return;
} }
if (static_cast<MediaTimeRestriction>(index) == if (static_cast<MacroConditionMedia::Time>(index) ==
MediaTimeRestriction::TIME_RESTRICTION_NONE) { MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
_time->setDisabled(true); _time->setDisabled(true);
} else { } else {
_time->setDisabled(false); _time->setDisabled(false);
} }
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_restriction = static_cast<MediaTimeRestriction>(index); _entryData->_restriction =
if (_entryData->_sourceType != MediaSourceType::SOURCE) { static_cast<MacroConditionMedia::Time>(index);
forMediaSourceOnSceneAddMediaCondition( if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
_entryData->_scene.GetScene(), _entryData.get(), _entryData->UpdateMediaSourcesOfSceneList();
_entryData->_sources);
} }
} }
@@ -537,10 +549,8 @@ void MacroConditionMediaEdit::TimeChanged(double seconds)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_time.seconds = seconds; _entryData->_time.seconds = seconds;
if (_entryData->_sourceType != MediaSourceType::SOURCE) { if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
forMediaSourceOnSceneAddMediaCondition( _entryData->UpdateMediaSourcesOfSceneList();
_entryData->_scene.GetScene(), _entryData.get(),
_entryData->_sources);
} }
} }
@@ -552,10 +562,8 @@ void MacroConditionMediaEdit::TimeUnitChanged(DurationUnit unit)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_time.displayUnit = unit; _entryData->_time.displayUnit = unit;
if (_entryData->_sourceType != MediaSourceType::SOURCE) { if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
forMediaSourceOnSceneAddMediaCondition( _entryData->UpdateMediaSourcesOfSceneList();
_entryData->_scene.GetScene(), _entryData.get(),
_entryData->_sources);
} }
} }
@@ -566,29 +574,29 @@ void MacroConditionMediaEdit::OnChangeChanged(int value)
} }
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_onlyMatchonChagne = value; _entryData->_onlyMatchOnChagne = value;
if (_entryData->_sourceType != MediaSourceType::SOURCE) { if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
forMediaSourceOnSceneAddMediaCondition( _entryData->UpdateMediaSourcesOfSceneList();
_entryData->_scene.GetScene(), _entryData.get(),
_entryData->_sources);
} }
} }
void MacroConditionMediaEdit::SetWidgetVisibility() void MacroConditionMediaEdit::SetWidgetVisibility()
{ {
_scenes->setVisible(_entryData->_sourceType != MediaSourceType::SOURCE); _scenes->setVisible(_entryData->_sourceType !=
MacroConditionMedia::Type::SOURCE);
if (!_onChange->isChecked()) { if (!_onChange->isChecked()) {
_onChange->hide(); _onChange->hide();
} }
} }
int getIdxFromMediaState(MediaState state) int getIdxFromMediaState(MacroConditionMedia::State state)
{ {
if (state < MediaState::LAST_OBS_MEDIA_STATE) { if (state < MacroConditionMedia::State::LAST_OBS_MEDIA_STATE) {
return static_cast<int>(state); return static_cast<int>(state);
} else { } else {
return static_cast<int>(state) - custom_media_states_offset + return static_cast<int>(state) - custom_media_states_offset +
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE); static_cast<int>(
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE);
} }
} }
@@ -599,15 +607,15 @@ void MacroConditionMediaEdit::UpdateEntryData()
} }
switch (_entryData->_sourceType) { switch (_entryData->_sourceType) {
case MediaSourceType::ANY: case MacroConditionMedia::Type::ANY:
_mediaSources->setCurrentText(obs_module_text( _mediaSources->setCurrentText(obs_module_text(
"AdvSceneSwitcher.condition.media.anyOnScene")); "AdvSceneSwitcher.condition.media.anyOnScene"));
break; break;
case MediaSourceType::ALL: case MacroConditionMedia::Type::ALL:
_mediaSources->setCurrentText(obs_module_text( _mediaSources->setCurrentText(obs_module_text(
"AdvSceneSwitcher.condition.media.allOnScene")); "AdvSceneSwitcher.condition.media.allOnScene"));
break; break;
case MediaSourceType::SOURCE: case MacroConditionMedia::Type::SOURCE:
_mediaSources->setCurrentText( _mediaSources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str()); GetWeakSourceName(_entryData->_source).c_str());
break; break;
@@ -621,9 +629,9 @@ void MacroConditionMediaEdit::UpdateEntryData()
static_cast<int>(_entryData->_restriction)); static_cast<int>(_entryData->_restriction));
_time->SetDuration(_entryData->_time); _time->SetDuration(_entryData->_time);
if (_entryData->_restriction == if (_entryData->_restriction ==
MediaTimeRestriction::TIME_RESTRICTION_NONE) { MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
_time->setDisabled(true); _time->setDisabled(true);
} }
_onChange->setChecked(_entryData->_onlyMatchonChagne); _onChange->setChecked(_entryData->_onlyMatchOnChagne);
SetWidgetVisibility(); SetWidgetVisibility();
} }

View File

@@ -8,39 +8,8 @@
#include "duration-control.hpp" #include "duration-control.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
enum class MediaTimeRestriction {
TIME_RESTRICTION_NONE,
TIME_RESTRICTION_SHORTER,
TIME_RESTRICTION_LONGER,
TIME_RESTRICTION_REMAINING_SHORTER,
TIME_RESTRICTION_REMAINING_LONGER,
};
constexpr auto custom_media_states_offset = 100; constexpr auto custom_media_states_offset = 100;
enum class MediaState {
// OBS's internal states
OBS_MEDIA_STATE_NONE,
OBS_MEDIA_STATE_PLAYING,
OBS_MEDIA_STATE_OPENING,
OBS_MEDIA_STATE_BUFFERING,
OBS_MEDIA_STATE_PAUSED,
OBS_MEDIA_STATE_STOPPED,
OBS_MEDIA_STATE_ENDED,
OBS_MEDIA_STATE_ERROR,
// Just a marker
LAST_OBS_MEDIA_STATE,
// states added for use in the plugin
PLAYLIST_ENDED = custom_media_states_offset,
ANY,
};
enum class MediaSourceType {
SOURCE,
ANY,
ALL,
};
class MacroConditionMedia : public MacroCondition { class MacroConditionMedia : public MacroCondition {
public: public:
MacroConditionMedia(Macro *m) : MacroCondition(m) {} MacroConditionMedia(Macro *m) : MacroCondition(m) {}
@@ -56,25 +25,57 @@ public:
} }
void ClearSignalHandler(); void ClearSignalHandler();
void ResetSignalHandler(); void ResetSignalHandler();
void UpdateMediaSourcesOfSceneList();
static void MediaStopped(void *data, calldata_t *); static void MediaStopped(void *data, calldata_t *);
static void MediaEnded(void *data, calldata_t *); static void MediaEnded(void *data, calldata_t *);
static void MediaNext(void *data, calldata_t *); static void MediaNext(void *data, calldata_t *);
MediaSourceType _sourceType = MediaSourceType::SOURCE; enum class Type {
SOURCE,
ANY,
ALL,
};
Type _sourceType = Type::SOURCE;
enum class State {
// OBS's internal states
OBS_MEDIA_STATE_NONE,
OBS_MEDIA_STATE_PLAYING,
OBS_MEDIA_STATE_OPENING,
OBS_MEDIA_STATE_BUFFERING,
OBS_MEDIA_STATE_PAUSED,
OBS_MEDIA_STATE_STOPPED,
OBS_MEDIA_STATE_ENDED,
OBS_MEDIA_STATE_ERROR,
// Just a marker
LAST_OBS_MEDIA_STATE,
// states added for use in the plugin
PLAYLIST_ENDED = custom_media_states_offset,
ANY,
};
State _state = State::OBS_MEDIA_STATE_NONE;
enum class Time {
TIME_RESTRICTION_NONE,
TIME_RESTRICTION_SHORTER,
TIME_RESTRICTION_LONGER,
TIME_RESTRICTION_REMAINING_SHORTER,
TIME_RESTRICTION_REMAINING_LONGER,
};
Time _restriction = Time::TIME_RESTRICTION_NONE;
SceneSelection _scene; SceneSelection _scene;
OBSWeakSource _source = nullptr; OBSWeakSource _source = nullptr;
std::vector<MacroConditionMedia> _sources; std::vector<MacroConditionMedia> _sources;
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
MediaTimeRestriction _restriction =
MediaTimeRestriction::TIME_RESTRICTION_NONE;
Duration _time; Duration _time;
bool _onlyMatchonChagne = false; bool _onlyMatchOnChagne = false;
private: private:
bool CheckTime(); bool CheckTime();
bool CheckState(); bool CheckState();
bool CheckPlaylistEnd(const obs_media_state); bool CheckPlaylistEnd(const obs_media_state);
bool CheckMediaMatch(); bool CheckMediaMatch();
void HandleSceneChange();
bool _stopped = false; bool _stopped = false;
bool _ended = false; bool _ended = false;
@@ -84,6 +85,8 @@ private:
bool _alreadyMatched = false; bool _alreadyMatched = false;
// Workaround to enable use of "ended" to specify end of VLC playlist // Workaround to enable use of "ended" to specify end of VLC playlist
bool _previousStateEnded = false; bool _previousStateEnded = false;
// Used to keep track of scene changes
OBSWeakSource _lastConfigureScene;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;

View File

@@ -48,36 +48,48 @@ std::string MacroConditionProcess::GetShortDesc()
MacroConditionProcessEdit::MacroConditionProcessEdit( MacroConditionProcessEdit::MacroConditionProcessEdit(
QWidget *parent, std::shared_ptr<MacroConditionProcess> entryData) QWidget *parent, std::shared_ptr<MacroConditionProcess> entryData)
: QWidget(parent) : QWidget(parent),
_processSelection(new QComboBox()),
_focused(new QCheckBox()),
_focusProcess(new QLabel()),
_focusLayout(new QHBoxLayout())
{ {
_processSelection = new QComboBox();
_processSelection->setEditable(true); _processSelection->setEditable(true);
_processSelection->setMaxVisibleItems(20); _processSelection->setMaxVisibleItems(20);
_focused = new QCheckBox();
QWidget::connect(_processSelection, QWidget::connect(_processSelection,
SIGNAL(currentTextChanged(const QString &)), this, SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ProcessChanged(const QString &))); SLOT(ProcessChanged(const QString &)));
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this, QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int))); SLOT(FocusChanged(int)));
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateFocusProcess()));
populateProcessSelection(_processSelection); populateProcessSelection(_processSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{processes}}", _processSelection}, {"{{processes}}", _processSelection},
{"{{focused}}", _focused}, {"{{focused}}", _focused},
{"{{focusProcess}}", _focusProcess},
}; };
QHBoxLayout *mainLayout = new QHBoxLayout; auto entryLayout = new QHBoxLayout;
placeWidgets( placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.process.entry"), obs_module_text("AdvSceneSwitcher.condition.process.entry"),
mainLayout, widgetPlaceholders); entryLayout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.process.entry.focus"),
_focusLayout, widgetPlaceholders);
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addLayout(_focusLayout);
setLayout(mainLayout); setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
_loading = false; _loading = false;
_timer.start(1000);
} }
void MacroConditionProcessEdit::ProcessChanged(const QString &text) void MacroConditionProcessEdit::ProcessChanged(const QString &text)
@@ -100,6 +112,22 @@ void MacroConditionProcessEdit::FocusChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_focus = state; _entryData->_focus = state;
SetWidgetVisibility();
}
void MacroConditionProcessEdit::UpdateFocusProcess()
{
_focusProcess->setText(
QString::fromStdString(switcher->currentForegroundProcess));
}
void MacroConditionProcessEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
setLayoutVisible(_focusLayout, _entryData->_focus);
adjustSize();
} }
void MacroConditionProcessEdit::UpdateEntryData() void MacroConditionProcessEdit::UpdateEntryData()
@@ -110,4 +138,5 @@ void MacroConditionProcessEdit::UpdateEntryData()
_processSelection->setCurrentText(_entryData->_process.c_str()); _processSelection->setCurrentText(_entryData->_process.c_str());
_focused->setChecked(_entryData->_focus); _focused->setChecked(_entryData->_focus);
SetWidgetVisibility();
} }

View File

@@ -44,14 +44,20 @@ public:
private slots: private slots:
void ProcessChanged(const QString &text); void ProcessChanged(const QString &text);
void FocusChanged(int state); void FocusChanged(int state);
void UpdateFocusProcess();
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_processSelection; QComboBox *_processSelection;
QCheckBox *_focused; QCheckBox *_focused;
QLabel *_focusProcess;
QHBoxLayout *_focusLayout;
QTimer _timer;
std::shared_ptr<MacroConditionProcess> _entryData; std::shared_ptr<MacroConditionProcess> _entryData;
private: private:
void SetWidgetVisibility();
bool _loading = true; bool _loading = true;
}; };

View File

@@ -13,7 +13,7 @@ bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
"AdvSceneSwitcher.condition.window"}); "AdvSceneSwitcher.condition.window"});
bool MacroConditionWindow::CheckWindowTitleSwitchDirect( bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
std::string &currentWindowTitle) const std::string &currentWindowTitle)
{ {
bool focus = (!_focus || _window == currentWindowTitle); bool focus = (!_focus || _window == currentWindowTitle);
bool fullscreen = (!_fullscreen || isFullscreen(_window)); bool fullscreen = (!_fullscreen || isFullscreen(_window));
@@ -23,7 +23,8 @@ bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
} }
bool MacroConditionWindow::CheckWindowTitleSwitchRegex( bool MacroConditionWindow::CheckWindowTitleSwitchRegex(
std::string &currentWindowTitle, std::vector<std::string> &windowList) const std::string &currentWindowTitle,
const std::vector<std::string> &windowList)
{ {
bool match = false; bool match = false;
for (auto &window : windowList) { for (auto &window : windowList) {
@@ -54,7 +55,7 @@ bool foregroundWindowChanged()
bool MacroConditionWindow::CheckCondition() bool MacroConditionWindow::CheckCondition()
{ {
std::string currentWindowTitle = switcher->currentTitle; const std::string &currentWindowTitle = switcher->currentTitle;
std::vector<std::string> windowList; std::vector<std::string> windowList;
GetWindowList(windowList); GetWindowList(windowList);
@@ -101,17 +102,18 @@ std::string MacroConditionWindow::GetShortDesc()
MacroConditionWindowEdit::MacroConditionWindowEdit( MacroConditionWindowEdit::MacroConditionWindowEdit(
QWidget *parent, std::shared_ptr<MacroConditionWindow> entryData) QWidget *parent, std::shared_ptr<MacroConditionWindow> entryData)
: QWidget(parent) : QWidget(parent),
_windowSelection(new QComboBox()),
_fullscreen(new QCheckBox()),
_maximized(new QCheckBox()),
_focused(new QCheckBox()),
_windowFocusChanged(new QCheckBox()),
_focusWindow(new QLabel()),
_focusLayout(new QHBoxLayout())
{ {
_windowSelection = new QComboBox();
_windowSelection->setEditable(true); _windowSelection->setEditable(true);
_windowSelection->setMaxVisibleItems(20); _windowSelection->setMaxVisibleItems(20);
_fullscreen = new QCheckBox();
_maximized = new QCheckBox();
_focused = new QCheckBox();
_windowFocusChanged = new QCheckBox();
QWidget::connect(_windowSelection, QWidget::connect(_windowSelection,
SIGNAL(currentTextChanged(const QString &)), this, SIGNAL(currentTextChanged(const QString &)), this,
SLOT(WindowChanged(const QString &))); SLOT(WindowChanged(const QString &)));
@@ -123,6 +125,8 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
SLOT(FocusedChanged(int))); SLOT(FocusedChanged(int)));
QWidget::connect(_windowFocusChanged, SIGNAL(stateChanged(int)), this, QWidget::connect(_windowFocusChanged, SIGNAL(stateChanged(int)), this,
SLOT(WindowFocusChanged(int))); SLOT(WindowFocusChanged(int)));
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateFocusWindow()));
populateWindowSelection(_windowSelection); populateWindowSelection(_windowSelection);
@@ -132,24 +136,31 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
{"{{maximized}}", _maximized}, {"{{maximized}}", _maximized},
{"{{focused}}", _focused}, {"{{focused}}", _focused},
{"{{windowFocusChanged}}", _windowFocusChanged}, {"{{windowFocusChanged}}", _windowFocusChanged},
{"{{focusWindow}}", _focusWindow},
}; };
QVBoxLayout *mainLayout = new QVBoxLayout; auto *line1Layout = new QHBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
placeWidgets(obs_module_text( placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.window.entry.line1"), "AdvSceneSwitcher.condition.window.entry.line1"),
line1Layout, widgetPlaceholders); line1Layout, widgetPlaceholders);
auto *line2Layout = new QHBoxLayout;
placeWidgets(obs_module_text( placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.window.entry.line2"), "AdvSceneSwitcher.condition.window.entry.line2"),
line2Layout, widgetPlaceholders); line2Layout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.window.entry.line3"),
_focusLayout, widgetPlaceholders);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout); mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout); mainLayout->addLayout(line2Layout);
mainLayout->addLayout(_focusLayout);
setLayout(mainLayout); setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
_loading = false; _loading = false;
_timer.start(1000);
} }
void MacroConditionWindowEdit::WindowChanged(const QString &text) void MacroConditionWindowEdit::WindowChanged(const QString &text)
@@ -192,6 +203,7 @@ void MacroConditionWindowEdit::FocusedChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_focus = state; _entryData->_focus = state;
SetWidgetVisibility();
} }
void MacroConditionWindowEdit::WindowFocusChanged(int state) void MacroConditionWindowEdit::WindowFocusChanged(int state)
@@ -202,6 +214,22 @@ void MacroConditionWindowEdit::WindowFocusChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_windowFocusChanged = state; _entryData->_windowFocusChanged = state;
SetWidgetVisibility();
}
void MacroConditionWindowEdit::UpdateFocusWindow()
{
_focusWindow->setText(QString::fromStdString(switcher->currentTitle));
}
void MacroConditionWindowEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
setLayoutVisible(_focusLayout,
_entryData->_focus || _entryData->_windowFocusChanged);
adjustSize();
} }
void MacroConditionWindowEdit::UpdateEntryData() void MacroConditionWindowEdit::UpdateEntryData()
@@ -215,4 +243,5 @@ void MacroConditionWindowEdit::UpdateEntryData()
_maximized->setChecked(_entryData->_maximized); _maximized->setChecked(_entryData->_maximized);
_focused->setChecked(_entryData->_focus); _focused->setChecked(_entryData->_focus);
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged); _windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
SetWidgetVisibility();
} }

View File

@@ -18,9 +18,11 @@ public:
} }
private: private:
bool CheckWindowTitleSwitchDirect(std::string &currentWindowTitle); bool
bool CheckWindowTitleSwitchRegex(std::string &currentWindowTitle, CheckWindowTitleSwitchDirect(const std::string &currentWindowTitle);
std::vector<std::string> &windowList); bool
CheckWindowTitleSwitchRegex(const std::string &currentWindowTitle,
const std::vector<std::string> &windowList);
public: public:
std::string _window; std::string _window;
@@ -56,6 +58,7 @@ private slots:
void MaximizedChanged(int state); void MaximizedChanged(int state);
void FocusedChanged(int state); void FocusedChanged(int state);
void WindowFocusChanged(int state); void WindowFocusChanged(int state);
void UpdateFocusWindow();
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -65,8 +68,13 @@ protected:
QCheckBox *_maximized; QCheckBox *_maximized;
QCheckBox *_focused; QCheckBox *_focused;
QCheckBox *_windowFocusChanged; QCheckBox *_windowFocusChanged;
QLabel *_focusWindow;
QHBoxLayout *_focusLayout;
QTimer _timer;
std::shared_ptr<MacroConditionWindow> _entryData; std::shared_ptr<MacroConditionWindow> _entryData;
private: private:
void SetWidgetVisibility();
bool _loading = true; bool _loading = true;
}; };

View File

@@ -33,6 +33,8 @@ static std::map<VideoCondition, std::string> conditionTypes = {
"AdvSceneSwitcher.condition.video.condition.pattern"}, "AdvSceneSwitcher.condition.video.condition.pattern"},
{VideoCondition::OBJECT, {VideoCondition::OBJECT,
"AdvSceneSwitcher.condition.video.condition.object"}, "AdvSceneSwitcher.condition.video.condition.object"},
{VideoCondition::BRIGHTNESS,
"AdvSceneSwitcher.condition.video.condition.brightness"},
}; };
cv::CascadeClassifier initObjectCascade(std::string &path) cv::CascadeClassifier initObjectCascade(std::string &path)
@@ -116,6 +118,7 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
_usePatternForChangedCheck); _usePatternForChangedCheck);
obs_data_set_double(obj, "threshold", _patternThreshold); obs_data_set_double(obj, "threshold", _patternThreshold);
obs_data_set_bool(obj, "useAlphaAsMask", _useAlphaAsMask); obs_data_set_bool(obj, "useAlphaAsMask", _useAlphaAsMask);
obs_data_set_double(obj, "brightness", _brightnessThreshold);
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str()); obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
obs_data_set_double(obj, "scaleFactor", _scaleFactor); obs_data_set_double(obj, "scaleFactor", _scaleFactor);
obs_data_set_int(obj, "minNeighbors", _minNeighbors); obs_data_set_int(obj, "minNeighbors", _minNeighbors);
@@ -155,6 +158,7 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
obs_data_get_bool(obj, "usePatternForChangedCheck"); obs_data_get_bool(obj, "usePatternForChangedCheck");
_patternThreshold = obs_data_get_double(obj, "threshold"); _patternThreshold = obs_data_get_double(obj, "threshold");
_useAlphaAsMask = obs_data_get_bool(obj, "useAlphaAsMask"); _useAlphaAsMask = obs_data_get_bool(obj, "useAlphaAsMask");
_brightnessThreshold = obs_data_get_double(obj, "brightness");
_modelDataPath = obs_data_get_string(obj, "modelDataPath"); _modelDataPath = obs_data_get_string(obj, "modelDataPath");
_scaleFactor = obs_data_get_double(obj, "scaleFactor"); _scaleFactor = obs_data_get_double(obj, "scaleFactor");
if (!isScaleFactorValid(_scaleFactor)) { if (!isScaleFactorValid(_scaleFactor)) {
@@ -255,6 +259,12 @@ bool MacroConditionVideo::ScreenshotContainsObject()
return objects.size() > 0; return objects.size() > 0;
} }
bool MacroConditionVideo::CheckBrightnessThreshold()
{
_currentBrigthness = getAvgBrightness(_screenshotData.image) / 255.;
return _currentBrigthness > _brightnessThreshold;
}
bool MacroConditionVideo::Compare() bool MacroConditionVideo::Compare()
{ {
if (_checkAreaEnable && _condition != VideoCondition::NO_IMAGE) { if (_checkAreaEnable && _condition != VideoCondition::NO_IMAGE) {
@@ -278,6 +288,8 @@ bool MacroConditionVideo::Compare()
return ScreenshotContainsPattern(); return ScreenshotContainsPattern();
case VideoCondition::OBJECT: case VideoCondition::OBJECT:
return ScreenshotContainsObject(); return ScreenshotContainsObject();
case VideoCondition::BRIGHTNESS:
return CheckBrightnessThreshold();
default: default:
break; break;
} }
@@ -309,6 +321,13 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
"AdvSceneSwitcher.condition.video.patternThresholdDescription"))), "AdvSceneSwitcher.condition.video.patternThresholdDescription"))),
_useAlphaAsMask(new QCheckBox(obs_module_text( _useAlphaAsMask(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"))), "AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"))),
_brightnessThreshold(new ThresholdSlider(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.brightnessThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.brightnessThresholdDescription"))),
_currentBrightness(new QLabel),
_modelDataPath(new FileSelection()), _modelDataPath(new FileSelection()),
_modelPathLayout(new QHBoxLayout), _modelPathLayout(new QHBoxLayout),
_objectScaleThreshold(new ThresholdSlider( _objectScaleThreshold(new ThresholdSlider(
@@ -366,6 +385,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
this, SLOT(PatternThresholdChanged(double))); this, SLOT(PatternThresholdChanged(double)));
QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this, QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this,
SLOT(UseAlphaAsMaskChanged(int))); SLOT(UseAlphaAsMaskChanged(int)));
QWidget::connect(_brightnessThreshold,
SIGNAL(DoubleValueChanged(double)), this,
SLOT(BrightnessThresholdChanged(double)));
QWidget::connect(_objectScaleThreshold, QWidget::connect(_objectScaleThreshold,
SIGNAL(DoubleValueChanged(double)), this, SIGNAL(DoubleValueChanged(double)), this,
SLOT(ObjectScaleThresholdChanged(double))); SLOT(ObjectScaleThresholdChanged(double)));
@@ -451,6 +473,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
mainLayout->addWidget(_usePatternForChangedCheck); mainLayout->addWidget(_usePatternForChangedCheck);
mainLayout->addWidget(_patternThreshold); mainLayout->addWidget(_patternThreshold);
mainLayout->addWidget(_useAlphaAsMask); mainLayout->addWidget(_useAlphaAsMask);
mainLayout->addWidget(_brightnessThreshold);
mainLayout->addWidget(_currentBrightness);
mainLayout->addLayout(_modelPathLayout); mainLayout->addLayout(_modelPathLayout);
mainLayout->addWidget(_objectScaleThreshold); mainLayout->addWidget(_objectScaleThreshold);
mainLayout->addLayout(_neighborsControlLayout); mainLayout->addLayout(_neighborsControlLayout);
@@ -465,6 +489,10 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
_loading = false; _loading = false;
connect(&_updateBrightnessTimer, &QTimer::timeout, this,
&MacroConditionVideoEdit::UpdateCurrentBrightness);
_updateBrightnessTimer.start(1000);
} }
void MacroConditionVideoEdit::UpdatePreviewTooltip() void MacroConditionVideoEdit::UpdatePreviewTooltip()
@@ -673,6 +701,16 @@ void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
_entryData->LoadImageFromFile(); _entryData->LoadImageFromFile();
} }
void MacroConditionVideoEdit::BrightnessThresholdChanged(double value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_brightnessThreshold = value;
}
void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value) void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
@@ -774,6 +812,14 @@ void MacroConditionVideoEdit::ShowMatchClicked()
_previewDialog.ShowMatch(); _previewDialog.ShowMatch();
} }
void MacroConditionVideoEdit::UpdateCurrentBrightness()
{
QString text = obs_module_text(
"AdvSceneSwitcher.condition.video.currentBrightness");
_currentBrightness->setText(
text.arg(_entryData->GetCurrentBrightness()));
}
void MacroConditionVideoEdit::SelectAreaClicked() void MacroConditionVideoEdit::SelectAreaClicked()
{ {
_previewDialog.show(); _previewDialog.show();
@@ -843,6 +889,10 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
_patternThreshold->setVisible(needsThreshold(_entryData->_condition)); _patternThreshold->setVisible(needsThreshold(_entryData->_condition));
_useAlphaAsMask->setVisible(_entryData->_condition == _useAlphaAsMask->setVisible(_entryData->_condition ==
VideoCondition::PATTERN); VideoCondition::PATTERN);
_brightnessThreshold->setVisible(_entryData->_condition ==
VideoCondition::BRIGHTNESS);
_currentBrightness->setVisible(_entryData->_condition ==
VideoCondition::BRIGHTNESS);
_showMatch->setVisible(needsShowMatch(_entryData->_condition)); _showMatch->setVisible(needsShowMatch(_entryData->_condition));
_objectScaleThreshold->setVisible( _objectScaleThreshold->setVisible(
needsObjectControls(_entryData->_condition)); needsObjectControls(_entryData->_condition));
@@ -884,6 +934,7 @@ void MacroConditionVideoEdit::UpdateEntryData()
_entryData->_usePatternForChangedCheck); _entryData->_usePatternForChangedCheck);
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold); _patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask); _useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
_brightnessThreshold->SetDoubleValue(_entryData->_brightnessThreshold);
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str()); _modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor); _objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
_minNeighbors->setValue(_entryData->_minNeighbors); _minNeighbors->setValue(_entryData->_minNeighbors);

View File

@@ -25,6 +25,7 @@ enum class VideoCondition {
NO_IMAGE, NO_IMAGE,
PATTERN, PATTERN,
OBJECT, OBJECT,
BRIGHTNESS,
}; };
class MacroConditionVideo : public MacroCondition { class MacroConditionVideo : public MacroCondition {
@@ -45,6 +46,7 @@ public:
bool LoadModelData(std::string &path); bool LoadModelData(std::string &path);
std::string GetModelDataPath() { return _modelDataPath; } std::string GetModelDataPath() { return _modelDataPath; }
void ResetLastMatch() { _lastMatchResult = false; } void ResetLastMatch() { _lastMatchResult = false; }
double GetCurrentBrightness() { return _currentBrigthness; }
VideoSelection _video; VideoSelection _video;
VideoCondition _condition = VideoCondition::MATCH; VideoCondition _condition = VideoCondition::MATCH;
@@ -60,6 +62,7 @@ public:
bool _usePatternForChangedCheck = false; bool _usePatternForChangedCheck = false;
PatternMatchData _patternData; PatternMatchData _patternData;
double _patternThreshold = 0.8; double _patternThreshold = 0.8;
double _brightnessThreshold = 0.5;
cv::CascadeClassifier _objectCascade; cv::CascadeClassifier _objectCascade;
double _scaleFactor = 1.1; double _scaleFactor = 1.1;
int _minNeighbors = minMinNeighbors; int _minNeighbors = minMinNeighbors;
@@ -76,6 +79,7 @@ private:
bool OutputChanged(); bool OutputChanged();
bool ScreenshotContainsPattern(); bool ScreenshotContainsPattern();
bool ScreenshotContainsObject(); bool ScreenshotContainsObject();
bool CheckBrightnessThreshold();
bool Compare(); bool Compare();
bool CheckShouldBeSkipped(); bool CheckShouldBeSkipped();
@@ -88,6 +92,7 @@ private:
"/res/cascadeClassifiers/haarcascade_frontalface_alt.xml"); "/res/cascadeClassifiers/haarcascade_frontalface_alt.xml");
bool _lastMatchResult = false; bool _lastMatchResult = false;
int _runCount = 0; int _runCount = 0;
double _currentBrigthness = 0.;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
@@ -123,6 +128,8 @@ private slots:
void PatternThresholdChanged(double); void PatternThresholdChanged(double);
void UseAlphaAsMaskChanged(int value); void UseAlphaAsMaskChanged(int value);
void BrightnessThresholdChanged(double);
void ModelPathChanged(const QString &text); void ModelPathChanged(const QString &text);
void ObjectScaleThresholdChanged(double); void ObjectScaleThresholdChanged(double);
void MinNeighborsChanged(int value); void MinNeighborsChanged(int value);
@@ -137,6 +144,8 @@ private slots:
void ThrottleEnableChanged(int value); void ThrottleEnableChanged(int value);
void ThrottleCountChanged(int value); void ThrottleCountChanged(int value);
void ShowMatchClicked(); void ShowMatchClicked();
void UpdateCurrentBrightness();
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -152,6 +161,9 @@ protected:
ThresholdSlider *_patternThreshold; ThresholdSlider *_patternThreshold;
QCheckBox *_useAlphaAsMask; QCheckBox *_useAlphaAsMask;
ThresholdSlider *_brightnessThreshold;
QLabel *_currentBrightness;
FileSelection *_modelDataPath; FileSelection *_modelDataPath;
QHBoxLayout *_modelPathLayout; QHBoxLayout *_modelPathLayout;
ThresholdSlider *_objectScaleThreshold; ThresholdSlider *_objectScaleThreshold;
@@ -177,6 +189,8 @@ protected:
std::shared_ptr<MacroConditionVideo> _entryData; std::shared_ptr<MacroConditionVideo> _entryData;
private: private:
QTimer _updateBrightnessTimer;
void SetWidgetVisibility(); void SetWidgetVisibility();
bool _loading = true; bool _loading = true;
}; };

View File

@@ -77,6 +77,21 @@ std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
return objects; return objects;
} }
uchar getAvgBrightness(QImage &img)
{
auto i = QImageToMat(img);
cv::Mat hsvImage, rgbImage;
cv::cvtColor(i, rgbImage, cv::COLOR_RGBA2RGB);
cv::cvtColor(rgbImage, hsvImage, cv::COLOR_RGB2HSV);
long long brightnessSum = 0;
for (int i = 0; i < hsvImage.rows; ++i) {
for (int j = 0; j < hsvImage.cols; ++j) {
brightnessSum += hsvImage.at<cv::Vec3b>(i, j)[2];
}
}
return brightnessSum / (hsvImage.rows * hsvImage.cols);
}
// Assumption is that QImage uses Format_RGBA8888. // Assumption is that QImage uses Format_RGBA8888.
// Conversion from: https://github.com/dbzhang800/QtOpenCV // Conversion from: https://github.com/dbzhang800/QtOpenCV
cv::Mat QImageToMat(const QImage &img) cv::Mat QImageToMat(const QImage &img)

View File

@@ -20,5 +20,6 @@ void matchPattern(QImage &img, QImage &pattern, double threshold,
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade, std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors, double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize); cv::Size minSize, cv::Size maxSize);
uchar getAvgBrightness(QImage &img);
cv::Mat QImageToMat(const QImage &img); cv::Mat QImageToMat(const QImage &img);
QImage MatToQImage(const cv::Mat &mat); QImage MatToQImage(const cv::Mat &mat);

View File

@@ -288,10 +288,38 @@ void GetProcessList(QStringList &list)
} }
} }
void GetForegroundProcessName(std::string &proc)
{
proc.resize(0);
@autoreleasepool {
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSArray *array = [ws runningApplications];
for (NSRunningApplication *app in array) {
if (!app.isActive) {
continue;
}
NSString *name = app.localizedName;
if (!name) {
break;
}
const char *str = name.UTF8String;
proc = std::string(str);
break;
}
}
}
void GetForegroundProcessName(QString &proc)
{
std::string temp;
GetForegroundProcessName(temp);
proc = QString::fromStdString(temp);
}
bool isInFocus(const QString &executable) bool isInFocus(const QString &executable)
{ {
std::string current; std::string current;
GetCurrentWindowTitle(current); GetForegroundProcessName(current);
// True if executable switch equals current window // True if executable switch equals current window
bool equals = (executable.toStdString() == current); bool equals = (executable.toStdString() == current);

View File

@@ -13,6 +13,7 @@ bool isMaximized(const std::string &title);
std::pair<int, int> getCursorPos(); std::pair<int, int> getCursorPos();
int secondsSinceLastInput(); int secondsSinceLastInput();
void GetProcessList(QStringList &processes); void GetProcessList(QStringList &processes);
void GetForegroundProcessName(std::string &name);
bool isInFocus(const QString &executable); bool isInFocus(const QString &executable);
void PressKeys(const std::vector<HotkeyType> keys, int duration); void PressKeys(const std::vector<HotkeyType> keys, int duration);
void PlatformInit(); void PlatformInit();

View File

@@ -148,6 +148,7 @@ struct SwitcherData {
std::vector<std::string> ignoreIdleWindows; std::vector<std::string> ignoreIdleWindows;
std::string lastTitle; std::string lastTitle;
std::string currentTitle; std::string currentTitle;
std::string currentForegroundProcess;
std::deque<ScreenRegionSwitch> screenRegionSwitches; std::deque<ScreenRegionSwitch> screenRegionSwitches;
std::pair<int, int> lastCursorPos = {0, 0}; std::pair<int, int> lastCursorPos = {0, 0};

View File

@@ -13,7 +13,7 @@ void SceneItemSelection::Save(obs_data_t *obj, const char *name) const
obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type)); obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type));
obs_data_set_int(data, idxTypeSaveName.data(), obs_data_set_int(data, idxTypeSaveName.data(),
static_cast<int>(_idxType)); static_cast<int>(_idxType));
if (_idxType != IdxType::INDIVIDUAL) { if (_idxType == IdxType::INDIVIDUAL) {
obs_data_set_int(data, idxSaveName.data(), _idx); obs_data_set_int(data, idxSaveName.data(), _idx);
} else { } else {
obs_data_set_int(data, idxSaveName.data(), 0); obs_data_set_int(data, idxSaveName.data(), 0);

View File

@@ -238,7 +238,7 @@ void GetProcessList(QStringList &processes)
CloseHandle(procSnapshot); CloseHandle(procSnapshot);
} }
bool isInFocus(const QString &executable) void GetForegroundProcessName(QString &proc)
{ {
// only checks if the current foreground window is from the same executable, // only checks if the current foreground window is from the same executable,
// may return true for any window from a program // may return true for any window from a program
@@ -249,21 +249,36 @@ bool isInFocus(const QString &executable)
HANDLE process = OpenProcess( HANDLE process = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (process == NULL) { if (process == NULL) {
return false; return;
} }
WCHAR executablePath[600]; WCHAR executablePath[600];
GetModuleFileNameEx(process, 0, executablePath, 600); GetModuleFileNameEx(process, 0, executablePath, 600);
CloseHandle(process); CloseHandle(process);
QString file = QString::fromWCharArray(executablePath) proc = QString::fromWCharArray(executablePath)
.split(QRegularExpression("(/|\\\\)")) .split(QRegularExpression("(/|\\\\)"))
.back(); .back();
}
void GetForegroundProcessName(std::string &proc)
{
QString temp;
GetForegroundProcessName(temp);
proc = temp.toStdString();
}
bool isInFocus(const QString &executable)
{
// only checks if the current foreground window is from the same executable,
// may return true for any window from a program
QString foregroundProc;
GetForegroundProcessName(foregroundProc);
// True if executable switch equals current window // True if executable switch equals current window
bool equals = (executable == file); bool equals = (executable == foregroundProc);
// True if executable switch matches current window // True if executable switch matches current window
bool matches = file.contains(QRegularExpression(executable)); bool matches = foregroundProc.contains(QRegularExpression(executable));
return (equals || matches); return (equals || matches);
} }