New upstream version 21.0.2+dfsg1
This commit is contained in:
parent
1f1bbb3518
commit
baafb6325b
706 changed files with 49633 additions and 5044 deletions
552
UI/frontend-plugins/frontend-tools/scripts.cpp
Normal file
552
UI/frontend-plugins/frontend-tools/scripts.cpp
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
#include "scripts.hpp"
|
||||
#include "frontend-tools-config.h"
|
||||
#include "../../properties-view.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QPushButton>
|
||||
#include <QFontDatabase>
|
||||
#include <QFont>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QResizeEvent>
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <obs-module.h>
|
||||
#include <obs-frontend-api.h>
|
||||
#include <obs-scripting.h>
|
||||
|
||||
#include <util/config-file.h>
|
||||
#include <util/platform.h>
|
||||
#include <util/util.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ui_scripts.h"
|
||||
|
||||
#if COMPILE_PYTHON && (defined(_WIN32) || defined(__APPLE__))
|
||||
#define PYTHON_UI 1
|
||||
#else
|
||||
#define PYTHON_UI 0
|
||||
#endif
|
||||
|
||||
#if ARCH_BITS == 64
|
||||
#define ARCH_NAME "64bit"
|
||||
#else
|
||||
#define ARCH_NAME "32bit"
|
||||
#endif
|
||||
|
||||
#define PYTHONPATH_LABEL_TEXT "PythonSettings.PythonInstallPath" ARCH_NAME
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
using OBSScript = OBSObj<obs_script_t*, obs_script_destroy>;
|
||||
|
||||
struct ScriptData {
|
||||
std::vector<OBSScript> scripts;
|
||||
|
||||
inline obs_script_t *FindScript(const char *path)
|
||||
{
|
||||
for (OBSScript &script : scripts) {
|
||||
const char *script_path = obs_script_get_path(script);
|
||||
if (strcmp(script_path, path) == 0) {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ScriptOpened(const char *path)
|
||||
{
|
||||
for (OBSScript &script : scripts) {
|
||||
const char *script_path = obs_script_get_path(script);
|
||||
if (strcmp(script_path, path) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static ScriptData *scriptData = nullptr;
|
||||
static ScriptsTool *scriptsWindow = nullptr;
|
||||
static ScriptLogWindow *scriptLogWindow = nullptr;
|
||||
static QPlainTextEdit *scriptLogWidget = nullptr;
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
ScriptLogWindow::ScriptLogWindow() : QWidget(nullptr)
|
||||
{
|
||||
const QFont fixedFont =
|
||||
QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
|
||||
QPlainTextEdit *edit = new QPlainTextEdit();
|
||||
edit->setReadOnly(true);
|
||||
edit->setFont(fixedFont);
|
||||
edit->setWordWrapMode(QTextOption::NoWrap);
|
||||
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||||
QPushButton *clearButton = new QPushButton(tr("Clear"));
|
||||
connect(clearButton, &QPushButton::clicked,
|
||||
this, &ScriptLogWindow::ClearWindow);
|
||||
QPushButton *closeButton = new QPushButton(tr("Close"));
|
||||
connect(closeButton, &QPushButton::clicked,
|
||||
this, &QDialog::hide);
|
||||
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(clearButton);
|
||||
buttonLayout->addWidget(closeButton);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->addWidget(edit);
|
||||
layout->addLayout(buttonLayout);
|
||||
|
||||
setLayout(layout);
|
||||
scriptLogWidget = edit;
|
||||
|
||||
resize(600, 400);
|
||||
|
||||
config_t *global_config = obs_frontend_get_global_config();
|
||||
const char *geom = config_get_string(global_config,
|
||||
"ScriptLogWindow", "geometry");
|
||||
if (geom != nullptr) {
|
||||
QByteArray ba = QByteArray::fromBase64(QByteArray(geom));
|
||||
restoreGeometry(ba);
|
||||
}
|
||||
|
||||
setWindowTitle(obs_module_text("ScriptLogWindow"));
|
||||
|
||||
connect(edit->verticalScrollBar(), &QAbstractSlider::sliderMoved,
|
||||
this, &ScriptLogWindow::ScrollChanged);
|
||||
}
|
||||
|
||||
ScriptLogWindow::~ScriptLogWindow()
|
||||
{
|
||||
config_t *global_config = obs_frontend_get_global_config();
|
||||
config_set_string(global_config,
|
||||
"ScriptLogWindow", "geometry",
|
||||
saveGeometry().toBase64().constData());
|
||||
}
|
||||
|
||||
void ScriptLogWindow::ScrollChanged(int val)
|
||||
{
|
||||
QScrollBar *scroll = scriptLogWidget->verticalScrollBar();
|
||||
bottomScrolled = (val == scroll->maximum());
|
||||
}
|
||||
|
||||
void ScriptLogWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
|
||||
if (bottomScrolled) {
|
||||
QScrollBar *scroll = scriptLogWidget->verticalScrollBar();
|
||||
scroll->setValue(scroll->maximum());
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptLogWindow::AddLogMsg(int log_level, QString msg)
|
||||
{
|
||||
QScrollBar *scroll = scriptLogWidget->verticalScrollBar();
|
||||
bottomScrolled = scroll->value() == scroll->maximum();
|
||||
|
||||
lines += QStringLiteral("\n");
|
||||
lines += msg;
|
||||
scriptLogWidget->setPlainText(lines);
|
||||
|
||||
if (bottomScrolled)
|
||||
scroll->setValue(scroll->maximum());
|
||||
|
||||
if (log_level <= LOG_WARNING) {
|
||||
show();
|
||||
raise();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptLogWindow::ClearWindow()
|
||||
{
|
||||
Clear();
|
||||
scriptLogWidget->setPlainText(QString());
|
||||
}
|
||||
|
||||
void ScriptLogWindow::Clear()
|
||||
{
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
ScriptsTool::ScriptsTool()
|
||||
: QWidget (nullptr),
|
||||
ui (new Ui_ScriptsTool)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
RefreshLists();
|
||||
|
||||
#if PYTHON_UI
|
||||
config_t *config = obs_frontend_get_global_config();
|
||||
const char *path = config_get_string(config, "Python",
|
||||
"Path" ARCH_NAME);
|
||||
ui->pythonPath->setText(path);
|
||||
ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT));
|
||||
#else
|
||||
delete ui->pythonSettingsTab;
|
||||
ui->pythonSettingsTab = nullptr;
|
||||
#endif
|
||||
|
||||
delete propertiesView;
|
||||
propertiesView = new QWidget();
|
||||
propertiesView->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Expanding);
|
||||
ui->propertiesLayout->addWidget(propertiesView);
|
||||
}
|
||||
|
||||
ScriptsTool::~ScriptsTool()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ScriptsTool::RemoveScript(const char *path)
|
||||
{
|
||||
for (size_t i = 0; i < scriptData->scripts.size(); i++) {
|
||||
OBSScript &script = scriptData->scripts[i];
|
||||
|
||||
const char *script_path = obs_script_get_path(script);
|
||||
if (strcmp(script_path, path) == 0) {
|
||||
scriptData->scripts.erase(
|
||||
scriptData->scripts.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsTool::ReloadScript(const char *path)
|
||||
{
|
||||
for (OBSScript &script : scriptData->scripts) {
|
||||
const char *script_path = obs_script_get_path(script);
|
||||
if (strcmp(script_path, path) == 0) {
|
||||
obs_script_reload(script);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsTool::RefreshLists()
|
||||
{
|
||||
ui->scripts->clear();
|
||||
|
||||
for (OBSScript &script : scriptData->scripts) {
|
||||
const char *script_file = obs_script_get_file(script);
|
||||
const char *script_path = obs_script_get_path(script);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(script_file);
|
||||
item->setData(Qt::UserRole, QString(script_path));
|
||||
ui->scripts->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsTool::on_close_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void ScriptsTool::on_addScripts_clicked()
|
||||
{
|
||||
const char **formats = obs_scripting_supported_formats();
|
||||
const char **cur_format = formats;
|
||||
QString extensions;
|
||||
QString filter;
|
||||
|
||||
while (*cur_format) {
|
||||
if (!extensions.isEmpty())
|
||||
extensions += QStringLiteral(" ");
|
||||
|
||||
extensions += QStringLiteral("*.");
|
||||
extensions += *cur_format;
|
||||
|
||||
cur_format++;
|
||||
}
|
||||
|
||||
if (!extensions.isEmpty()) {
|
||||
filter += obs_module_text("FileFilter.ScriptFiles");
|
||||
filter += QStringLiteral(" (");
|
||||
filter += extensions;
|
||||
filter += QStringLiteral(")");
|
||||
}
|
||||
|
||||
if (filter.isEmpty())
|
||||
return;
|
||||
|
||||
static std::string lastBrowsedDir;
|
||||
|
||||
if (lastBrowsedDir.empty()) {
|
||||
BPtr<char> baseScriptPath = obs_module_file("scripts");
|
||||
lastBrowsedDir = baseScriptPath;
|
||||
}
|
||||
|
||||
QFileDialog dlg(this, obs_module_text("AddScripts"));
|
||||
dlg.setFileMode(QFileDialog::ExistingFiles);
|
||||
dlg.setDirectory(QDir(lastBrowsedDir.c_str()));
|
||||
dlg.setNameFilter(filter);
|
||||
dlg.exec();
|
||||
|
||||
QStringList files = dlg.selectedFiles();
|
||||
if (!files.count())
|
||||
return;
|
||||
|
||||
lastBrowsedDir = dlg.directory().path().toUtf8().constData();
|
||||
|
||||
for (const QString &file : files) {
|
||||
QByteArray pathBytes = file.toUtf8();
|
||||
const char *path = pathBytes.constData();
|
||||
|
||||
if (scriptData->ScriptOpened(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
obs_script_t *script = obs_script_create(path, NULL);
|
||||
if (script) {
|
||||
const char *script_file = obs_script_get_file(script);
|
||||
|
||||
scriptData->scripts.emplace_back(script);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(script_file);
|
||||
item->setData(Qt::UserRole, QString(file));
|
||||
ui->scripts->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsTool::on_removeScripts_clicked()
|
||||
{
|
||||
QList<QListWidgetItem *> items = ui->scripts->selectedItems();
|
||||
|
||||
for (QListWidgetItem *item : items)
|
||||
RemoveScript(item->data(Qt::UserRole).toString()
|
||||
.toUtf8().constData());
|
||||
RefreshLists();
|
||||
}
|
||||
|
||||
void ScriptsTool::on_reloadScripts_clicked()
|
||||
{
|
||||
QList<QListWidgetItem *> items = ui->scripts->selectedItems();
|
||||
for (QListWidgetItem *item : items)
|
||||
ReloadScript(item->data(Qt::UserRole).toString()
|
||||
.toUtf8().constData());
|
||||
|
||||
on_scripts_currentRowChanged(ui->scripts->currentRow());
|
||||
}
|
||||
|
||||
void ScriptsTool::on_scriptLog_clicked()
|
||||
{
|
||||
scriptLogWindow->show();
|
||||
scriptLogWindow->raise();
|
||||
}
|
||||
|
||||
void ScriptsTool::on_pythonPathBrowse_clicked()
|
||||
{
|
||||
QString curPath = ui->pythonPath->text();
|
||||
QString newPath = QFileDialog::getExistingDirectory(
|
||||
this,
|
||||
ui->pythonPathLabel->text(),
|
||||
curPath);
|
||||
|
||||
if (newPath.isEmpty())
|
||||
return;
|
||||
|
||||
QByteArray array = newPath.toUtf8();
|
||||
const char *path = array.constData();
|
||||
|
||||
config_t *config = obs_frontend_get_global_config();
|
||||
config_set_string(config, "Python", "Path" ARCH_NAME, path);
|
||||
|
||||
ui->pythonPath->setText(newPath);
|
||||
|
||||
if (obs_scripting_python_loaded())
|
||||
return;
|
||||
if (!obs_scripting_load_python(path))
|
||||
return;
|
||||
|
||||
for (OBSScript &script : scriptData->scripts) {
|
||||
enum obs_script_lang lang = obs_script_get_lang(script);
|
||||
if (lang == OBS_SCRIPT_LANG_PYTHON) {
|
||||
obs_script_reload(script);
|
||||
}
|
||||
}
|
||||
|
||||
on_scripts_currentRowChanged(ui->scripts->currentRow());
|
||||
}
|
||||
|
||||
void ScriptsTool::on_scripts_currentRowChanged(int row)
|
||||
{
|
||||
ui->propertiesLayout->removeWidget(propertiesView);
|
||||
delete propertiesView;
|
||||
|
||||
if (row == -1) {
|
||||
propertiesView = new QWidget();
|
||||
propertiesView->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Expanding);
|
||||
ui->propertiesLayout->addWidget(propertiesView);
|
||||
ui->description->setText(QString());
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray array = ui->scripts->item(row)->data(Qt::UserRole)
|
||||
.toString().toUtf8();
|
||||
const char *path = array.constData();
|
||||
|
||||
obs_script_t *script = scriptData->FindScript(path);
|
||||
if (!script) {
|
||||
propertiesView = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
OBSData settings = obs_script_get_settings(script);
|
||||
obs_data_release(settings);
|
||||
|
||||
propertiesView = new OBSPropertiesView(settings, script,
|
||||
(PropertiesReloadCallback)obs_script_get_properties,
|
||||
(PropertiesUpdateCallback)obs_script_update);
|
||||
ui->propertiesLayout->addWidget(propertiesView);
|
||||
ui->description->setText(obs_script_get_description(script));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
extern "C" void FreeScripts()
|
||||
{
|
||||
obs_scripting_unload();
|
||||
}
|
||||
|
||||
static void obs_event(enum obs_frontend_event event, void *)
|
||||
{
|
||||
if (event == OBS_FRONTEND_EVENT_EXIT) {
|
||||
delete scriptData;
|
||||
delete scriptsWindow;
|
||||
delete scriptLogWindow;
|
||||
|
||||
} else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP) {
|
||||
scriptLogWindow->hide();
|
||||
scriptLogWindow->Clear();
|
||||
|
||||
delete scriptData;
|
||||
scriptData = new ScriptData;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_script_data(obs_data_t *load_data, bool, void *)
|
||||
{
|
||||
obs_data_array_t *array = obs_data_get_array(load_data,
|
||||
"scripts-tool");
|
||||
|
||||
delete scriptData;
|
||||
scriptData = new ScriptData;
|
||||
|
||||
size_t size = obs_data_array_count(array);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
obs_data_t *obj = obs_data_array_item(array, i);
|
||||
const char *path = obs_data_get_string(obj, "path");
|
||||
obs_data_t *settings = obs_data_get_obj(obj, "settings");
|
||||
|
||||
obs_script_t *script = obs_script_create(path, settings);
|
||||
if (script) {
|
||||
scriptData->scripts.emplace_back(script);
|
||||
}
|
||||
|
||||
obs_data_release(settings);
|
||||
obs_data_release(obj);
|
||||
}
|
||||
|
||||
if (scriptsWindow)
|
||||
scriptsWindow->RefreshLists();
|
||||
|
||||
obs_data_array_release(array);
|
||||
}
|
||||
|
||||
static void save_script_data(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (!saving)
|
||||
return;
|
||||
|
||||
obs_data_array_t *array = obs_data_array_create();
|
||||
|
||||
for (OBSScript &script : scriptData->scripts) {
|
||||
const char *script_path = obs_script_get_path(script);
|
||||
obs_data_t *settings = obs_script_save(script);
|
||||
|
||||
obs_data_t *obj = obs_data_create();
|
||||
obs_data_set_string(obj, "path", script_path);
|
||||
obs_data_set_obj(obj, "settings", settings);
|
||||
obs_data_array_push_back(array, obj);
|
||||
obs_data_release(obj);
|
||||
|
||||
obs_data_release(settings);
|
||||
}
|
||||
|
||||
obs_data_set_array(save_data, "scripts-tool", array);
|
||||
obs_data_array_release(array);
|
||||
}
|
||||
|
||||
static void script_log(void *, obs_script_t *script, int log_level,
|
||||
const char *message)
|
||||
{
|
||||
QString qmsg;
|
||||
|
||||
if (script) {
|
||||
qmsg = QStringLiteral("[%1] %2").arg(
|
||||
obs_script_get_file(script),
|
||||
message);
|
||||
} else {
|
||||
qmsg = QStringLiteral("[Unknown Script] %1").arg(message);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(scriptLogWindow, "AddLogMsg",
|
||||
Q_ARG(int, log_level),
|
||||
Q_ARG(QString, qmsg));
|
||||
}
|
||||
|
||||
extern "C" void InitScripts()
|
||||
{
|
||||
scriptLogWindow = new ScriptLogWindow();
|
||||
|
||||
obs_scripting_load();
|
||||
obs_scripting_set_log_callback(script_log, nullptr);
|
||||
|
||||
QAction *action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
||||
obs_module_text("Scripts"));
|
||||
|
||||
#if PYTHON_UI
|
||||
config_t *config = obs_frontend_get_global_config();
|
||||
const char *python_path = config_get_string(config, "Python",
|
||||
"Path" ARCH_NAME);
|
||||
|
||||
if (!obs_scripting_python_loaded() && python_path && *python_path)
|
||||
obs_scripting_load_python(python_path);
|
||||
#endif
|
||||
|
||||
scriptData = new ScriptData;
|
||||
|
||||
auto cb = [] ()
|
||||
{
|
||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||
|
||||
if (!scriptsWindow) {
|
||||
scriptsWindow = new ScriptsTool();
|
||||
scriptsWindow->show();
|
||||
} else {
|
||||
scriptsWindow->show();
|
||||
scriptsWindow->raise();
|
||||
}
|
||||
|
||||
obs_frontend_pop_ui_translation();
|
||||
};
|
||||
|
||||
obs_frontend_add_save_callback(save_script_data, nullptr);
|
||||
obs_frontend_add_preload_callback(load_script_data, nullptr);
|
||||
obs_frontend_add_event_callback(obs_event, nullptr);
|
||||
|
||||
action->connect(action, &QAction::triggered, cb);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue