Compare commits

...

14 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
WarmUpTill
8b8829f7df Fix scene item selection not returning expected number of items 2022-10-04 23:35:58 -07:00
WarmUpTill
6fff3b54e6 Remove version number from dep package name 2022-10-04 12:01:47 -07:00
32 changed files with 705 additions and 319 deletions

View File

@@ -92,21 +92,21 @@ jobs:
if [[ '${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}' != '' && \
'${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}' != '' && \
'${{ secrets.MACOS_SIGNING_CERT }}' != '' ]] {
print '::set-output name=haveCodesignIdent::true'
print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT
} else {
print '::set-output name=haveCodesignIdent::false'
print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT
}
if [[ '${{ secrets.MACOS_NOTARIZATION_USERNAME }}' != '' && \
'${{ secrets.MACOS_NOTARIZATION_PASSWORD }}' != '' ]] {
print '::set-output name=haveNotarizationUser::true'
print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT
} else {
print '::set-output name=haveNotarizationUser::false'
print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT
}
print '::endgroup::'
print "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
print "::set-output name=commitHash::${"$(git rev-parse HEAD)"[0,9]}"
print "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
print "commitHash=${"$(git rev-parse HEAD)"[0,9]}" >> $GITHUB_OUTPUT
echo "$PWD/.github/scripts" >> $GITHUB_PATH
- name: Restore Compilation Cache
@@ -123,9 +123,9 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
run: |
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 {
print '::set-output name=found::false'
print 'found=false' >> $GITHUB_OUTPUT
}
- name: Install Apple Developer Certificate
@@ -206,8 +206,8 @@ jobs:
id: setup
run: |
## SETUP ENVIRONMENT SCRIPT
echo "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
echo "::set-output name=commitHash::$(git rev-parse HEAD | cut -c1-9)"
echo "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
echo "commitHash=$(git rev-parse HEAD | cut -c1-9)" >> $GITHUB_OUTPUT
echo "$PWD/.github/scripts" >> $GITHUB_PATH
- name: Restore Compilation Cache
@@ -225,9 +225,9 @@ jobs:
run: |
## 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
echo '::set-output name=found::true'
echo 'found=true' >> $GITHUB_OUTPUT
else
echo '::set-output name=found::false'
echo 'found=false' >> $GITHUB_OUTPUT
fi
- name: Build Plugin
@@ -295,7 +295,7 @@ jobs:
run: |
## SETUP ENVIRONMENT SCRIPT
$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
id: seekingTesters
@@ -316,7 +316,7 @@ jobs:
$false
}
Write-Output "::set-output name=found::$(([string]${LabelFound}).ToLower())"
"found=$(([string]${LabelFound}).ToLower())" >> $env:GITHUB_OUTPUT
- name: Build Plugin
uses: ./plugin/.github/actions/build-plugin
@@ -367,7 +367,7 @@ jobs:
id: metadata
run: |
## METADATA SCRIPT
echo "::set-output name=version::${GITHUB_REF/refs\/tags\//}"
echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Download build artifacts
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
```
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)
```
@@ -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.
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.
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`.
The key functions to add conditions or are the Register() functions.
## Macro condition
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(
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`.
These are basically plugins themselves that get attempted to be loaded on startup of the advanced scene switcher.

View File

@@ -346,8 +346,7 @@ else()
set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${LINUX_MAINTAINER_EMAIL}")
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}")
set(CPACK_PACKAGE_FILE_NAME
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-linux-x86_64")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-linux-x86_64")
set(CPACK_GENERATOR "DEB")
if(${QT_VERSION} STREQUAL "6")

View File

@@ -1,5 +1,5 @@
#define MyAppName "@CMAKE_PROJECT_NAME@"
#define MyAppVersion "@CMAKE_PROJECT_VERSION@"
#define MyAppVersion "@GIT_TAG@"
#define MyAppPublisher "@PLUGIN_AUTHOR@"
#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.audio="Audio"
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
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.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"

View File

@@ -55,6 +55,11 @@ AdvSceneSwitcher.generalTab.priority.time="Time"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
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
AdvSceneSwitcher.macroTab.title="Macro"
@@ -128,6 +133,7 @@ AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
AdvSceneSwitcher.condition.window.entry.line3="Current foreground window: {{focusWindow}}"
AdvSceneSwitcher.condition.file="File"
AdvSceneSwitcher.condition.file.type.match="matches"
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.pattern="matches pattern"
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.file="Use existing file"
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.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.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.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."
@@ -196,6 +206,7 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="Idle"
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
AdvSceneSwitcher.condition.pluginState="Plugin state"
@@ -537,10 +548,6 @@ AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{
; Transition Tab
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.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"

View File

@@ -54,6 +54,10 @@ AdvSceneSwitcher.generalTab.priority.time="Tiempo"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.generalTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.generalTab.transitionOverride="Establecer anulaciones de transición"
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
AdvSceneSwitcher.macroTab.title="Macro"
@@ -478,10 +482,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="Continuar con el elemento selecci
; Transition Tab
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.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"

View File

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

View File

@@ -53,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Zaman"
AdvSceneSwitcher.generalTab.priority.audio="Ses"
AdvSceneSwitcher.generalTab.priority.video="Video"
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
AdvSceneSwitcher.macroTab.title="Makro"
@@ -391,10 +395,6 @@ AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyon
; Transition Tab
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.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"

View File

@@ -54,6 +54,9 @@ AdvSceneSwitcher.generalTab.priority.time="时间"
AdvSceneSwitcher.generalTab.priority.audio="音频"
AdvSceneSwitcher.generalTab.priority.video="视频"
AdvSceneSwitcher.generalTab.priority.macro="宏"
AdvSceneSwitcher.generalTab.setTransitionBy="更改转场特效时:"
AdvSceneSwitcher.generalTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="更改激活转场特效类型"
; Macro Tab
AdvSceneSwitcher.macroTab.title="宏"
@@ -437,9 +440,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="转场特效"
AdvSceneSwitcher.transitionTab.setTransitionBy="更改转场特效时:"
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特效类型"
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.defaultTransition="当切换到这个场景时修改默认转场特效"

View File

@@ -68,7 +68,7 @@
<x>0</x>
<y>0</y>
<width>957</width>
<height>817</height>
<height>905</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
@@ -419,6 +419,49 @@
</layout>
</widget>
</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>
<widget class="QGroupBox" name="priorityBox">
<property name="title">
@@ -610,7 +653,7 @@
<enum>Qt::MoveAction</enum>
</property>
<property name="spacing">
<number>1</number>
<number>0</number>
</property>
<property name="sortingEnabled">
<bool>false</bool>
@@ -763,7 +806,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_33">
<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>
<widget class="QLabel" name="label_20">
<property name="text">
@@ -783,16 +826,36 @@
</item>
<item>
<widget class="QCheckBox" name="runMacroInParallel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="runMacroOnChange">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
@@ -1710,44 +1773,6 @@
<string>AdvSceneSwitcher.transitionTab.title</string>
</attribute>
<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>
<widget class="QGroupBox" name="transitionsGroup">
<property name="title">

View File

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

View File

@@ -496,6 +496,39 @@ void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
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)
{
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, "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)
@@ -674,6 +714,11 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_set_default_int(obj, "threadPriority",
QThread::NormalPriority);
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)

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)
{
if (loading) {
@@ -335,13 +302,6 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
}
obs_data_set_array(obj, "defaultTransitions", 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",
default_def_transition_dealy);
obs_data_set_int(obj, "defTransitionDelay",
@@ -380,11 +340,6 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
}
obs_data_array_release(defaultTransitionsArray);
transitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
adjustActiveTransitionType =
obs_data_get_bool(obj, "adjustActiveTransitionType");
// Check for invalid config
if (!transitionOverrideOverride && !adjustActiveTransitionType) {
adjustActiveTransitionType = true;

View File

@@ -434,10 +434,24 @@ std::string getProcNameFromPid(int pid)
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();
std::string current = getProcNameFromPid(pid);
}
bool isInFocus(const QString &executable)
{
std::string current;
GetForegroundProcessName(current);
// True if executable switch equals current window
bool equals = (executable.toStdString() == current);

View File

@@ -67,6 +67,26 @@ bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
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 match = false;
@@ -92,11 +112,7 @@ bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
match = cur <= _dateTime;
break;
case DateCondition::BETWEEN:
if (_dateTime2 > _dateTime) {
match = cur >= _dateTime && cur <= _dateTime2;
} else {
match = cur >= _dateTime2 && cur <= _dateTime;
}
match = CheckBetween(cur);
break;
default:
break;
@@ -232,12 +248,12 @@ void MacroConditionDate::SetTime2(const QTime &time)
QDateTime MacroConditionDate::GetDateTime1()
{
return _updateOnRepeat ? _dateTime : _origDateTime;
return _repeat && _updateOnRepeat ? _dateTime : _origDateTime;
}
QDateTime MacroConditionDate::GetDateTime2()
{
return _updateOnRepeat ? _dateTime2 : _origDateTime2;
return _repeat && _updateOnRepeat ? _dateTime2 : _origDateTime2;
}
QDateTime MacroConditionDate::GetNextMatchDateTime()
@@ -576,12 +592,12 @@ void MacroConditionDateEdit::UpdateEntryData()
_dayOfWeek->setCurrentIndex(
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
_ignoreWeekTime->setChecked(!_entryData->_ignoreTime);
_weekTime->setDateTime(_entryData->GetDateTime1());
_weekTime->setTime(_entryData->GetDateTime1().time());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_date->setDateTime(_entryData->GetDateTime1());
_time->setDateTime(_entryData->GetDateTime1());
_date2->setDateTime(_entryData->GetDateTime2());
_time2->setDateTime(_entryData->GetDateTime2());
_date->setDate(_entryData->GetDateTime1().date());
_time->setTime(_entryData->GetDateTime1().time());
_date2->setDate(_entryData->GetDateTime2().date());
_time2->setTime(_entryData->GetDateTime2().time());
_ignoreDate->setChecked(!_entryData->_ignoreDate);
_ignoreTime->setChecked(!_entryData->_ignoreTime);
_repeat->setChecked(_entryData->_repeat);

View File

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

View File

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

View File

@@ -8,39 +8,8 @@
#include "duration-control.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;
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 {
public:
MacroConditionMedia(Macro *m) : MacroCondition(m) {}
@@ -56,25 +25,57 @@ public:
}
void ClearSignalHandler();
void ResetSignalHandler();
void UpdateMediaSourcesOfSceneList();
static void MediaStopped(void *data, calldata_t *);
static void MediaEnded(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;
OBSWeakSource _source = nullptr;
std::vector<MacroConditionMedia> _sources;
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
MediaTimeRestriction _restriction =
MediaTimeRestriction::TIME_RESTRICTION_NONE;
Duration _time;
bool _onlyMatchonChagne = false;
bool _onlyMatchOnChagne = false;
private:
bool CheckTime();
bool CheckState();
bool CheckPlaylistEnd(const obs_media_state);
bool CheckMediaMatch();
void HandleSceneChange();
bool _stopped = false;
bool _ended = false;
@@ -84,6 +85,8 @@ private:
bool _alreadyMatched = false;
// Workaround to enable use of "ended" to specify end of VLC playlist
bool _previousStateEnded = false;
// Used to keep track of scene changes
OBSWeakSource _lastConfigureScene;
static bool _registered;
static const std::string id;

View File

@@ -48,36 +48,48 @@ std::string MacroConditionProcess::GetShortDesc()
MacroConditionProcessEdit::MacroConditionProcessEdit(
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->setMaxVisibleItems(20);
_focused = new QCheckBox();
QWidget::connect(_processSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ProcessChanged(const QString &)));
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateFocusProcess()));
populateProcessSelection(_processSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{processes}}", _processSelection},
{"{{focused}}", _focused},
{"{{focusProcess}}", _focusProcess},
};
QHBoxLayout *mainLayout = new QHBoxLayout;
auto entryLayout = new QHBoxLayout;
placeWidgets(
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);
_entryData = entryData;
UpdateEntryData();
_loading = false;
_timer.start(1000);
}
void MacroConditionProcessEdit::ProcessChanged(const QString &text)
@@ -100,6 +112,22 @@ void MacroConditionProcessEdit::FocusChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m);
_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()
@@ -110,4 +138,5 @@ void MacroConditionProcessEdit::UpdateEntryData()
_processSelection->setCurrentText(_entryData->_process.c_str());
_focused->setChecked(_entryData->_focus);
SetWidgetVisibility();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -77,6 +77,21 @@ std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
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.
// Conversion from: https://github.com/dbzhang800/QtOpenCV
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,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize);
uchar getAvgBrightness(QImage &img);
cv::Mat QImageToMat(const QImage &img);
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)
{
std::string current;
GetCurrentWindowTitle(current);
GetForegroundProcessName(current);
// True if executable switch equals current window
bool equals = (executable.toStdString() == current);

View File

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

View File

@@ -148,6 +148,7 @@ struct SwitcherData {
std::vector<std::string> ignoreIdleWindows;
std::string lastTitle;
std::string currentTitle;
std::string currentForegroundProcess;
std::deque<ScreenRegionSwitch> screenRegionSwitches;
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, idxTypeSaveName.data(),
static_cast<int>(_idxType));
if (_idxType != IdxType::INDIVIDUAL) {
if (_idxType == IdxType::INDIVIDUAL) {
obs_data_set_int(data, idxSaveName.data(), _idx);
} else {
obs_data_set_int(data, idxSaveName.data(), 0);
@@ -160,7 +160,7 @@ SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
}
name = var->Value();
} else {
auto name = GetWeakSourceName(_sceneItem);
name = GetWeakSourceName(_sceneItem);
}
int count = getCountOfSceneItemOccurance(sceneSelection, name, false);
auto items = getSceneItemsWithName(scene, name);

View File

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