mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
21 Commits
1.26.4
...
source-int
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e683fd82f4 | ||
|
|
17c72b772d | ||
|
|
3ff9a1f270 | ||
|
|
7d120d8b6a | ||
|
|
53cfe4b030 | ||
|
|
4dcd748a1f | ||
|
|
223ce0d0a0 | ||
|
|
5bd3341681 | ||
|
|
bb527d6910 | ||
|
|
dfedbcf31a | ||
|
|
1bbfd16b3f | ||
|
|
7f5737d03f | ||
|
|
7702541d81 | ||
|
|
3bc15e585c | ||
|
|
d77101b6aa | ||
|
|
c1e23f9c7a | ||
|
|
70415cd8f4 | ||
|
|
f50cd76493 | ||
|
|
e4af698881 | ||
|
|
33120a359c | ||
|
|
091a364097 |
3
.github/scripts/.Aptfile
vendored
3
.github/scripts/.Aptfile
vendored
@@ -9,4 +9,5 @@ package 'libxtst-dev'
|
||||
package 'libxss-dev'
|
||||
package 'libopencv-dev'
|
||||
package 'libtesseract-dev'
|
||||
package 'libprocps-dev'
|
||||
package 'libprocps-dev'
|
||||
package 'libusb-1.0-0-dev'
|
||||
1
.github/scripts/.Brewfile
vendored
1
.github/scripts/.Brewfile
vendored
@@ -3,3 +3,4 @@ brew "coreutils"
|
||||
brew "cmake"
|
||||
brew "jq"
|
||||
brew "xcbeautify"
|
||||
brew "automake"
|
||||
|
||||
3
.github/scripts/.Wingetfile
vendored
3
.github/scripts/.Wingetfile
vendored
@@ -1,3 +1,4 @@
|
||||
package 'cmake', path: 'Cmake\bin', bin: 'cmake'
|
||||
package 'innosetup', path: 'Inno Setup 6', bin: 'iscc'
|
||||
package 'OpenSSL', path: 'OpenSSL', bin: 'openssl'
|
||||
package 'OpenSSL', path: 'OpenSSL', bin: 'openssl'
|
||||
package 'Microsoft.VisualStudio.Locator', path: 'vswhere', bin: 'vswhere'
|
||||
51
.github/scripts/.build-deps.zsh
vendored
51
.github/scripts/.build-deps.zsh
vendored
@@ -380,7 +380,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
pushd ${advss_dep_path}
|
||||
log_info "Prepare openssl ..."
|
||||
rm -rf openssl
|
||||
git clone git://git.openssl.org/openssl.git --branch openssl-3.1.2 --depth 1
|
||||
git clone https://github.com/openssl/openssl.git --branch openssl-3.1.2 --depth 1
|
||||
mv openssl openssl_x86
|
||||
cp -r openssl_x86 openssl_arm
|
||||
|
||||
@@ -405,6 +405,55 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
log_info "Clean up openssl dir..."
|
||||
mv openssl_x86 openssl
|
||||
rm -rf openssl_arm
|
||||
popd
|
||||
|
||||
pushd ${project_root}/deps/libusb
|
||||
log_info "Prepare libusb ..."
|
||||
|
||||
log_info "Building libusb x86 (deps) ..."
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.9
|
||||
mkdir ${project_root}/deps/libusb/out_x86
|
||||
./autogen.sh
|
||||
./configure --host=x86_64-apple-darwin --prefix=${advss_dep_path}
|
||||
make && make install
|
||||
|
||||
log_info "Building libusb x86 ..."
|
||||
make clean
|
||||
rm -r ${project_root}/deps/libusb/out_x86
|
||||
mkdir ${project_root}/deps/libusb/out_x86
|
||||
./configure --host=aarch64-apple-darwin --prefix=${project_root}/deps/libusb/out_x86
|
||||
make && make install
|
||||
|
||||
log_info "Building libusb arm ..."
|
||||
make clean
|
||||
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
|
||||
export CC=$(xcrun --sdk macosx --find clang)
|
||||
export CXX=$(xcrun --sdk macosx --find clang++)
|
||||
export CFLAGS="-arch arm64 -isysroot $SDKROOT"
|
||||
export CXXFLAGS="-arch arm64 -isysroot $SDKROOT"
|
||||
export LDFLAGS="-arch arm64 -isysroot $SDKROOT"
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
mkdir ${project_root}/deps/libusb/out_arm
|
||||
./configure --host=aarch64-apple-darwin --prefix=${project_root}/deps/libusb/out_arm
|
||||
make && make install
|
||||
|
||||
log_info "Combine arm and x86 libusb binaries ..."
|
||||
lipo -create ${project_root}/deps/libusb/out_x86/lib/libusb-1.0.0.dylib \
|
||||
${project_root}/deps/libusb/out_arm/lib/libusb-1.0.0.dylib \
|
||||
-output ${advss_dep_path}/lib/temp-libusb-1.0.0.dylib
|
||||
mv ${advss_dep_path}/lib/temp-libusb-1.0.0.dylib ${advss_dep_path}/lib/libusb-1.0.0.dylib
|
||||
install_name_tool -id @rpath/libusb-1.0.0.dylib ${advss_dep_path}/lib/libusb-1.0.0.dylib
|
||||
log_info "Clean up libusb ..."
|
||||
|
||||
unset SDKROOT
|
||||
unset CC
|
||||
unset CXX
|
||||
unset CFLAGS
|
||||
unset CXXFLAGS
|
||||
unset LDFLAGS
|
||||
unset MACOSX_DEPLOYMENT_TARGET
|
||||
|
||||
popd
|
||||
;;
|
||||
linux)
|
||||
# Nothing to do for now
|
||||
|
||||
20
.github/scripts/Build-Deps-Windows.ps1
vendored
20
.github/scripts/Build-Deps-Windows.ps1
vendored
@@ -199,6 +199,26 @@ function Build {
|
||||
|
||||
Log-Information "Install tesseract..."
|
||||
Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${ADVSSDepPath}" @TesseractCmakeArgs
|
||||
|
||||
Push-Location -Stack BuildLibusbTemp
|
||||
Ensure-Location $ProjectRoot
|
||||
|
||||
$LibusbPath = "${ProjectRoot}/deps/libusb"
|
||||
|
||||
Log-Information "Building libusb..."
|
||||
$msbuildExe = vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1
|
||||
|
||||
if ($msbuildExe) {
|
||||
Invoke-External $msbuildExe "${LibusbPath}/msvc/libusb.sln" /property:Configuration=Release /property:Platform=x64
|
||||
|
||||
$libusbBuildResultDirectory = "${LibusbPath}/build/v143/x64/Release"
|
||||
if (-not (Test-Path -Path $libusbBuildResultDirectory)) {
|
||||
$libusbBuildResultDirectory = "${LibusbPath}/x64/Release/dll"
|
||||
}
|
||||
Copy-Item -Path "${libusbBuildResultDirectory}/*" -Destination ${ADVSSDepPath} -Recurse -Force
|
||||
} else {
|
||||
Log-Information "Failed to locate msbuild.exe - skipping libusb build"
|
||||
}
|
||||
}
|
||||
|
||||
Build
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,3 +25,6 @@
|
||||
[submodule "deps/json"]
|
||||
path = deps/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "deps/libusb"]
|
||||
path = deps/libusb
|
||||
url = https://github.com/libusb/libusb.git
|
||||
|
||||
@@ -157,6 +157,8 @@ target_sources(
|
||||
lib/utils/backup.hpp
|
||||
lib/utils/curl-helper.cpp
|
||||
lib/utils/curl-helper.hpp
|
||||
lib/utils/cursor-shape-changer.cpp
|
||||
lib/utils/cursor-shape-changer.hpp
|
||||
lib/utils/double-slider.cpp
|
||||
lib/utils/double-slider.hpp
|
||||
lib/utils/duration-control.cpp
|
||||
@@ -174,6 +176,8 @@ target_sources(
|
||||
lib/utils/item-selection-helpers.hpp
|
||||
lib/utils/layout-helpers.cpp
|
||||
lib/utils/layout-helpers.hpp
|
||||
lib/utils/list-controls.cpp
|
||||
lib/utils/list-controls.hpp
|
||||
lib/utils/list-editor.cpp
|
||||
lib/utils/list-editor.hpp
|
||||
lib/utils/log-helper.cpp
|
||||
|
||||
@@ -12,11 +12,13 @@ class localeEntry:
|
||||
locale = ""
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
lineNum = -1
|
||||
|
||||
def __init__(self, locale, widgetPlaceholders, qStringArgs) -> None:
|
||||
def __init__(self, locale, widgetPlaceholders, qStringArgs, lineNum) -> None:
|
||||
self.locale = locale
|
||||
self.widgetPlaceholders = widgetPlaceholders
|
||||
self.qStringArgs = qStringArgs
|
||||
self.lineNum = lineNum
|
||||
|
||||
|
||||
def getNonDefaultLocales(dir):
|
||||
@@ -32,7 +34,7 @@ def getNonDefaultLocales(dir):
|
||||
def getAllLocaleEntries(file):
|
||||
localeEntries = []
|
||||
with open(file, "r", encoding="UTF-8") as f:
|
||||
for line in f.readlines():
|
||||
for lineNum, line in enumerate(f):
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
|
||||
@@ -49,7 +51,9 @@ def getAllLocaleEntries(file):
|
||||
qStringArgs.append(match.group(0))
|
||||
|
||||
localeEntries.append(
|
||||
localeEntry(line.split("=")[0], widgetPlaceholders, qStringArgs)
|
||||
localeEntry(
|
||||
line.split("=")[0], widgetPlaceholders, qStringArgs, lineNum + 1
|
||||
)
|
||||
)
|
||||
|
||||
return localeEntries
|
||||
@@ -73,8 +77,8 @@ def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
if expectedLocaleEntry is None:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" not found in "{}"'.format(
|
||||
localeEntry.locale, file, defaultLocaleFile
|
||||
'ERROR: Locale entry "{}" from "{}:{}" not found in "{}"'.format(
|
||||
localeEntry.locale, file, localeEntry.lineNum, defaultLocaleFile
|
||||
)
|
||||
)
|
||||
continue
|
||||
@@ -83,8 +87,13 @@ def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
if placeholder not in expectedLocaleEntry.widgetPlaceholders:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does contain "{}" widget placeholder while "{}" does not'.format(
|
||||
localeEntry.locale, file, placeholder, defaultLocaleFile
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does contain "{}" widget placeholder while "{}:{}" does not'.format(
|
||||
localeEntry.locale,
|
||||
file,
|
||||
localeEntry.lineNum,
|
||||
placeholder,
|
||||
defaultLocaleFile,
|
||||
expectedLocaleEntry.lineNum,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -92,8 +101,8 @@ def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
if placeholder not in localeEntry.widgetPlaceholders:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does not contain "{}" widget placeholder'.format(
|
||||
localeEntry.locale, file, placeholder
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does not contain "{}" widget placeholder'.format(
|
||||
localeEntry.locale, file, localeEntry.lineNum, placeholder
|
||||
)
|
||||
)
|
||||
|
||||
@@ -101,8 +110,13 @@ def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
if arg not in expectedLocaleEntry.qStringArgs:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does contain "{}" QString arg while "{}" does not'.format(
|
||||
localeEntry.locale, file, arg, defaultLocaleFile
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does contain "{}" QString arg while "{}:{}" does not'.format(
|
||||
localeEntry.locale,
|
||||
file,
|
||||
localeEntry.lineNum,
|
||||
arg,
|
||||
defaultLocaleFile,
|
||||
expectedLocaleEntry.lineNum,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -110,8 +124,8 @@ def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
if arg not in localeEntry.qStringArgs:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does not contain "{}" QString arg'.format(
|
||||
localeEntry.locale, file, arg
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does not contain "{}" QString arg'.format(
|
||||
localeEntry.locale, file, localeEntry.lineNum, arg
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -60,10 +60,36 @@ if(OS_MACOS)
|
||||
${dep}_Development)
|
||||
endfunction()
|
||||
|
||||
function(install_advss_plugin_dependency_file ${target} dep)
|
||||
target_sources(advanced-scene-switcher PRIVATE ${dep})
|
||||
set_source_files_properties(${dep} PROPERTIES MACOSX_PACKAGE_LOCATION
|
||||
${ADVSS_BUNDLE_PLUGIN_DIR})
|
||||
function(install_advss_plugin_dependency_file target dep)
|
||||
get_filename_component(_FILENAME ${dep} NAME)
|
||||
string(REGEX REPLACE "\\.[^.]*$" "" _FILENAMENOEXT ${_FILENAME})
|
||||
set(_DEP_NAME "${target}-${_FILENAMENOEXT}")
|
||||
|
||||
install(
|
||||
FILES "${dep}"
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT ${_DEP_NAME}_Runtime
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT ${_DEP_NAME}_Runtime
|
||||
NAMELINK_COMPONENT ${_DEP_NAME}_Development)
|
||||
|
||||
install(
|
||||
FILES "${dep}"
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT obs_${_DEP_NAME}
|
||||
EXCLUDE_FROM_ALL
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT obs_${_DEP_NAME}
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" --install ${CMAKE_CURRENT_BINARY_DIR} --config
|
||||
$<CONFIG> --prefix ${CMAKE_INSTALL_PREFIX} --component obs_${_DEP_NAME}
|
||||
COMMENT "Installing ${_DEP_NAME} to OBS rundir\n"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
@@ -427,7 +427,6 @@ AdvSceneSwitcher.action.replay.saveWarn="Warnung: Ein zu häufiges Speichern kan
|
||||
AdvSceneSwitcher.action.replay.type.stop="Replay Buffer stoppen"
|
||||
AdvSceneSwitcher.action.replay.type.start="Replay Buffer starten"
|
||||
AdvSceneSwitcher.action.replay.type.save="Replay Buffer speichern"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Stream"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Stream starten"
|
||||
|
||||
@@ -620,7 +620,7 @@ AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Slide changed"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Current slide number is"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slidePath="Current slide path is"
|
||||
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Information about the slide show status will only be updated based on the configured time between slides"
|
||||
AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}"
|
||||
AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}{{regex}}"
|
||||
AdvSceneSwitcher.condition.twitch="Twitch"
|
||||
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online="Stream went online"
|
||||
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.offline="Stream went offline"
|
||||
@@ -703,6 +703,16 @@ AdvSceneSwitcher.condition.folder.condition.folderRemove="A directory was remove
|
||||
AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
|
||||
AdvSceneSwitcher.condition.folder.enableFilter="Only evaluate to true, if the changed path matches a patern"
|
||||
AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
|
||||
AdvSceneSwitcher.condition.usb="USB"
|
||||
AdvSceneSwitcher.condition.usb.description="A USB device matching the following properties is connected:"
|
||||
AdvSceneSwitcher.condition.usb.vendorID="Vendor ID:"
|
||||
AdvSceneSwitcher.condition.usb.productID="Product ID:"
|
||||
AdvSceneSwitcher.condition.usb.busNumber="Bus Number:"
|
||||
AdvSceneSwitcher.condition.usb.deviceAddress="Device Address:"
|
||||
AdvSceneSwitcher.condition.usb.vendorName="Vendor Name:"
|
||||
AdvSceneSwitcher.condition.usb.productName="Product Name:"
|
||||
AdvSceneSwitcher.condition.usb.serialNumber="Serial Number:"
|
||||
AdvSceneSwitcher.condition.noDevicesFoundWarning="No USB devices detected!\nThe plugin might not have the required permissions to check for USB devices."
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.scene="Switch scene"
|
||||
@@ -748,10 +758,12 @@ AdvSceneSwitcher.action.recording.split.hint="Make sure to enable automatic file
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="Replay buffer"
|
||||
AdvSceneSwitcher.action.replay.saveWarn="Warning: Saving too frequently might result in the replay buffer not actually being saved!"
|
||||
AdvSceneSwitcher.action.replay.durationWarn="Warning: Changing the maximum replay time will only apply the next time the replay buffer is started!"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.start="Start replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.save="Save replay buffer"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.replay.type.duration="Set maximum replay time"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}{{duration}}"
|
||||
AdvSceneSwitcher.action.streaming="Streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
|
||||
@@ -797,6 +809,12 @@ AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field orde
|
||||
AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog"
|
||||
AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter dialog"
|
||||
AdvSceneSwitcher.action.source.type.openPropertiesDialog="Open properties dialog"
|
||||
AdvSceneSwitcher.action.source.type.interact="Interact"
|
||||
AdvSceneSwitcher.action.source.type.interact.selectType="Interaction type:{{type}}"
|
||||
AdvSceneSwitcher.action.source.type.interact.mouseClick="Send mouse click"
|
||||
AdvSceneSwitcher.action.source.type.interact.mouseMove="Send mouse move"
|
||||
AdvSceneSwitcher.action.source.type.interact.mouseWheel="Send mouse wheel"
|
||||
AdvSceneSwitcher.action.source.type.interact.keyPress="Send key press"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.source.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\""
|
||||
@@ -1117,6 +1135,11 @@ AdvSceneSwitcher.item.emptyName="Empty name not allowed!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="Name not available!"
|
||||
AdvSceneSwitcher.item.nameReserved="Name reserved!"
|
||||
|
||||
AdvSceneSwitcher.listControls.add="Add"
|
||||
AdvSceneSwitcher.listControls.remove="Remove"
|
||||
AdvSceneSwitcher.listControls.up="Up"
|
||||
AdvSceneSwitcher.listControls.down="Down"
|
||||
|
||||
AdvSceneSwitcher.macroSegmentSelection.invalid="Invalid selection!"
|
||||
|
||||
AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in this field!"
|
||||
@@ -1440,24 +1463,13 @@ AdvSceneSwitcher.tempVar.twitch.status.prediction.description="The status of the
|
||||
|
||||
AdvSceneSwitcher.tempVar.twitch.id.goal="Goal ID"
|
||||
AdvSceneSwitcher.tempVar.twitch.type.goal="Goal type"
|
||||
AdvSceneSwitcher.tempVar.twitch.type.goal.description="The type of goal. Possible values are:\n
|
||||
follow — The goal is to increase followers.\n
|
||||
subscription — The goal is to increase subscriptions. This type shows the net increase or decrease in tier points associated with the subscriptions.\n
|
||||
subscription_count — The goal is to increase subscriptions. This type shows the net increase or decrease in the number of subscriptions.\n
|
||||
new_subscription — The goal is to increase subscriptions. This type shows only the net increase in tier points associated with the subscriptions (it does not account for users that unsubscribed since the goal started).\n
|
||||
new_subscription_count — The goal is to increase subscriptions. This type shows only the net increase in the number of subscriptions (it does not account for users that unsubscribed since the goal started)."
|
||||
AdvSceneSwitcher.tempVar.twitch.type.goal.description="The type of goal. Possible values are:\n follow — The goal is to increase followers.\n subscription — The goal is to increase subscriptions. This type shows the net increase or decrease in tier points associated with the subscriptions.\n subscription_count — The goal is to increase subscriptions. This type shows the net increase or decrease in the number of subscriptions.\n new_subscription — The goal is to increase subscriptions. This type shows only the net increase in tier points associated with the subscriptions (it does not account for users that unsubscribed since the goal started).\n new_subscription_count — The goal is to increase subscriptions. This type shows only the net increase in the number of subscriptions (it does not account for users that unsubscribed since the goal started)."
|
||||
AdvSceneSwitcher.tempVar.twitch.description.goal="Goal description"
|
||||
AdvSceneSwitcher.tempVar.twitch.description.goal.description="A description of the goal, if specified. The description may contain a maximum of 40 characters."
|
||||
AdvSceneSwitcher.tempVar.twitch.is_achieved.goal="Goal is achieved"
|
||||
AdvSceneSwitcher.tempVar.twitch.is_achieved.goal.description="A Boolean value that indicates whether the broadcaster achieved their goal. Is true if the goal was achieved; otherwise, false."
|
||||
AdvSceneSwitcher.tempVar.twitch.current_amount.goal="Goal current amount"
|
||||
AdvSceneSwitcher.tempVar.twitch.current_amount.goal.description="The goal's current value.\n
|
||||
The goal's type determines how this value is increased or decreased.\n
|
||||
If type is follow, this field is set to the broadcaster's current number of followers. This number increases with new followers and decreases when users unfollow the broadcaster.\n
|
||||
If type is subscription, this field is increased and decreased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased or decreased by 2, not 1.\n
|
||||
If type is subscription_count, this field is increased by 1 for each new subscription and decreased by 1 for each user that unsubscribes.\n
|
||||
If type is new_subscription, this field is increased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased by 2, not 1.\n
|
||||
If type is new_subscription_count, this field is increased by 1 for each new subscription."
|
||||
AdvSceneSwitcher.tempVar.twitch.current_amount.goal.description="The goal's current value.\nThe goal's type determines how this value is increased or decreased.\n If type is follow, this field is set to the broadcaster's current number of followers. This number increases with new followers and decreases when users unfollow the broadcaster.\n If type is subscription, this field is increased and decreased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased or decreased by 2, not 1.\n If type is subscription_count, this field is increased by 1 for each new subscription and decreased by 1 for each user that unsubscribes.\n If type is new_subscription, this field is increased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased by 2, not 1.\n If type is new_subscription_count, this field is increased by 1 for each new subscription."
|
||||
AdvSceneSwitcher.tempVar.twitch.target_amount.goal="Goal target amount"
|
||||
AdvSceneSwitcher.tempVar.twitch.target_amount.goal.description="The goal's target value. For example, if the broadcaster has 200 followers before creating the goal, and their goal is to double that number, this field is set to 400."
|
||||
AdvSceneSwitcher.tempVar.twitch.started_at.goal="Goal start time"
|
||||
@@ -1775,6 +1787,21 @@ AdvSceneSwitcher.tempVar.source.settings="Settings string"
|
||||
AdvSceneSwitcher.tempVar.filter.setting="Setting value"
|
||||
AdvSceneSwitcher.tempVar.filter.settings="Settings string"
|
||||
|
||||
AdvSceneSwitcher.tempVar.slideShow.index="Slide index"
|
||||
AdvSceneSwitcher.tempVar.slideShow.index.description="The index of the slide currently being displayed.\nIf the index cannot be determined, -1 is returned."
|
||||
AdvSceneSwitcher.tempVar.slideShow.path="Slide path"
|
||||
AdvSceneSwitcher.tempVar.slideShow.path.description="The filesystem path of the slide currently being displayed."
|
||||
AdvSceneSwitcher.tempVar.slideShow.fileName="Slide file name"
|
||||
AdvSceneSwitcher.tempVar.slideShow.fileName.description="The name of file of the slide currently being displayed."
|
||||
|
||||
AdvSceneSwitcher.tempVar.usb.vendorID="Vendor ID"
|
||||
AdvSceneSwitcher.tempVar.usb.productID="Product ID"
|
||||
AdvSceneSwitcher.tempVar.usb.busNumber="Bus Number"
|
||||
AdvSceneSwitcher.tempVar.usb.deviceAddress="Device Address"
|
||||
AdvSceneSwitcher.tempVar.usb.vendorName="Vendor Name"
|
||||
AdvSceneSwitcher.tempVar.usb.productName="Product Name"
|
||||
AdvSceneSwitcher.tempVar.usb.serialNumber="Serial Number"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
@@ -1866,6 +1893,27 @@ AdvSceneSwitcher.audio.monitor.both="Monitor and Output"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="No buttons found!"
|
||||
|
||||
AdvSceneSwitcher.clearBufferOnMatch="Clear message buffer when matching message was found"
|
||||
|
||||
AdvSceneSwitcher.key="Key:"
|
||||
AdvSceneSwitcher.key.up="Key up:"
|
||||
|
||||
AdvSceneSwitcher.modifierKey="Modifier keys:"
|
||||
AdvSceneSwitcher.modifierKey.shift="Shift"
|
||||
AdvSceneSwitcher.modifierKey.ctrl="Ctrl"
|
||||
AdvSceneSwitcher.modifierKey.alt="Alt"
|
||||
AdvSceneSwitcher.modifierKey.meta="Meta"
|
||||
|
||||
AdvSceneSwitcher.mouse.button="Mouse button:"
|
||||
AdvSceneSwitcher.mouse.button.left="Left"
|
||||
AdvSceneSwitcher.mouse.button.middle="Middle"
|
||||
AdvSceneSwitcher.mouse.button.right="Right"
|
||||
AdvSceneSwitcher.mouse.position="Mouse position:"
|
||||
AdvSceneSwitcher.mouse.up="Mouse up event:"
|
||||
AdvSceneSwitcher.mouse.count="Mouse click count:"
|
||||
AdvSceneSwitcher.mouse.leave="Mouse leave event:"
|
||||
AdvSceneSwitcher.mouse.scrollAmount="Mouse scroll amount:"
|
||||
|
||||
; Legacy tabs below - please don't waste your time adding translations for these :)
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
|
||||
@@ -348,7 +348,6 @@ AdvSceneSwitcher.action.replay.saveWarn="Advertencia: ¡Guardar con demasiada fr
|
||||
AdvSceneSwitcher.action.replay.type.stop="Detener el búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.type.start="Iniciar búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.type.save="Guardar búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
|
||||
|
||||
2090
data/locale/ja-JP.ini
Normal file
2090
data/locale/ja-JP.ini
Normal file
File diff suppressed because it is too large
Load Diff
@@ -137,7 +137,6 @@ AdvSceneSwitcher.action.replay="Буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера"
|
||||
AdvSceneSwitcher.action.replay.type.save="Сохранить буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
|
||||
|
||||
@@ -279,7 +279,6 @@ AdvSceneSwitcher.action.replay="Tekrar arabelleği"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur"
|
||||
AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat"
|
||||
AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Yayın"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
|
||||
|
||||
@@ -481,7 +481,6 @@ AdvSceneSwitcher.action.replay.saveWarn="警告:保存过于频繁可能会导
|
||||
AdvSceneSwitcher.action.replay.type.stop="停止回放缓存"
|
||||
AdvSceneSwitcher.action.replay.type.start="启动回放缓存"
|
||||
AdvSceneSwitcher.action.replay.type.save="保存回放缓存"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="推流"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="停止推流"
|
||||
AdvSceneSwitcher.action.streaming.type.start="开始推流"
|
||||
|
||||
1
deps/libusb
vendored
Submodule
1
deps/libusb
vendored
Submodule
Submodule deps/libusb added at d52e355daa
@@ -68,7 +68,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>962</width>
|
||||
<height>1033</height>
|
||||
<height>1040</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||
@@ -572,40 +572,22 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_32">
|
||||
<item>
|
||||
<widget class="QPushButton" name="priorityUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="priorityUp">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../../../forms/images/up.png</normaloff>../../../forms/images/up.png</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="priorityDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="priorityDown">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../../../forms/images/down.png</normaloff>../../../forms/images/down.png</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">downArrowIconSmall</string>
|
||||
</property>
|
||||
@@ -739,135 +721,47 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_61">
|
||||
<layout class="QHBoxLayout" name="macroControlLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="macroAdd">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.macroAddButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="macroRemove">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.macroRemoveButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_130">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_46">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_131">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="macroUp">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.macroUpButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="macroDown">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.macroDownButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">downArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_65">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -960,22 +854,13 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroSettings">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="macroSettings">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.openMacroSettingsButton</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">propertiesIconSmall</string>
|
||||
</property>
|
||||
@@ -1013,173 +898,67 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<layout class="QHBoxLayout" name="conditionControlsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="conditionAdd">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.conditionAddButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="conditionRemove">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.conditionRemoveButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_135">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_48">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_134">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="conditionTop">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.conditionTopButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="conditionUp">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.conditionUpButton</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="conditionDown">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.conditionDownButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">downArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="conditionBottom">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.conditionBottomButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_15">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -1213,155 +992,62 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<layout class="QHBoxLayout" name="actionControlsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="actionAdd">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.actionAddButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="actionRemove">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.actionRemoveButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_133">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_47">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_132">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="actionTop">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.actionTopButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="actionUp">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.actionUpButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="actionDown">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.actionDownButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">downArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="actionBottom">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.actionBottomButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -1378,22 +1064,13 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="toggleElseActions">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="toggleElseActions">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.toggleShowElseSection</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -1425,170 +1102,64 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_33">
|
||||
<layout class="QHBoxLayout" name="elseActionControlsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="elseActionAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="elseActionAdd">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionAddButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="elseActionRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="elseActionRemove">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionRemoveButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_138">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_50">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_139">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="elseActionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="elseActionTop">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionTopButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="elseActionUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="elseActionUp">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="elseActionDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="elseActionDown">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">downArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="elseActionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="elseActionBottom">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_22">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -70,6 +70,9 @@ static void DisplayMissingDependencyWarning()
|
||||
QString warning(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage"));
|
||||
DisplayMessage(warning.arg(failedLibsString));
|
||||
|
||||
// Only display the warning once per plugin load
|
||||
switcher->loadFailureLibs.clear();
|
||||
}
|
||||
|
||||
static void DisplayMissingDataDirWarning()
|
||||
|
||||
@@ -49,15 +49,9 @@ void MacroInputVariables::Load(obs_data_t *obj)
|
||||
void MacroInputVariables::SetValues(const StringList &values)
|
||||
{
|
||||
if (_inputVariables.size() != (size_t)values.size()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
blog(LOG_WARNING,
|
||||
"%s - sizes do not match (variables: %ld, values %d)",
|
||||
__func__, _inputVariables.size(), values.size());
|
||||
#else
|
||||
blog(LOG_WARNING,
|
||||
"%s - sizes do not match (variables: %ld, values %lld)",
|
||||
__func__, _inputVariables.size(), values.size());
|
||||
#endif
|
||||
"%s - sizes do not match (variables: %d, values %d)",
|
||||
__func__, (int)_inputVariables.size(), (int)values.size());
|
||||
}
|
||||
|
||||
for (size_t index = 0;
|
||||
|
||||
@@ -40,9 +40,12 @@ void MacroList::SetContent(const std::vector<MacroRef> ¯os)
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroList::AddControl(QWidget *widget)
|
||||
QAction *MacroList::AddControl(QWidget *widget, bool addSeperator)
|
||||
{
|
||||
_controlsLayout->insertWidget(_controlsLayout->count() - 1, widget);
|
||||
if (addSeperator) {
|
||||
_controls->addSeparator();
|
||||
}
|
||||
return _controls->addWidget(widget);
|
||||
}
|
||||
|
||||
int MacroList::CurrentRow()
|
||||
|
||||
@@ -9,7 +9,7 @@ class ADVSS_EXPORT MacroList final : public ListEditor {
|
||||
public:
|
||||
MacroList(QWidget *parent, bool allowDuplicates, bool reorder);
|
||||
void SetContent(const std::vector<MacroRef> &);
|
||||
void AddControl(QWidget *);
|
||||
QAction *AddControl(QWidget *, bool addSeperator = true);
|
||||
int CurrentRow();
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -19,11 +19,13 @@ MacroSelection::MacroSelection(QWidget *parent)
|
||||
addItem(QString::fromStdString(m->Name()));
|
||||
}
|
||||
|
||||
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
|
||||
QWidget::connect(GetSettingsWindow(),
|
||||
SIGNAL(MacroAdded(const QString &)), this,
|
||||
SLOT(MacroAdd(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
|
||||
QWidget::connect(GetSettingsWindow(),
|
||||
SIGNAL(MacroRemoved(const QString &)), this,
|
||||
SLOT(MacroRemove(const QString &)));
|
||||
QWidget::connect(parent,
|
||||
QWidget::connect(GetSettingsWindow(),
|
||||
SIGNAL(MacroRenamed(const QString &, const QString &)),
|
||||
this,
|
||||
SLOT(MacroRename(const QString &, const QString &)));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "action-queue.hpp"
|
||||
#include "cursor-shape-changer.hpp"
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-export-import-dialog.hpp"
|
||||
@@ -774,10 +775,12 @@ void AdvSceneSwitcher::on_macroSettings_clicked()
|
||||
static void moveControlsToSplitter(QSplitter *splitter, int idx,
|
||||
QLayoutItem *item)
|
||||
{
|
||||
static int splitterHandleWidth = 38;
|
||||
static int splitterHandleWidth = 32;
|
||||
auto handle = splitter->handle(idx);
|
||||
auto layout = item->layout();
|
||||
layout->setContentsMargins(7, 7, 7, 7);
|
||||
int leftMargin, rightMargin;
|
||||
layout->getContentsMargins(&leftMargin, nullptr, &rightMargin, nullptr);
|
||||
layout->setContentsMargins(leftMargin, 0, rightMargin, 0);
|
||||
handle->setLayout(layout);
|
||||
splitter->setHandleWidth(splitterHandleWidth);
|
||||
splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
|
||||
@@ -827,6 +830,32 @@ static void runSegmentHighligtChecks(AdvSceneSwitcher *ss)
|
||||
}
|
||||
}
|
||||
|
||||
static QToolBar *
|
||||
setupToolBar(const std::initializer_list<std::initializer_list<QWidget *>>
|
||||
&widgetGroups)
|
||||
{
|
||||
auto toolbar = new QToolBar();
|
||||
toolbar->setIconSize({16, 16});
|
||||
|
||||
QAction *lastSeperator = nullptr;
|
||||
|
||||
for (const auto &widgetGroup : widgetGroups) {
|
||||
for (const auto &widget : widgetGroup) {
|
||||
toolbar->addWidget(widget);
|
||||
}
|
||||
lastSeperator = toolbar->addSeparator();
|
||||
}
|
||||
|
||||
if (lastSeperator) {
|
||||
toolbar->removeAction(lastSeperator);
|
||||
}
|
||||
|
||||
// Prevent "extension" button from showing up
|
||||
toolbar->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupMacroTab()
|
||||
{
|
||||
ui->macroElseActions->installEventFilter(this);
|
||||
@@ -835,6 +864,11 @@ void AdvSceneSwitcher::SetupMacroTab()
|
||||
if (GetMacros().size() == 0 && !switcher->disableHints) {
|
||||
addPulse = HighlightWidget(ui->macroAdd, QColor(Qt::green));
|
||||
}
|
||||
|
||||
auto macroControls = setupToolBar({{ui->macroAdd, ui->macroRemove},
|
||||
{ui->macroUp, ui->macroDown}});
|
||||
ui->macroControlLayout->addWidget(macroControls);
|
||||
|
||||
ui->macros->Reset(GetMacros(),
|
||||
GetGlobalMacroSettings()._highlightExecuted);
|
||||
connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this,
|
||||
@@ -886,13 +920,7 @@ void AdvSceneSwitcher::SetupMacroTab()
|
||||
SLOT(HighlightOnChange()));
|
||||
onChangeHighlightTimer.start();
|
||||
|
||||
// Move condition controls into splitter handle layout
|
||||
moveControlsToSplitter(ui->macroActionConditionSplitter, 1,
|
||||
ui->macroConditionsLayout->takeAt(1));
|
||||
moveControlsToSplitter(ui->macroElseActionSplitter, 1,
|
||||
ui->macroActionsLayout->takeAt(1));
|
||||
|
||||
// Set action and condition control icons
|
||||
// Set action and condition toolbars
|
||||
const std::string pathPrefix =
|
||||
GetDataFilePath("res/images/" + GetThemeTypeName());
|
||||
SetButtonIcon(ui->actionTop, (pathPrefix + "DoubleUp.svg").c_str());
|
||||
@@ -907,6 +935,44 @@ void AdvSceneSwitcher::SetupMacroTab()
|
||||
SetButtonIcon(ui->toggleElseActions,
|
||||
(pathPrefix + "NotEqual.svg").c_str());
|
||||
|
||||
auto conditionToolbar =
|
||||
setupToolBar({{ui->conditionAdd, ui->conditionRemove},
|
||||
{ui->conditionTop, ui->conditionUp,
|
||||
ui->conditionDown, ui->conditionBottom}});
|
||||
auto actionToolbar = setupToolBar({{ui->actionAdd, ui->actionRemove},
|
||||
{ui->actionTop, ui->actionUp,
|
||||
ui->actionDown, ui->actionBottom}});
|
||||
auto elseActionToolbar =
|
||||
setupToolBar({{ui->elseActionAdd, ui->elseActionRemove},
|
||||
{ui->elseActionTop, ui->elseActionUp,
|
||||
ui->elseActionDown, ui->elseActionBottom}});
|
||||
|
||||
ui->conditionControlsLayout->addWidget(conditionToolbar);
|
||||
ui->actionControlsLayout->insertWidget(0, actionToolbar);
|
||||
ui->elseActionControlsLayout->addWidget(elseActionToolbar);
|
||||
|
||||
// Move condition controls into splitter handle layout
|
||||
moveControlsToSplitter(ui->macroActionConditionSplitter, 1,
|
||||
ui->macroConditionsLayout->takeAt(1));
|
||||
moveControlsToSplitter(ui->macroElseActionSplitter, 1,
|
||||
ui->macroActionsLayout->takeAt(1));
|
||||
|
||||
// Override splitter cursor icon when hovering over controls in splitter
|
||||
SetCursorOnWidgetHover(ui->conditionAdd, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->conditionRemove,
|
||||
Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->conditionTop, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->conditionUp, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->conditionDown, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->conditionBottom,
|
||||
Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->actionAdd, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->actionRemove, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->actionTop, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->actionUp, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->actionDown, Qt::CursorShape::ArrowCursor);
|
||||
SetCursorOnWidgetHover(ui->actionBottom, Qt::CursorShape::ArrowCursor);
|
||||
|
||||
// Reserve more space for macro edit area than for the macro list
|
||||
ui->macroListMacroEditSplitter->setStretchFactor(0, 1);
|
||||
ui->macroListMacroEditSplitter->setStretchFactor(1, 4);
|
||||
@@ -1400,7 +1466,7 @@ static void fade(QWidget *widget, bool fadeOut)
|
||||
animation->start(QPropertyAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
|
||||
static void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
|
||||
{
|
||||
for (const auto &widget : widgets) {
|
||||
fade(widget, fadeOut);
|
||||
|
||||
34
lib/utils/cursor-shape-changer.cpp
Normal file
34
lib/utils/cursor-shape-changer.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "cursor-shape-changer.hpp"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QEvent>
|
||||
#include <QGuiApplication>
|
||||
|
||||
namespace advss {
|
||||
|
||||
CursorShapeChanger::CursorShapeChanger(QObject *parent, Qt::CursorShape shape)
|
||||
: QObject(parent),
|
||||
_shape(shape)
|
||||
{
|
||||
}
|
||||
|
||||
bool CursorShapeChanger::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if ((e->type() == QEvent::HoverEnter || e->type() == QEvent::Enter) &&
|
||||
!_overrideActive) {
|
||||
_overrideActive = true;
|
||||
QGuiApplication::setOverrideCursor(QCursor(_shape));
|
||||
} else if (_overrideActive && e->type() == QEvent::Leave) {
|
||||
_overrideActive = false;
|
||||
QGuiApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
return QObject::eventFilter(o, e);
|
||||
}
|
||||
|
||||
void SetCursorOnWidgetHover(QWidget *widget, Qt::CursorShape shape)
|
||||
{
|
||||
widget->installEventFilter(new CursorShapeChanger(widget, shape));
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
20
lib/utils/cursor-shape-changer.hpp
Normal file
20
lib/utils/cursor-shape-changer.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class CursorShapeChanger : public QObject {
|
||||
public:
|
||||
CursorShapeChanger(QObject *, Qt::CursorShape shape);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
private:
|
||||
Qt::CursorShape _shape;
|
||||
std::atomic_bool _overrideActive = {false};
|
||||
};
|
||||
|
||||
void SetCursorOnWidgetHover(QWidget *widget, Qt::CursorShape shape);
|
||||
|
||||
} // namespace advss
|
||||
44
lib/utils/list-controls.cpp
Normal file
44
lib/utils/list-controls.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "list-controls.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace advss {
|
||||
|
||||
ListControls::ListControls(QWidget *parent, bool reorder) : QToolBar(parent)
|
||||
{
|
||||
setObjectName("listControls");
|
||||
setStyleSheet("#listControls { background-color: transparent; }");
|
||||
|
||||
setIconSize({16, 16});
|
||||
|
||||
AddActionHelper("addIconSmall", "AdvSceneSwitcher.listControls.add",
|
||||
[this]() { Add(); });
|
||||
AddActionHelper("removeIconSmall",
|
||||
"AdvSceneSwitcher.listControls.remove",
|
||||
[this]() { Remove(); });
|
||||
|
||||
if (reorder) {
|
||||
addSeparator();
|
||||
AddActionHelper("upArrowIconSmall",
|
||||
"AdvSceneSwitcher.listControls.up",
|
||||
[this]() { Up(); });
|
||||
AddActionHelper("downArrowIconSmall",
|
||||
"AdvSceneSwitcher.listControls.down",
|
||||
[this]() { Down(); });
|
||||
}
|
||||
}
|
||||
|
||||
void ListControls::AddActionHelper(const char *theme, const char *tooltip,
|
||||
const std::function<void()> &signal)
|
||||
{
|
||||
auto button = new QToolButton(this);
|
||||
button->setToolTip(obs_module_text(tooltip));
|
||||
button->setProperty("themeID", QVariant(QString(theme)));
|
||||
(void)addWidget(button);
|
||||
button->connect(button, &QToolButton::clicked, this, signal);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
25
lib/utils/list-controls.hpp
Normal file
25
lib/utils/list-controls.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <QToolBar>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ADVSS_EXPORT ListControls final : public QToolBar {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListControls(QWidget *parent = nullptr, bool reorder = true);
|
||||
|
||||
signals:
|
||||
void Add();
|
||||
void Remove();
|
||||
void Up();
|
||||
void Down();
|
||||
|
||||
private:
|
||||
void AddActionHelper(const char *theme, const char *tooltip,
|
||||
const std::function<void()> &signal);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -6,53 +6,19 @@ namespace advss {
|
||||
ListEditor::ListEditor(QWidget *parent, bool reorder)
|
||||
: QWidget(parent),
|
||||
_list(new QListWidget()),
|
||||
_add(new QPushButton()),
|
||||
_remove(new QPushButton()),
|
||||
_up(new QPushButton()),
|
||||
_down(new QPushButton()),
|
||||
_controlsLayout(new QHBoxLayout()),
|
||||
_controls(new ListControls(this, reorder)),
|
||||
_mainLayout(new QVBoxLayout())
|
||||
{
|
||||
_add->setMaximumWidth(22);
|
||||
_add->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_add->setFlat(true);
|
||||
_remove->setMaximumWidth(22);
|
||||
_remove->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_remove->setFlat(true);
|
||||
_up->setMaximumWidth(22);
|
||||
_up->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
||||
_up->setFlat(true);
|
||||
_down->setMaximumWidth(22);
|
||||
_down->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||
_down->setFlat(true);
|
||||
|
||||
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
|
||||
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(Remove()));
|
||||
QWidget::connect(_up, SIGNAL(clicked()), this, SLOT(Up()));
|
||||
QWidget::connect(_down, SIGNAL(clicked()), this, SLOT(Down()));
|
||||
QWidget::connect(_controls, SIGNAL(Add()), this, SLOT(Add()));
|
||||
QWidget::connect(_controls, SIGNAL(Remove()), this, SLOT(Remove()));
|
||||
QWidget::connect(_controls, SIGNAL(Up()), this, SLOT(Up()));
|
||||
QWidget::connect(_controls, SIGNAL(Down()), this, SLOT(Down()));
|
||||
QWidget::connect(_list, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
|
||||
this, SLOT(Clicked(QListWidgetItem *)));
|
||||
|
||||
_controlsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_controlsLayout->addWidget(_add);
|
||||
_controlsLayout->addWidget(_remove);
|
||||
if (reorder) {
|
||||
auto line = new QFrame();
|
||||
line->setFrameShape(QFrame::VLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
_controlsLayout->addWidget(line);
|
||||
_controlsLayout->addWidget(_up);
|
||||
_controlsLayout->addWidget(_down);
|
||||
}
|
||||
_controlsLayout->addStretch();
|
||||
|
||||
_mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_mainLayout->addWidget(_list);
|
||||
_mainLayout->addLayout(_controlsLayout);
|
||||
_mainLayout->addWidget(_controls);
|
||||
setLayout(_mainLayout);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
#include "list-controls.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QListWidget>
|
||||
#include <QLayout>
|
||||
|
||||
@@ -27,11 +27,7 @@ protected:
|
||||
void UpdateListSize();
|
||||
|
||||
QListWidget *_list;
|
||||
QPushButton *_add;
|
||||
QPushButton *_remove;
|
||||
QPushButton *_up;
|
||||
QPushButton *_down;
|
||||
QHBoxLayout *_controlsLayout;
|
||||
ListControls *_controls;
|
||||
QVBoxLayout *_mainLayout;
|
||||
};
|
||||
|
||||
|
||||
@@ -88,12 +88,10 @@ RegexConfig RegexConfig::PartialMatchRegexConfig()
|
||||
|
||||
RegexConfigWidget::RegexConfigWidget(QWidget *parent, bool showEnable)
|
||||
: QWidget(parent),
|
||||
_openSettings(new QPushButton()),
|
||||
_openSettings(new QToolButton()),
|
||||
_enable(new QPushButton())
|
||||
{
|
||||
_openSettings->setMaximumWidth(22);
|
||||
SetButtonIcon(_openSettings, ":/settings/images/settings/general.svg");
|
||||
_openSettings->setFlat(true);
|
||||
_openSettings->setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.regex.configure"));
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <obs-data.h>
|
||||
#include <QWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QToolButton>
|
||||
#include <QWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -63,7 +64,7 @@ signals:
|
||||
private:
|
||||
void SetVisibility();
|
||||
|
||||
QPushButton *_openSettings;
|
||||
QToolButton *_openSettings;
|
||||
QPushButton *_enable;
|
||||
RegexConfig _config;
|
||||
};
|
||||
|
||||
@@ -15,20 +15,16 @@ ResourceTable::ResourceTable(QTabWidget *parent, const QString &help,
|
||||
const std::function<void()> &openSettings)
|
||||
: QWidget(parent),
|
||||
_table(new QTableWidget()),
|
||||
_add(new QPushButton()),
|
||||
_remove(new QPushButton()),
|
||||
_add(new QToolButton()),
|
||||
_remove(new QToolButton()),
|
||||
_help(new QLabel(help))
|
||||
{
|
||||
_add->setMaximumWidth(22);
|
||||
_add->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_add->setFlat(true);
|
||||
_add->setToolTip(addToolTip);
|
||||
|
||||
_remove->setMaximumWidth(22);
|
||||
_remove->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_remove->setFlat(true);
|
||||
_remove->setToolTip(removeToolTip);
|
||||
|
||||
_help->setWordWrap(true);
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QToolButton>
|
||||
#include <QTableWidget>
|
||||
|
||||
class QResizeEvent;
|
||||
@@ -33,8 +33,8 @@ protected:
|
||||
|
||||
private:
|
||||
QTableWidget *_table;
|
||||
QPushButton *_add;
|
||||
QPushButton *_remove;
|
||||
QToolButton *_add;
|
||||
QToolButton *_remove;
|
||||
QLabel *_help;
|
||||
|
||||
QObject *_highlightConnection = nullptr;
|
||||
|
||||
@@ -131,7 +131,9 @@ void StatusControl::SetStarted()
|
||||
_button->setText(
|
||||
obs_module_text("AdvSceneSwitcher.generalTab.status.stop"));
|
||||
_status->setText(obs_module_text("AdvSceneSwitcher.status.active"));
|
||||
_pulse->deleteLater();
|
||||
if (_pulse) {
|
||||
_pulse->deleteLater();
|
||||
}
|
||||
SetStatusStyleSheet(false);
|
||||
_setToStopped = false;
|
||||
}
|
||||
|
||||
@@ -182,6 +182,10 @@ void TempVariableRef::Load(obs_data_t *obj, Macro *macro, const char *name)
|
||||
|
||||
void TempVariableRef::PostLoad(int idx, SegmentType type, Macro *macro)
|
||||
{
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<MacroSegment>> segments;
|
||||
switch (type) {
|
||||
case SegmentType::NONE:
|
||||
@@ -458,7 +462,7 @@ void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
|
||||
void AutoUpdateTooltipLabel::enterEvent(QEvent *event)
|
||||
#endif
|
||||
{
|
||||
_timer->start(1000);
|
||||
_timer->start(300);
|
||||
QLabel::enterEvent(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ void SetHeightToContentHeight(QListWidget *list)
|
||||
list->setMaximumHeight(height);
|
||||
}
|
||||
|
||||
void SetButtonIcon(QPushButton *button, const char *path)
|
||||
void SetButtonIcon(QAbstractButton *button, const char *path)
|
||||
{
|
||||
QIcon icon;
|
||||
icon.addFile(QString::fromUtf8(path), QSize(), QIcon::Normal,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QColor>
|
||||
#include <QComboBox>
|
||||
#include <QIcon>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <string>
|
||||
@@ -19,7 +19,7 @@ EXPORT QObject *HighlightWidget(QWidget *widget, QColor startColor,
|
||||
bool once = false);
|
||||
|
||||
EXPORT void SetHeightToContentHeight(QListWidget *list);
|
||||
EXPORT void SetButtonIcon(QPushButton *button, const char *path);
|
||||
EXPORT void SetButtonIcon(QAbstractButton *button, const char *path);
|
||||
EXPORT int
|
||||
FindIdxInRagne(QComboBox *list, int start, int stop, const std::string &value,
|
||||
Qt::MatchFlags = Qt::MatchExactly | Qt::MatchCaseSensitive);
|
||||
|
||||
@@ -31,4 +31,5 @@ install_advss_plugin_dependency(...)
|
||||
add_plugin(midi)
|
||||
add_plugin(openvr)
|
||||
add_plugin(twitch)
|
||||
add_plugin(usb)
|
||||
add_plugin(video)
|
||||
|
||||
@@ -166,6 +166,8 @@ target_sources(
|
||||
utils/source-properties-button.hpp
|
||||
utils/source-settings-helpers.cpp
|
||||
utils/source-settings-helpers.hpp
|
||||
utils/source-interact.cpp
|
||||
utils/source-interact.hpp
|
||||
utils/source-setting.cpp
|
||||
utils/source-setting.hpp
|
||||
utils/striped-frame.cpp
|
||||
|
||||
@@ -60,8 +60,13 @@ bool MacroActionProjector::PerformAction()
|
||||
}
|
||||
|
||||
if (_fullscreen && _monitor == -1) {
|
||||
blog(LOG_INFO, "refusing to open fullscreen projector"
|
||||
" with invalid display selection");
|
||||
blog(LOG_INFO, "refusing to open fullscreen projector "
|
||||
"with invalid display selection");
|
||||
return true;
|
||||
}
|
||||
if (_fullscreen && MonitorSetupChanged()) {
|
||||
blog(LOG_INFO, "refusing to open fullscreen projector - "
|
||||
"monitor setup seems to have changed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -109,13 +114,6 @@ bool MacroActionProjector::Load(obs_data_t *obj)
|
||||
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
|
||||
if (MonitorSetupChanged()) {
|
||||
blog(LOG_INFO, "monitor setup seems to have changed! "
|
||||
"resetting projector action monitor selection!");
|
||||
_monitor = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -149,10 +147,13 @@ void MacroActionProjector::SetMonitor(int idx)
|
||||
|
||||
int MacroActionProjector::GetMonitor() const
|
||||
{
|
||||
if (MonitorSetupChanged()) {
|
||||
return -1;
|
||||
}
|
||||
return _monitor;
|
||||
}
|
||||
|
||||
bool MacroActionProjector::MonitorSetupChanged()
|
||||
bool MacroActionProjector::MonitorSetupChanged() const
|
||||
{
|
||||
if (_monitorName.empty()) {
|
||||
return false;
|
||||
@@ -248,41 +249,25 @@ void MacroActionProjectorEdit::UpdateEntryData()
|
||||
|
||||
void MacroActionProjectorEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_source = source;
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::MonitorChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetMonitor(value);
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::WindowTypeChanged(int)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_fullscreen =
|
||||
_windowTypes->currentText() ==
|
||||
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
|
||||
@@ -291,11 +276,7 @@ void MacroActionProjectorEdit::WindowTypeChanged(int)
|
||||
|
||||
void MacroActionProjectorEdit::TypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_type = static_cast<MacroActionProjector::Type>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
bool _fullscreen = true;
|
||||
|
||||
private:
|
||||
bool MonitorSetupChanged();
|
||||
bool MonitorSetupChanged() const;
|
||||
|
||||
int _monitor = -1;
|
||||
// Only used to detect display setup changes
|
||||
@@ -66,17 +66,17 @@ private slots:
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void MonitorChanged(int value);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_windowTypes;
|
||||
QComboBox *_types;
|
||||
SceneSelectionWidget *_scenes;
|
||||
SourceSelectionWidget *_sources;
|
||||
QHBoxLayout *_monitorSelection;
|
||||
QComboBox *_monitors;
|
||||
std::shared_ptr<MacroActionProjector> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
std::shared_ptr<MacroActionProjector> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <util/config-file.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -12,31 +13,49 @@ bool MacroActionReplayBuffer::_registered = MacroActionFactory::Register(
|
||||
{MacroActionReplayBuffer::Create, MacroActionReplayBufferEdit::Create,
|
||||
"AdvSceneSwitcher.action.replay"});
|
||||
|
||||
static const std::map<ReplayBufferAction, std::string> actionTypes = {
|
||||
{ReplayBufferAction::STOP, "AdvSceneSwitcher.action.replay.type.stop"},
|
||||
{ReplayBufferAction::START,
|
||||
"AdvSceneSwitcher.action.replay.type.start"},
|
||||
{ReplayBufferAction::SAVE, "AdvSceneSwitcher.action.replay.type.save"},
|
||||
static const std::map<MacroActionReplayBuffer::Action, std::string>
|
||||
actionTypes = {
|
||||
{MacroActionReplayBuffer::Action::STOP,
|
||||
"AdvSceneSwitcher.action.replay.type.stop"},
|
||||
{MacroActionReplayBuffer::Action::START,
|
||||
"AdvSceneSwitcher.action.replay.type.start"},
|
||||
{MacroActionReplayBuffer::Action::SAVE,
|
||||
"AdvSceneSwitcher.action.replay.type.save"},
|
||||
{MacroActionReplayBuffer::Action::DURATION,
|
||||
"AdvSceneSwitcher.action.replay.type.duration"},
|
||||
};
|
||||
|
||||
bool MacroActionReplayBuffer::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case ReplayBufferAction::STOP:
|
||||
case Action::STOP:
|
||||
if (obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_stop();
|
||||
}
|
||||
break;
|
||||
case ReplayBufferAction::START:
|
||||
case Action::START:
|
||||
if (!obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_start();
|
||||
}
|
||||
break;
|
||||
case ReplayBufferAction::SAVE:
|
||||
case Action::SAVE:
|
||||
if (obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_save();
|
||||
}
|
||||
break;
|
||||
case Action::DURATION: {
|
||||
auto conf = obs_frontend_get_profile_config();
|
||||
auto value = std::to_string(_duration.Seconds());
|
||||
config_set_string(conf, "SimpleOutput", "RecRBTime",
|
||||
value.c_str());
|
||||
config_set_string(conf, "AdvOut", "RecRBTime", value.c_str());
|
||||
|
||||
if (config_save(conf) != CONFIG_SUCCESS) {
|
||||
blog(LOG_WARNING,
|
||||
"failed to set replay buffer duration");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -58,14 +77,15 @@ bool MacroActionReplayBuffer::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionReplayBuffer::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<ReplayBufferAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -90,23 +110,24 @@ MacroActionReplayBufferEdit::MacroActionReplayBufferEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionReplayBuffer> entryData)
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox()),
|
||||
_saveWarning(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.replay.saveWarn")))
|
||||
_warning(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.replay.saveWarn"))),
|
||||
_duration(new DurationSelection(this, false, 5.))
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
auto layout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.replay.entry"),
|
||||
layout, widgetPlaceholders);
|
||||
layout,
|
||||
{{"{{actions}}", _actions}, {"{{duration}}", _duration}});
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(layout);
|
||||
mainLayout->addWidget(_saveWarning);
|
||||
mainLayout->addWidget(_warning);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -120,21 +141,41 @@ void MacroActionReplayBufferEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_saveWarning->setVisible(_entryData->_action ==
|
||||
ReplayBufferAction::SAVE);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
SetWidgetVisiblity();
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::SetWidgetVisiblity()
|
||||
{
|
||||
_warning->setVisible(_entryData->_action ==
|
||||
MacroActionReplayBuffer::Action::SAVE);
|
||||
_duration->setVisible(_entryData->_action ==
|
||||
MacroActionReplayBuffer::Action::DURATION);
|
||||
_warning->setText(obs_module_text(
|
||||
(_entryData->_action == MacroActionReplayBuffer::Action::SAVE)
|
||||
? "AdvSceneSwitcher.action.replay.saveWarn"
|
||||
: "AdvSceneSwitcher.action.replay.durationWarn"));
|
||||
_warning->setVisible(_entryData->_action ==
|
||||
MacroActionReplayBuffer::Action::SAVE ||
|
||||
_entryData->_action ==
|
||||
MacroActionReplayBuffer::Action::DURATION);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_action =
|
||||
static_cast<MacroActionReplayBuffer::Action>(value);
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<ReplayBufferAction>(value);
|
||||
_saveWarning->setVisible(_entryData->_action ==
|
||||
ReplayBufferAction::SAVE);
|
||||
adjustSize();
|
||||
SetWidgetVisiblity();
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::DurationChanged(const Duration &duration)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_duration = duration;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class ReplayBufferAction {
|
||||
STOP,
|
||||
START,
|
||||
SAVE,
|
||||
};
|
||||
|
||||
class MacroActionReplayBuffer : public MacroAction {
|
||||
public:
|
||||
MacroActionReplayBuffer(Macro *m) : MacroAction(m) {}
|
||||
@@ -23,7 +17,14 @@ public:
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
|
||||
ReplayBufferAction _action = ReplayBufferAction::STOP;
|
||||
enum class Action {
|
||||
STOP,
|
||||
START,
|
||||
SAVE,
|
||||
DURATION,
|
||||
};
|
||||
Action _action = Action::STOP;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -49,13 +50,16 @@ public:
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
QLabel *_saveWarning;
|
||||
std::shared_ptr<MacroActionReplayBuffer> _entryData;
|
||||
void DurationChanged(const Duration &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisiblity();
|
||||
|
||||
QComboBox *_actions;
|
||||
QLabel *_warning;
|
||||
DurationSelection *_duration;
|
||||
|
||||
std::shared_ptr<MacroActionReplayBuffer> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -197,11 +197,7 @@ MacroActionSequenceEdit::MacroActionSequenceEdit(
|
||||
|
||||
_resetIndex->setMinimum(1);
|
||||
|
||||
auto line = new QFrame();
|
||||
line->setFrameShape(QFrame::VLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
_macroList->AddControl(line);
|
||||
_macroList->AddControl(_continueFrom);
|
||||
(void)_macroList->AddControl(_continueFrom);
|
||||
|
||||
QWidget::connect(_macroList, SIGNAL(Added(const std::string &)), this,
|
||||
SLOT(Add(const std::string &)));
|
||||
|
||||
@@ -36,6 +36,8 @@ const static std::map<MacroActionSource::Action, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.source.type.openFilterDialog"},
|
||||
{MacroActionSource::Action::OPEN_PROPERTIES_DIALOG,
|
||||
"AdvSceneSwitcher.action.source.type.openPropertiesDialog"},
|
||||
{MacroActionSource::Action::INTERACT,
|
||||
"AdvSceneSwitcher.action.source.type.interact"},
|
||||
};
|
||||
|
||||
const static std::map<obs_deinterlace_mode, std::string> deinterlaceModes = {
|
||||
@@ -142,6 +144,9 @@ bool MacroActionSource::PerformAction()
|
||||
case Action::SETTINGS_BUTTON:
|
||||
PressSourceButton(_button, s);
|
||||
break;
|
||||
case Action::INTERACT:
|
||||
_interaction.SendToSource(s);
|
||||
break;
|
||||
case Action::DEINTERLACE_MODE:
|
||||
obs_source_set_deinterlace_mode(s, _deinterlaceMode);
|
||||
break;
|
||||
@@ -199,6 +204,7 @@ bool MacroActionSource::Save(obs_data_t *obj) const
|
||||
static_cast<int>(_deinterlaceMode));
|
||||
obs_data_set_int(obj, "deinterlaceOrder",
|
||||
static_cast<int>(_deinterlaceOrder));
|
||||
_interaction.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -223,6 +229,7 @@ bool MacroActionSource::Load(obs_data_t *obj)
|
||||
obs_data_get_int(obj, "deinterlaceMode"));
|
||||
_deinterlaceOrder = static_cast<obs_deinterlace_field_order>(
|
||||
obs_data_get_int(obj, "deinterlaceOrder"));
|
||||
_interaction.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -309,7 +316,8 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
_warning(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.warning"))),
|
||||
_refreshSettingSelection(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.refresh")))
|
||||
obs_module_text("AdvSceneSwitcher.action.source.refresh"))),
|
||||
_interaction(new SourceInteractionWidget())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
auto sources = GetSourceNames();
|
||||
@@ -349,6 +357,13 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
SLOT(SelectionChanged(const SourceSetting &)));
|
||||
QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this,
|
||||
SLOT(RefreshVariableSourceSelectionValue()));
|
||||
QWidget::connect(
|
||||
_interaction,
|
||||
SIGNAL(SettingsChanged(SourceInteractionInstance *)), this,
|
||||
SLOT(InteractionSettingsChanged(SourceInteractionInstance *)));
|
||||
QWidget::connect(_interaction,
|
||||
SIGNAL(TypeChanged(SourceInteraction::Type)), this,
|
||||
SLOT(InteractionTypeChanged(SourceInteraction::Type)));
|
||||
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
entryLayout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -381,6 +396,7 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
buttonLayout->addWidget(_getSettings);
|
||||
buttonLayout->addStretch();
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
mainLayout->addWidget(_interaction);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -411,6 +427,7 @@ void MacroActionSourceEdit::UpdateEntryData()
|
||||
static_cast<int>(_entryData->_settingsInputMethod)));
|
||||
_tempVars->SetVariable(_entryData->_tempVar);
|
||||
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
|
||||
_interaction->SetSourceInteractionSelection(_entryData->_interaction);
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
@@ -561,6 +578,29 @@ void MacroActionSourceEdit::ManualSettingsValueChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::InteractionTypeChanged(SourceInteraction::Type value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_interaction.SetType(value);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::InteractionSettingsChanged(
|
||||
SourceInteractionInstance *value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
auto lock = LockContext();
|
||||
_entryData->_interaction.SetSettings(value);
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::RefreshVariableSourceSelectionValue()
|
||||
{
|
||||
_sourceSettings->SetSource(_entryData->_source.GetSource());
|
||||
@@ -617,6 +657,8 @@ void MacroActionSourceEdit::SetWidgetVisibility()
|
||||
_entryData->_source.GetType() ==
|
||||
SourceSelection::Type::VARIABLE);
|
||||
|
||||
_interaction->setVisible(_entryData->_action ==
|
||||
MacroActionSource::Action::INTERACT);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "source-interact.hpp"
|
||||
#include "source-properties-button.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "source-setting.hpp"
|
||||
@@ -34,6 +35,7 @@ public:
|
||||
OBS_DEINTERLACE_FIELD_ORDER_TOP;
|
||||
TempVariableRef _tempVar;
|
||||
SourceSetting _setting;
|
||||
SourceInteraction _interaction;
|
||||
|
||||
enum class Action {
|
||||
ENABLE,
|
||||
@@ -46,6 +48,7 @@ public:
|
||||
OPEN_INTERACTION_DIALOG,
|
||||
OPEN_FILTER_DIALOG,
|
||||
OPEN_PROPERTIES_DIALOG,
|
||||
INTERACT,
|
||||
};
|
||||
Action _action = Action::SETTINGS;
|
||||
|
||||
@@ -92,6 +95,8 @@ private slots:
|
||||
void ManualSettingsValueChanged();
|
||||
void RefreshVariableSourceSelectionValue();
|
||||
|
||||
void InteractionTypeChanged(SourceInteraction::Type value);
|
||||
void InteractionSettingsChanged(SourceInteractionInstance *value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -112,6 +117,7 @@ private:
|
||||
QComboBox *_deinterlaceOrder;
|
||||
QLabel *_warning;
|
||||
QPushButton *_refreshSettingSelection;
|
||||
SourceInteractionWidget *_interaction;
|
||||
|
||||
std::shared_ptr<MacroActionSource> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <QListView>
|
||||
#include <util/config-file.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -64,7 +65,7 @@ MacroConditionStats::~MacroConditionStats()
|
||||
os_cpu_usage_info_destroy(_cpu_info);
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckFPS()
|
||||
bool MacroConditionStats::CheckFPS() const
|
||||
{
|
||||
switch (_condition) {
|
||||
case Condition::ABOVE:
|
||||
@@ -79,7 +80,7 @@ bool MacroConditionStats::CheckFPS()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckCPU()
|
||||
bool MacroConditionStats::CheckCPU() const
|
||||
{
|
||||
double usage = os_cpu_usage_info_query(_cpu_info);
|
||||
|
||||
@@ -96,7 +97,7 @@ bool MacroConditionStats::CheckCPU()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckMemory()
|
||||
bool MacroConditionStats::CheckMemory() const
|
||||
{
|
||||
auto rss =
|
||||
(long double)os_get_proc_resident_size() / (1024.0l * 1024.0l);
|
||||
@@ -114,7 +115,7 @@ bool MacroConditionStats::CheckMemory()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckAvgFrametime()
|
||||
bool MacroConditionStats::CheckAvgFrametime() const
|
||||
{
|
||||
auto num = (long double)obs_get_average_frame_time_ns() / 1000000.0l;
|
||||
|
||||
@@ -266,7 +267,7 @@ bool MacroConditionStats::CheckStreamBitrate()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckStreamMBSent()
|
||||
bool MacroConditionStats::CheckStreamMBSent() const
|
||||
{
|
||||
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
||||
uint64_t totalBytes = output ? obs_output_get_total_bytes(output) : 0;
|
||||
@@ -322,7 +323,7 @@ bool MacroConditionStats::CheckRecordingBitrate()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckRecordingMBSent()
|
||||
bool MacroConditionStats::CheckRecordingMBSent() const
|
||||
{
|
||||
OBSOutputAutoRelease output = obs_frontend_get_recording_output();
|
||||
uint64_t totalBytes = output ? obs_output_get_total_bytes(output) : 0;
|
||||
@@ -341,6 +342,53 @@ bool MacroConditionStats::CheckRecordingMBSent()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Based on OBSBasic::GetCurrentOutputPath()
|
||||
static const char *getCurrentOutputPath()
|
||||
{
|
||||
const char *path = nullptr;
|
||||
auto config = obs_frontend_get_profile_config();
|
||||
if (!config) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const char *mode = config_get_string(config, "Output", "Mode");
|
||||
|
||||
if (strcmp(mode, "Advanced") == 0) {
|
||||
const char *advanced_mode =
|
||||
config_get_string(config, "AdvOut", "RecType");
|
||||
|
||||
if (strcmp(advanced_mode, "FFmpeg") == 0) {
|
||||
path = config_get_string(config, "AdvOut",
|
||||
"FFFilePath");
|
||||
} else {
|
||||
path = config_get_string(config, "AdvOut",
|
||||
"RecFilePath");
|
||||
}
|
||||
} else {
|
||||
path = config_get_string(config, "SimpleOutput", "FilePath");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckDiskUsage() const
|
||||
{
|
||||
#define MBYTE (1024ULL * 1024ULL)
|
||||
auto path = getCurrentOutputPath();
|
||||
auto mb = os_get_free_disk_space(path) / MBYTE;
|
||||
switch (_condition) {
|
||||
case Condition::ABOVE:
|
||||
return mb > _value;
|
||||
case Condition::EQUALS:
|
||||
return DoubleEquals(mb, _value, 0.1);
|
||||
case Condition::BELOW:
|
||||
return mb < _value;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStats::CheckCondition()
|
||||
{
|
||||
switch (_type) {
|
||||
@@ -349,8 +397,7 @@ bool MacroConditionStats::CheckCondition()
|
||||
case Type::CPU_USAGE:
|
||||
return CheckCPU();
|
||||
case Type::DISK_USAGE:
|
||||
// TODO: not implemented
|
||||
break;
|
||||
return CheckDiskUsage();
|
||||
case Type::MEM_USAGE:
|
||||
return CheckMemory();
|
||||
case Type::AVG_FRAMETIME:
|
||||
@@ -412,24 +459,13 @@ std::string MacroConditionStats::GetShortDesc() const
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateStatsTypes(QComboBox *list)
|
||||
template<class T>
|
||||
static inline void populateList(QComboBox *list,
|
||||
const std::map<T, std::string> &map)
|
||||
{
|
||||
list->clear();
|
||||
for (auto entry : statsTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
// TODO: not implemented
|
||||
if (entry.first == MacroConditionStats::Type::DISK_USAGE) {
|
||||
qobject_cast<QListView *>(list->view())
|
||||
->setRowHidden(list->count() - 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
list->clear();
|
||||
for (auto entry : statsConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
for (const auto &[_, name] : map) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,8 +478,8 @@ MacroConditionStatsEdit::MacroConditionStatsEdit(
|
||||
{
|
||||
_value->setMaximum(999999999999);
|
||||
|
||||
populateStatsTypes(_stats);
|
||||
populateConditionSelection(_condition);
|
||||
populateList(_stats, statsTypes);
|
||||
populateList(_condition, statsConditionTypes);
|
||||
|
||||
setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.condition.stats.dockHint"));
|
||||
@@ -457,14 +493,12 @@ MacroConditionStatsEdit::MacroConditionStatsEdit(
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{value}}", _value},
|
||||
{"{{stats}}", _stats},
|
||||
{"{{condition}}", _condition},
|
||||
};
|
||||
auto layout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.stats.entry"),
|
||||
layout, widgetPlaceholders);
|
||||
layout,
|
||||
{{"{{value}}", _value},
|
||||
{"{{stats}}", _stats},
|
||||
{"{{condition}}", _condition}});
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -474,22 +508,14 @@ MacroConditionStatsEdit::MacroConditionStatsEdit(
|
||||
|
||||
void MacroConditionStatsEdit::ValueChanged(const NumberVariable<double> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_value = value;
|
||||
}
|
||||
|
||||
void MacroConditionStatsEdit::StatsTypeChanged(int type)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_type =
|
||||
static_cast<MacroConditionStats::Type>(type);
|
||||
}
|
||||
@@ -501,11 +527,7 @@ void MacroConditionStatsEdit::StatsTypeChanged(int type)
|
||||
|
||||
void MacroConditionStatsEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionStats::Condition>(cond);
|
||||
}
|
||||
|
||||
@@ -50,18 +50,19 @@ public:
|
||||
Condition _condition = Condition::ABOVE;
|
||||
|
||||
private:
|
||||
bool CheckFPS();
|
||||
bool CheckCPU();
|
||||
bool CheckMemory();
|
||||
bool CheckAvgFrametime();
|
||||
bool CheckFPS() const;
|
||||
bool CheckCPU() const;
|
||||
bool CheckMemory() const;
|
||||
bool CheckAvgFrametime() const;
|
||||
bool CheckRenderLag();
|
||||
bool CheckEncodeLag();
|
||||
bool CheckStreamDroppedFrames();
|
||||
bool CheckStreamBitrate();
|
||||
bool CheckStreamMBSent();
|
||||
bool CheckStreamMBSent() const;
|
||||
bool CheckRecordingDroppedFrames();
|
||||
bool CheckRecordingBitrate();
|
||||
bool CheckRecordingMBSent();
|
||||
bool CheckRecordingMBSent() const;
|
||||
bool CheckDiskUsage() const;
|
||||
|
||||
os_cpu_usage_info_t *_cpu_info = nullptr;
|
||||
uint32_t _first_encoded = 0xFFFFFFFF;
|
||||
@@ -109,15 +110,14 @@ private slots:
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_stats;
|
||||
QComboBox *_condition;
|
||||
VariableDoubleSpinBox *_value;
|
||||
std::shared_ptr<MacroConditionStats> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_stats;
|
||||
QComboBox *_condition;
|
||||
VariableDoubleSpinBox *_value;
|
||||
|
||||
std::shared_ptr<MacroConditionStats> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "macro-helpers.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionSlideshow::id = "slideshow";
|
||||
@@ -50,25 +52,28 @@ bool MacroConditionSlideshow::CheckCondition()
|
||||
switch (_condition) {
|
||||
case MacroConditionSlideshow::Condition::SLIDE_CHANGED:
|
||||
if (!_slideChanged) {
|
||||
SetVariableValue("false");
|
||||
SetVariableValues("false");
|
||||
return false;
|
||||
}
|
||||
_slideChanged = false;
|
||||
SetVariableValue("true");
|
||||
SetVariableValues("true");
|
||||
return true;
|
||||
case MacroConditionSlideshow::Condition::SLIDE_INDEX:
|
||||
if (_currentIndex == -1) {
|
||||
SetVariableValue("-1");
|
||||
SetVariableValues("-1");
|
||||
return false;
|
||||
}
|
||||
SetVariableValue(std::to_string(_currentIndex + 1));
|
||||
SetVariableValues(std::to_string(_currentIndex + 1));
|
||||
return _currentIndex == _index - 1;
|
||||
case MacroConditionSlideshow::Condition::SLIDE_PATH:
|
||||
if (_currentPath[0] == '\0') {
|
||||
SetVariableValue("");
|
||||
SetVariableValues("");
|
||||
return false;
|
||||
}
|
||||
SetVariableValue(_currentPath);
|
||||
SetVariableValues(_currentPath);
|
||||
if (_regex.Enabled()) {
|
||||
return _regex.Matches(_currentPath, _path);
|
||||
}
|
||||
return std::string(_path) == std::string(_currentPath);
|
||||
}
|
||||
|
||||
@@ -82,6 +87,7 @@ bool MacroConditionSlideshow::Save(obs_data_t *obj) const
|
||||
_source.Save(obj);
|
||||
_index.Save(obj, "index");
|
||||
_path.Save(obj, "path");
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,6 +98,7 @@ bool MacroConditionSlideshow::Load(obs_data_t *obj)
|
||||
_source.Load(obj);
|
||||
_index.Load(obj, "index");
|
||||
_path.Load(obj, "path");
|
||||
_regex.Load(obj);
|
||||
|
||||
auto s = _source.GetSource();
|
||||
AddSignalHandler(s);
|
||||
@@ -175,6 +182,35 @@ void MacroConditionSlideshow::Reset()
|
||||
_currentPath = "";
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
AddTempvar(
|
||||
"index",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.slideShow.index"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.slideShow.index.description"));
|
||||
AddTempvar(
|
||||
"path",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.slideShow.path"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.slideShow.path.description"));
|
||||
AddTempvar(
|
||||
"fileName",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.slideShow.fileName"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.slideShow.fileName.description"));
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::SetVariableValues(const std::string &value)
|
||||
{
|
||||
SetVariableValue(value);
|
||||
SetTempVarValue("index", std::to_string(_currentIndex + 1));
|
||||
SetTempVarValue("path", _currentPath ? _currentPath : "");
|
||||
QFileInfo fileInfo(_currentPath ? _currentPath : "");
|
||||
SetTempVarValue("fileName", fileInfo.fileName().toStdString());
|
||||
}
|
||||
|
||||
static void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : conditions) {
|
||||
@@ -184,11 +220,14 @@ static void populateConditionSelection(QComboBox *list)
|
||||
|
||||
static QStringList getSlideshowNames()
|
||||
{
|
||||
static constexpr std::array<std::string_view, 2> slideShowIds{
|
||||
"slideshow", "slideshow_v2"};
|
||||
auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */
|
||||
{
|
||||
QStringList *list = reinterpret_cast<QStringList *>(param);
|
||||
std::string sourceId = obs_source_get_id(source);
|
||||
if (sourceId.compare("slideshow") == 0) {
|
||||
auto sourceId = obs_source_get_id(source);
|
||||
if (std::find(slideShowIds.begin(), slideShowIds.end(),
|
||||
sourceId) != slideShowIds.end()) {
|
||||
*list << obs_source_get_name(source);
|
||||
}
|
||||
return true;
|
||||
@@ -205,7 +244,9 @@ MacroConditionSlideshowEdit::MacroConditionSlideshowEdit(
|
||||
_conditions(new QComboBox(this)),
|
||||
_index(new VariableSpinBox(this)),
|
||||
_path(new VariableLineEdit(this)),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true))
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_regex(new RegexConfigWidget(this)),
|
||||
_layout(new QHBoxLayout())
|
||||
{
|
||||
setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip"));
|
||||
@@ -227,16 +268,19 @@ MacroConditionSlideshowEdit::MacroConditionSlideshowEdit(
|
||||
this, SLOT(IndexChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_path, SIGNAL(editingFinished()), this,
|
||||
SLOT(PathChanged()));
|
||||
QWidget::connect(_regex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
|
||||
auto layout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.slideshow.entry"),
|
||||
layout,
|
||||
_layout,
|
||||
{{"{{conditions}}", _conditions},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{index}}", _index},
|
||||
{"{{path}}", _path}});
|
||||
setLayout(layout);
|
||||
{"{{path}}", _path},
|
||||
{"{{regex}}", _regex}});
|
||||
setLayout(_layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
@@ -245,11 +289,7 @@ MacroConditionSlideshowEdit::MacroConditionSlideshowEdit(
|
||||
|
||||
void MacroConditionSlideshowEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionSlideshow::Condition>(index);
|
||||
SetWidgetVisibility();
|
||||
@@ -257,11 +297,7 @@ void MacroConditionSlideshowEdit::ConditionChanged(int index)
|
||||
|
||||
void MacroConditionSlideshowEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetSource(source);
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
@@ -269,24 +305,22 @@ void MacroConditionSlideshowEdit::SourceChanged(const SourceSelection &source)
|
||||
|
||||
void MacroConditionSlideshowEdit::IndexChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_index = value;
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::PathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_path = _path->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::RegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
@@ -297,6 +331,7 @@ void MacroConditionSlideshowEdit::UpdateEntryData()
|
||||
_sources->SetSource(_entryData->GetSource());
|
||||
_index->SetValue(_entryData->_index);
|
||||
_path->setText(_entryData->_path);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -310,6 +345,14 @@ void MacroConditionSlideshowEdit::SetWidgetVisibility()
|
||||
MacroConditionSlideshow::Condition::SLIDE_INDEX);
|
||||
_path->setVisible(_entryData->_condition ==
|
||||
MacroConditionSlideshow::Condition::SLIDE_PATH);
|
||||
_regex->setVisible(_entryData->_condition ==
|
||||
MacroConditionSlideshow::Condition::SLIDE_PATH);
|
||||
if (_entryData->_condition ==
|
||||
MacroConditionSlideshow::Condition::SLIDE_PATH) {
|
||||
RemoveStretchIfPresent(_layout);
|
||||
} else {
|
||||
AddStretchIfNecessary(_layout);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
@@ -31,12 +32,15 @@ public:
|
||||
Condition _condition = Condition::SLIDE_CHANGED;
|
||||
IntVariable _index;
|
||||
StringVariable _path;
|
||||
RegexConfig _regex;
|
||||
|
||||
private:
|
||||
static void SlideChanged(void *condition, calldata_t *);
|
||||
void RemoveSignalHandler();
|
||||
void AddSignalHandler(const OBSWeakSource &);
|
||||
void Reset();
|
||||
void SetupTempVars();
|
||||
void SetVariableValues(const std::string &value);
|
||||
|
||||
SourceSelection _source;
|
||||
OBSWeakSource _currentSignalSource;
|
||||
@@ -70,6 +74,7 @@ private slots:
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void IndexChanged(const NumberVariable<int> &);
|
||||
void PathChanged();
|
||||
void RegexChanged(const RegexConfig &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -80,6 +85,8 @@ private:
|
||||
VariableSpinBox *_index;
|
||||
VariableLineEdit *_path;
|
||||
SourceSelectionWidget *_sources;
|
||||
RegexConfigWidget *_regex;
|
||||
QHBoxLayout *_layout;
|
||||
|
||||
std::shared_ptr<MacroConditionSlideshow> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -47,17 +47,27 @@ bool MacroConditionWebsocket::CheckCondition()
|
||||
continue;
|
||||
}
|
||||
if (_regex.Enabled()) {
|
||||
if (_regex.Matches(*message, _message)) {
|
||||
SetTempVarValue("message", *message);
|
||||
SetVariableValue(*message);
|
||||
return true;
|
||||
if (!_regex.Matches(*message, _message)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SetTempVarValue("message", *message);
|
||||
SetVariableValue(*message);
|
||||
if (_clearBufferOnMatch) {
|
||||
_messageBuffer->Clear();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (*message == std::string(_message)) {
|
||||
SetTempVarValue("message", *message);
|
||||
SetVariableValue(*message);
|
||||
return true;
|
||||
if (*message != std::string(_message)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SetTempVarValue("message", *message);
|
||||
SetVariableValue(*message);
|
||||
if (_clearBufferOnMatch) {
|
||||
_messageBuffer->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SetVariableValue("");
|
||||
@@ -72,6 +82,8 @@ bool MacroConditionWebsocket::Save(obs_data_t *obj) const
|
||||
_regex.Save(obj);
|
||||
obs_data_set_string(obj, "connection",
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -89,6 +101,11 @@ bool MacroConditionWebsocket::Load(obs_data_t *obj)
|
||||
_connection =
|
||||
GetWeakConnectionByName(obs_data_get_string(obj, "connection"));
|
||||
|
||||
_clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch");
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
_clearBufferOnMatch = true;
|
||||
}
|
||||
|
||||
SetType(_type);
|
||||
return true;
|
||||
}
|
||||
@@ -160,6 +177,8 @@ MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||
_message(new VariableTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent)),
|
||||
_connection(new WSConnectionSelection(this)),
|
||||
_clearBufferOnMatch(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.clearBufferOnMatch"))),
|
||||
_editLayout(new QHBoxLayout())
|
||||
{
|
||||
populateConditionSelection(_conditions);
|
||||
@@ -174,6 +193,8 @@ MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
||||
this,
|
||||
SLOT(ConnectionSelectionChanged(const QString &)));
|
||||
QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ClearBufferOnMatchChanged(int)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(_editLayout);
|
||||
@@ -183,6 +204,7 @@ MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||
regexLayout->addStretch();
|
||||
regexLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->addLayout(regexLayout);
|
||||
mainLayout->addWidget(_clearBufferOnMatch);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -232,6 +254,7 @@ void MacroConditionWebsocketEdit::UpdateEntryData()
|
||||
_message->setPlainText(_entryData->_message);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_connection->SetConnection(_entryData->GetConnection());
|
||||
_clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch);
|
||||
|
||||
if (_entryData->GetType() == MacroConditionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
@@ -245,11 +268,7 @@ void MacroConditionWebsocketEdit::UpdateEntryData()
|
||||
|
||||
void MacroConditionWebsocketEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetType(static_cast<MacroConditionWebsocket::Type>(index));
|
||||
if (_entryData->GetType() == MacroConditionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
@@ -262,11 +281,7 @@ void MacroConditionWebsocketEdit::ConditionChanged(int index)
|
||||
|
||||
void MacroConditionWebsocketEdit::MessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_message = _message->toPlainText().toUtf8().constData();
|
||||
|
||||
adjustSize();
|
||||
@@ -276,22 +291,20 @@ void MacroConditionWebsocketEdit::MessageChanged()
|
||||
void MacroConditionWebsocketEdit::ConnectionSelectionChanged(
|
||||
const QString &connection)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetConnection(connection.toStdString());
|
||||
emit(HeaderInfoChanged(connection));
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::ClearBufferOnMatchChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_clearBufferOnMatch = value;
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::RegexChanged(const RegexConfig &conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_regex = conf;
|
||||
|
||||
adjustSize();
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
std::weak_ptr<WSConnection> GetConnection() const;
|
||||
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
RegexConfig _regex;
|
||||
bool _clearBufferOnMatch = true;
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
@@ -69,12 +70,10 @@ private slots:
|
||||
void MessageChanged();
|
||||
void RegexChanged(const RegexConfig &);
|
||||
void ConnectionSelectionChanged(const QString &);
|
||||
void ClearBufferOnMatchChanged(int);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroConditionWebsocket> _entryData;
|
||||
|
||||
private:
|
||||
void SetupRequestEdit();
|
||||
void SetupEventEdit();
|
||||
@@ -83,7 +82,10 @@ private:
|
||||
VariableTextEdit *_message;
|
||||
RegexConfigWidget *_regex;
|
||||
WSConnectionSelection *_connection;
|
||||
QCheckBox *_clearBufferOnMatch;
|
||||
QHBoxLayout *_editLayout;
|
||||
|
||||
std::shared_ptr<MacroConditionWebsocket> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
882
plugins/base/utils/source-interact.cpp
Normal file
882
plugins/base/utils/source-interact.cpp
Normal file
@@ -0,0 +1,882 @@
|
||||
#include "source-interact.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
|
||||
namespace advss {
|
||||
|
||||
void Position::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
x.Save(data, "x");
|
||||
y.Save(data, "y");
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void Position::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
x.Load(data, "x");
|
||||
y.Load(data, "y");
|
||||
}
|
||||
|
||||
void Modifier::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_bool(data, "shift", _shift);
|
||||
obs_data_set_bool(data, "ctrl", _ctrl);
|
||||
obs_data_set_bool(data, "alt", _alt);
|
||||
obs_data_set_bool(data, "meta", _meta);
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void Modifier::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
_shift = obs_data_get_bool(data, "shift");
|
||||
_ctrl = obs_data_get_bool(data, "ctrl");
|
||||
_alt = obs_data_get_bool(data, "alt");
|
||||
_meta = obs_data_get_bool(data, "meta");
|
||||
}
|
||||
|
||||
uint32_t Modifier::GetModifiers() const
|
||||
{
|
||||
uint32_t modifiers = INTERACT_NONE;
|
||||
if (_shift) {
|
||||
modifiers |= INTERACT_SHIFT_KEY;
|
||||
}
|
||||
if (_ctrl) {
|
||||
modifiers |= INTERACT_CONTROL_KEY;
|
||||
}
|
||||
if (_alt) {
|
||||
modifiers |= INTERACT_ALT_KEY;
|
||||
}
|
||||
if (_alt) {
|
||||
modifiers |= INTERACT_COMMAND_KEY;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
SourceInteraction::SourceInteraction()
|
||||
{
|
||||
_interaction = std::make_unique<MouseClick>();
|
||||
}
|
||||
|
||||
SourceInteraction::SourceInteraction(const SourceInteraction &other)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
other.Save(data);
|
||||
Load(data);
|
||||
}
|
||||
|
||||
void SourceInteraction::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_int(data, "type", static_cast<int>(_type));
|
||||
_interaction->Save(data, "settings");
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void SourceInteraction::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
_type = static_cast<Type>(obs_data_get_int(data, "type"));
|
||||
UpdateType();
|
||||
_interaction->Load(data, "settings");
|
||||
}
|
||||
|
||||
void SourceInteraction::SetSettings(SourceInteractionInstance *value)
|
||||
{
|
||||
_interaction.reset(value);
|
||||
}
|
||||
|
||||
void SourceInteraction::SetType(Type t)
|
||||
{
|
||||
_type = t;
|
||||
UpdateType();
|
||||
}
|
||||
|
||||
void SourceInteraction::SendToSource(obs_source_t *source) const
|
||||
{
|
||||
_interaction->SendToSource(source);
|
||||
}
|
||||
|
||||
void SourceInteraction::UpdateType()
|
||||
{
|
||||
switch (_type) {
|
||||
case SourceInteraction::Type::CLICK:
|
||||
_interaction.reset(new MouseClick);
|
||||
break;
|
||||
case SourceInteraction::Type::MOVE:
|
||||
_interaction.reset(new MouseMove);
|
||||
break;
|
||||
case SourceInteraction::Type::WHEEL:
|
||||
_interaction.reset(new MouseWheel);
|
||||
break;
|
||||
case SourceInteraction::Type::KEY:
|
||||
_interaction.reset(new KeyPress);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MouseClick::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_int(data, "button", static_cast<int>(_button));
|
||||
obs_data_set_bool(data, "up", _up);
|
||||
_pos.Save(data);
|
||||
_count.Save(data, "count");
|
||||
_modifier.Save(data);
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void MouseClick::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
_button = static_cast<Button>(obs_data_get_int(data, "button"));
|
||||
_up = obs_data_get_bool(data, "up");
|
||||
_pos.Load(data);
|
||||
_count.Load(data, "count");
|
||||
_modifier.Load(data);
|
||||
}
|
||||
|
||||
void MouseClick::SendToSource(obs_source_t *source) const
|
||||
{
|
||||
obs_mouse_event ev = {_modifier.GetModifiers(), _pos.x, _pos.y};
|
||||
obs_source_send_mouse_click(source, &ev, static_cast<int32_t>(_button),
|
||||
_up, _count);
|
||||
}
|
||||
|
||||
void MouseMove::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
_pos.Save(data);
|
||||
_modifier.Save(data);
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void MouseMove::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
_pos.Load(data);
|
||||
_modifier.Load(data);
|
||||
}
|
||||
|
||||
void MouseMove::SendToSource(obs_source_t *source) const
|
||||
{
|
||||
obs_mouse_event ev = {_modifier.GetModifiers(), _pos.x, _pos.y};
|
||||
obs_source_send_mouse_move(source, &ev, _leaveSource);
|
||||
}
|
||||
|
||||
void MouseWheel::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
_pos.Save(data);
|
||||
_amount.Save(data, "amount");
|
||||
_pos.Save(data);
|
||||
_modifier.Save(data);
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void MouseWheel::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
_pos.Load(data);
|
||||
_amount.Load(data, "amount");
|
||||
_modifier.Load(data);
|
||||
}
|
||||
|
||||
void MouseWheel::SendToSource(obs_source_t *source) const
|
||||
{
|
||||
obs_mouse_event ev = {_modifier.GetModifiers(), _pos.x, _pos.y};
|
||||
obs_source_send_mouse_wheel(source, &ev, _amount.x, _amount.y);
|
||||
}
|
||||
|
||||
void KeyPress::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_bool(data, "up", _up);
|
||||
_modifier.Save(data);
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void KeyPress::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
_up = obs_data_get_bool(data, "up");
|
||||
_modifier.Load(data);
|
||||
}
|
||||
|
||||
void KeyPress::SendToSource(obs_source_t *source) const
|
||||
{
|
||||
char text = _text;
|
||||
obs_key_event ev = {_modifier.GetModifiers(), &text, 0, 0, 0};
|
||||
obs_source_send_key_click(source, &ev, _up);
|
||||
}
|
||||
|
||||
PositionSelection::PositionSelection(int min, int max, QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_x(new VariableSpinBox),
|
||||
_y(new VariableSpinBox)
|
||||
{
|
||||
_x->setMinimum(min);
|
||||
_y->setMinimum(min);
|
||||
_x->setMaximum(max);
|
||||
_y->setMaximum(max);
|
||||
|
||||
connect(_x, SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(XChanged(const NumberVariable<int> &)));
|
||||
connect(_y, SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(YChanged(const NumberVariable<int> &)));
|
||||
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(_x);
|
||||
layout->setStretch(0, 3);
|
||||
auto label = new QLabel("x");
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(_y);
|
||||
layout->setStretch(2, 3);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void PositionSelection::SetPosition(const Position &pos)
|
||||
{
|
||||
_x->SetValue(pos.x);
|
||||
_y->SetValue(pos.y);
|
||||
}
|
||||
|
||||
Position PositionSelection::GetPosition() const
|
||||
{
|
||||
return Position{_x->Value(), _y->Value()};
|
||||
}
|
||||
|
||||
void PositionSelection::XChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
emit PositionChanged(Position{value, _y->Value()});
|
||||
}
|
||||
|
||||
void PositionSelection::YChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
emit PositionChanged(Position{_x->Value(), value});
|
||||
}
|
||||
|
||||
ModifierSelection::ModifierSelection(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_shift(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.modifierKey.shift"))),
|
||||
_ctrl(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.modifierKey.ctrl"))),
|
||||
_alt(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.modifierKey.alt"))),
|
||||
_meta(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.modifierKey.meta")))
|
||||
{
|
||||
connect(_shift, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ShiftChanged(int)));
|
||||
connect(_ctrl, SIGNAL(stateChanged(int)), this, SLOT(CtrlChanged(int)));
|
||||
connect(_alt, SIGNAL(stateChanged(int)), this, SLOT(AltChanged(int)));
|
||||
connect(_meta, SIGNAL(stateChanged(int)), this, SLOT(MetaChanged(int)));
|
||||
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(_shift);
|
||||
layout->addWidget(_ctrl);
|
||||
layout->addWidget(_alt);
|
||||
layout->addWidget(_meta);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void ModifierSelection::SetModifier(const Modifier &modifier)
|
||||
{
|
||||
_shift->setChecked(modifier._shift);
|
||||
_ctrl->setChecked(modifier._ctrl);
|
||||
_alt->setChecked(modifier._alt);
|
||||
_meta->setChecked(modifier._meta);
|
||||
}
|
||||
|
||||
Modifier ModifierSelection::GetCurrentModifier() const
|
||||
{
|
||||
Modifier modifier;
|
||||
modifier.EnableShift(_shift->isChecked());
|
||||
modifier.EnableCtrl(_ctrl->isChecked());
|
||||
modifier.EnableAlt(_alt->isChecked());
|
||||
modifier.EnableMeta(_meta->isChecked());
|
||||
return modifier;
|
||||
}
|
||||
|
||||
void ModifierSelection::ShiftChanged(int value)
|
||||
{
|
||||
auto modifier = GetCurrentModifier();
|
||||
modifier.EnableShift(value);
|
||||
emit ModifierChanged(modifier);
|
||||
}
|
||||
|
||||
void ModifierSelection::CtrlChanged(int value)
|
||||
{
|
||||
auto modifier = GetCurrentModifier();
|
||||
modifier.EnableCtrl(value);
|
||||
emit ModifierChanged(modifier);
|
||||
}
|
||||
|
||||
void ModifierSelection::AltChanged(int value)
|
||||
{
|
||||
auto modifier = GetCurrentModifier();
|
||||
modifier.EnableAlt(value);
|
||||
emit ModifierChanged(modifier);
|
||||
}
|
||||
|
||||
void ModifierSelection::MetaChanged(int value)
|
||||
{
|
||||
auto modifier = GetCurrentModifier();
|
||||
modifier.EnableMeta(value);
|
||||
emit ModifierChanged(modifier);
|
||||
}
|
||||
|
||||
static inline void populateInteractionTypeSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.type.interact.mouseClick"),
|
||||
(int)SourceInteraction::Type::CLICK);
|
||||
list->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.type.interact.mouseMove"),
|
||||
(int)SourceInteraction::Type::MOVE);
|
||||
list->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.type.interact.mouseWheel"),
|
||||
(int)SourceInteraction::Type::WHEEL);
|
||||
list->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.type.interact.keyPress"),
|
||||
(int)SourceInteraction::Type::KEY);
|
||||
}
|
||||
|
||||
SourceInteractionWidget::SourceInteractionWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_type(new QComboBox),
|
||||
_settingsLayout(new QHBoxLayout)
|
||||
{
|
||||
populateInteractionTypeSelection(_type);
|
||||
|
||||
connect(_type, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TypeChange(int)));
|
||||
auto typeLayout = new QHBoxLayout();
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.type.interact.selectType"),
|
||||
typeLayout, {{"{{type}}", _type}});
|
||||
|
||||
SetupSettingsWidget(SourceInteraction::Type::CLICK);
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addLayout(typeLayout);
|
||||
layout->addLayout(_settingsLayout);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::SetupSettingsWidget(SourceInteraction::Type type)
|
||||
{
|
||||
_type->setCurrentIndex(_type->findData(static_cast<int>(type)));
|
||||
ClearLayout(_settingsLayout);
|
||||
switch (type) {
|
||||
case SourceInteraction::Type::CLICK:
|
||||
SetupMouseClick();
|
||||
break;
|
||||
case SourceInteraction::Type::MOVE:
|
||||
SetupMouseMove();
|
||||
break;
|
||||
case SourceInteraction::Type::WHEEL:
|
||||
SetupMouseWheel();
|
||||
break;
|
||||
case SourceInteraction::Type::KEY:
|
||||
SetupKeyPress();
|
||||
break;
|
||||
}
|
||||
_settingsLayout->update();
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::SetupMouseClick()
|
||||
{
|
||||
auto widget = new MouseClickSettings();
|
||||
connect(widget, SIGNAL(MouseClickSettingsChanged(const MouseClick &)),
|
||||
this, SLOT(MouseClickSettingsChanged(const MouseClick &)));
|
||||
_settingsLayout->addWidget(widget);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::SetupMouseMove()
|
||||
{
|
||||
auto widget = new MouseMoveSettings();
|
||||
connect(widget, SIGNAL(MouseMoveSettingsChanged(const MouseMove &)),
|
||||
this, SLOT(MouseMoveSettingsChanged(const MouseMove &)));
|
||||
_settingsLayout->addWidget(widget);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::SetupMouseWheel()
|
||||
{
|
||||
auto widget = new MouseWheelSettings();
|
||||
connect(widget, SIGNAL(MouseWheelSettingsChanged(const MouseWheel &)),
|
||||
this, SLOT(MouseWheelSettingsChanged(const MouseWheel &)));
|
||||
_settingsLayout->addWidget(widget);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::SetupKeyPress()
|
||||
{
|
||||
auto widget = new KeyPressSettings();
|
||||
connect(widget, SIGNAL(KeyPressSettingsChanged(const KeyPress &)), this,
|
||||
SLOT(KeyPressSettingsChanged(const KeyPress &)));
|
||||
_settingsLayout->addWidget(widget);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::TypeChange(int idx)
|
||||
{
|
||||
auto type = static_cast<SourceInteraction::Type>(
|
||||
_type->itemData(idx).toInt());
|
||||
SetupSettingsWidget(type);
|
||||
emit TypeChanged(type);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::SetSourceInteractionSelection(
|
||||
const SourceInteraction &interaction)
|
||||
{
|
||||
SetupSettingsWidget(interaction._type);
|
||||
|
||||
auto item = _settingsLayout->itemAt(0);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
auto widget = item->widget();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (interaction._type) {
|
||||
case SourceInteraction::Type::CLICK: {
|
||||
auto mouseClickWidget =
|
||||
dynamic_cast<MouseClickSettings *>(widget);
|
||||
if (!mouseClickWidget) {
|
||||
return;
|
||||
}
|
||||
auto mouseClickSettings = dynamic_cast<MouseClick *>(
|
||||
interaction._interaction.get());
|
||||
if (!mouseClickSettings) {
|
||||
return;
|
||||
}
|
||||
mouseClickWidget->SetMouseClickSettings(*mouseClickSettings);
|
||||
}
|
||||
return;
|
||||
case SourceInteraction::Type::MOVE: {
|
||||
|
||||
auto mouseMoveWidget =
|
||||
dynamic_cast<MouseMoveSettings *>(widget);
|
||||
if (!mouseMoveWidget) {
|
||||
return;
|
||||
}
|
||||
auto mouseMoveSettings = dynamic_cast<MouseMove *>(
|
||||
interaction._interaction.get());
|
||||
if (!mouseMoveSettings) {
|
||||
return;
|
||||
}
|
||||
mouseMoveWidget->SetMouseMoveSettings(*mouseMoveSettings);
|
||||
}
|
||||
return;
|
||||
case SourceInteraction::Type::WHEEL: {
|
||||
auto mouseWheelWidget =
|
||||
dynamic_cast<MouseWheelSettings *>(widget);
|
||||
if (!mouseWheelWidget) {
|
||||
return;
|
||||
}
|
||||
auto mouseWheelSettings = dynamic_cast<MouseWheel *>(
|
||||
interaction._interaction.get());
|
||||
if (!mouseWheelSettings) {
|
||||
return;
|
||||
}
|
||||
mouseWheelWidget->SetMouseWheelSettings(*mouseWheelSettings);
|
||||
}
|
||||
return;
|
||||
case SourceInteraction::Type::KEY: {
|
||||
auto KeyPressWidget = dynamic_cast<KeyPressSettings *>(widget);
|
||||
if (!KeyPressWidget) {
|
||||
return;
|
||||
}
|
||||
auto KeyPressSettings = dynamic_cast<KeyPress *>(
|
||||
interaction._interaction.get());
|
||||
if (!KeyPressSettings) {
|
||||
return;
|
||||
}
|
||||
KeyPressWidget->SetKeyPressSettings(*KeyPressSettings);
|
||||
}
|
||||
return;
|
||||
}
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::MouseClickSettingsChanged(const MouseClick &value)
|
||||
{
|
||||
auto click = new MouseClick();
|
||||
*click = value;
|
||||
emit SettingsChanged(click);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::MouseMoveSettingsChanged(const MouseMove &value)
|
||||
{
|
||||
auto move = new MouseMove();
|
||||
*move = value;
|
||||
emit SettingsChanged(move);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::MouseWheelSettingsChanged(const MouseWheel &value)
|
||||
{
|
||||
auto wheel = new MouseWheel();
|
||||
*wheel = value;
|
||||
emit SettingsChanged(wheel);
|
||||
}
|
||||
|
||||
void SourceInteractionWidget::KeyPressSettingsChanged(const KeyPress &value)
|
||||
{
|
||||
auto key = new KeyPress();
|
||||
*key = value;
|
||||
emit SettingsChanged(key);
|
||||
}
|
||||
|
||||
static inline void populateMouseButtonSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.mouse.button.left"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.mouse.button.middle"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.mouse.button.right"));
|
||||
}
|
||||
|
||||
MouseClickSettings::MouseClickSettings(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_button(new QComboBox),
|
||||
_position(new PositionSelection(0, 999999)),
|
||||
_up(new QCheckBox),
|
||||
_count(new VariableSpinBox),
|
||||
_modifier(new ModifierSelection)
|
||||
{
|
||||
_count->setMinimum(1);
|
||||
_count->setMaximum(99);
|
||||
populateMouseButtonSelection(_button);
|
||||
|
||||
connect(_button, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ButtonChanged(int)));
|
||||
connect(_position, SIGNAL(PositionChanged(const Position &)), this,
|
||||
SLOT(PositionChanged(const Position &)));
|
||||
connect(_up, SIGNAL(stateChanged(int)), this, SLOT(UpChanged(int)));
|
||||
connect(_count,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(CountChanged(const NumberVariable<int> &)));
|
||||
connect(_modifier, SIGNAL(ModifierChanged(const Modifier &)), this,
|
||||
SLOT(ModifierChanged(const Modifier &)));
|
||||
|
||||
auto layout = new QGridLayout();
|
||||
layout->setColumnStretch(1, 3);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
int row = 0;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.button")),
|
||||
row, 0);
|
||||
layout->addWidget(_button, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.position")),
|
||||
row, 0);
|
||||
layout->addWidget(_position, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.up")), row,
|
||||
0);
|
||||
layout->addWidget(_up, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.count")),
|
||||
row, 0);
|
||||
layout->addWidget(_count, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.modifierKey")),
|
||||
row, 0);
|
||||
layout->addWidget(_modifier, row, 1);
|
||||
++row;
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MouseClickSettings::SetMouseClickSettings(const MouseClick &click)
|
||||
{
|
||||
_button->setCurrentIndex(static_cast<int>(click._button));
|
||||
_position->SetPosition(click._pos);
|
||||
_up->setChecked(click._up);
|
||||
_count->SetValue(click._count);
|
||||
_modifier->SetModifier(click._modifier);
|
||||
}
|
||||
|
||||
void MouseClickSettings::ButtonChanged(int button)
|
||||
{
|
||||
auto click = GetCurrentMouseClick();
|
||||
click.SetButton(static_cast<MouseClick::Button>(button));
|
||||
emit MouseClickSettingsChanged(click);
|
||||
}
|
||||
|
||||
void MouseClickSettings::PositionChanged(const Position &pos)
|
||||
{
|
||||
auto click = GetCurrentMouseClick();
|
||||
click.SetPosition(pos);
|
||||
emit MouseClickSettingsChanged(click);
|
||||
}
|
||||
|
||||
void MouseClickSettings::UpChanged(int value)
|
||||
{
|
||||
auto click = GetCurrentMouseClick();
|
||||
click.SetDirection(value);
|
||||
emit MouseClickSettingsChanged(click);
|
||||
}
|
||||
|
||||
void MouseClickSettings::CountChanged(const IntVariable &value)
|
||||
{
|
||||
auto click = GetCurrentMouseClick();
|
||||
click.SetCount(value);
|
||||
emit MouseClickSettingsChanged(click);
|
||||
}
|
||||
|
||||
void MouseClickSettings::ModifierChanged(const Modifier &modifier)
|
||||
{
|
||||
auto click = GetCurrentMouseClick();
|
||||
click.SetModifier(modifier);
|
||||
emit MouseClickSettingsChanged(click);
|
||||
}
|
||||
|
||||
MouseClick MouseClickSettings::GetCurrentMouseClick() const
|
||||
{
|
||||
MouseClick click;
|
||||
|
||||
click.SetButton(
|
||||
static_cast<MouseClick::Button>(_button->currentIndex()));
|
||||
click.SetPosition(_position->GetPosition());
|
||||
click.SetDirection(_up->isChecked());
|
||||
click.SetCount(_count->Value());
|
||||
click.SetModifier(_modifier->GetModifier());
|
||||
return click;
|
||||
}
|
||||
|
||||
MouseMoveSettings::MouseMoveSettings(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_position(new PositionSelection(0, 999999)),
|
||||
_leaveEvent(new QCheckBox),
|
||||
_modifier(new ModifierSelection)
|
||||
{
|
||||
connect(_position, SIGNAL(PositionChanged(const Position &)), this,
|
||||
SLOT(PositionChanged(const Position &)));
|
||||
connect(_leaveEvent, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(LeaveChanged(int)));
|
||||
connect(_modifier, SIGNAL(ModifierChanged(const Modifier &)), this,
|
||||
SLOT(ModifierChanged(const Modifier &)));
|
||||
|
||||
auto layout = new QGridLayout();
|
||||
layout->setColumnStretch(1, 3);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
int row = 0;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.position")),
|
||||
row, 0);
|
||||
layout->addWidget(_position, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.leave")),
|
||||
row, 0);
|
||||
layout->addWidget(_leaveEvent, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.modifierKey")),
|
||||
row, 0);
|
||||
layout->addWidget(_modifier, row, 1);
|
||||
++row;
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MouseMoveSettings::SetMouseMoveSettings(const MouseMove &move)
|
||||
{
|
||||
_position->SetPosition(move._pos);
|
||||
_leaveEvent->setChecked(move._leaveSource);
|
||||
_modifier->SetModifier(move._modifier);
|
||||
}
|
||||
|
||||
void MouseMoveSettings::PositionChanged(const Position &pos)
|
||||
{
|
||||
auto move = GetCurrentMouseMove();
|
||||
move.SetPosition(pos);
|
||||
emit MouseMoveSettingsChanged(move);
|
||||
}
|
||||
|
||||
void MouseMoveSettings::LeaveChanged(int value)
|
||||
{
|
||||
auto move = GetCurrentMouseMove();
|
||||
move.SetLeave(value);
|
||||
emit MouseMoveSettingsChanged(move);
|
||||
}
|
||||
|
||||
void MouseMoveSettings::ModifierChanged(const Modifier &modifier)
|
||||
{
|
||||
auto move = GetCurrentMouseMove();
|
||||
move.SetModifier(modifier);
|
||||
emit MouseMoveSettingsChanged(move);
|
||||
}
|
||||
|
||||
MouseMove MouseMoveSettings::GetCurrentMouseMove() const
|
||||
{
|
||||
MouseMove move;
|
||||
move.SetPosition(_position->GetPosition());
|
||||
move.SetLeave(_leaveEvent->isChecked());
|
||||
move.SetModifier(_modifier->GetModifier());
|
||||
return move;
|
||||
}
|
||||
|
||||
MouseWheelSettings::MouseWheelSettings(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_position(new PositionSelection(0, 999999)),
|
||||
_amount(new PositionSelection(-999999, 999999)),
|
||||
_modifier(new ModifierSelection)
|
||||
{
|
||||
connect(_position, SIGNAL(PositionChanged(const Position &)), this,
|
||||
SLOT(PositionChanged(const Position &)));
|
||||
connect(_amount, SIGNAL(PositionChanged(const Position &)), this,
|
||||
SLOT(AmountChanged(const Position &)));
|
||||
connect(_modifier, SIGNAL(ModifierChanged(const Modifier &)), this,
|
||||
SLOT(ModifierChanged(const Modifier &)));
|
||||
|
||||
auto layout = new QGridLayout();
|
||||
layout->setColumnStretch(1, 3);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
int row = 0;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.mouse.position")),
|
||||
row, 0);
|
||||
layout->addWidget(_position, row, 1);
|
||||
++row;
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.mouse.scrollAmount")),
|
||||
row, 0);
|
||||
layout->addWidget(_amount, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.modifierKey")),
|
||||
row, 0);
|
||||
layout->addWidget(_modifier, row, 1);
|
||||
++row;
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MouseWheelSettings::SetMouseWheelSettings(const MouseWheel &wheel)
|
||||
{
|
||||
_position->SetPosition(wheel._pos);
|
||||
_position->SetPosition(wheel._amount);
|
||||
_modifier->SetModifier(wheel._modifier);
|
||||
}
|
||||
|
||||
void MouseWheelSettings::PositionChanged(const Position &pos)
|
||||
{
|
||||
auto wheel = GetCurrentMouseWheel();
|
||||
wheel.SetPosition(pos);
|
||||
emit MouseWheelSettingsChanged(wheel);
|
||||
}
|
||||
|
||||
void MouseWheelSettings::AmountChanged(const Position &amount)
|
||||
{
|
||||
auto wheel = GetCurrentMouseWheel();
|
||||
wheel.SetAmount(amount);
|
||||
emit MouseWheelSettingsChanged(wheel);
|
||||
}
|
||||
|
||||
void MouseWheelSettings::ModifierChanged(const Modifier &modifier)
|
||||
{
|
||||
auto move = GetCurrentMouseWheel();
|
||||
move.SetModifier(modifier);
|
||||
emit MouseWheelSettingsChanged(move);
|
||||
}
|
||||
|
||||
MouseWheel MouseWheelSettings::GetCurrentMouseWheel() const
|
||||
{
|
||||
MouseWheel wheel;
|
||||
wheel.SetPosition(_position->GetPosition());
|
||||
wheel.SetAmount(_amount->GetPosition());
|
||||
wheel.SetModifier(_modifier->GetModifier());
|
||||
return wheel;
|
||||
}
|
||||
|
||||
KeyPressSettings::KeyPressSettings(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_text(new QLineEdit()),
|
||||
_up(new QCheckBox()),
|
||||
_modifier(new ModifierSelection)
|
||||
{
|
||||
_text->setMaxLength(1);
|
||||
|
||||
connect(_text, SIGNAL(editingFinished()), this, SLOT(TextChanged()));
|
||||
connect(_up, SIGNAL(stateChanged(int)), this, SLOT(UpChanged(int)));
|
||||
connect(_modifier, SIGNAL(ModifierChanged(const Modifier &)), this,
|
||||
SLOT(ModifierChanged(const Modifier &)));
|
||||
|
||||
auto layout = new QGridLayout();
|
||||
layout->setColumnStretch(1, 3);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(obs_module_text("AdvSceneSwitcher.key")),
|
||||
row, 0);
|
||||
layout->addWidget(_text, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.key.up")), row, 0);
|
||||
layout->addWidget(_up, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.modifierKey")),
|
||||
row, 0);
|
||||
layout->addWidget(_modifier, row, 1);
|
||||
++row;
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void KeyPressSettings::SetKeyPressSettings(const KeyPress &key)
|
||||
{
|
||||
_text->setText(QString(key._text));
|
||||
_up->setChecked(key._up);
|
||||
_modifier->SetModifier(key._modifier);
|
||||
}
|
||||
|
||||
void KeyPressSettings::TextChanged()
|
||||
{
|
||||
auto key = GetCurrentKeyPress();
|
||||
key.SetKey(_text->text().at(0).toLatin1());
|
||||
emit KeyPressSettingsChanged(key);
|
||||
}
|
||||
|
||||
void KeyPressSettings::UpChanged(int value)
|
||||
{
|
||||
auto key = GetCurrentKeyPress();
|
||||
key.SetDirection(value);
|
||||
emit KeyPressSettingsChanged(key);
|
||||
}
|
||||
|
||||
void KeyPressSettings::ModifierChanged(const Modifier &modifier)
|
||||
{
|
||||
auto move = GetCurrentKeyPress();
|
||||
move.SetModifier(modifier);
|
||||
emit KeyPressSettingsChanged(move);
|
||||
}
|
||||
|
||||
KeyPress KeyPressSettings::GetCurrentKeyPress() const
|
||||
{
|
||||
KeyPress key;
|
||||
key.SetDirection(_up->isChecked());
|
||||
key.SetModifier(_modifier->GetModifier());
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
337
plugins/base/utils/source-interact.hpp
Normal file
337
plugins/base/utils/source-interact.hpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#pragma once
|
||||
#include "variable-number.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QSpinBox>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <QLineEdit>
|
||||
#include <QLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct Position {
|
||||
void Save(obs_data_t *obj, const char *name = "position") const;
|
||||
void Load(obs_data_t *obj, const char *name = "position");
|
||||
IntVariable x = 0;
|
||||
IntVariable y = 0;
|
||||
};
|
||||
|
||||
class Modifier {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *name = "modifier") const;
|
||||
void Load(obs_data_t *obj, const char *name = "modifier");
|
||||
|
||||
void EnableShift(bool value) { _shift = value; }
|
||||
void EnableCtrl(bool value) { _ctrl = value; }
|
||||
void EnableAlt(bool value) { _alt = value; }
|
||||
void EnableMeta(bool value) { _meta = value; }
|
||||
uint32_t GetModifiers() const;
|
||||
|
||||
private:
|
||||
bool _shift = false;
|
||||
bool _ctrl = false;
|
||||
bool _alt = false;
|
||||
bool _meta = false;
|
||||
|
||||
friend class ModifierSelection;
|
||||
};
|
||||
|
||||
class SourceInteractionInstance;
|
||||
|
||||
class SourceInteraction {
|
||||
public:
|
||||
SourceInteraction();
|
||||
SourceInteraction(const SourceInteraction &);
|
||||
void Save(obs_data_t *obj, const char *name = "interaction") const;
|
||||
void Load(obs_data_t *obj, const char *name = "interaction");
|
||||
|
||||
enum class Type {
|
||||
CLICK,
|
||||
MOVE,
|
||||
WHEEL,
|
||||
KEY,
|
||||
};
|
||||
void SetType(Type);
|
||||
void SetSettings(SourceInteractionInstance *);
|
||||
void SendToSource(obs_source_t *source) const;
|
||||
|
||||
private:
|
||||
void UpdateType();
|
||||
|
||||
Type _type = Type::CLICK;
|
||||
std::unique_ptr<SourceInteractionInstance> _interaction;
|
||||
friend class SourceInteractionWidget;
|
||||
};
|
||||
|
||||
class SourceInteractionInstance {
|
||||
public:
|
||||
virtual void Save(obs_data_t *obj, const char *name) const = 0;
|
||||
virtual void Load(obs_data_t *obj, const char *name) = 0;
|
||||
|
||||
void SetModifier(Modifier modifier) { _modifier = modifier; }
|
||||
virtual void SendToSource(obs_source_t *source) const = 0;
|
||||
|
||||
virtual ~SourceInteractionInstance(){};
|
||||
|
||||
protected:
|
||||
Modifier _modifier;
|
||||
};
|
||||
|
||||
/* Interaction types below */
|
||||
|
||||
class MouseClick : public SourceInteractionInstance {
|
||||
public:
|
||||
void Save(obs_data_t *obj,
|
||||
const char *name = "mouseClick") const override;
|
||||
void Load(obs_data_t *obj, const char *name = "mouseClick") override;
|
||||
|
||||
enum class Button {
|
||||
LEFT,
|
||||
MIDDLE,
|
||||
RIGHT,
|
||||
};
|
||||
|
||||
void SetButton(Button button) { _button = button; }
|
||||
void SetDirection(bool up) { _up = up; }
|
||||
void SetCount(const IntVariable &count) { _count = count; }
|
||||
void SetPosition(const Position &pos) { _pos = pos; }
|
||||
|
||||
void SendToSource(obs_source_t *source) const override;
|
||||
|
||||
private:
|
||||
Button _button = Button::LEFT;
|
||||
Position _pos;
|
||||
bool _up = false;
|
||||
IntVariable _count = 1;
|
||||
|
||||
friend class MouseClickSettings;
|
||||
};
|
||||
|
||||
class MouseMove : public SourceInteractionInstance {
|
||||
public:
|
||||
void Save(obs_data_t *obj,
|
||||
const char *name = "mouseMove") const override;
|
||||
void Load(obs_data_t *obj, const char *name = "mouseMove") override;
|
||||
|
||||
void SetLeave(bool leave) { _leaveSource = leave; }
|
||||
void SetPosition(const Position &pos) { _pos = pos; }
|
||||
|
||||
void SendToSource(obs_source_t *source) const override;
|
||||
|
||||
private:
|
||||
Position _pos;
|
||||
bool _leaveSource = false;
|
||||
|
||||
friend class MouseMoveSettings;
|
||||
};
|
||||
|
||||
class MouseWheel : public SourceInteractionInstance {
|
||||
public:
|
||||
void Save(obs_data_t *obj,
|
||||
const char *name = "mouseWheel") const override;
|
||||
void Load(obs_data_t *obj, const char *name = "mouseWheel") override;
|
||||
|
||||
void SetPosition(const Position &pos) { _pos = pos; }
|
||||
void SetAmount(const Position &amount) { _amount = amount; }
|
||||
|
||||
void SendToSource(obs_source_t *source) const override;
|
||||
|
||||
private:
|
||||
Position _pos;
|
||||
Position _amount;
|
||||
|
||||
friend class MouseWheelSettings;
|
||||
};
|
||||
|
||||
class KeyPress : public SourceInteractionInstance {
|
||||
public:
|
||||
void Save(obs_data_t *obj,
|
||||
const char *name = "keyPress") const override;
|
||||
void Load(obs_data_t *obj, const char *name = "keyPress") override;
|
||||
|
||||
void SetDirection(bool up) { _up = up; }
|
||||
void SetKey(char value) { _text = value; }
|
||||
|
||||
void SendToSource(obs_source_t *source) const override;
|
||||
|
||||
private:
|
||||
char _text;
|
||||
bool _up = false;
|
||||
|
||||
friend class KeyPressSettings;
|
||||
};
|
||||
|
||||
/* Helper widgets below */
|
||||
|
||||
class PositionSelection : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PositionSelection(int min, int max, QWidget *parent = 0);
|
||||
void SetPosition(const Position &);
|
||||
Position GetPosition() const;
|
||||
|
||||
private slots:
|
||||
void XChanged(const NumberVariable<int> &);
|
||||
void YChanged(const NumberVariable<int> &);
|
||||
signals:
|
||||
void PositionChanged(const Position &value);
|
||||
|
||||
private:
|
||||
VariableSpinBox *_x;
|
||||
VariableSpinBox *_y;
|
||||
};
|
||||
|
||||
class ModifierSelection : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModifierSelection(QWidget *parent = 0);
|
||||
void SetModifier(const Modifier &);
|
||||
Modifier GetModifier() const { return GetCurrentModifier(); }
|
||||
|
||||
private slots:
|
||||
void ShiftChanged(int);
|
||||
void CtrlChanged(int);
|
||||
void AltChanged(int);
|
||||
void MetaChanged(int);
|
||||
signals:
|
||||
void ModifierChanged(const Modifier &value);
|
||||
|
||||
private:
|
||||
Modifier GetCurrentModifier() const;
|
||||
|
||||
QCheckBox *_shift;
|
||||
QCheckBox *_ctrl;
|
||||
QCheckBox *_alt;
|
||||
QCheckBox *_meta;
|
||||
};
|
||||
|
||||
/* Settings widgets below */
|
||||
|
||||
class SourceInteractionWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SourceInteractionWidget(QWidget *parent = 0);
|
||||
void SetSourceInteractionSelection(const SourceInteraction &);
|
||||
|
||||
private slots:
|
||||
void TypeChange(int);
|
||||
void MouseClickSettingsChanged(const MouseClick &);
|
||||
void MouseMoveSettingsChanged(const MouseMove &);
|
||||
void MouseWheelSettingsChanged(const MouseWheel &);
|
||||
void KeyPressSettingsChanged(const KeyPress &);
|
||||
signals:
|
||||
void TypeChanged(SourceInteraction::Type value);
|
||||
void SettingsChanged(SourceInteractionInstance *value);
|
||||
|
||||
private:
|
||||
void SetupSettingsWidget(SourceInteraction::Type);
|
||||
void SetupMouseClick();
|
||||
void SetupMouseMove();
|
||||
void SetupMouseWheel();
|
||||
void SetupKeyPress();
|
||||
|
||||
QComboBox *_type;
|
||||
QHBoxLayout *_settingsLayout;
|
||||
};
|
||||
|
||||
class MouseClickSettings : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MouseClickSettings(QWidget *parent = 0);
|
||||
void SetMouseClickSettings(const MouseClick &);
|
||||
|
||||
private slots:
|
||||
void ButtonChanged(int);
|
||||
void PositionChanged(const Position &);
|
||||
void UpChanged(int);
|
||||
void CountChanged(const NumberVariable<int> &);
|
||||
void ModifierChanged(const Modifier &);
|
||||
signals:
|
||||
void MouseClickSettingsChanged(const MouseClick &value);
|
||||
|
||||
private:
|
||||
MouseClick GetCurrentMouseClick() const;
|
||||
|
||||
QComboBox *_button;
|
||||
PositionSelection *_position;
|
||||
QCheckBox *_up;
|
||||
VariableSpinBox *_count;
|
||||
ModifierSelection *_modifier;
|
||||
};
|
||||
|
||||
class MouseMoveSettings : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MouseMoveSettings(QWidget *parent = 0);
|
||||
void SetMouseMoveSettings(const MouseMove &);
|
||||
|
||||
private slots:
|
||||
void PositionChanged(const Position &);
|
||||
void LeaveChanged(int);
|
||||
void ModifierChanged(const Modifier &);
|
||||
signals:
|
||||
void MouseMoveSettingsChanged(const MouseMove &value);
|
||||
|
||||
private:
|
||||
MouseMove GetCurrentMouseMove() const;
|
||||
|
||||
PositionSelection *_position;
|
||||
QCheckBox *_leaveEvent;
|
||||
ModifierSelection *_modifier;
|
||||
};
|
||||
|
||||
class MouseWheelSettings : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MouseWheelSettings(QWidget *parent = 0);
|
||||
void SetMouseWheelSettings(const MouseWheel &);
|
||||
|
||||
private slots:
|
||||
void PositionChanged(const Position &);
|
||||
void AmountChanged(const Position &);
|
||||
void ModifierChanged(const Modifier &);
|
||||
signals:
|
||||
void MouseWheelSettingsChanged(const MouseWheel &value);
|
||||
|
||||
private:
|
||||
MouseWheel GetCurrentMouseWheel() const;
|
||||
|
||||
PositionSelection *_position;
|
||||
PositionSelection *_amount;
|
||||
ModifierSelection *_modifier;
|
||||
};
|
||||
|
||||
class KeyPressSettings : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KeyPressSettings(QWidget *parent = 0);
|
||||
void SetKeyPressSettings(const KeyPress &);
|
||||
|
||||
private slots:
|
||||
void TextChanged();
|
||||
void UpChanged(int);
|
||||
void ModifierChanged(const Modifier &);
|
||||
signals:
|
||||
void KeyPressSettingsChanged(const KeyPress &value);
|
||||
|
||||
private:
|
||||
KeyPress GetCurrentKeyPress() const;
|
||||
|
||||
QLineEdit *_text;
|
||||
QCheckBox *_up;
|
||||
ModifierSelection *_modifier;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -33,6 +33,9 @@ bool MacroConditionMidi::CheckCondition()
|
||||
}
|
||||
if (message->Matches(_message)) {
|
||||
SetVariableValues(*message);
|
||||
if (_clearBufferOnMatch) {
|
||||
_messageBuffer->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -45,6 +48,8 @@ bool MacroConditionMidi::Save(obs_data_t *obj) const
|
||||
MacroCondition::Save(obj);
|
||||
_message.Save(obj);
|
||||
_device.Save(obj);
|
||||
obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,6 +59,10 @@ bool MacroConditionMidi::Load(obs_data_t *obj)
|
||||
_message.Load(obj);
|
||||
_device.Load(obj);
|
||||
_messageBuffer = _device.RegisterForMidiMessages();
|
||||
_clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch");
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
_clearBufferOnMatch = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,7 +115,9 @@ MacroConditionMidiEdit::MacroConditionMidiEdit(
|
||||
_resetMidiDevices(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.midi.resetDevices"))),
|
||||
_listen(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.midi.startListen")))
|
||||
obs_module_text("AdvSceneSwitcher.midi.startListen"))),
|
||||
_clearBufferOnMatch(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.clearBufferOnMatch")))
|
||||
{
|
||||
QWidget::connect(_devices,
|
||||
SIGNAL(DeviceSelectionChanged(const MidiDevice &)),
|
||||
@@ -119,6 +130,8 @@ MacroConditionMidiEdit::MacroConditionMidiEdit(
|
||||
SLOT(ResetMidiDevices()));
|
||||
QWidget::connect(_listen, SIGNAL(clicked()), this,
|
||||
SLOT(ToggleListen()));
|
||||
QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ClearBufferOnMatchChanged(int)));
|
||||
QWidget::connect(&_listenTimer, SIGNAL(timeout()), this,
|
||||
SLOT(SetMessageSelectionToLastReceived()));
|
||||
|
||||
@@ -135,6 +148,7 @@ MacroConditionMidiEdit::MacroConditionMidiEdit(
|
||||
mainLayout->addWidget(_message);
|
||||
mainLayout->addLayout(listenLayout);
|
||||
mainLayout->addWidget(_resetMidiDevices);
|
||||
mainLayout->addWidget(_clearBufferOnMatch);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_listenTimer.setInterval(100);
|
||||
@@ -157,6 +171,7 @@ void MacroConditionMidiEdit::UpdateEntryData()
|
||||
|
||||
_message->SetMessage(_entryData->_message);
|
||||
_devices->SetDevice(_entryData->GetDevice());
|
||||
_clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
@@ -182,14 +197,16 @@ void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device)
|
||||
|
||||
void MacroConditionMidiEdit::MidiMessageChanged(const MidiMessage &message)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_message = message;
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::ClearBufferOnMatchChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_clearBufferOnMatch = value;
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::ResetMidiDevices()
|
||||
{
|
||||
auto lock = LockContext();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "midi-helpers.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
@@ -23,6 +24,7 @@ public:
|
||||
void SetDevice(const MidiDevice &dev);
|
||||
const MidiDevice &GetDevice() const { return _device; }
|
||||
MidiMessage _message;
|
||||
bool _clearBufferOnMatch = true;
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
@@ -55,6 +57,7 @@ public:
|
||||
private slots:
|
||||
void DeviceSelectionChanged(const MidiDevice &);
|
||||
void MidiMessageChanged(const MidiMessage &);
|
||||
void ClearBufferOnMatchChanged(int);
|
||||
void ResetMidiDevices();
|
||||
void ToggleListen();
|
||||
void SetMessageSelectionToLastReceived();
|
||||
@@ -64,12 +67,13 @@ signals:
|
||||
private:
|
||||
void EnableListening(bool);
|
||||
|
||||
std::shared_ptr<MacroConditionMidi> _entryData;
|
||||
|
||||
MidiDeviceSelection *_devices;
|
||||
MidiMessageSelection *_message;
|
||||
QPushButton *_resetMidiDevices;
|
||||
QPushButton *_listen;
|
||||
QCheckBox *_clearBufferOnMatch;
|
||||
|
||||
std::shared_ptr<MacroConditionMidi> _entryData;
|
||||
QTimer _listenTimer;
|
||||
MidiMessageBuffer _messageBuffer;
|
||||
bool _currentlyListening = false;
|
||||
|
||||
@@ -281,9 +281,8 @@ void ProgressDialog::CategoryCountUpdated(int value)
|
||||
}
|
||||
|
||||
TwitchCategorySearchButton::TwitchCategorySearchButton(QWidget *parent)
|
||||
: QPushButton(parent)
|
||||
: QToolButton(parent)
|
||||
{
|
||||
setMaximumWidth(22);
|
||||
const std::string pathPrefix =
|
||||
GetDataFilePath("res/images/" + GetThemeTypeName());
|
||||
SetButtonIcon(this, (pathPrefix + "Search.svg").c_str());
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -94,7 +94,7 @@ private:
|
||||
static std::map<QString, int> _streamingCategories;
|
||||
};
|
||||
|
||||
class TwitchCategorySearchButton : public QPushButton {
|
||||
class TwitchCategorySearchButton : public QToolButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
@@ -175,19 +175,26 @@ void MacroActionTwitch::StartCommercial(
|
||||
"/helix/channels/commercial", {},
|
||||
data.Get());
|
||||
|
||||
if (result.status != 200) {
|
||||
if (result.status == 200) {
|
||||
OBSDataArrayAutoRelease replyArray =
|
||||
obs_data_get_array(result.data, "data");
|
||||
OBSDataAutoRelease replyData =
|
||||
obs_data_array_item(replyArray, 0);
|
||||
vblog(LOG_INFO,
|
||||
"Commercial started! (%d)\n"
|
||||
"length: %lld\n"
|
||||
"message: %s\n"
|
||||
"retry_after: %lld\n",
|
||||
result.status, obs_data_get_int(replyData, "length"),
|
||||
obs_data_get_string(replyData, "message"),
|
||||
obs_data_get_int(replyData, "retry_after"));
|
||||
} else {
|
||||
blog(LOG_INFO,
|
||||
"Failed to start commercial! (%d)\n"
|
||||
"length: %lld\n"
|
||||
"message: %s\n"
|
||||
"retry_after: %lld\n",
|
||||
result.status, obs_data_get_int(replyData, "length"),
|
||||
obs_data_get_string(replyData, "message"),
|
||||
obs_data_get_int(replyData, "retry_after"));
|
||||
"error: %s\n"
|
||||
"message: %s\n",
|
||||
result.status, obs_data_get_string(result.data, "error"),
|
||||
obs_data_get_string(result.data, "message"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -305,6 +305,10 @@ bool MacroConditionTwitch::CheckChannelGenericEvents(TwitchToken &token)
|
||||
std::bind(&MacroConditionTwitch::SetTempVarValue, this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
|
||||
if (_clearBufferOnMatch) {
|
||||
_eventBuffer->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -343,6 +347,10 @@ bool MacroConditionTwitch::CheckChannelLiveEvents(TwitchToken &token)
|
||||
std::bind(&MacroConditionTwitch::SetTempVarValue, this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
|
||||
if (_clearBufferOnMatch) {
|
||||
_eventBuffer->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -376,11 +384,18 @@ bool MacroConditionTwitch::CheckChatMessages(TwitchToken &token)
|
||||
if (!message) {
|
||||
continue;
|
||||
}
|
||||
if (stringMatches(_regexChat, message->message, _chatMessage)) {
|
||||
SetTempVarValue("chatter", message->source.nick);
|
||||
SetTempVarValue("chat_message", message->message);
|
||||
return true;
|
||||
if (!stringMatches(_regexChat, message->message,
|
||||
_chatMessage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SetTempVarValue("chatter", message->source.nick);
|
||||
SetTempVarValue("chat_message", message->message);
|
||||
|
||||
if (_clearBufferOnMatch) {
|
||||
_eventBuffer->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -588,6 +603,8 @@ bool MacroConditionTwitch::Save(obs_data_t *obj) const
|
||||
_chatMessage.Save(obj, "chatMessage");
|
||||
_regexChat.Save(obj, "regexChat");
|
||||
_category.Save(obj);
|
||||
obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -605,6 +622,10 @@ bool MacroConditionTwitch::Load(obs_data_t *obj)
|
||||
_chatMessage.Load(obj, "chatMessage");
|
||||
_regexChat.Load(obj, "regexChat");
|
||||
_category.Load(obj);
|
||||
_clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch");
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
_clearBufferOnMatch = false;
|
||||
}
|
||||
|
||||
_subscriptionID = "";
|
||||
ResetChatConnection();
|
||||
@@ -1244,7 +1265,9 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
||||
_regexTitle(new RegexConfigWidget(parent)),
|
||||
_chatMessage(new VariableTextEdit(this, 5, 1, 1)),
|
||||
_regexChat(new RegexConfigWidget(parent)),
|
||||
_category(new TwitchCategoryWidget(this))
|
||||
_category(new TwitchCategoryWidget(this)),
|
||||
_clearBufferOnMatch(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.clearBufferOnMatch")))
|
||||
{
|
||||
_streamTitle->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
@@ -1280,6 +1303,8 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
||||
SLOT(CategoreyChanged(const TwitchCategory &)));
|
||||
QWidget::connect(this, SIGNAL(TempVarsChanged()), window(),
|
||||
SIGNAL(SegmentTempVarsChanged()));
|
||||
QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ClearBufferOnMatchChanged(int)));
|
||||
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.twitch.entry"),
|
||||
_layout,
|
||||
@@ -1305,6 +1330,7 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
||||
mainLayout->addLayout(chatLayout);
|
||||
mainLayout->addLayout(accountLayout);
|
||||
mainLayout->addWidget(_tokenWarning);
|
||||
mainLayout->addWidget(_clearBufferOnMatch);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_tokenCheckTimer.start(1000);
|
||||
@@ -1336,11 +1362,7 @@ void MacroConditionTwitchEdit::ConditionChanged(int idx)
|
||||
|
||||
void MacroConditionTwitchEdit::TwitchTokenChanged(const QString &token)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetToken(GetWeakTwitchTokenByQString(token));
|
||||
_category->SetToken(_entryData->GetToken());
|
||||
_channel->SetToken(_entryData->GetToken());
|
||||
@@ -1390,11 +1412,7 @@ void MacroConditionTwitchEdit::CheckToken()
|
||||
|
||||
void MacroConditionTwitchEdit::ChannelChanged(const TwitchChannel &channel)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetChannel(channel);
|
||||
_pointsReward->SetChannel(channel);
|
||||
_entryData->ResetChatConnection();
|
||||
@@ -1403,41 +1421,25 @@ void MacroConditionTwitchEdit::ChannelChanged(const TwitchChannel &channel)
|
||||
void MacroConditionTwitchEdit::PointsRewardChanged(
|
||||
const TwitchPointsReward &pointsReward)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->SetPointsReward(pointsReward);
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::StreamTitleChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_streamTitle = _streamTitle->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::ChatMessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_chatMessage = _chatMessage->toPlainText().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_regexTitle = conf;
|
||||
|
||||
adjustSize();
|
||||
@@ -1446,11 +1448,7 @@ void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
|
||||
|
||||
void MacroConditionTwitchEdit::RegexChatChanged(const RegexConfig &conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_regexChat = conf;
|
||||
|
||||
adjustSize();
|
||||
@@ -1459,14 +1457,16 @@ void MacroConditionTwitchEdit::RegexChatChanged(const RegexConfig &conf)
|
||||
|
||||
void MacroConditionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_category = category;
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::ClearBufferOnMatchChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_clearBufferOnMatch = value;
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::SetWidgetVisibility()
|
||||
{
|
||||
auto condition = _entryData->GetCondition();
|
||||
@@ -1492,6 +1492,10 @@ void MacroConditionTwitchEdit::SetWidgetVisibility()
|
||||
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED);
|
||||
_category->setVisible(
|
||||
condition == MacroConditionTwitch::Condition::CATEGORY_POLLING);
|
||||
_clearBufferOnMatch->setVisible(
|
||||
_entryData->IsUsingEventSubCondition() ||
|
||||
_entryData->GetCondition() ==
|
||||
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED);
|
||||
|
||||
if (condition == MacroConditionTwitch::Condition::TITLE_POLLING) {
|
||||
RemoveStretchIfPresent(_layout);
|
||||
@@ -1525,6 +1529,7 @@ void MacroConditionTwitchEdit::UpdateEntryData()
|
||||
_regexChat->SetRegexConfig(_entryData->_regexChat);
|
||||
_category->SetToken(_entryData->GetToken());
|
||||
_category->SetCategory(_entryData->_category);
|
||||
_clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch);
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ public:
|
||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||
TwitchPointsReward GetPointsReward() const { return _pointsReward; }
|
||||
void ResetChatConnection();
|
||||
bool IsUsingEventSubCondition();
|
||||
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
@@ -103,6 +104,7 @@ public:
|
||||
StringVariable _chatMessage;
|
||||
RegexConfig _regexChat = RegexConfig::PartialMatchRegexConfig();
|
||||
TwitchCategory _category;
|
||||
bool _clearBufferOnMatch = false;
|
||||
|
||||
private:
|
||||
bool CheckChannelGenericEvents(TwitchToken &token);
|
||||
@@ -112,7 +114,6 @@ private:
|
||||
void RegisterEventSubscription();
|
||||
void ResetSubscription();
|
||||
void SetupEventSubscription(EventSub &);
|
||||
bool IsUsingEventSubCondition();
|
||||
bool EventSubscriptionIsSetup(const std::shared_ptr<EventSub> &);
|
||||
void AddChannelGenericEventSubscription(
|
||||
const char *version, bool includeModeratorId = false,
|
||||
@@ -169,14 +170,12 @@ private slots:
|
||||
void RegexTitleChanged(const RegexConfig &);
|
||||
void RegexChatChanged(const RegexConfig &);
|
||||
void CategoreyChanged(const TwitchCategory &);
|
||||
void ClearBufferOnMatchChanged(int);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
void TempVarsChanged();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroConditionTwitch> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
void SetTokenWarning(bool visible, const QString &text = "");
|
||||
@@ -193,7 +192,9 @@ private:
|
||||
VariableTextEdit *_chatMessage;
|
||||
RegexConfigWidget *_regexChat;
|
||||
TwitchCategoryWidget *_category;
|
||||
QCheckBox *_clearBufferOnMatch;
|
||||
|
||||
std::shared_ptr<MacroConditionTwitch> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
110
plugins/usb/CMakeLists.txt
Normal file
110
plugins/usb/CMakeLists.txt
Normal file
@@ -0,0 +1,110 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(advanced-scene-switcher-usb)
|
||||
|
||||
# --- Check libusb requirements ---
|
||||
|
||||
get_target_property(ADVSS_SOURCE_DIR advanced-scene-switcher-lib SOURCE_DIR)
|
||||
set(libusb_DEPS_DIR "${ADVSS_SOURCE_DIR}/deps/libusb")
|
||||
|
||||
if(OS_LINUX)
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(NOT PKG_CONFIG_FOUND)
|
||||
message(WARNING "pkg-config not found!\n" "USB condition will be disabled!")
|
||||
return()
|
||||
endif()
|
||||
pkg_check_modules(libusb OPTIONAL libusb)
|
||||
find_path(
|
||||
libusb_HEADER_DIR
|
||||
NAMES libusb.h
|
||||
PATHS ${libusb_INCLUDEDIR} /usr/include /usr/local/include
|
||||
/opt/local/include
|
||||
PATH_SUFFIXES libusb-1.0)
|
||||
find_library(
|
||||
libusb_LINK_LIBRARIES
|
||||
NAMES usb-1.0 usb
|
||||
PATHS /usr/lib /usr/local/lib /opt/local/lib)
|
||||
|
||||
elseif(OS_MACOS)
|
||||
|
||||
find_path(
|
||||
libusb_HEADER_DIR
|
||||
NAMES libusb.h
|
||||
PATHS ${libusb_DEPS_DIR}/libusb)
|
||||
|
||||
# The NO_DEFAULT_PATH option is set since we have to use the "universal"
|
||||
# variant of the library, which combines the x86 and arm variants
|
||||
find_library(
|
||||
libusb_LINK_LIBRARIES
|
||||
NAMES usb-1.0.0 usb-1.0 usb
|
||||
PATHS ${CMAKE_PREFIX_PATH}
|
||||
PATH_SUFFIXES lib
|
||||
NO_DEFAULT_PATH)
|
||||
find_file(
|
||||
libusb_RUNTIME_LIBRARY
|
||||
NAMES libusb-1.0.0.dylib
|
||||
PATHS ${CMAKE_PREFIX_PATH}
|
||||
PATH_SUFFIXES lib
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
elseif(OS_WINDOWS)
|
||||
|
||||
find_path(
|
||||
libusb_HEADER_DIR
|
||||
NAMES libusb.h
|
||||
PATHS ${libusb_DEPS_DIR}/libusb)
|
||||
|
||||
# Intentionally not using find_library since we have to use the libusb-1.0.lib
|
||||
# file from within the dll folder instead of the lib folder to avoid linking
|
||||
# issues due to conflicting LIBCMT versions
|
||||
find_file(
|
||||
libusb_LINK_LIBRARIES
|
||||
NAMES libusb-1.0.lib
|
||||
PATHS ${CMAKE_PREFIX_PATH}
|
||||
PATH_SUFFIXES dll)
|
||||
|
||||
find_file(
|
||||
libusb_RUNTIME_LIBRARY
|
||||
NAMES libusb-1.0.dll
|
||||
PATHS ${CMAKE_PREFIX_PATH}
|
||||
PATH_SUFFIXES dll)
|
||||
|
||||
endif()
|
||||
|
||||
if(NOT libusb_HEADER_DIR)
|
||||
message(WARNING "libusb headers not found. Make sure libusb is installed. "
|
||||
"USB condition will be disabled!\n\n"
|
||||
"libusb sources are available under: ${libusb_DEPS_DIR}")
|
||||
return()
|
||||
endif()
|
||||
if(NOT libusb_LINK_LIBRARIES)
|
||||
message(WARNING "libusb library not found. Make sure libusb is installed. "
|
||||
"USB condition will be disabled!\n\n"
|
||||
"libusb sources are available under: ${libusb_DEPS_DIR}")
|
||||
return()
|
||||
endif()
|
||||
if(NOT OS_LINUX AND NOT libusb_RUNTIME_LIBRARY)
|
||||
message(
|
||||
WARNING "libusb runtime library not found. Make sure libusb is installed. "
|
||||
"USB condition will be disabled!\n\n"
|
||||
"libusb sources are available under: ${libusb_DEPS_DIR}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
add_library(${PROJECT_NAME} MODULE)
|
||||
|
||||
target_sources(
|
||||
${PROJECT_NAME} PRIVATE macro-condition-usb.cpp macro-condition-usb.hpp
|
||||
usb-helpers.cpp usb-helpers.hpp)
|
||||
|
||||
setup_advss_plugin(${PROJECT_NAME})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${libusb_HEADER_DIR})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${libusb_LINK_LIBRARIES})
|
||||
install_advss_plugin(${PROJECT_NAME})
|
||||
if(NOT OS_LINUX)
|
||||
install_advss_plugin_dependency(TARGET ${PROJECT_NAME} DEPENDENCIES
|
||||
${libusb_RUNTIME_LIBRARY})
|
||||
endif()
|
||||
381
plugins/usb/macro-condition-usb.cpp
Normal file
381
plugins/usb/macro-condition-usb.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
#include "macro-condition-usb.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
#include "obs.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionUSB::id = "usb";
|
||||
|
||||
bool MacroConditionUSB::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionUSB::id,
|
||||
{MacroConditionUSB::Create, MacroConditionUSBEdit::Create,
|
||||
"AdvSceneSwitcher.condition.usb"});
|
||||
|
||||
bool MacroConditionUSB::DeviceMatchOption::Matches(
|
||||
const std::string &value) const
|
||||
{
|
||||
if (!regex.Enabled()) {
|
||||
return pattern == value;
|
||||
}
|
||||
return regex.Matches(value, pattern);
|
||||
}
|
||||
|
||||
void MacroConditionUSB::DeviceMatchOption::Save(obs_data_t *obj,
|
||||
const char *name) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_string(data, "pattern", pattern.c_str());
|
||||
regex.Save(data);
|
||||
obs_data_set_obj(obj, name, data);
|
||||
}
|
||||
|
||||
void MacroConditionUSB::DeviceMatchOption::Load(obs_data_t *obj,
|
||||
const char *name)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
|
||||
pattern = obs_data_get_string(data, "pattern");
|
||||
regex.Load(data);
|
||||
}
|
||||
|
||||
bool MacroConditionUSB::CheckCondition()
|
||||
{
|
||||
const auto devs = GetUSBDevices();
|
||||
if (devs.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &dev : devs) {
|
||||
if (_vendorID.Matches(dev.vendorID) &&
|
||||
_productID.Matches(dev.productID) &&
|
||||
_busNumber.Matches(dev.busNumber) &&
|
||||
_deviceAddress.Matches(dev.deviceAddress) &&
|
||||
_vendorName.Matches(dev.vendorName) &&
|
||||
_productName.Matches(dev.productName) &&
|
||||
_serialNumber.Matches(dev.serialNumber)) {
|
||||
SetTempVarValue("vendorID", dev.vendorID);
|
||||
SetTempVarValue("productID", dev.productID);
|
||||
SetTempVarValue("busNumber", dev.busNumber);
|
||||
SetTempVarValue("deviceAddress", dev.deviceAddress);
|
||||
SetTempVarValue("vendorName", dev.vendorName);
|
||||
SetTempVarValue("productName", dev.productName);
|
||||
SetTempVarValue("serialNumber", dev.serialNumber);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionUSB::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_vendorID.Save(obj, "vendorID");
|
||||
_productID.Save(obj, "productID");
|
||||
_busNumber.Save(obj, "busNumber");
|
||||
_deviceAddress.Save(obj, "deviceAddress");
|
||||
_vendorName.Save(obj, "vendorName");
|
||||
_productName.Save(obj, "productName");
|
||||
_serialNumber.Save(obj, "serialNumber");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionUSB::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_vendorID.Load(obj, "vendorID");
|
||||
_productID.Load(obj, "productID");
|
||||
_busNumber.Load(obj, "busNumber");
|
||||
_deviceAddress.Load(obj, "deviceAddress");
|
||||
_vendorName.Load(obj, "vendorName");
|
||||
_productName.Load(obj, "productName");
|
||||
_serialNumber.Load(obj, "serialNumber");
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionUSB::SetupTempVars()
|
||||
{
|
||||
AddTempvar("vendorName",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.vendorName"));
|
||||
AddTempvar("productName",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.productName"));
|
||||
AddTempvar("vendorID",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.vendorID"));
|
||||
AddTempvar("productID",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.productID"));
|
||||
AddTempvar("busNumber",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.busNumber"));
|
||||
AddTempvar(
|
||||
"deviceAddress",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.deviceAddress"));
|
||||
AddTempvar(
|
||||
"serialNumber",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.usb.serialNumber"));
|
||||
}
|
||||
|
||||
static void setupDevicePropertySelection(QComboBox *list,
|
||||
const QSet<QString> &items)
|
||||
{
|
||||
list->setEditable(true);
|
||||
list->setMaxVisibleItems(20);
|
||||
list->setDuplicatesEnabled(false);
|
||||
for (const auto &item : items) {
|
||||
list->addItem(item);
|
||||
}
|
||||
list->model()->sort(0);
|
||||
}
|
||||
|
||||
void static populateNewLayoutRow(QGridLayout *layout, int &row,
|
||||
const char *labelText, QComboBox *property,
|
||||
RegexConfigWidget *regex)
|
||||
{
|
||||
layout->addWidget(new QLabel(obs_module_text(labelText)), row, 0);
|
||||
auto settingsLayout = new QHBoxLayout();
|
||||
property->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::MinimumExpanding);
|
||||
settingsLayout->addWidget(property);
|
||||
settingsLayout->addWidget(regex);
|
||||
settingsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addLayout(settingsLayout, row, 1);
|
||||
row++;
|
||||
}
|
||||
|
||||
MacroConditionUSBEdit::MacroConditionUSBEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionUSB> entryData)
|
||||
: QWidget(parent),
|
||||
_vendorID(new QComboBox()),
|
||||
_productID(new QComboBox()),
|
||||
_busNumber(new QComboBox()),
|
||||
_deviceAddress(new QComboBox()),
|
||||
_vendorName(new QComboBox()),
|
||||
_productName(new QComboBox()),
|
||||
_serialNumber(new QComboBox()),
|
||||
_vendorIDRegex(new RegexConfigWidget()),
|
||||
_productIDRegex(new RegexConfigWidget()),
|
||||
_busNumberRegex(new RegexConfigWidget()),
|
||||
_deviceAddressRegex(new RegexConfigWidget()),
|
||||
_vendorNameRegex(new RegexConfigWidget()),
|
||||
_productNameRegex(new RegexConfigWidget()),
|
||||
_serialNumberRegex(new RegexConfigWidget())
|
||||
{
|
||||
QSet<QString> vendorIDs;
|
||||
QSet<QString> productIDs;
|
||||
QSet<QString> busNumbers;
|
||||
QSet<QString> deviceAddresses;
|
||||
QSet<QString> vendorNames;
|
||||
QSet<QString> productNames;
|
||||
QSet<QString> serialNumbers;
|
||||
|
||||
const auto devs = GetUSBDevices();
|
||||
for (const auto &dev : devs) {
|
||||
vendorIDs.insert(QString::fromStdString(dev.vendorID));
|
||||
productIDs.insert(QString::fromStdString(dev.productID));
|
||||
busNumbers.insert(QString::fromStdString(dev.busNumber));
|
||||
deviceAddresses.insert(
|
||||
QString::fromStdString(dev.deviceAddress));
|
||||
vendorNames.insert(QString::fromStdString(dev.vendorName));
|
||||
productNames.insert(QString::fromStdString(dev.productName));
|
||||
serialNumbers.insert(QString::fromStdString(dev.serialNumber));
|
||||
}
|
||||
|
||||
setupDevicePropertySelection(_vendorID, vendorIDs);
|
||||
setupDevicePropertySelection(_productID, productIDs);
|
||||
setupDevicePropertySelection(_busNumber, busNumbers);
|
||||
setupDevicePropertySelection(_deviceAddress, deviceAddresses);
|
||||
setupDevicePropertySelection(_vendorName, vendorNames);
|
||||
setupDevicePropertySelection(_productName, productNames);
|
||||
setupDevicePropertySelection(_serialNumber, serialNumbers);
|
||||
|
||||
QWidget::connect(_vendorID, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::VendorIDChanged);
|
||||
QWidget::connect(_productID, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::ProductIDChanged);
|
||||
QWidget::connect(_busNumber, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::BusNumberChanged);
|
||||
QWidget::connect(_deviceAddress, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::DeviceAddressChanged);
|
||||
QWidget::connect(_vendorName, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::VendorNameChanged);
|
||||
QWidget::connect(_productName, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::ProductNameChanged);
|
||||
QWidget::connect(_serialNumber, &QComboBox::currentTextChanged, this,
|
||||
&MacroConditionUSBEdit::SerialNumberChanged);
|
||||
QWidget::connect(_vendorIDRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(VendorIDRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_productIDRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(ProductIDRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_busNumberRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(BusNumberRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_deviceAddressRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(DeviceAddressRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_vendorNameRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(VendorNameRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_productNameRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(ProductNameRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_serialNumberRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(SerialNumberRegexChanged(const RegexConfig &)));
|
||||
|
||||
int row = 0;
|
||||
auto devicePropertyLayout = new QGridLayout;
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.vendorName",
|
||||
_vendorName, _vendorNameRegex);
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.productName",
|
||||
_productName, _productNameRegex);
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.vendorID",
|
||||
_vendorID, _vendorIDRegex);
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.productID",
|
||||
_productID, _productIDRegex);
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.busNumber",
|
||||
_busNumber, _busNumberRegex);
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.deviceAddress",
|
||||
_deviceAddress, _deviceAddressRegex);
|
||||
populateNewLayoutRow(devicePropertyLayout, row,
|
||||
"AdvSceneSwitcher.condition.usb.serialNumber",
|
||||
_serialNumber, _serialNumberRegex);
|
||||
MinimizeSizeOfColumn(devicePropertyLayout, 0);
|
||||
devicePropertyLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.condition.usb.description")));
|
||||
layout->addLayout(devicePropertyLayout);
|
||||
if (devs.empty()) {
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.noDevicesFoundWarning")));
|
||||
}
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_vendorID->setCurrentText(
|
||||
QString::fromStdString(_entryData->_vendorID.pattern));
|
||||
_productID->setCurrentText(
|
||||
QString::fromStdString(_entryData->_productID.pattern));
|
||||
_busNumber->setCurrentText(
|
||||
QString::fromStdString(_entryData->_busNumber.pattern));
|
||||
_deviceAddress->setCurrentText(
|
||||
QString::fromStdString(_entryData->_deviceAddress.pattern));
|
||||
_vendorName->setCurrentText(
|
||||
QString::fromStdString(_entryData->_vendorName.pattern));
|
||||
_productName->setCurrentText(
|
||||
QString::fromStdString(_entryData->_productName.pattern));
|
||||
_serialNumber->setCurrentText(
|
||||
QString::fromStdString(_entryData->_serialNumber.pattern));
|
||||
_vendorIDRegex->SetRegexConfig(_entryData->_vendorID.regex);
|
||||
_productIDRegex->SetRegexConfig(_entryData->_productID.regex);
|
||||
_busNumberRegex->SetRegexConfig(_entryData->_busNumber.regex);
|
||||
_deviceAddressRegex->SetRegexConfig(_entryData->_deviceAddress.regex);
|
||||
_vendorNameRegex->SetRegexConfig(_entryData->_vendorName.regex);
|
||||
_productNameRegex->SetRegexConfig(_entryData->_productName.regex);
|
||||
_serialNumberRegex->SetRegexConfig(_entryData->_serialNumber.regex);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::VendorIDChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_vendorID.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::ProductIDChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_productID.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::BusNumberChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_busNumber.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::DeviceAddressChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_deviceAddress.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::VendorNameChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_vendorName.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::ProductNameChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_productName.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::SerialNumberChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_serialNumber.pattern = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::VendorIDRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_vendorID.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::ProductIDRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_productID.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::BusNumberRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_busNumber.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::DeviceAddressRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_deviceAddress.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::VendorNameRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_vendorName.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::ProductNameRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_productName.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionUSBEdit::SerialNumberRegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_serialNumber.regex = regex;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
97
plugins/usb/macro-condition-usb.hpp
Normal file
97
plugins/usb/macro-condition-usb.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "usb-helpers.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionUSB : public MacroCondition {
|
||||
public:
|
||||
MacroConditionUSB(Macro *m) : MacroCondition(m, true) {}
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionUSB>(m);
|
||||
}
|
||||
|
||||
struct DeviceMatchOption {
|
||||
std::string pattern = ".*";
|
||||
RegexConfig regex = RegexConfig(true);
|
||||
|
||||
bool Matches(const std::string &value) const;
|
||||
void Save(obs_data_t *, const char *) const;
|
||||
void Load(obs_data_t *, const char *);
|
||||
};
|
||||
|
||||
DeviceMatchOption _vendorID;
|
||||
DeviceMatchOption _productID;
|
||||
DeviceMatchOption _busNumber;
|
||||
DeviceMatchOption _deviceAddress;
|
||||
DeviceMatchOption _vendorName;
|
||||
DeviceMatchOption _productName;
|
||||
DeviceMatchOption _serialNumber;
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionUSBEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionUSBEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroConditionUSB> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionUSBEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionUSB>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void VendorIDChanged(const QString &text);
|
||||
void ProductIDChanged(const QString &text);
|
||||
void BusNumberChanged(const QString &text);
|
||||
void DeviceAddressChanged(const QString &text);
|
||||
void VendorNameChanged(const QString &text);
|
||||
void ProductNameChanged(const QString &text);
|
||||
void SerialNumberChanged(const QString &text);
|
||||
void VendorIDRegexChanged(const RegexConfig ®ex);
|
||||
void ProductIDRegexChanged(const RegexConfig ®ex);
|
||||
void BusNumberRegexChanged(const RegexConfig ®ex);
|
||||
void DeviceAddressRegexChanged(const RegexConfig ®ex);
|
||||
void VendorNameRegexChanged(const RegexConfig ®ex);
|
||||
void ProductNameRegexChanged(const RegexConfig ®ex);
|
||||
void SerialNumberRegexChanged(const RegexConfig ®ex);
|
||||
|
||||
private:
|
||||
QComboBox *_vendorID;
|
||||
QComboBox *_productID;
|
||||
QComboBox *_busNumber;
|
||||
QComboBox *_deviceAddress;
|
||||
QComboBox *_vendorName;
|
||||
QComboBox *_productName;
|
||||
QComboBox *_serialNumber;
|
||||
RegexConfigWidget *_vendorIDRegex;
|
||||
RegexConfigWidget *_productIDRegex;
|
||||
RegexConfigWidget *_busNumberRegex;
|
||||
RegexConfigWidget *_deviceAddressRegex;
|
||||
RegexConfigWidget *_vendorNameRegex;
|
||||
RegexConfigWidget *_productNameRegex;
|
||||
RegexConfigWidget *_serialNumberRegex;
|
||||
|
||||
std::shared_ptr<MacroConditionUSB> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
233
plugins/usb/usb-helpers.cpp
Normal file
233
plugins/usb/usb-helpers.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "usb-helpers.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <libusb.h>
|
||||
#include <mutex>
|
||||
|
||||
#define LOG_PREFIX "[usb] "
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool setup();
|
||||
static bool setupDone = setup();
|
||||
static bool hotplugsAreSupported = false;
|
||||
static std::mutex mutex;
|
||||
|
||||
static bool setup()
|
||||
{
|
||||
AddPluginInitStep([]() {
|
||||
libusb_init(NULL);
|
||||
hotplugsAreSupported =
|
||||
libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG);
|
||||
});
|
||||
AddPluginCleanupStep([]() { libusb_exit(NULL); });
|
||||
return true;
|
||||
}
|
||||
|
||||
static void logLibusbError(int value, const char *msg)
|
||||
{
|
||||
if (value >= LIBUSB_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
vblog(LOG_WARNING, LOG_PREFIX "%s: %s", msg, libusb_strerror(value));
|
||||
}
|
||||
|
||||
static USBDeviceInfo
|
||||
getDeviceInfo(libusb_device *device, libusb_device_handle *handle,
|
||||
const struct libusb_device_descriptor &descriptor)
|
||||
{
|
||||
char vendor_name[256] = {};
|
||||
char product_name[256] = {};
|
||||
char serial_number[256] = {};
|
||||
int ret = libusb_get_string_descriptor_ascii(handle,
|
||||
descriptor.iManufacturer,
|
||||
(uint8_t *)vendor_name,
|
||||
sizeof(vendor_name));
|
||||
logLibusbError(ret, "Failed to query vendor name");
|
||||
ret = libusb_get_string_descriptor_ascii(handle, descriptor.iProduct,
|
||||
(uint8_t *)product_name,
|
||||
sizeof(product_name));
|
||||
logLibusbError(ret, "Failed to query product name");
|
||||
ret = libusb_get_string_descriptor_ascii(handle,
|
||||
descriptor.iSerialNumber,
|
||||
(uint8_t *)serial_number,
|
||||
sizeof(serial_number));
|
||||
logLibusbError(ret, "Failed to query serial number");
|
||||
|
||||
const USBDeviceInfo deviceInfo = {
|
||||
std::to_string(descriptor.idVendor),
|
||||
std::to_string(descriptor.idProduct),
|
||||
std::to_string(libusb_get_bus_number(device)),
|
||||
std::to_string(libusb_get_device_address(device)),
|
||||
vendor_name,
|
||||
product_name,
|
||||
serial_number};
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
static std::vector<USBDeviceInfo> pollUSBDevices()
|
||||
{
|
||||
libusb_device **devices;
|
||||
ssize_t count = libusb_get_device_list(NULL, &devices);
|
||||
if (count < 0) {
|
||||
logLibusbError(count, "Failed to query device list");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<USBDeviceInfo> result;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
libusb_device *device = devices[i];
|
||||
struct libusb_device_descriptor descriptor;
|
||||
|
||||
int ret = libusb_get_device_descriptor(device, &descriptor);
|
||||
if (ret != LIBUSB_SUCCESS) {
|
||||
logLibusbError(ret, "Error getting device descriptor");
|
||||
continue;
|
||||
}
|
||||
|
||||
libusb_device_handle *handle;
|
||||
ret = libusb_open(device, &handle);
|
||||
if (ret != LIBUSB_SUCCESS) {
|
||||
logLibusbError(ret, "Error opening device");
|
||||
continue;
|
||||
}
|
||||
result.emplace_back(getDeviceInfo(device, handle, descriptor));
|
||||
libusb_close(handle);
|
||||
}
|
||||
|
||||
libusb_free_device_list(devices, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int hotplugCallback(struct libusb_context *ctx,
|
||||
struct libusb_device *device,
|
||||
libusb_hotplug_event event, void *user_data)
|
||||
{
|
||||
auto devices = static_cast<std::vector<USBDeviceInfo> *>(user_data);
|
||||
|
||||
static libusb_device_handle *handle = nullptr;
|
||||
struct libusb_device_descriptor descriptor;
|
||||
|
||||
int ret = libusb_get_device_descriptor(device, &descriptor);
|
||||
logLibusbError(ret, "Error getting device descriptor");
|
||||
|
||||
ret = libusb_open(device, &handle);
|
||||
if (ret != LIBUSB_SUCCESS) {
|
||||
logLibusbError(ret, "Error opening device");
|
||||
return 0;
|
||||
}
|
||||
const auto deviceInfo = getDeviceInfo(device, handle, descriptor);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
|
||||
devices->emplace_back(deviceInfo);
|
||||
} else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
|
||||
devices->erase(std::find(devices->begin(), devices->end(),
|
||||
deviceInfo));
|
||||
}
|
||||
|
||||
libusb_close(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::vector<USBDeviceInfo> getHotplugBasedDeviceList()
|
||||
{
|
||||
static std::vector<USBDeviceInfo> devices;
|
||||
static bool hotplugSetupDone = false;
|
||||
|
||||
if (!hotplugSetupDone) {
|
||||
hotplugSetupDone = true;
|
||||
devices = pollUSBDevices();
|
||||
|
||||
int ret = libusb_hotplug_register_callback(
|
||||
nullptr,
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
|
||||
0, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
|
||||
LIBUSB_HOTPLUG_MATCH_ANY, hotplugCallback, &devices,
|
||||
nullptr);
|
||||
if (ret != LIBUSB_SUCCESS) {
|
||||
hotplugsAreSupported = false;
|
||||
} else {
|
||||
blog(LOG_WARNING, LOG_PREFIX "hotplug supported!");
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return devices;
|
||||
}
|
||||
|
||||
static std::vector<USBDeviceInfo> getPollingBasedDeviceList()
|
||||
{
|
||||
// Polling can be very expensive
|
||||
// Perform this operation at most once every 10 seconds
|
||||
static const int timeout = 10;
|
||||
static std::vector<USBDeviceInfo> deviceList = {};
|
||||
static std::chrono::high_resolution_clock::time_point lastUpdate = {};
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
|
||||
.count() >= timeout) {
|
||||
deviceList = pollUSBDevices();
|
||||
lastUpdate = now;
|
||||
}
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
std::vector<USBDeviceInfo> GetUSBDevices()
|
||||
{
|
||||
// Hotplug events do not seem to be firing consistently during testing
|
||||
// on Linux so for now only rely on polling based functionality instead
|
||||
return getPollingBasedDeviceList();
|
||||
|
||||
/*
|
||||
if (hotplugsAreSupported) {
|
||||
return getHotplugBasedDeviceList();
|
||||
} else {
|
||||
return getPollingBasedDeviceList();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
QStringList GetUSBDevicesStringList()
|
||||
{
|
||||
QStringList result;
|
||||
const auto devices = GetUSBDevices();
|
||||
for (const auto &device : devices) {
|
||||
result << device.ToQString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string USBDeviceInfo::ToString() const
|
||||
{
|
||||
return "Vendor ID: " + vendorID + "\nProduct ID: " + productID +
|
||||
"\nBus Number:" + busNumber +
|
||||
"\nDevice Address:" + deviceAddress +
|
||||
"\nVendor Name:" + vendorName + "\nProduct Name:" + productName +
|
||||
"\nSerial Number:" + serialNumber;
|
||||
}
|
||||
|
||||
QString USBDeviceInfo::ToQString() const
|
||||
{
|
||||
return QString::fromStdString(ToString());
|
||||
}
|
||||
|
||||
bool USBDeviceInfo::operator==(const USBDeviceInfo &other)
|
||||
{
|
||||
return vendorID == other.vendorID && productID == other.productID &&
|
||||
busNumber == other.busNumber &&
|
||||
deviceAddress == other.deviceAddress &&
|
||||
vendorName == other.vendorName &&
|
||||
productName == other.productName &&
|
||||
serialNumber == other.serialNumber;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
27
plugins/usb/usb-helpers.hpp
Normal file
27
plugins/usb/usb-helpers.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct USBDeviceInfo {
|
||||
std::string vendorID;
|
||||
std::string productID;
|
||||
std::string busNumber;
|
||||
std::string deviceAddress;
|
||||
std::string vendorName;
|
||||
std::string productName;
|
||||
std::string serialNumber;
|
||||
|
||||
std::string ToString() const;
|
||||
QString ToQString() const;
|
||||
|
||||
bool operator==(const USBDeviceInfo &other);
|
||||
};
|
||||
|
||||
std::vector<USBDeviceInfo> GetUSBDevices();
|
||||
QStringList GetUSBDevicesStringList();
|
||||
|
||||
} // namespace advss
|
||||
@@ -12,7 +12,7 @@ QObject *HighlightWidget(QWidget *widget, QColor startColor, QColor endColor,
|
||||
|
||||
void SetHeightToContentHeight(QListWidget *list) {}
|
||||
|
||||
void SetButtonIcon(QPushButton *button, const char *path) {}
|
||||
void SetButtonIcon(QAbstractButton *button, const char *path) {}
|
||||
|
||||
int FindIdxInRagne(QComboBox *list, int start, int stop,
|
||||
const std::string &value, Qt::MatchFlags flags)
|
||||
|
||||
Reference in New Issue
Block a user