mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-11 11:47:41 -05:00
Move scripting support to seperate project
This commit is contained in:
28
plugins/scripting/CMakeLists.txt
Normal file
28
plugins/scripting/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(advanced-scene-switcher-scripting)
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
add_library(${PROJECT_NAME} MODULE)
|
||||
|
||||
target_sources(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE macro-action-script.cpp
|
||||
macro-action-script.hpp
|
||||
macro-condition-script.cpp
|
||||
macro-condition-script.hpp
|
||||
macro-script-handler.cpp
|
||||
macro-script-handler.hpp
|
||||
macro-segment-script.cpp
|
||||
macro-segment-script.hpp)
|
||||
|
||||
target_sources(
|
||||
${PROJECT_NAME} PRIVATE utils/properties-view.cpp utils/properties-view.hpp
|
||||
utils/properties-view.moc.hpp)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/utils")
|
||||
|
||||
setup_advss_plugin(${PROJECT_NAME})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||
install_advss_plugin(${PROJECT_NAME})
|
||||
119
plugins/scripting/macro-action-script.cpp
Normal file
119
plugins/scripting/macro-action-script.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "macro-action-script.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroActionScript::MacroActionScript(
|
||||
Macro *m, const std::string &id, const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: MacroAction(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName),
|
||||
_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
MacroActionScript::MacroActionScript(const advss::MacroActionScript &other)
|
||||
: MacroAction(other.GetMacro()),
|
||||
MacroSegmentScript(other),
|
||||
_id(other._id)
|
||||
{
|
||||
}
|
||||
|
||||
bool MacroActionScript::PerformAction()
|
||||
{
|
||||
if (!ScriptHandler::ActionIdIsValid(_id)) {
|
||||
blog(LOG_WARNING, "skipping unknown script action \"%s\"",
|
||||
_id.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
(void)SendTriggerSignal();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionScript::LogAction() const
|
||||
{
|
||||
ablog(LOG_INFO, "performing script action \"%s\"", GetId().c_str());
|
||||
}
|
||||
|
||||
bool MacroActionScript::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
MacroSegmentScript::Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionScript::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
MacroSegmentScript::Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionScript::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionScript>(*this);
|
||||
}
|
||||
|
||||
void MacroActionScript::WaitForCompletion() const
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
start - start);
|
||||
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
|
||||
|
||||
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||
while (!TriggerIsCompleted()) {
|
||||
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((double)timePassed.count() > timeoutMs) {
|
||||
blog(LOG_INFO, "script action timeout (%s)",
|
||||
_id.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
GetMacroWaitCV().wait_for(lock, 10ms);
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
timePassed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - start);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionScript::RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText)
|
||||
{
|
||||
AddTempvar(variableId, name, helpText);
|
||||
}
|
||||
|
||||
void MacroActionScript::DeregisterAllTempVarsHelper()
|
||||
{
|
||||
MacroSegment::SetupTempVars();
|
||||
}
|
||||
|
||||
void MacroActionScript::SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value)
|
||||
{
|
||||
MacroAction::SetTempVarValue(variableId, value);
|
||||
}
|
||||
|
||||
void MacroActionScript::SetupTempVars()
|
||||
{
|
||||
// This just exists so MacroSegment::SetupTempVars() is not called.
|
||||
// We want the ScriptHandler to handle the registration and clearing of
|
||||
// the temp vars.
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
38
plugins/scripting/macro-action-script.hpp
Normal file
38
plugins/scripting/macro-action-script.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "macro-segment-script.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionScript : public MacroAction, public MacroSegmentScript {
|
||||
public:
|
||||
MacroActionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
MacroActionScript(const advss::MacroActionScript &);
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return _id; };
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
|
||||
private:
|
||||
void WaitForCompletion() const;
|
||||
void RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText);
|
||||
void DeregisterAllTempVarsHelper();
|
||||
void SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value);
|
||||
void SetupTempVars();
|
||||
|
||||
std::string _id = "";
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
108
plugins/scripting/macro-condition-script.cpp
Normal file
108
plugins/scripting/macro-condition-script.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "macro-condition-script.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroConditionScript::MacroConditionScript(
|
||||
Macro *m, const std::string &id, const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: MacroCondition(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName),
|
||||
_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
MacroConditionScript::MacroConditionScript(
|
||||
const advss::MacroConditionScript &other)
|
||||
: MacroCondition(other.GetMacro()),
|
||||
MacroSegmentScript(other),
|
||||
_id(other._id)
|
||||
{
|
||||
}
|
||||
|
||||
bool MacroConditionScript::CheckCondition()
|
||||
{
|
||||
if (!ScriptHandler::ConditionIdIsValid(_id)) {
|
||||
blog(LOG_WARNING, "skipping unknown script condition \"%s\"",
|
||||
_id.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return SendTriggerSignal();
|
||||
}
|
||||
|
||||
bool MacroConditionScript::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
MacroSegmentScript::Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionScript::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
MacroSegmentScript::Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionScript::WaitForCompletion() const
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
start - start);
|
||||
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
|
||||
|
||||
while (!TriggerIsCompleted()) {
|
||||
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((double)timePassed.count() > timeoutMs) {
|
||||
blog(LOG_INFO, "script condition timeout (%s)",
|
||||
_id.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
timePassed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - start);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionScript::RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText)
|
||||
{
|
||||
AddTempvar(variableId, name, helpText);
|
||||
}
|
||||
|
||||
void MacroConditionScript::DeregisterAllTempVarsHelper()
|
||||
{
|
||||
MacroSegment::SetupTempVars();
|
||||
}
|
||||
|
||||
void MacroConditionScript::SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value)
|
||||
{
|
||||
MacroCondition::SetTempVarValue(variableId, value);
|
||||
}
|
||||
|
||||
void MacroConditionScript::SetupTempVars()
|
||||
{
|
||||
// This just exists so MacroSegment::SetupTempVars() is not called.
|
||||
// We want the ScriptHandler to handle the registration and clearing of
|
||||
// the temp vars.
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
36
plugins/scripting/macro-condition-script.hpp
Normal file
36
plugins/scripting/macro-condition-script.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "macro-segment-script.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionScript : public MacroCondition, public MacroSegmentScript {
|
||||
public:
|
||||
MacroConditionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
MacroConditionScript(const advss::MacroConditionScript &);
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return _id; };
|
||||
|
||||
private:
|
||||
void WaitForCompletion() const;
|
||||
void RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText);
|
||||
void DeregisterAllTempVarsHelper();
|
||||
void SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value);
|
||||
void SetupTempVars();
|
||||
|
||||
std::string _id = "";
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
667
plugins/scripting/macro-script-handler.cpp
Normal file
667
plugins/scripting/macro-script-handler.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "macro-action-script.hpp"
|
||||
#include "macro-condition-script.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <obs-properties.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
std::mutex ScriptHandler::_mutex = {};
|
||||
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_actions = {};
|
||||
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_conditions =
|
||||
{};
|
||||
|
||||
/* Procedure handler helpers */
|
||||
|
||||
#define RETURN_STATUS(status) \
|
||||
{ \
|
||||
calldata_set_bool(data, "success", status); \
|
||||
return; \
|
||||
}
|
||||
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
||||
#define RETURN_FAILURE() RETURN_STATUS(false);
|
||||
|
||||
static constexpr std::string_view nameParam = "name";
|
||||
static constexpr std::string_view defaultSettingsParam = "default_settings";
|
||||
static constexpr std::string_view propertiesSignalParam =
|
||||
"properties_signal_name";
|
||||
static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
|
||||
static constexpr std::string_view newInstanceSignalNameParam =
|
||||
"new_instance_signal_name";
|
||||
static constexpr std::string_view deletedInstanceSignalNameParam =
|
||||
"deleted_instance_signal_name";
|
||||
|
||||
static std::string getRegisterScriptSegmentDeclString(const char *funcName)
|
||||
{
|
||||
return std::string("bool ") + funcName + "(in string " +
|
||||
nameParam.data() + ", in ptr " + defaultSettingsParam.data() +
|
||||
", out string " + propertiesSignalParam.data() +
|
||||
", out string " + triggerSignalParam.data() + ")";
|
||||
}
|
||||
|
||||
static std::string getDeregisterScriptSegmentDeclString(const char *funcName)
|
||||
{
|
||||
return std::string("bool ") + funcName + "(in string " +
|
||||
nameParam.data() + ")";
|
||||
}
|
||||
|
||||
/* Script actions */
|
||||
|
||||
static constexpr std::string_view registerActionFuncName =
|
||||
"advss_register_script_action";
|
||||
static constexpr std::string_view deregisterActionFuncName =
|
||||
"advss_deregister_script_action";
|
||||
|
||||
static const std::string registerScriptActionDeclString =
|
||||
getRegisterScriptSegmentDeclString(registerActionFuncName.data());
|
||||
static const std::string deregisterScriptActionDeclString =
|
||||
getDeregisterScriptSegmentDeclString(deregisterActionFuncName.data());
|
||||
|
||||
/* Script conditions */
|
||||
|
||||
static constexpr std::string_view registerConditionFuncName =
|
||||
"advss_register_script_condition";
|
||||
static constexpr std::string_view deregisterConditionFuncName =
|
||||
"advss_deregister_script_condition";
|
||||
|
||||
static const std::string registerScriptConditionDeclString =
|
||||
getRegisterScriptSegmentDeclString(registerConditionFuncName.data());
|
||||
static const std::string deregisterScriptConditionDeclString =
|
||||
getDeregisterScriptSegmentDeclString(
|
||||
deregisterConditionFuncName.data());
|
||||
|
||||
/* Script variables */
|
||||
|
||||
static constexpr std::string_view valueParam = "value";
|
||||
static constexpr std::string_view tempVarIdParam = "temp_var_id";
|
||||
static constexpr std::string_view tempVarNameParam = "temp_var_name";
|
||||
static constexpr std::string_view tempVarHelpParam = "temp_var_help";
|
||||
static constexpr std::string_view getVariableValueFuncName =
|
||||
"advss_get_variable_value";
|
||||
static constexpr std::string_view setVariableValueFuncName =
|
||||
"advss_set_variable_value";
|
||||
static constexpr std::string_view registerTempVarFuncName =
|
||||
"advss_register_temp_var";
|
||||
static constexpr std::string_view deregisterAllTempVarsFuncName =
|
||||
"advss_deregister_temp_vars";
|
||||
static constexpr std::string_view setTempVarValueFuncName =
|
||||
"advss_set_temp_var_value";
|
||||
static const std::string getVariableValueDeclString =
|
||||
std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
|
||||
nameParam.data() + ", out string " + valueParam.data() + ")";
|
||||
static const std::string setVariableValueDeclString =
|
||||
std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
|
||||
nameParam.data() + ", in string " + valueParam.data() + ")";
|
||||
static const std::string registerTempVarDeclString =
|
||||
std::string("bool ") + registerTempVarFuncName.data() + "(in string " +
|
||||
tempVarIdParam.data() + ", in string " + tempVarNameParam.data() +
|
||||
", in string " + tempVarHelpParam.data() + ", in int " +
|
||||
GetInstanceIdParamName().data() + ")";
|
||||
static const std::string deregisterAllTempVarsDeclString =
|
||||
std::string("bool ") + deregisterAllTempVarsFuncName.data() +
|
||||
"(in int " + GetInstanceIdParamName().data() + ")";
|
||||
static const std::string setTempVarValueDeclString =
|
||||
std::string("bool ") + setTempVarValueFuncName.data() + "(in string " +
|
||||
tempVarIdParam.data() + ", in string " + valueParam.data() +
|
||||
", in int " + GetInstanceIdParamName().data() + ")";
|
||||
|
||||
/* Plugin status */
|
||||
|
||||
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
|
||||
static constexpr std::string_view startSignalName = "advss_plugin_started";
|
||||
static constexpr std::string_view getRunningStatusFuncName =
|
||||
"advss_plugin_running";
|
||||
static constexpr std::string_view resetIntervalSignalName =
|
||||
"advss_interval_reset";
|
||||
|
||||
static const std::string stopSignalDeclString =
|
||||
std::string("void ") + stopSignalName.data() + "()";
|
||||
static const std::string startSignalDeclString =
|
||||
std::string("void ") + startSignalName.data() + "()";
|
||||
static const std::string resetIntervalDeclString =
|
||||
std::string("void ") + resetIntervalSignalName.data() + "()";
|
||||
static const std::string getRunningStatusDeclString =
|
||||
std::string("bool ") + getRunningStatusFuncName.data() + "()";
|
||||
|
||||
static bool setup();
|
||||
static bool setupDone = setup();
|
||||
|
||||
static bool setup()
|
||||
{
|
||||
proc_handler_t *ph = obs_get_proc_handler();
|
||||
assert(ph != NULL);
|
||||
|
||||
proc_handler_add(ph, registerScriptActionDeclString.c_str(),
|
||||
&ScriptHandler::RegisterScriptAction, nullptr);
|
||||
proc_handler_add(ph, deregisterScriptActionDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterScriptAction, nullptr);
|
||||
proc_handler_add(ph, registerScriptConditionDeclString.c_str(),
|
||||
&ScriptHandler::RegisterScriptCondition, nullptr);
|
||||
proc_handler_add(ph, deregisterScriptConditionDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterScriptCondition, nullptr);
|
||||
proc_handler_add(ph, getVariableValueDeclString.c_str(),
|
||||
&ScriptHandler::GetVariableValue, nullptr);
|
||||
proc_handler_add(ph, setVariableValueDeclString.c_str(),
|
||||
&ScriptHandler::SetVariableValue, nullptr);
|
||||
proc_handler_add(ph, registerTempVarDeclString.c_str(),
|
||||
&ScriptHandler::RegisterTempVar, nullptr);
|
||||
proc_handler_add(ph, deregisterAllTempVarsDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterAllTempVars, nullptr);
|
||||
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
|
||||
&ScriptHandler::SetTempVarValue, nullptr);
|
||||
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
|
||||
&ScriptHandler::GetRunningStatus, nullptr);
|
||||
|
||||
auto sh = obs_get_signal_handler();
|
||||
signal_handler_add(sh, stopSignalDeclString.c_str());
|
||||
signal_handler_add(sh, startSignalDeclString.c_str());
|
||||
signal_handler_add(sh, resetIntervalDeclString.c_str());
|
||||
|
||||
static constexpr auto triggerSignal = [](const std::string_view &name) {
|
||||
auto sh = obs_get_signal_handler();
|
||||
struct calldata data;
|
||||
calldata_init(&data);
|
||||
signal_handler_signal(sh, name.data(), &data);
|
||||
calldata_free(&data);
|
||||
};
|
||||
|
||||
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
|
||||
AddStartStep([]() { triggerSignal(startSignalName); });
|
||||
AddStopStep([]() { triggerSignal(stopSignalName); });
|
||||
return true;
|
||||
}
|
||||
|
||||
static void replaceProblematicChars(std::string &string)
|
||||
{
|
||||
std::transform(string.begin(), string.end(), string.begin(), [](char c) {
|
||||
if (std::isspace(static_cast<unsigned char>(c)) || c == '(' ||
|
||||
c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
|
||||
return '_';
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
static std::string nameToScriptID(const std::string &name)
|
||||
{
|
||||
return std::string("script_") + name;
|
||||
}
|
||||
|
||||
static std::string getTriggerSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += "_run";
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getCompletionSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
auto signal = getTriggerSignal(name, isAction);
|
||||
signal += "_complete";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getPropertiesSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_get_properties";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getNewInstanceSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_new_instance";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getDeletedInstanceSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_deleted_instance";
|
||||
return signal;
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
|
||||
{
|
||||
const char *actionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
|
||||
strlen(actionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerScriptActionDeclString.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
obs_data_t *defaultSettingsPtr = nullptr;
|
||||
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
|
||||
&defaultSettingsPtr)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerActionFuncName.data(),
|
||||
defaultSettingsParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
OBSData defaultSettings(defaultSettingsPtr);
|
||||
obs_data_release(defaultSettingsPtr);
|
||||
|
||||
if (_actions.count(actionName) > 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! Action \"%s\" already exists!",
|
||||
registerActionFuncName.data(), actionName);
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(actionName);
|
||||
auto triggerSignalName = getTriggerSignal(actionName, true);
|
||||
auto completionSignalName = getCompletionSignal(actionName, true);
|
||||
auto propertiesSignalName = getPropertiesSignal(actionName, true);
|
||||
auto newInstanceSignalName = getNewInstanceSignal(actionName, true);
|
||||
auto deletedInstanceSignalName =
|
||||
getDeletedInstanceSignal(actionName, true);
|
||||
|
||||
const auto createScriptAction =
|
||||
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||
completionSignalName, newInstanceSignalName,
|
||||
deletedInstanceSignalName](
|
||||
Macro *m) -> std::shared_ptr<MacroAction> {
|
||||
return std::make_shared<MacroActionScript>(
|
||||
m, id, defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName);
|
||||
};
|
||||
if (!MacroActionFactory::Register(id, {createScriptAction,
|
||||
MacroSegmentScriptEdit::Create,
|
||||
actionName})) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Action id \"%s\" already exists!",
|
||||
registerActionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "[%s] successful for \"%s\"",
|
||||
registerActionFuncName.data(), actionName);
|
||||
|
||||
calldata_set_string(data, triggerSignalParam.data(),
|
||||
triggerSignalName.c_str());
|
||||
calldata_set_string(data, propertiesSignalParam.data(),
|
||||
propertiesSignalName.c_str());
|
||||
calldata_set_string(data, newInstanceSignalNameParam.data(),
|
||||
newInstanceSignalName.c_str());
|
||||
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
|
||||
deletedInstanceSignalName.c_str());
|
||||
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
|
||||
triggerSignalName,
|
||||
completionSignalName,
|
||||
newInstanceSignalName,
|
||||
deletedInstanceSignalName));
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterScriptAction(void *, calldata_t *data)
|
||||
{
|
||||
const char *actionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
|
||||
strlen(actionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterActionFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(actionName);
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_actions.count(id) == 0) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Action \"%s\" was never registered!",
|
||||
deregisterActionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
if (!MacroActionFactory::Deregister(id)) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Action id \"%s\" does not exist!",
|
||||
deregisterActionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto it = _actions.find(id);
|
||||
if (it != _actions.end()) {
|
||||
_actions.erase(it);
|
||||
}
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
|
||||
{
|
||||
const char *conditionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
|
||||
strlen(conditionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerScriptConditionDeclString.data(),
|
||||
nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
obs_data_t *defaultSettingsPtr = nullptr;
|
||||
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
|
||||
&defaultSettingsPtr)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerScriptConditionDeclString.data(),
|
||||
defaultSettingsParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
OBSData defaultSettings(defaultSettingsPtr);
|
||||
obs_data_release(defaultSettingsPtr);
|
||||
|
||||
if (_conditions.count(conditionName) > 0) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition \"%s\" already exists!",
|
||||
registerConditionFuncName.data(), conditionName);
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(conditionName);
|
||||
auto triggerSignalName = getTriggerSignal(conditionName, false);
|
||||
auto completionSignalName = getCompletionSignal(conditionName, false);
|
||||
auto propertiesSignalName = getPropertiesSignal(conditionName, false);
|
||||
auto newInstanceSignalName = getNewInstanceSignal(conditionName, false);
|
||||
auto deletedInstanceSignalName =
|
||||
getDeletedInstanceSignal(conditionName, false);
|
||||
|
||||
const auto createScriptCondition =
|
||||
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||
completionSignalName, newInstanceSignalName,
|
||||
deletedInstanceSignalName](
|
||||
Macro *m) -> std::shared_ptr<MacroCondition> {
|
||||
return std::make_shared<MacroConditionScript>(
|
||||
m, id, defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName);
|
||||
};
|
||||
if (!MacroConditionFactory::Register(
|
||||
id, {createScriptCondition, MacroSegmentScriptEdit::Create,
|
||||
conditionName})) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition id \"%s\" already exists!",
|
||||
registerConditionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "[%s] successful for \"%s\"",
|
||||
registerConditionFuncName.data(), conditionName);
|
||||
|
||||
calldata_set_string(data, triggerSignalParam.data(),
|
||||
triggerSignalName.c_str());
|
||||
calldata_set_string(data, propertiesSignalParam.data(),
|
||||
propertiesSignalName.c_str());
|
||||
calldata_set_string(data, newInstanceSignalNameParam.data(),
|
||||
newInstanceSignalName.c_str());
|
||||
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
|
||||
deletedInstanceSignalName.c_str());
|
||||
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
|
||||
triggerSignalName,
|
||||
completionSignalName,
|
||||
newInstanceSignalName,
|
||||
deletedInstanceSignalName));
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterScriptCondition(void *, calldata_t *data)
|
||||
{
|
||||
const char *conditionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
|
||||
strlen(conditionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterConditionFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(conditionName);
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_conditions.count(id) == 0) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition \"%s\" was never registered!",
|
||||
deregisterConditionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
if (!MacroConditionFactory::Deregister(id)) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition id \"%s\" does not exist!",
|
||||
deregisterConditionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto it = _conditions.find(id);
|
||||
if (it != _conditions.end()) {
|
||||
_conditions.erase(it);
|
||||
}
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::GetVariableValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
|
||||
strlen(variableName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto weakVariable = GetWeakVariableByName(variableName);
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! \"%s\" variable does not exist!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
calldata_set_string(data, valueParam.data(), variable->Value().c_str());
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
|
||||
strlen(variableName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *variableValue;
|
||||
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
|
||||
strlen(variableValue) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
getVariableValueFuncName.data(), valueParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto weakVariable = GetWeakVariableByName(variableName);
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! \"%s\" variable does not exist!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
variable->SetValue(variableValue);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
|
||||
{
|
||||
calldata_set_bool(data, "is_running", PluginIsRunning());
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
|
||||
strlen(variableId) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarIdParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *name;
|
||||
if (!calldata_get_string(data, tempVarNameParam.data(), &name) ||
|
||||
strlen(name) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarNameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *helpText;
|
||||
if (!calldata_get_string(data, tempVarHelpParam.data(), &helpText)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarHelpParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::RegisterTempVar(variableId, name, helpText,
|
||||
instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterAllTempVars(void *, calldata_t *data)
|
||||
{
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterAllTempVarsFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::DeregisterAllTempVars(instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::SetTempVarValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
|
||||
strlen(variableId) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(), tempVarIdParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const char *variableValue;
|
||||
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
|
||||
strlen(variableValue) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(), valueParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::SetTempVarValue(variableId, variableValue,
|
||||
instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
bool ScriptHandler::ActionIdIsValid(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _actions.count(id) > 0;
|
||||
}
|
||||
|
||||
bool ScriptHandler::ConditionIdIsValid(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _conditions.count(id) > 0;
|
||||
}
|
||||
|
||||
static std::string signalNameToTriggerSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "()";
|
||||
}
|
||||
|
||||
static std::string signalNameToPropertiesSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "(in ptr " +
|
||||
GetPropertiesSignalParamName().data() + ")";
|
||||
}
|
||||
static std::string signalNameToCompletionSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("bool ") + name + "(in int " +
|
||||
GetCompletionIdParamName().data() + ")";
|
||||
}
|
||||
|
||||
static std::string signalNameToInstanceSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "(out int " +
|
||||
GetInstanceIdParamName().data() + ")";
|
||||
}
|
||||
|
||||
ScriptSegmentType::ScriptSegmentType(
|
||||
const std::string &id, const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: _id(id)
|
||||
{
|
||||
auto sh = obs_get_signal_handler();
|
||||
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToPropertiesSignalDecl(propertiesSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh, signalNameToTriggerSignalDecl(triggerSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToCompletionSignalDecl(completionSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToInstanceSignalDecl(newInstanceSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh, signalNameToInstanceSignalDecl(deletedInstanceSignalName)
|
||||
.c_str());
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
70
plugins/scripting/macro-script-handler.hpp
Normal file
70
plugins/scripting/macro-script-handler.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <obs.hpp>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ScriptSegmentType {
|
||||
public:
|
||||
ScriptSegmentType() = delete;
|
||||
ScriptSegmentType(const std::string &id,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
|
||||
private:
|
||||
std::string _id;
|
||||
};
|
||||
|
||||
class ScriptHandler {
|
||||
public:
|
||||
ScriptHandler() = delete;
|
||||
static void RegisterScriptAction(void *ctx, calldata_t *data);
|
||||
static void DeregisterScriptAction(void *ctx, calldata_t *data);
|
||||
static void RegisterScriptCondition(void *ctx, calldata_t *data);
|
||||
static void DeregisterScriptCondition(void *ctx, calldata_t *data);
|
||||
static void GetVariableValue(void *ctx, calldata_t *data);
|
||||
static void RegisterTempVar(void *ctx, calldata_t *data);
|
||||
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
|
||||
static void SetTempVarValue(void *ctx, calldata_t *data);
|
||||
static void SetVariableValue(void *ctx, calldata_t *data);
|
||||
static void GetRunningStatus(void *ctx, calldata_t *data);
|
||||
static bool ActionIdIsValid(const std::string &id);
|
||||
static bool ConditionIdIsValid(const std::string &id);
|
||||
|
||||
private:
|
||||
static std::mutex _mutex;
|
||||
static std::unordered_map<std::string, ScriptSegmentType> _actions;
|
||||
static std::unordered_map<std::string, ScriptSegmentType> _conditions;
|
||||
};
|
||||
|
||||
static constexpr std::string_view GetPropertiesSignalParamName()
|
||||
{
|
||||
return "properties";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetActionCompletionSignalParamName()
|
||||
{
|
||||
return "completion_signal_name";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetCompletionIdParamName()
|
||||
{
|
||||
return "completion_id";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetResultSignalParamName()
|
||||
{
|
||||
return "result";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetInstanceIdParamName()
|
||||
{
|
||||
return "instance_id";
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
307
plugins/scripting/macro-segment-script.cpp
Normal file
307
plugins/scripting/macro-segment-script.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
#include "macro-segment-script.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-action.hpp"
|
||||
#include "macro-condition.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "properties-view.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
static std::atomic_int completionIdCounter = 0;
|
||||
static std::atomic_int instanceIdCounter = 0;
|
||||
static std::mutex instanceMtx;
|
||||
static std::vector<MacroSegmentScript *> instances{};
|
||||
|
||||
MacroSegmentScript::MacroSegmentScript(
|
||||
obs_data_t *defaultSettings, const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: _settings(obs_data_get_defaults(defaultSettings)),
|
||||
_propertiesSignal(propertiesSignalName),
|
||||
_triggerSignal(triggerSignalName),
|
||||
_completionSignal(completionSignalName),
|
||||
_newInstanceSignal(newInstanceSignalName),
|
||||
_deletedInstanceSignal(deletedInstanceSignalName),
|
||||
_instanceId(++instanceIdCounter)
|
||||
{
|
||||
signal_handler_connect(obs_get_signal_handler(),
|
||||
completionSignalName.c_str(),
|
||||
&MacroSegmentScript::CompletionSignalReceived,
|
||||
this);
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.emplace_back(this);
|
||||
|
||||
SignalNewInstance();
|
||||
}
|
||||
|
||||
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
|
||||
: _settings(obs_data_create()),
|
||||
_propertiesSignal(other._propertiesSignal),
|
||||
_triggerSignal(other._triggerSignal),
|
||||
_completionSignal(other._completionSignal),
|
||||
_newInstanceSignal(other._newInstanceSignal),
|
||||
_deletedInstanceSignal(other._deletedInstanceSignal),
|
||||
_instanceId(++instanceIdCounter)
|
||||
{
|
||||
signal_handler_connect(obs_get_signal_handler(),
|
||||
_completionSignal.c_str(),
|
||||
&MacroSegmentScript::CompletionSignalReceived,
|
||||
this);
|
||||
obs_data_apply(_settings.Get(), other._settings.Get());
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.emplace_back(this);
|
||||
|
||||
SignalNewInstance();
|
||||
}
|
||||
|
||||
MacroSegmentScript::~MacroSegmentScript()
|
||||
{
|
||||
auto data = calldata_create();
|
||||
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(),
|
||||
_deletedInstanceSignal.c_str(), data);
|
||||
calldata_destroy(data);
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.erase(std::remove(instances.begin(), instances.end(), this),
|
||||
instances.end());
|
||||
}
|
||||
|
||||
void MacroSegmentScript::RegisterTempVar(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText,
|
||||
int instanceId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
for (auto instance : instances) {
|
||||
if (instance->_instanceId != instanceId) {
|
||||
continue;
|
||||
}
|
||||
instance->RegisterTempVarHelper(variableId, name, helpText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentScript::DeregisterAllTempVars(int instanceId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
for (auto instance : instances) {
|
||||
if (instance->_instanceId != instanceId) {
|
||||
continue;
|
||||
}
|
||||
instance->DeregisterAllTempVarsHelper();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentScript::SetTempVarValue(const std::string &variableId,
|
||||
const std::string &value,
|
||||
int instanceId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
for (auto instance : instances) {
|
||||
if (instance->_instanceId != instanceId) {
|
||||
continue;
|
||||
}
|
||||
instance->SetTempVarValueHelper(variableId, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroSegmentScript::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_obj(obj, "settings", _settings.Get());
|
||||
_timeout.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroSegmentScript::Load(obs_data_t *obj)
|
||||
{
|
||||
OBSDataAutoRelease settings = obs_data_get_obj(obj, "settings");
|
||||
obs_data_apply(_settings.Get(), settings);
|
||||
_timeout.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
obs_properties_t *MacroSegmentScript::GetProperties() const
|
||||
{
|
||||
auto data = calldata_create();
|
||||
signal_handler_signal(obs_get_signal_handler(),
|
||||
_propertiesSignal.c_str(), data);
|
||||
obs_properties_t *properties = nullptr;
|
||||
if (!calldata_get_ptr(data, GetPropertiesSignalParamName().data(),
|
||||
&properties)) {
|
||||
calldata_destroy(data);
|
||||
return nullptr;
|
||||
}
|
||||
calldata_destroy(data);
|
||||
return properties;
|
||||
}
|
||||
|
||||
void MacroSegmentScript::UpdateSettings(obs_data_t *newSettings) const
|
||||
{
|
||||
obs_data_clear(_settings.Get());
|
||||
obs_data_apply(_settings.Get(), newSettings);
|
||||
}
|
||||
|
||||
bool MacroSegmentScript::SendTriggerSignal()
|
||||
{
|
||||
_completionId = ++completionIdCounter;
|
||||
_triggerIsComplete = false;
|
||||
_triggerResult = false;
|
||||
|
||||
auto data = calldata_create();
|
||||
calldata_set_string(data, GetActionCompletionSignalParamName().data(),
|
||||
_completionSignal.c_str());
|
||||
calldata_set_int(data, GetCompletionIdParamName().data(),
|
||||
_completionId);
|
||||
calldata_set_string(data, "settings", obs_data_get_json(GetSettings()));
|
||||
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
|
||||
data);
|
||||
calldata_destroy(data);
|
||||
|
||||
SetMacroAbortWait(false);
|
||||
WaitForCompletion();
|
||||
|
||||
return _triggerResult;
|
||||
}
|
||||
|
||||
void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
|
||||
{
|
||||
auto segment = static_cast<MacroSegmentScript *>(param);
|
||||
long long int id;
|
||||
if (!calldata_get_int(data, GetCompletionIdParamName().data(), &id)) {
|
||||
blog(LOG_WARNING,
|
||||
"received completion signal without \"%s\" parameter",
|
||||
GetCompletionIdParamName().data());
|
||||
return;
|
||||
}
|
||||
bool result;
|
||||
if (!calldata_get_bool(data, GetResultSignalParamName().data(),
|
||||
&result)) {
|
||||
blog(LOG_WARNING,
|
||||
"received completion signal without \"%s\" parameter",
|
||||
GetResultSignalParamName().data());
|
||||
return;
|
||||
}
|
||||
if (id != segment->_completionId) {
|
||||
return;
|
||||
}
|
||||
segment->_triggerIsComplete = true;
|
||||
segment->_triggerResult = result;
|
||||
}
|
||||
|
||||
void MacroSegmentScript::SignalNewInstance() const
|
||||
{
|
||||
std::thread signalNewInstanceThread([this]() {
|
||||
auto data = calldata_create();
|
||||
calldata_set_int(data, GetInstanceIdParamName().data(),
|
||||
_instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(),
|
||||
_newInstanceSignal.c_str(), data);
|
||||
calldata_destroy(data);
|
||||
});
|
||||
signalNewInstanceThread.detach();
|
||||
}
|
||||
|
||||
obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj)
|
||||
{
|
||||
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||
if (!segmentEdit) {
|
||||
return nullptr;
|
||||
}
|
||||
return segmentEdit->_entryData->GetProperties();
|
||||
}
|
||||
|
||||
void MacroSegmentScriptEdit::UpdateSettings(void *obj, obs_data_t *settings)
|
||||
{
|
||||
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||
if (!segmentEdit || segmentEdit->_loading || !segmentEdit->_entryData) {
|
||||
return;
|
||||
}
|
||||
auto lock = LockContext();
|
||||
segmentEdit->_entryData->UpdateSettings(settings);
|
||||
}
|
||||
|
||||
MacroSegmentScriptEdit::MacroSegmentScriptEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroSegmentScript> entryData)
|
||||
: QWidget(parent),
|
||||
_timeout(new DurationSelection(this))
|
||||
{
|
||||
QWidget::connect(_timeout, &DurationSelection::DurationChanged, this,
|
||||
&MacroSegmentScriptEdit::TimeoutChanged);
|
||||
|
||||
auto timeoutLayout = new QHBoxLayout();
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.timeout"),
|
||||
timeoutLayout, {{"{{timeout}}", _timeout}});
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
|
||||
auto properties = entryData->GetProperties();
|
||||
if (!!properties) {
|
||||
obs_properties_destroy(properties);
|
||||
|
||||
// We need a separate OBSData object here as we can't risk
|
||||
// entryData->_settings being modified while it is currently used
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_apply(data, entryData->GetSettings());
|
||||
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||
auto propertiesView =
|
||||
new OBSPropertiesView(data.Get(), this, GetProperties,
|
||||
nullptr, UpdateSettings);
|
||||
layout->addWidget(propertiesView);
|
||||
connect(propertiesView, &OBSPropertiesView::PropertiesResized,
|
||||
this, [this]() {
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
});
|
||||
#else
|
||||
layout->addWidget(new QLabel(
|
||||
"Displaying script properties not supported when compiled for OBS 29!"));
|
||||
#endif
|
||||
}
|
||||
|
||||
layout->addLayout(timeoutLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroSegmentScriptEdit::UpdateEntryData()
|
||||
{
|
||||
_timeout->SetDuration(_entryData->_timeout);
|
||||
}
|
||||
|
||||
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> segment)
|
||||
{
|
||||
return new MacroSegmentScriptEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
|
||||
}
|
||||
|
||||
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> segment)
|
||||
{
|
||||
return new MacroSegmentScriptEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
|
||||
}
|
||||
|
||||
void MacroSegmentScriptEdit::TimeoutChanged(const Duration &timeout)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_timeout = timeout;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
101
plugins/scripting/macro-segment-script.hpp
Normal file
101
plugins/scripting/macro-segment-script.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "duration-control.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <obs-data.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class Macro;
|
||||
class MacroAction;
|
||||
class MacroCondition;
|
||||
|
||||
class MacroSegmentScript {
|
||||
public:
|
||||
MacroSegmentScript(obs_data_t *defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
MacroSegmentScript(const advss::MacroSegmentScript &);
|
||||
virtual ~MacroSegmentScript();
|
||||
|
||||
static void RegisterTempVar(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText,
|
||||
int instanceId);
|
||||
static void DeregisterAllTempVars(int instanceId);
|
||||
static void SetTempVarValue(const std::string &variableId,
|
||||
const std::string &value, int instanceId);
|
||||
|
||||
protected:
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
obs_properties_t *GetProperties() const;
|
||||
OBSData GetSettings() const { return _settings.Get(); }
|
||||
void UpdateSettings(obs_data_t *newSettings) const;
|
||||
|
||||
bool SendTriggerSignal();
|
||||
double GetTimeoutSeconds() const { return _timeout.Seconds(); };
|
||||
bool TriggerIsCompleted() const { return _triggerIsComplete; }
|
||||
|
||||
virtual void RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText) = 0;
|
||||
virtual void DeregisterAllTempVarsHelper() = 0;
|
||||
virtual void SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value) = 0;
|
||||
|
||||
const int64_t _instanceId;
|
||||
|
||||
private:
|
||||
virtual void WaitForCompletion() const = 0;
|
||||
static void CompletionSignalReceived(void *param, calldata_t *data);
|
||||
void SignalNewInstance() const;
|
||||
|
||||
OBSDataAutoRelease _settings;
|
||||
std::string _propertiesSignal = "";
|
||||
|
||||
std::string _triggerSignal = "";
|
||||
std::string _completionSignal = "";
|
||||
std::string _newInstanceSignal = "";
|
||||
std::string _deletedInstanceSignal = "";
|
||||
std::atomic_bool _triggerIsComplete = {false};
|
||||
bool _triggerResult = false;
|
||||
int64_t _completionId = 0;
|
||||
|
||||
Duration _timeout = Duration(10.0);
|
||||
|
||||
friend class MacroSegmentScriptEdit;
|
||||
};
|
||||
|
||||
class MacroSegmentScriptEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroSegmentScriptEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroSegmentScript> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> segment);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> segment);
|
||||
|
||||
private slots:
|
||||
void TimeoutChanged(const Duration &);
|
||||
|
||||
private:
|
||||
static obs_properties_t *GetProperties(void *obj);
|
||||
static void UpdateSettings(void *obj, obs_data_t *settings);
|
||||
|
||||
DurationSelection *_timeout;
|
||||
|
||||
std::shared_ptr<MacroSegmentScript> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
2540
plugins/scripting/utils/properties-view.cpp
Normal file
2540
plugins/scripting/utils/properties-view.cpp
Normal file
File diff suppressed because it is too large
Load Diff
264
plugins/scripting/utils/properties-view.hpp
Normal file
264
plugins/scripting/utils/properties-view.hpp
Normal file
@@ -0,0 +1,264 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs-data.h>
|
||||
#include <obs.hpp>
|
||||
#include <qtimer.h>
|
||||
#include <QPointer>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <QScrollArea>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||
|
||||
class QFormLayout;
|
||||
class QLabel;
|
||||
class QResizeEvent;
|
||||
|
||||
typedef obs_properties_t *(*PropertiesReloadCallback)(void *obj);
|
||||
typedef void (*PropertiesUpdateCallback)(void *obj, obs_data_t *old_settings,
|
||||
obs_data_t *new_settings);
|
||||
typedef void (*PropertiesVisualUpdateCb)(void *obj, obs_data_t *settings);
|
||||
|
||||
namespace advss {
|
||||
|
||||
class OBSPropertiesView;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
class VScrollArea : public QScrollArea {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VScrollArea(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
class SpinBoxIgnoreScroll : public QSpinBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SpinBoxIgnoreScroll(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
class OBSPlainTextEdit : public QPlainTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OBSPlainTextEdit(QWidget *parent = nullptr,
|
||||
bool monospace = true);
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
class WidgetInfo : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
friend class OBSPropertiesView;
|
||||
|
||||
private:
|
||||
OBSPropertiesView *view;
|
||||
obs_property_t *property;
|
||||
QWidget *widget;
|
||||
QPointer<QTimer> update_timer;
|
||||
bool recently_updated = false;
|
||||
OBSData old_settings_cache;
|
||||
|
||||
void BoolChanged(const char *setting);
|
||||
void IntChanged(const char *setting);
|
||||
void FloatChanged(const char *setting);
|
||||
void TextChanged(const char *setting);
|
||||
bool PathChanged(const char *setting);
|
||||
void ListChanged(const char *setting);
|
||||
bool ColorChangedInternal(const char *setting, bool supportAlpha);
|
||||
bool ColorChanged(const char *setting);
|
||||
bool ColorAlphaChanged(const char *setting);
|
||||
bool FontChanged(const char *setting);
|
||||
void GroupChanged(const char *setting);
|
||||
void EditableListChanged();
|
||||
void ButtonClicked();
|
||||
|
||||
void TogglePasswordText(bool checked);
|
||||
|
||||
public:
|
||||
inline WidgetInfo(OBSPropertiesView *view_, obs_property_t *prop,
|
||||
QWidget *widget_)
|
||||
: view(view_),
|
||||
property(prop),
|
||||
widget(widget_)
|
||||
{
|
||||
}
|
||||
|
||||
~WidgetInfo()
|
||||
{
|
||||
if (update_timer) {
|
||||
update_timer->stop();
|
||||
QMetaObject::invokeMethod(update_timer, "timeout");
|
||||
update_timer->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
public slots:
|
||||
|
||||
void ControlChanged();
|
||||
|
||||
/* editable list */
|
||||
void EditListAdd();
|
||||
void EditListAddText();
|
||||
void EditListAddFiles();
|
||||
void EditListAddDir();
|
||||
void EditListRemove();
|
||||
void EditListEdit();
|
||||
void EditListUp();
|
||||
void EditListDown();
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
class OBSPropertiesView : public VScrollArea {
|
||||
Q_OBJECT
|
||||
|
||||
friend class WidgetInfo;
|
||||
|
||||
using properties_delete_t = decltype(&obs_properties_destroy);
|
||||
using properties_t =
|
||||
std::unique_ptr<obs_properties_t, properties_delete_t>;
|
||||
|
||||
private:
|
||||
QWidget *widget = nullptr;
|
||||
properties_t properties;
|
||||
OBSData settings;
|
||||
OBSWeakObjectAutoRelease weakObj;
|
||||
void *rawObj = nullptr;
|
||||
std::string type;
|
||||
PropertiesReloadCallback reloadCallback;
|
||||
PropertiesUpdateCallback callback = nullptr;
|
||||
PropertiesVisualUpdateCb visUpdateCb = nullptr;
|
||||
int minSize;
|
||||
std::vector<std::unique_ptr<WidgetInfo>> children;
|
||||
std::string lastFocused;
|
||||
QWidget *lastWidget = nullptr;
|
||||
bool deferUpdate;
|
||||
bool enableDefer = true;
|
||||
|
||||
template<typename Sender, typename SenderParent, typename... Args>
|
||||
QWidget *NewWidget(obs_property_t *prop, Sender *widget,
|
||||
void (SenderParent::*signal)(Args...));
|
||||
|
||||
QWidget *AddCheckbox(obs_property_t *prop);
|
||||
QWidget *AddText(obs_property_t *prop, QFormLayout *layout,
|
||||
QLabel *&label);
|
||||
void AddPath(obs_property_t *prop, QFormLayout *layout, QLabel **label);
|
||||
void AddInt(obs_property_t *prop, QFormLayout *layout, QLabel **label);
|
||||
void AddFloat(obs_property_t *prop, QFormLayout *layout,
|
||||
QLabel **label);
|
||||
QWidget *AddList(obs_property_t *prop, bool &warning);
|
||||
void AddEditableList(obs_property_t *prop, QFormLayout *layout,
|
||||
QLabel *&label);
|
||||
QWidget *AddButton(obs_property_t *prop);
|
||||
void AddColorInternal(obs_property_t *prop, QFormLayout *layout,
|
||||
QLabel *&label, bool supportAlpha);
|
||||
void AddColor(obs_property_t *prop, QFormLayout *layout,
|
||||
QLabel *&label);
|
||||
void AddColorAlpha(obs_property_t *prop, QFormLayout *layout,
|
||||
QLabel *&label);
|
||||
void AddFont(obs_property_t *prop, QFormLayout *layout, QLabel *&label);
|
||||
void AddFrameRate(obs_property_t *prop, bool &warning,
|
||||
QFormLayout *layout, QLabel *&label);
|
||||
|
||||
void AddGroup(obs_property_t *prop, QFormLayout *layout);
|
||||
|
||||
void AddProperty(obs_property_t *property, QFormLayout *layout);
|
||||
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
void GetScrollPos(int &h, int &v, int &hend, int &vend);
|
||||
void SetScrollPos(int h, int v, int old_hend, int old_vend);
|
||||
|
||||
private slots:
|
||||
void RefreshProperties();
|
||||
|
||||
public slots:
|
||||
void ReloadProperties();
|
||||
void SignalChanged();
|
||||
|
||||
signals:
|
||||
void PropertiesResized();
|
||||
void Changed();
|
||||
void PropertiesRefreshed();
|
||||
|
||||
public:
|
||||
OBSPropertiesView(OBSData settings, obs_object_t *obj,
|
||||
PropertiesReloadCallback reloadCallback,
|
||||
PropertiesUpdateCallback callback,
|
||||
PropertiesVisualUpdateCb cb = nullptr,
|
||||
int minSize = 0);
|
||||
OBSPropertiesView(OBSData settings, void *obj,
|
||||
PropertiesReloadCallback reloadCallback,
|
||||
PropertiesUpdateCallback callback,
|
||||
PropertiesVisualUpdateCb cb = nullptr,
|
||||
int minSize = 0);
|
||||
OBSPropertiesView(OBSData settings, const char *type,
|
||||
PropertiesReloadCallback reloadCallback,
|
||||
int minSize = 0);
|
||||
|
||||
#define obj_constructor(type) \
|
||||
inline OBSPropertiesView(OBSData settings, obs_##type##_t *type, \
|
||||
PropertiesReloadCallback reloadCallback, \
|
||||
PropertiesUpdateCallback callback, \
|
||||
PropertiesVisualUpdateCb cb = nullptr, \
|
||||
int minSize = 0) \
|
||||
: OBSPropertiesView(settings, (obs_object_t *)type, \
|
||||
reloadCallback, callback, cb, minSize) \
|
||||
{ \
|
||||
}
|
||||
|
||||
obj_constructor(source);
|
||||
obj_constructor(output);
|
||||
obj_constructor(encoder);
|
||||
obj_constructor(service);
|
||||
#undef obj_constructor
|
||||
|
||||
inline obs_data_t *GetSettings() const { return settings; }
|
||||
|
||||
inline void UpdateSettings()
|
||||
{
|
||||
if (callback)
|
||||
callback(OBSGetStrongRef(weakObj), nullptr, settings);
|
||||
else if (visUpdateCb)
|
||||
visUpdateCb(OBSGetStrongRef(weakObj), settings);
|
||||
}
|
||||
inline bool DeferUpdate() const { return deferUpdate; }
|
||||
inline void SetDeferrable(bool deferrable) { enableDefer = deferrable; }
|
||||
|
||||
inline OBSObject GetObject() const { return OBSGetStrongRef(weakObj); }
|
||||
|
||||
#define Def_IsObject(type) \
|
||||
inline bool IsObject(obs_##type##_t *type) const \
|
||||
{ \
|
||||
OBSObject obj = OBSGetStrongRef(weakObj); \
|
||||
return obj.Get() == (obs_object_t *)type; \
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
Def_IsObject(source)
|
||||
Def_IsObject(output)
|
||||
Def_IsObject(encoder)
|
||||
Def_IsObject(service)
|
||||
/* clang-format on */
|
||||
|
||||
#undef Def_IsObject
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
#endif
|
||||
63
plugins/scripting/utils/properties-view.moc.hpp
Normal file
63
plugins/scripting/utils/properties-view.moc.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QSpinBox>
|
||||
#include <QStackedWidget>
|
||||
#include <QWidget>
|
||||
|
||||
#include <obs.h>
|
||||
#include <media-io/frame-rate.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4505)
|
||||
#endif
|
||||
|
||||
static bool operator!=(const media_frames_per_second &a,
|
||||
const media_frames_per_second &b)
|
||||
{
|
||||
return a.numerator != b.numerator || a.denominator != b.denominator;
|
||||
}
|
||||
|
||||
static bool operator==(const media_frames_per_second &a,
|
||||
const media_frames_per_second &b)
|
||||
{
|
||||
return !(a != b);
|
||||
}
|
||||
|
||||
using frame_rate_range_t =
|
||||
std::pair<media_frames_per_second, media_frames_per_second>;
|
||||
using frame_rate_ranges_t = std::vector<frame_rate_range_t>;
|
||||
|
||||
class OBSFrameRatePropertyWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
frame_rate_ranges_t fps_ranges;
|
||||
|
||||
QComboBox *modeSelect = nullptr;
|
||||
QStackedWidget *modeDisplay = nullptr;
|
||||
|
||||
QWidget *labels = nullptr;
|
||||
QLabel *currentFPS = nullptr;
|
||||
QLabel *timePerFrame = nullptr;
|
||||
QLabel *minLabel = nullptr;
|
||||
QLabel *maxLabel = nullptr;
|
||||
|
||||
QComboBox *simpleFPS = nullptr;
|
||||
|
||||
QComboBox *fpsRange = nullptr;
|
||||
QSpinBox *numEdit = nullptr;
|
||||
QSpinBox *denEdit = nullptr;
|
||||
|
||||
bool updating = false;
|
||||
|
||||
const char *name = nullptr;
|
||||
obs_data_t *settings = nullptr;
|
||||
|
||||
QLabel *warningLabel = nullptr;
|
||||
|
||||
OBSFrameRatePropertyWidget() = default;
|
||||
};
|
||||
Reference in New Issue
Block a user