mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95537f39e4 | ||
|
|
0490c97ad9 | ||
|
|
4b17314c66 | ||
|
|
7459793fe3 | ||
|
|
8a39a859be | ||
|
|
f8a592f17a | ||
|
|
f824ac8f4e | ||
|
|
8e9323173a |
2
.github/workflows/build-debian.yml
vendored
2
.github/workflows/build-debian.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
|||||||
# devscripts and libobs-dev are needed but they were already installed
|
# devscripts and libobs-dev are needed but they were already installed
|
||||||
# from check_libobs_revision and install_frontend_header sections.
|
# from check_libobs_revision and install_frontend_header sections.
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev
|
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
|
||||||
- name: build
|
- name: build
|
||||||
run: |
|
run: |
|
||||||
debuild --no-lintian --no-sign
|
debuild --no-lintian --no-sign
|
||||||
|
|||||||
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -1,10 +1,6 @@
|
|||||||
name: build obs plugin
|
name: build obs plugin
|
||||||
|
|
||||||
on:
|
on: [push, pull_request, workflow_dispatch]
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PLUGIN_NAME: SceneSwitcher
|
PLUGIN_NAME: SceneSwitcher
|
||||||
@@ -183,7 +179,8 @@ jobs:
|
|||||||
libxss-dev \
|
libxss-dev \
|
||||||
libx11-xcb-dev \
|
libx11-xcb-dev \
|
||||||
libxcb-xfixes0-dev \
|
libxcb-xfixes0-dev \
|
||||||
libopencv-dev
|
libopencv-dev \
|
||||||
|
libprocps-dev
|
||||||
- name: 'Configure'
|
- name: 'Configure'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Note that you will have to use OBS version 26.1 or newer!
|
Note that you will have to use OBS version 27 or newer!
|
||||||
|
|
||||||
--- Windows ---
|
--- Windows ---
|
||||||
Recommended: Run the provided installer. (You might have to click 'More info' and select 'Run anyway' if it is blocked by Windows)
|
Recommended: Run the provided installer. (You might have to click 'More info' and select 'Run anyway' if it is blocked by Windows)
|
||||||
@@ -8,17 +8,17 @@ It is usually located at 'C:\Program Files (x86)\obs-studio\'.
|
|||||||
Remember to install the Visual C++ Redistributable for Visual Studio 2019, if you have not done so already. (See plugin overview page)
|
Remember to install the Visual C++ Redistributable for Visual Studio 2019, if you have not done so already. (See plugin overview page)
|
||||||
|
|
||||||
--- macOS ---
|
--- macOS ---
|
||||||
Recommended: Run the provided installer. (You might have to right-click or control-click and select 'open' if it is blocked)
|
Run the provided installer. (You might have to right-click or control-click and select 'open' if it is blocked)
|
||||||
|
|
||||||
Alternatively extract the *so file and data folder and either ...
|
|
||||||
|
|
||||||
... right-click the OBS app inside your Applications folder and choose 'Show Package Contents'.
|
|
||||||
Copy the advanced-scene-switcher.so file to 'Contents/Plugins' and the 'data' folder to 'Contents/Resources'.
|
|
||||||
|
|
||||||
... or copy the advanced-scene-switcher.so file to Library/Application Support/obs-studio/plugins/advanced-scene-switcher/bin/ .
|
|
||||||
And the 'data' folder to 'Library/Application Support/obs-studio/plugins/advanced-scene-switcher/'.
|
|
||||||
|
|
||||||
--- Linux ---
|
--- Linux ---
|
||||||
|
Note that the plugin has dependencies to:
|
||||||
|
* libXss
|
||||||
|
* libopencv-imgproc
|
||||||
|
* libopencv-objdetect
|
||||||
|
Optional:
|
||||||
|
* libXtst
|
||||||
|
* libcurl
|
||||||
|
|
||||||
Copy the advanced-scene-switcher.so file and into the OBS Studio plugin folder.
|
Copy the advanced-scene-switcher.so file and into the OBS Studio plugin folder.
|
||||||
The location of this folder can vary, so you might have to look around a bit.
|
The location of this folder can vary, so you might have to look around a bit.
|
||||||
|
|
||||||
|
|||||||
@@ -137,9 +137,20 @@ endif()
|
|||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
||||||
link_libraries(${X11_LIBRARIES})
|
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
||||||
|
if(NOT PROCPS_INCLUDE_DIR)
|
||||||
|
message(
|
||||||
|
FATAL_ERROR "procps include dir not found - please set PROCPS_INCLUDE_DIR"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
find_library(PROCPS_LIBRARY NAMES procps)
|
||||||
|
if(NOT PROCPS_LIBRARY)
|
||||||
|
message(FATAL_ERROR "procps lib not found - please set PROCPS_LIBRARY")
|
||||||
|
endif()
|
||||||
|
message (STATUS "WHAT THE FUCK IS GOING ON ${PROCPS_LIBRARY}")
|
||||||
|
link_libraries(${X11_LIBRARIES} ${procps_LIBRARIES})
|
||||||
include_directories("${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
|
include_directories("${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
|
||||||
"${X11_Xss_INCLUDE_PATH}")
|
"${X11_Xss_INCLUDE_PATH}" "${PROCPS_INCLUDE_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -154,7 +165,7 @@ elseif(APPLE)
|
|||||||
else()
|
else()
|
||||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
src/linux/advanced-scene-switcher-nix.cpp)
|
src/linux/advanced-scene-switcher-nix.cpp)
|
||||||
set(advanced-scene-switcher_PLATFORM_LIBS Xss)
|
set(advanced-scene-switcher_PLATFORM_LIBS Xss ${PROCPS_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# asio and websocketpp
|
# asio and websocketpp
|
||||||
@@ -401,11 +412,7 @@ if(BUILD_OUT_OF_TREE)
|
|||||||
else()
|
else()
|
||||||
# In tree build
|
# In tree build
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
advanced-scene-switcher
|
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
|
||||||
${advanced-scene-switcher_PLATFORM_LIBS}
|
${OpenCV_LIBRARIES} obs-frontend-api Qt5::Widgets libobs)
|
||||||
${OpenCV_LIBRARIES}
|
|
||||||
obs-frontend-api
|
|
||||||
Qt5::Widgets
|
|
||||||
libobs)
|
|
||||||
install_obs_plugin_with_data(advanced-scene-switcher data)
|
install_obs_plugin_with_data(advanced-scene-switcher data)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match do not switc
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
||||||
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||||
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
||||||
@@ -119,6 +120,8 @@ AdvSceneSwitcher.condition.video.condition.object="contains object"
|
|||||||
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"
|
||||||
|
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Use pattern matching"
|
||||||
|
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will allow you to control how much the image has to change for the condition to be true."
|
||||||
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."
|
||||||
@@ -139,6 +142,8 @@ AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU lo
|
|||||||
AdvSceneSwitcher.condition.stream="Streaming"
|
AdvSceneSwitcher.condition.stream="Streaming"
|
||||||
AdvSceneSwitcher.condition.stream.state.start="Stream running"
|
AdvSceneSwitcher.condition.stream.state.start="Stream running"
|
||||||
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
|
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
|
||||||
|
AdvSceneSwitcher.condition.stream.state.starting="Stream starting"
|
||||||
|
AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping"
|
||||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||||
AdvSceneSwitcher.condition.record="Recording"
|
AdvSceneSwitcher.condition.record="Recording"
|
||||||
AdvSceneSwitcher.condition.record.state.start="Recording running"
|
AdvSceneSwitcher.condition.record.state.start="Recording running"
|
||||||
@@ -614,6 +619,8 @@ AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task
|
|||||||
|
|
||||||
AdvSceneSwitcher.status.active="Active"
|
AdvSceneSwitcher.status.active="Active"
|
||||||
AdvSceneSwitcher.status.inactive="Inactive"
|
AdvSceneSwitcher.status.inactive="Inactive"
|
||||||
|
AdvSceneSwitcher.running="Plugin running"
|
||||||
|
AdvSceneSwitcher.stopped="Plugin stopped"
|
||||||
|
|
||||||
AdvSceneSwitcher.unit.milliseconds="milliseconds"
|
AdvSceneSwitcher.unit.milliseconds="milliseconds"
|
||||||
AdvSceneSwitcher.unit.secends="seconds"
|
AdvSceneSwitcher.unit.secends="seconds"
|
||||||
|
|||||||
@@ -306,6 +306,30 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="showTrayNotifications">
|
||||||
|
<property name="text">
|
||||||
|
<string>AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_48">
|
<layout class="QHBoxLayout" name="horizontalLayout_48">
|
||||||
<item>
|
<item>
|
||||||
@@ -355,8 +379,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_41">
|
<layout class="QHBoxLayout" name="horizontalLayout_41">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="priorityList">
|
<widget class="QListWidget" name="priorityList"/>
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
@@ -36,9 +37,16 @@ bool translationAvailable()
|
|||||||
void AdvSceneSwitcher::loadUI()
|
void AdvSceneSwitcher::loadUI()
|
||||||
{
|
{
|
||||||
if (!translationAvailable()) {
|
if (!translationAvailable()) {
|
||||||
(void)DisplayMessage(
|
QString msg = "Failed to find plug-in's 'data' directory.\n"
|
||||||
"Failed to find plug-in's 'data' directory.\n"
|
"Please check installation instructions!\n\n"
|
||||||
"Please check installation instructions!");
|
"Data most likely expected at:\n\n";
|
||||||
|
#ifdef _WIN32
|
||||||
|
msg += QString::fromStdString(
|
||||||
|
(std::filesystem::current_path().string()));
|
||||||
|
msg += "/";
|
||||||
|
#endif
|
||||||
|
msg += obs_get_module_data_path(obs_current_module());
|
||||||
|
(void)DisplayMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
@@ -104,33 +112,23 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
|||||||
{
|
{
|
||||||
if (saving) {
|
if (saving) {
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
switcher->Prune();
|
switcher->Prune();
|
||||||
|
|
||||||
obs_data_t *obj = obs_data_create();
|
obs_data_t *obj = obs_data_create();
|
||||||
|
|
||||||
switcher->saveSettings(obj);
|
switcher->saveSettings(obj);
|
||||||
|
|
||||||
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
||||||
|
|
||||||
obs_data_release(obj);
|
obs_data_release(obj);
|
||||||
} else {
|
} else {
|
||||||
switcher->m.lock();
|
switcher->m.lock();
|
||||||
|
|
||||||
obs_data_t *obj =
|
obs_data_t *obj =
|
||||||
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
||||||
|
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
obj = obs_data_create();
|
obj = obs_data_create();
|
||||||
}
|
}
|
||||||
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
||||||
AskBackup(obj);
|
AskBackup(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
switcher->loadSettings(obj);
|
switcher->loadSettings(obj);
|
||||||
|
|
||||||
obs_data_release(obj);
|
obs_data_release(obj);
|
||||||
|
|
||||||
switcher->m.unlock();
|
switcher->m.unlock();
|
||||||
|
|
||||||
// Stop the scene switcher at least once to
|
// Stop the scene switcher at least once to
|
||||||
@@ -166,9 +164,7 @@ void SwitcherData::Thread()
|
|||||||
bool setPrevSceneAfterLinger = false;
|
bool setPrevSceneAfterLinger = false;
|
||||||
bool macroMatch = false;
|
bool macroMatch = false;
|
||||||
macroSceneSwitched = false;
|
macroSceneSwitched = false;
|
||||||
|
|
||||||
endTime = std::chrono::high_resolution_clock::now();
|
endTime = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
auto runTime =
|
auto runTime =
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
endTime - startTime);
|
endTime - startTime);
|
||||||
@@ -415,6 +411,12 @@ void SwitcherData::Start()
|
|||||||
if (networkConfig.ClientEnabled) {
|
if (networkConfig.ClientEnabled) {
|
||||||
client.connect(networkConfig.GetClientUri());
|
client.connect(networkConfig.GetClientUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showSystemTrayNotifications) {
|
||||||
|
DisplayTrayMessage(
|
||||||
|
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.running"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetMacroCounters()
|
void ResetMacroCounters()
|
||||||
@@ -441,6 +443,12 @@ void SwitcherData::Stop()
|
|||||||
|
|
||||||
server.stop();
|
server.stop();
|
||||||
client.disconnect();
|
client.disconnect();
|
||||||
|
|
||||||
|
if (showSystemTrayNotifications) {
|
||||||
|
DisplayTrayMessage(
|
||||||
|
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.stopped"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::setWaitScene()
|
void SwitcherData::setWaitScene()
|
||||||
@@ -558,6 +566,18 @@ void setTranstionEnd()
|
|||||||
std::chrono::high_resolution_clock::now();
|
std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setStreamStarting()
|
||||||
|
{
|
||||||
|
switcher->lastStreamStartingTime =
|
||||||
|
std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStreamStopping()
|
||||||
|
{
|
||||||
|
switcher->lastStreamStoppingTime =
|
||||||
|
std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
// Note to future self:
|
// Note to future self:
|
||||||
// be careful using switcher->m here as there is potential for deadlocks when using
|
// be careful using switcher->m here as there is potential for deadlocks when using
|
||||||
// frontend functions such as obs_frontend_set_current_scene()
|
// frontend functions such as obs_frontend_set_current_scene()
|
||||||
@@ -597,6 +617,12 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
|||||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
||||||
setTranstionEnd();
|
setTranstionEnd();
|
||||||
break;
|
break;
|
||||||
|
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
|
||||||
|
setStreamStarting();
|
||||||
|
break;
|
||||||
|
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
|
||||||
|
setStreamStopping();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,15 @@ void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state)
|
|||||||
switcher->saveWindowGeo = state;
|
switcher->saveWindowGeo = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_showTrayNotifications_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switcher->showSystemTrayNotifications = state;
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -503,6 +512,8 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
|||||||
static_cast<int>(autoStartEvent));
|
static_cast<int>(autoStartEvent));
|
||||||
|
|
||||||
obs_data_set_bool(obj, "verbose", verbose);
|
obs_data_set_bool(obj, "verbose", verbose);
|
||||||
|
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
||||||
|
showSystemTrayNotifications);
|
||||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||||
|
|
||||||
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
||||||
@@ -574,6 +585,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_get_int(obj, "autoStartEvent"));
|
obs_data_get_int(obj, "autoStartEvent"));
|
||||||
|
|
||||||
verbose = obs_data_get_bool(obj, "verbose");
|
verbose = obs_data_get_bool(obj, "verbose");
|
||||||
|
showSystemTrayNotifications =
|
||||||
|
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
||||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||||
|
|
||||||
obs_data_set_default_int(obj, "priority0", default_priority_0);
|
obs_data_set_default_int(obj, "priority0", default_priority_0);
|
||||||
@@ -800,6 +813,8 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
|
|
||||||
ui->verboseLogging->setChecked(switcher->verbose);
|
ui->verboseLogging->setChecked(switcher->verbose);
|
||||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||||
|
ui->showTrayNotifications->setChecked(
|
||||||
|
switcher->showSystemTrayNotifications);
|
||||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||||
|
|
||||||
for (int p : switcher->functionNamesByPriority) {
|
for (int p : switcher->functionNamesByPriority) {
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public slots:
|
|||||||
|
|
||||||
void on_verboseLogging_stateChanged(int state);
|
void on_verboseLogging_stateChanged(int state);
|
||||||
void on_saveWindowGeo_stateChanged(int state);
|
void on_saveWindowGeo_stateChanged(int state);
|
||||||
|
void on_showTrayNotifications_stateChanged(int state);
|
||||||
void on_uiHintsDisable_stateChanged(int state);
|
void on_uiHintsDisable_stateChanged(int state);
|
||||||
|
|
||||||
void on_exportSettings_clicked();
|
void on_exportSettings_clicked();
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
enum class StreamState {
|
enum class StreamState {
|
||||||
STOP,
|
STOP,
|
||||||
START,
|
START,
|
||||||
|
STARTING,
|
||||||
|
STOPPING,
|
||||||
};
|
};
|
||||||
|
|
||||||
class MacroConditionStream : public MacroCondition {
|
class MacroConditionStream : public MacroCondition {
|
||||||
@@ -22,6 +24,9 @@ public:
|
|||||||
StreamState _streamState;
|
StreamState _streamState;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::chrono::high_resolution_clock::time_point _lastStreamStartingTime{};
|
||||||
|
std::chrono::high_resolution_clock::time_point _lastStreamStoppingTime{};
|
||||||
|
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
static const std::string id;
|
static const std::string id;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public:
|
|||||||
VideoCondition _condition = VideoCondition::MATCH;
|
VideoCondition _condition = VideoCondition::MATCH;
|
||||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
bool _useAlphaAsMask = false;
|
bool _useAlphaAsMask = false;
|
||||||
|
bool _usePatternForChangedCheck = false;
|
||||||
PatternMatchData _patternData;
|
PatternMatchData _patternData;
|
||||||
double _patternThreshold = 0.8;
|
double _patternThreshold = 0.8;
|
||||||
cv::CascadeClassifier _objectCascade;
|
cv::CascadeClassifier _objectCascade;
|
||||||
@@ -65,6 +66,7 @@ public:
|
|||||||
int _throttleCount = 3;
|
int _throttleCount = 3;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool OutputChanged();
|
||||||
bool ScreenshotContainsPattern();
|
bool ScreenshotContainsPattern();
|
||||||
bool ScreenshotContainsObject();
|
bool ScreenshotContainsObject();
|
||||||
bool Compare();
|
bool Compare();
|
||||||
@@ -127,6 +129,7 @@ private slots:
|
|||||||
void ConditionChanged(int cond);
|
void ConditionChanged(int cond);
|
||||||
void ImagePathChanged(const QString &text);
|
void ImagePathChanged(const QString &text);
|
||||||
void ImageBrowseButtonClicked();
|
void ImageBrowseButtonClicked();
|
||||||
|
void UsePatternForChangedCheckChanged(int value);
|
||||||
void PatternThresholdChanged(double);
|
void PatternThresholdChanged(double);
|
||||||
void UseAlphaAsMaskChanged(int value);
|
void UseAlphaAsMaskChanged(int value);
|
||||||
|
|
||||||
@@ -148,6 +151,7 @@ protected:
|
|||||||
QComboBox *_videoSelection;
|
QComboBox *_videoSelection;
|
||||||
QComboBox *_condition;
|
QComboBox *_condition;
|
||||||
|
|
||||||
|
QCheckBox *_usePatternForChangedCheck;
|
||||||
FileSelection *_imagePath;
|
FileSelection *_imagePath;
|
||||||
ThresholdSlider *_patternThreshold;
|
ThresholdSlider *_patternThreshold;
|
||||||
QCheckBox *_useAlphaAsMask;
|
QCheckBox *_useAlphaAsMask;
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ struct SwitcherData {
|
|||||||
bool stop = false;
|
bool stop = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
bool disableHints = false;
|
bool disableHints = false;
|
||||||
|
bool showSystemTrayNotifications = false;
|
||||||
bool showFrame = false;
|
bool showFrame = false;
|
||||||
bool transitionOverrideOverride = false;
|
bool transitionOverrideOverride = false;
|
||||||
bool adjustActiveTransitionType = true;
|
bool adjustActiveTransitionType = true;
|
||||||
@@ -81,6 +82,8 @@ struct SwitcherData {
|
|||||||
OBSWeakSource previousScene = nullptr;
|
OBSWeakSource previousScene = nullptr;
|
||||||
std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
|
std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
|
||||||
std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
|
std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
|
||||||
|
std::chrono::high_resolution_clock::time_point lastStreamStartingTime{};
|
||||||
|
std::chrono::high_resolution_clock::time_point lastStreamStoppingTime{};
|
||||||
OBSWeakSource lastRandomScene;
|
OBSWeakSource lastRandomScene;
|
||||||
SceneGroup *lastRandomSceneGroup;
|
SceneGroup *lastRandomSceneGroup;
|
||||||
OBSWeakSource nonMatchingScene;
|
OBSWeakSource nonMatchingScene;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ void listAddClicked(QListWidget *list, QWidget *newWidget,
|
|||||||
bool listMoveUp(QListWidget *list);
|
bool listMoveUp(QListWidget *list);
|
||||||
bool listMoveDown(QListWidget *list);
|
bool listMoveDown(QListWidget *list);
|
||||||
bool DisplayMessage(const QString &msg, bool question = false);
|
bool DisplayMessage(const QString &msg, bool question = false);
|
||||||
|
void DisplayTrayMessage(const QString &title, const QString &msg);
|
||||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
void addSelectionEntry(QComboBox *sel, const char *description,
|
||||||
bool selectable = false, const char *tooltip = "");
|
bool selectable = false, const char *tooltip = "");
|
||||||
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
|
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QLibrary>
|
#include <QLibrary>
|
||||||
|
#include <proc/readproc.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
static Display *xdisplay = 0;
|
static Display *xdisplay = 0;
|
||||||
|
|
||||||
@@ -363,17 +366,78 @@ bool isFullscreen(const std::string &title)
|
|||||||
void GetProcessList(QStringList &processes)
|
void GetProcessList(QStringList &processes)
|
||||||
{
|
{
|
||||||
processes.clear();
|
processes.clear();
|
||||||
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
PROCTAB *proc = openproc(PROC_FILLSTAT);
|
||||||
std::string s = GetWindowTitle(i);
|
proc_t proc_info;
|
||||||
if (s != "")
|
memset(&proc_info, 0, sizeof(proc_info));
|
||||||
processes << QString::fromStdString(s);
|
while (readproc(proc, &proc_info) != NULL) {
|
||||||
|
QString procName(proc_info.cmd);
|
||||||
|
if (!procName.isEmpty() && !processes.contains(proc_info.cmd)) {
|
||||||
|
processes << QString(proc_info.cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
closeproc(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getForegroundProcessPid()
|
||||||
|
{
|
||||||
|
if (!ewmhIsSupported()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dpy = disp();
|
||||||
|
Atom active = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", true);
|
||||||
|
Atom actualType;
|
||||||
|
int format;
|
||||||
|
unsigned long num, bytes;
|
||||||
|
Window *window = 0;
|
||||||
|
int pid = -1;
|
||||||
|
|
||||||
|
Window rootWin = RootWindow(dpy, 0);
|
||||||
|
|
||||||
|
int xstatus = XGetWindowProperty(dpy, rootWin, active, 0L, ~0L, false,
|
||||||
|
AnyPropertyType, &actualType, &format,
|
||||||
|
&num, &bytes, (uint8_t **)&window);
|
||||||
|
|
||||||
|
if (xstatus == 0 && window == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Atom atom, actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long nitems;
|
||||||
|
unsigned long bytes_after;
|
||||||
|
unsigned char *prop;
|
||||||
|
atom = XInternAtom(dpy, "_NET_WM_PID", True);
|
||||||
|
auto status = XGetWindowProperty(dpy, *window, atom, 0, 1024, False,
|
||||||
|
AnyPropertyType, &actual_type,
|
||||||
|
&actual_format, &nitems, &bytes_after,
|
||||||
|
&prop);
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (!prop) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = prop[1] * 256;
|
||||||
|
pid += prop[0];
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getProcNameFromPid(int pid)
|
||||||
|
{
|
||||||
|
std::string path = "/proc/" + std::to_string(pid) + "/comm";
|
||||||
|
std::ifstream t(path);
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << t.rdbuf();
|
||||||
|
return buffer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInFocus(const QString &executable)
|
bool isInFocus(const QString &executable)
|
||||||
{
|
{
|
||||||
std::string current;
|
auto pid = getForegroundProcessPid();
|
||||||
GetCurrentWindowTitle(current);
|
std::string current = getProcNameFromPid(pid);
|
||||||
|
|
||||||
// True if executable switch equals current window
|
// True if executable switch equals current window
|
||||||
bool equals = (executable.toStdString() == current);
|
bool equals = (executable.toStdString() == current);
|
||||||
|
|||||||
@@ -13,11 +13,21 @@ bool MacroConditionStream::_registered = MacroConditionFactory::Register(
|
|||||||
static std::map<StreamState, std::string> streamStates = {
|
static std::map<StreamState, std::string> streamStates = {
|
||||||
{StreamState::STOP, "AdvSceneSwitcher.condition.stream.state.stop"},
|
{StreamState::STOP, "AdvSceneSwitcher.condition.stream.state.stop"},
|
||||||
{StreamState::START, "AdvSceneSwitcher.condition.stream.state.start"},
|
{StreamState::START, "AdvSceneSwitcher.condition.stream.state.start"},
|
||||||
|
{StreamState::STARTING,
|
||||||
|
"AdvSceneSwitcher.condition.stream.state.starting"},
|
||||||
|
{StreamState::STOPPING,
|
||||||
|
"AdvSceneSwitcher.condition.stream.state.stopping"},
|
||||||
};
|
};
|
||||||
|
|
||||||
bool MacroConditionStream::CheckCondition()
|
bool MacroConditionStream::CheckCondition()
|
||||||
{
|
{
|
||||||
bool stateMatch = false;
|
bool stateMatch = false;
|
||||||
|
|
||||||
|
bool streamStarting = switcher->lastStreamStartingTime !=
|
||||||
|
_lastStreamStartingTime;
|
||||||
|
bool streamStopping = switcher->lastStreamStoppingTime !=
|
||||||
|
_lastStreamStoppingTime;
|
||||||
|
|
||||||
switch (_streamState) {
|
switch (_streamState) {
|
||||||
case StreamState::STOP:
|
case StreamState::STOP:
|
||||||
stateMatch = !obs_frontend_streaming_active();
|
stateMatch = !obs_frontend_streaming_active();
|
||||||
@@ -25,9 +35,22 @@ bool MacroConditionStream::CheckCondition()
|
|||||||
case StreamState::START:
|
case StreamState::START:
|
||||||
stateMatch = obs_frontend_streaming_active();
|
stateMatch = obs_frontend_streaming_active();
|
||||||
break;
|
break;
|
||||||
|
case StreamState::STARTING:
|
||||||
|
stateMatch = streamStarting;
|
||||||
|
break;
|
||||||
|
case StreamState::STOPPING:
|
||||||
|
stateMatch = streamStopping;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (streamStarting) {
|
||||||
|
_lastStreamStartingTime = switcher->lastStreamStartingTime;
|
||||||
|
}
|
||||||
|
if (streamStopping) {
|
||||||
|
_lastStreamStoppingTime = switcher->lastStreamStoppingTime;
|
||||||
|
}
|
||||||
return stateMatch;
|
return stateMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
|
|||||||
GetWeakSourceName(_videoSource).c_str());
|
GetWeakSourceName(_videoSource).c_str());
|
||||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
obs_data_set_string(obj, "filePath", _file.c_str());
|
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||||
|
obs_data_set_bool(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_string(obj, "modelDataPath", _modelDataPath.c_str());
|
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
|
||||||
@@ -132,6 +134,8 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
|
|||||||
_condition =
|
_condition =
|
||||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||||
_file = obs_data_get_string(obj, "filePath");
|
_file = obs_data_get_string(obj, "filePath");
|
||||||
|
_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");
|
||||||
_modelDataPath = obs_data_get_string(obj, "modelDataPath");
|
_modelDataPath = obs_data_get_string(obj, "modelDataPath");
|
||||||
@@ -284,6 +288,18 @@ bool MacroConditionVideo::ScreenshotContainsPattern()
|
|||||||
return countNonZero(result) > 0;
|
return countNonZero(result) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroConditionVideo::OutputChanged()
|
||||||
|
{
|
||||||
|
if (_usePatternForChangedCheck) {
|
||||||
|
cv::Mat result;
|
||||||
|
_patternData = createPatternData(_matchImage);
|
||||||
|
matchPattern(_screenshotData->image, _patternData,
|
||||||
|
_patternThreshold, result, _useAlphaAsMask);
|
||||||
|
return countNonZero(result) == 0;
|
||||||
|
}
|
||||||
|
return _screenshotData->image != _matchImage;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -319,9 +335,9 @@ bool MacroConditionVideo::Compare()
|
|||||||
case VideoCondition::DIFFER:
|
case VideoCondition::DIFFER:
|
||||||
return _screenshotData->image != _matchImage;
|
return _screenshotData->image != _matchImage;
|
||||||
case VideoCondition::HAS_CHANGED:
|
case VideoCondition::HAS_CHANGED:
|
||||||
return _screenshotData->image != _matchImage;
|
return OutputChanged();
|
||||||
case VideoCondition::HAS_NOT_CHANGED:
|
case VideoCondition::HAS_NOT_CHANGED:
|
||||||
return _screenshotData->image == _matchImage;
|
return !OutputChanged();
|
||||||
case VideoCondition::NO_IMAGE:
|
case VideoCondition::NO_IMAGE:
|
||||||
return _screenshotData->image.isNull();
|
return _screenshotData->image.isNull();
|
||||||
case VideoCondition::PATTERN:
|
case VideoCondition::PATTERN:
|
||||||
@@ -398,6 +414,10 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
|
|
||||||
_imagePath = new FileSelection();
|
_imagePath = new FileSelection();
|
||||||
_imagePath->Button()->disconnect();
|
_imagePath->Button()->disconnect();
|
||||||
|
_usePatternForChangedCheck = new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"));
|
||||||
|
_usePatternForChangedCheck->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip"));
|
||||||
_patternThreshold = new ThresholdSlider(
|
_patternThreshold = new ThresholdSlider(
|
||||||
0., 1.,
|
0., 1.,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
@@ -445,6 +465,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
SLOT(ImagePathChanged(const QString &)));
|
SLOT(ImagePathChanged(const QString &)));
|
||||||
QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this,
|
QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this,
|
||||||
SLOT(ImageBrowseButtonClicked()));
|
SLOT(ImageBrowseButtonClicked()));
|
||||||
|
QWidget::connect(_usePatternForChangedCheck, SIGNAL(stateChanged(int)),
|
||||||
|
this, SLOT(UsePatternForChangedCheckChanged(int)));
|
||||||
QWidget::connect(_patternThreshold, SIGNAL(DoubleValueChanged(double)),
|
QWidget::connect(_patternThreshold, SIGNAL(DoubleValueChanged(double)),
|
||||||
this, SLOT(PatternThresholdChanged(double)));
|
this, SLOT(PatternThresholdChanged(double)));
|
||||||
QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this,
|
||||||
@@ -523,6 +545,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
showMatchLayout->addStretch();
|
showMatchLayout->addStretch();
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addLayout(entryLine1Layout);
|
mainLayout->addLayout(entryLine1Layout);
|
||||||
|
mainLayout->addWidget(_usePatternForChangedCheck);
|
||||||
mainLayout->addWidget(_patternThreshold);
|
mainLayout->addWidget(_patternThreshold);
|
||||||
mainLayout->addWidget(_useAlphaAsMask);
|
mainLayout->addWidget(_useAlphaAsMask);
|
||||||
mainLayout->addLayout(_modelPathLayout);
|
mainLayout->addLayout(_modelPathLayout);
|
||||||
@@ -683,6 +706,18 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
|
|||||||
ImagePathChanged(path);
|
ImagePathChanged(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_usePatternForChangedCheck = value;
|
||||||
|
_patternThreshold->setVisible(value);
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -828,9 +863,9 @@ void MacroConditionVideoEdit::ShowMatchClicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QImage markedIamge;
|
QImage markedIamge;
|
||||||
QImage pattern = _entryData->GetMatchImage();
|
|
||||||
if (_entryData->_condition == VideoCondition::PATTERN) {
|
if (_entryData->_condition == VideoCondition::PATTERN) {
|
||||||
cv::Mat result;
|
cv::Mat result;
|
||||||
|
QImage pattern = _entryData->GetMatchImage();
|
||||||
matchPattern(screenshot->image, pattern,
|
matchPattern(screenshot->image, pattern,
|
||||||
_entryData->_patternThreshold, result,
|
_entryData->_patternThreshold, result,
|
||||||
_entryData->_useAlphaAsMask);
|
_entryData->_useAlphaAsMask);
|
||||||
@@ -899,6 +934,19 @@ bool needsThrottleControls(VideoCondition cond)
|
|||||||
cond == VideoCondition::OBJECT;
|
cond == VideoCondition::OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool needsThreshold(VideoCondition cond)
|
||||||
|
{
|
||||||
|
return cond == VideoCondition::PATTERN ||
|
||||||
|
cond == VideoCondition::HAS_CHANGED ||
|
||||||
|
cond == VideoCondition::HAS_NOT_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool patternControlIsOptional(VideoCondition cond)
|
||||||
|
{
|
||||||
|
return cond == VideoCondition::HAS_CHANGED ||
|
||||||
|
cond == VideoCondition::HAS_NOT_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
void setLayoutVisible(QLayout *layout, bool visible)
|
void setLayoutVisible(QLayout *layout, bool visible)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < layout->count(); ++i) {
|
for (int i = 0; i < layout->count(); ++i) {
|
||||||
@@ -912,8 +960,9 @@ void setLayoutVisible(QLayout *layout, bool visible)
|
|||||||
void MacroConditionVideoEdit::SetWidgetVisibility()
|
void MacroConditionVideoEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
|
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
|
||||||
_patternThreshold->setVisible(_entryData->_condition ==
|
_usePatternForChangedCheck->setVisible(
|
||||||
VideoCondition::PATTERN);
|
patternControlIsOptional(_entryData->_condition));
|
||||||
|
_patternThreshold->setVisible(needsThreshold(_entryData->_condition));
|
||||||
_useAlphaAsMask->setVisible(_entryData->_condition ==
|
_useAlphaAsMask->setVisible(_entryData->_condition ==
|
||||||
VideoCondition::PATTERN);
|
VideoCondition::PATTERN);
|
||||||
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
|
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
|
||||||
@@ -931,6 +980,13 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
|
|||||||
needsObjectControls(_entryData->_condition));
|
needsObjectControls(_entryData->_condition));
|
||||||
setLayoutVisible(_throttleControlLayout,
|
setLayoutVisible(_throttleControlLayout,
|
||||||
needsThrottleControls(_entryData->_condition));
|
needsThrottleControls(_entryData->_condition));
|
||||||
|
|
||||||
|
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
|
||||||
|
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
|
||||||
|
_patternThreshold->setVisible(
|
||||||
|
_entryData->_usePatternForChangedCheck);
|
||||||
|
}
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,6 +1000,8 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
|||||||
GetWeakSourceName(_entryData->_videoSource).c_str());
|
GetWeakSourceName(_entryData->_videoSource).c_str());
|
||||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||||
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||||
|
_usePatternForChangedCheck->setChecked(
|
||||||
|
_entryData->_usePatternForChangedCheck);
|
||||||
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
||||||
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
|
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
|
||||||
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <set>
|
#include <set>
|
||||||
@@ -443,6 +444,17 @@ bool DisplayMessage(const QString &msg, bool question)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisplayTrayMessage(const QString &title, const QString &msg)
|
||||||
|
{
|
||||||
|
auto tray = reinterpret_cast<QSystemTrayIcon *>(
|
||||||
|
obs_frontend_get_system_tray());
|
||||||
|
if (!tray) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tray->showMessage(title, msg);
|
||||||
|
}
|
||||||
|
|
||||||
void addSelectionEntry(QComboBox *sel, const char *description, bool selectable,
|
void addSelectionEntry(QComboBox *sel, const char *description, bool selectable,
|
||||||
const char *tooltip)
|
const char *tooltip)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user