mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Settings dialog (fixes #4) and WIP authentication (Buggy and needs testing)
This commit is contained in:
parent
5f7061c586
commit
bf317987c3
@ -4,11 +4,16 @@ project(obs-websocket)
|
|||||||
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
include(external/FindLibObs.cmake)
|
include(external/FindLibObs.cmake)
|
||||||
find_package(LibObs REQUIRED)
|
find_package(LibObs REQUIRED)
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt5Core REQUIRED)
|
||||||
find_package(Qt5WebSockets REQUIRED)
|
find_package(Qt5WebSockets REQUIRED)
|
||||||
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
|
||||||
|
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
|
||||||
|
set(ENABLED_PROGRAMS false)
|
||||||
|
|
||||||
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
||||||
if(OBS_FRONTEND_LIB EQUAL "OBS_FRONTEND_LIB-NOTFOUND")
|
if(OBS_FRONTEND_LIB EQUAL "OBS_FRONTEND_LIB-NOTFOUND")
|
||||||
@ -21,7 +26,8 @@ set(obs-websocket_SOURCES
|
|||||||
WSRequestHandler.cpp
|
WSRequestHandler.cpp
|
||||||
WSEvents.cpp
|
WSEvents.cpp
|
||||||
Config.cpp
|
Config.cpp
|
||||||
Utils.cpp)
|
Utils.cpp
|
||||||
|
forms/settings-dialog.cpp)
|
||||||
|
|
||||||
set(obs-websocket_HEADERS
|
set(obs-websocket_HEADERS
|
||||||
obs-websocket.h
|
obs-websocket.h
|
||||||
@ -29,19 +35,24 @@ set(obs-websocket_HEADERS
|
|||||||
WSRequestHandler.h
|
WSRequestHandler.h
|
||||||
WSEvents.h
|
WSEvents.h
|
||||||
Config.h
|
Config.h
|
||||||
Utils.h)
|
Utils.h
|
||||||
|
forms/settings-dialog.h)
|
||||||
|
|
||||||
add_library(obs-websocket MODULE
|
add_library(obs-websocket MODULE
|
||||||
${obs-websocket_SOURCES}
|
${obs-websocket_SOURCES}
|
||||||
${obs-websocket_HEADERS})
|
${obs-websocket_HEADERS})
|
||||||
qt5_use_modules(obs-websocket
|
add_dependencies(obs-websocket mbedcrypto)
|
||||||
Core WebSockets)
|
|
||||||
include_directories(
|
include_directories(
|
||||||
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
||||||
${Qt5Core_INCLUDES}
|
${Qt5Core_INCLUDES}
|
||||||
${Qt5WebSockets_INCLUDES})
|
${Qt5WebSockets_INCLUDES}
|
||||||
|
${Qt5Widgets_INCLUDES}
|
||||||
|
${mbedcrypto_INCLUDES}
|
||||||
|
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
|
||||||
target_link_libraries(obs-websocket
|
target_link_libraries(obs-websocket
|
||||||
libobs
|
libobs
|
||||||
${OBS_FRONTEND_LIB}
|
${OBS_FRONTEND_LIB}
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::WebSockets)
|
Qt5::WebSockets
|
||||||
|
Qt5::Widgets
|
||||||
|
mbedcrypto)
|
||||||
|
113
Config.cpp
113
Config.cpp
@ -1,16 +1,102 @@
|
|||||||
|
#include <mbedtls/base64.h>
|
||||||
|
#include <mbedtls/sha256.h>
|
||||||
|
#include <obs-frontend-api.h>
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#define CONFIG_SECTION_NAME "obs-websocket"
|
||||||
|
#define CONFIG_PARAM_CHALLENGE "auth_hash"
|
||||||
|
#define CONFIG_PARAM_SALT "auth_salt"
|
||||||
|
#define CONFIG_PARAM_AUTHREQUIRED "auth_required"
|
||||||
|
|
||||||
Config *Config::_instance = new Config();
|
Config *Config::_instance = new Config();
|
||||||
|
|
||||||
Config::Config() {
|
Config::Config() {
|
||||||
|
// Default settings
|
||||||
AuthRequired = false;
|
AuthRequired = false;
|
||||||
Challenge = "";
|
Challenge = "";
|
||||||
Salt = "";
|
Salt = "";
|
||||||
SettingsLoaded = false;
|
SettingsLoaded = false;
|
||||||
|
|
||||||
|
mbedtls_entropy_init(&entropy);
|
||||||
|
mbedtls_ctr_drbg_init(&rng);
|
||||||
|
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
|
||||||
|
//mbedtls_ctr_drbg_set_prediction_resistance(&rng, MBEDTLS_CTR_DRBG_PR_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::~Config() {
|
||||||
|
mbedtls_ctr_drbg_free(&rng);
|
||||||
|
mbedtls_entropy_free(&entropy);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Config::GenerateSalt(mbedtls_ctr_drbg_context *rng) {
|
||||||
|
// Generate 32 random chars
|
||||||
|
unsigned char *random_chars = (unsigned char *)bzalloc(32);
|
||||||
|
mbedtls_ctr_drbg_random(rng, random_chars, 32);
|
||||||
|
|
||||||
|
// Convert the 32 random chars to a base64 string
|
||||||
|
unsigned char *salt = (unsigned char*)bzalloc(64);
|
||||||
|
size_t salt_bytes;
|
||||||
|
mbedtls_base64_encode(salt, 64, &salt_bytes, random_chars, 32);
|
||||||
|
salt[salt_bytes] = 0; // Null-terminate the string
|
||||||
|
|
||||||
|
bfree(random_chars);
|
||||||
|
return (char *)salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Config::GenerateChallenge(const char *password, const char *salt) {
|
||||||
|
size_t passwordLength = strlen(password);
|
||||||
|
size_t saltLength = strlen(salt);
|
||||||
|
|
||||||
|
// Concatenate the password and the salt
|
||||||
|
unsigned char *passAndSalt = (unsigned char*)bzalloc(passwordLength + saltLength);
|
||||||
|
memcpy(passAndSalt, password, passwordLength);
|
||||||
|
memcpy(passAndSalt + passwordLength, salt, saltLength);
|
||||||
|
passAndSalt[passwordLength + saltLength] = 0; // Null-terminate the string
|
||||||
|
|
||||||
|
// Generate a SHA256 hash of the password
|
||||||
|
unsigned char *challengeHash = (unsigned char *)bzalloc(32);
|
||||||
|
mbedtls_sha256(passAndSalt, passwordLength + saltLength, challengeHash, 0);
|
||||||
|
|
||||||
|
// Encode SHA256 hash to Base64
|
||||||
|
unsigned char *challenge = (unsigned char*)bzalloc(64);
|
||||||
|
size_t challenge_bytes = 0;
|
||||||
|
mbedtls_base64_encode(challenge, 64, &challenge_bytes, challengeHash, 32);
|
||||||
|
challenge[64] = 0; // Null-terminate the string
|
||||||
|
|
||||||
|
bfree(passAndSalt);
|
||||||
|
bfree(challengeHash);
|
||||||
|
return (char*)challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SetPassword(const char *password) {
|
void Config::SetPassword(const char *password) {
|
||||||
|
const char *new_salt = GenerateSalt(&rng);
|
||||||
|
const char *new_challenge = GenerateChallenge(password, new_salt);
|
||||||
|
|
||||||
|
this->Salt = new_salt;
|
||||||
|
this->Challenge = new_challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::CheckAuth(const char *response) {
|
||||||
|
size_t challengeLength = strlen(this->Challenge);
|
||||||
|
size_t responseLength = strlen(response);
|
||||||
|
|
||||||
|
// Concatenate challenge and auth response
|
||||||
|
char *challengeAndResponse = (char*)bzalloc(challengeLength + responseLength);
|
||||||
|
memcpy(challengeAndResponse, this->Challenge, challengeLength);
|
||||||
|
memcpy(challengeAndResponse + challengeLength, response, responseLength);
|
||||||
|
challengeAndResponse[challengeLength + responseLength] = 0; // Null-terminate the string
|
||||||
|
|
||||||
|
// Generate a SHA256 hash of challengeAndResponse
|
||||||
|
unsigned char *hash = (unsigned char*)bzalloc(32);
|
||||||
|
mbedtls_sha256((unsigned char*)challengeAndResponse, challengeLength + responseLength, hash, 0);
|
||||||
|
|
||||||
|
// Encode the SHA256 hash to Base64
|
||||||
|
unsigned char *expected_response = (unsigned char*)bzalloc(64);
|
||||||
|
size_t base64_size = 0;
|
||||||
|
mbedtls_base64_encode(expected_response, 64, &base64_size, hash, 32);
|
||||||
|
expected_response[64] = 0; // Null-terminate the string
|
||||||
|
|
||||||
|
return (strcmp((char*)expected_response, response) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) {
|
void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) {
|
||||||
@ -18,28 +104,23 @@ void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_d
|
|||||||
|
|
||||||
if (saving) {
|
if (saving) {
|
||||||
obs_data_t *settings = obs_data_create();
|
obs_data_t *settings = obs_data_create();
|
||||||
obs_data_set_bool(settings, "auth_required", conf->AuthRequired);
|
obs_data_set_bool(settings, CONFIG_PARAM_AUTHREQUIRED, conf->AuthRequired);
|
||||||
obs_data_set_string(settings, "auth_hash", conf->Challenge);
|
obs_data_set_string(settings, CONFIG_PARAM_CHALLENGE, conf->Challenge);
|
||||||
obs_data_set_string(settings, "auth_salt", conf->Salt);
|
obs_data_set_string(settings, CONFIG_PARAM_SALT, conf->Salt);
|
||||||
|
|
||||||
obs_data_set_obj(save_data, "obs-websocket", settings);
|
obs_data_set_obj(save_data, CONFIG_SECTION_NAME, settings);
|
||||||
|
|
||||||
obs_data_release(settings);
|
obs_data_release(settings);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
obs_data_t *settings = obs_data_get_obj(save_data, "obs-websocket");
|
obs_data_t *settings = obs_data_get_obj(save_data, CONFIG_SECTION_NAME);
|
||||||
if (!settings) {
|
if (settings) {
|
||||||
settings = obs_data_create();
|
conf->AuthRequired = obs_data_get_bool(settings, CONFIG_PARAM_AUTHREQUIRED);
|
||||||
obs_data_set_bool(settings, "auth_required", conf->AuthRequired);
|
conf->Challenge = obs_data_get_string(settings, CONFIG_PARAM_CHALLENGE);
|
||||||
obs_data_set_string(settings, "auth_hash", conf->Challenge);
|
conf->Salt = obs_data_get_string(settings, CONFIG_PARAM_SALT);
|
||||||
obs_data_set_string(settings, "auth_salt", conf->Salt);
|
|
||||||
|
conf->SettingsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf->AuthRequired = obs_data_get_bool(settings, "auth_required");
|
|
||||||
conf->Challenge = obs_data_get_string(settings, "auth_hash");
|
|
||||||
conf->Salt = obs_data_get_string(settings, "auth_salt");
|
|
||||||
conf->SettingsLoaded = true;
|
|
||||||
|
|
||||||
obs_data_release(settings);
|
obs_data_release(settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
Config.h
13
Config.h
@ -2,21 +2,30 @@
|
|||||||
#define CONFIG_H
|
#define CONFIG_H
|
||||||
|
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
|
#include <mbedtls/entropy.h>
|
||||||
|
#include <mbedtls/ctr_drbg.h>
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
|
~Config();
|
||||||
void SetPassword(const char *password);
|
void SetPassword(const char *password);
|
||||||
|
bool CheckAuth(const char *userChallenge);
|
||||||
|
static const char* GenerateSalt(mbedtls_ctr_drbg_context *rng);
|
||||||
|
static const char* GenerateChallenge(const char *password, const char *salt);
|
||||||
|
static void OBSSaveCallback(obs_data_t *save_data, bool saving, void *);
|
||||||
|
|
||||||
bool AuthRequired;
|
bool AuthRequired;
|
||||||
const char *Challenge;
|
const char *Challenge;
|
||||||
const char *Salt;
|
const char *Salt;
|
||||||
bool SettingsLoaded;
|
bool SettingsLoaded;
|
||||||
static void OBSSaveCallback(obs_data_t *save_data, bool saving, void *);
|
|
||||||
|
|
||||||
static Config* Current();
|
static Config* Current();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Config *_instance;
|
static Config *_instance;
|
||||||
|
mbedtls_entropy_context entropy;
|
||||||
|
mbedtls_ctr_drbg_context rng;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
@ -77,6 +77,7 @@ void WSRequestHandler::processTextMessage(QString textMessage) {
|
|||||||
void WSRequestHandler::socketDisconnected() {
|
void WSRequestHandler::socketDisconnected() {
|
||||||
blog(LOG_INFO, "[obs-websockets] client %s:%d disconnected", _client->peerAddress().toString().toStdString(), _client->peerPort());
|
blog(LOG_INFO, "[obs-websockets] client %s:%d disconnected", _client->peerAddress().toString().toStdString(), _client->peerPort());
|
||||||
|
|
||||||
|
_authenticated = false;
|
||||||
_client->deleteLater();
|
_client->deleteLater();
|
||||||
emit disconnected();
|
emit disconnected();
|
||||||
}
|
}
|
||||||
@ -147,10 +148,13 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler *owner) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : Implement auth here
|
if (!(owner->_authenticated) && Config::Current()->CheckAuth(auth)) {
|
||||||
|
owner->_authenticated = true;
|
||||||
owner->_authenticated = true;
|
owner->SendOKResponse();
|
||||||
owner->SendOKResponse();
|
}
|
||||||
|
else {
|
||||||
|
owner->SendErrorResponse("Authentication Failed.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *owner) {
|
||||||
|
4
data/locale/en-US.ini
Normal file
4
data/locale/en-US.ini
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Menu.SettingsItem="Websocket server settings"
|
||||||
|
Settings.DialogTitle="obs-websocket"
|
||||||
|
Settings.AuthRequired="Enable authentication"
|
||||||
|
Settings.Password="Password"
|
4
data/locale/fr-FR.ini
Normal file
4
data/locale/fr-FR.ini
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Menu.SettingsItem="Paramètres du serveur Websocket"
|
||||||
|
Settings.DialogTitle="obs-websocket"
|
||||||
|
Settings.AuthRequired="Activer l'authentification"
|
||||||
|
Settings.Password="Mot de passe"
|
70
forms/settings-dialog.cpp
Normal file
70
forms/settings-dialog.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include <obs-frontend-api.h>
|
||||||
|
#include "settings-dialog.h"
|
||||||
|
#include "ui_settings-dialog.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#define CHANGE_ME "changeme"
|
||||||
|
|
||||||
|
SettingsDialog::SettingsDialog(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::SettingsDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->authRequired, &QCheckBox::stateChanged, this, &SettingsDialog::AuthCheckboxChanged);
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::FormAccepted);
|
||||||
|
|
||||||
|
AuthCheckboxChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::showEvent(QShowEvent *event) {
|
||||||
|
ui->authRequired->setChecked(Config::Current()->AuthRequired);
|
||||||
|
ui->password->setText(CHANGE_ME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::ToggleShowHide() {
|
||||||
|
if (!isVisible()) {
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::AuthCheckboxChanged() {
|
||||||
|
if (ui->authRequired->isChecked()) {
|
||||||
|
ui->password->setEnabled(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui->password->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::FormAccepted() {
|
||||||
|
if (ui->authRequired->isChecked()) {
|
||||||
|
if (ui->password->text() != CHANGE_ME) {
|
||||||
|
QByteArray pwd = ui->password->text().toLocal8Bit();
|
||||||
|
const char *new_password = pwd;
|
||||||
|
|
||||||
|
blog(LOG_INFO, "new password : %s", new_password);
|
||||||
|
Config::Current()->SetPassword(new_password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(Config::Current()->Challenge, "") != 0) {
|
||||||
|
Config::Current()->AuthRequired = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Config::Current()->AuthRequired = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Config::Current()->AuthRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_save();
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDialog::~SettingsDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
28
forms/settings-dialog.h
Normal file
28
forms/settings-dialog.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef SETTINGSDIALOG_H
|
||||||
|
#define SETTINGSDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SettingsDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SettingsDialog(QWidget *parent = 0);
|
||||||
|
~SettingsDialog();
|
||||||
|
void showEvent(QShowEvent *event);
|
||||||
|
void ToggleShowHide();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void AuthCheckboxChanged();
|
||||||
|
void FormAccepted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::SettingsDialog *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SETTINGSDIALOG_H
|
101
forms/settings-dialog.ui
Normal file
101
forms/settings-dialog.ui
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SettingsDialog</class>
|
||||||
|
<widget class="QDialog" name="SettingsDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>354</width>
|
||||||
|
<height>110</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Settings.DialogTitle</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizeGripEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetDefaultConstraint</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_password">
|
||||||
|
<property name="text">
|
||||||
|
<string>Settings.Password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="password">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="authRequired">
|
||||||
|
<property name="text">
|
||||||
|
<string>Settings.AuthRequired</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>SettingsDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>SettingsDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -1,26 +1,40 @@
|
|||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "WSEvents.h"
|
#include "WSEvents.h"
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "forms/settings-dialog.h"
|
||||||
|
|
||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
||||||
|
|
||||||
WSEvents *eventHandler;
|
WSEvents *eventHandler;
|
||||||
WSServer *server;
|
WSServer *server;
|
||||||
|
SettingsDialog *settings_dialog;
|
||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %.2f)", OBS_WEBSOCKET_VERSION);
|
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %.1f)", OBS_WEBSOCKET_VERSION);
|
||||||
|
|
||||||
server = new WSServer(4444);
|
server = new WSServer(4444);
|
||||||
eventHandler = new WSEvents(server);
|
eventHandler = new WSEvents(server);
|
||||||
|
|
||||||
obs_frontend_add_save_callback(Config::OBSSaveCallback, Config::Current());
|
obs_frontend_add_save_callback(Config::OBSSaveCallback, Config::Current());
|
||||||
|
|
||||||
|
QAction *menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(obs_module_text("Menu.SettingsItem"));
|
||||||
|
|
||||||
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
settings_dialog = new SettingsDialog();
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
auto menu_cb = [] {
|
||||||
|
settings_dialog->ToggleShowHide();
|
||||||
|
};
|
||||||
|
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user