clang-format: Increase column limit from 80 to 120

This commit is contained in:
Ryan Foster 2024-10-04 17:33:58 -04:00
parent 109f64c446
commit a1fbf1015f
736 changed files with 22684 additions and 45435 deletions

View File

@ -44,7 +44,7 @@ BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeColon
BreakStringLiterals: false # apparently unpredictable BreakStringLiterals: false # apparently unpredictable
ColumnLimit: 80 ColumnLimit: 120
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8 ConstructorInitializerIndentWidth: 8

View File

@ -6,8 +6,7 @@ AbsoluteSlider::AbsoluteSlider(QWidget *parent) : SliderIgnoreScroll(parent)
setMouseTracking(true); setMouseTracking(true);
} }
AbsoluteSlider::AbsoluteSlider(Qt::Orientation orientation, QWidget *parent) AbsoluteSlider::AbsoluteSlider(Qt::Orientation orientation, QWidget *parent) : SliderIgnoreScroll(orientation, parent)
: SliderIgnoreScroll(orientation, parent)
{ {
installEventFilter(this); installEventFilter(this);
setMouseTracking(true); setMouseTracking(true);
@ -15,8 +14,7 @@ AbsoluteSlider::AbsoluteSlider(Qt::Orientation orientation, QWidget *parent)
void AbsoluteSlider::mousePressEvent(QMouseEvent *event) void AbsoluteSlider::mousePressEvent(QMouseEvent *event)
{ {
dragging = (event->buttons() & Qt::LeftButton || dragging = (event->buttons() & Qt::LeftButton || event->buttons() & Qt::MiddleButton);
event->buttons() & Qt::MiddleButton);
if (dragging) { if (dragging) {
setSliderDown(true); setSliderDown(true);
@ -59,8 +57,7 @@ bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event)
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Up || if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) {
keyEvent->key() == Qt::Key_Down) {
return true; return true;
} }
} }
@ -78,10 +75,8 @@ int AbsoluteSlider::posToRangeValue(QMouseEvent *event)
int sliderMax; int sliderMax;
int handleLength; int handleLength;
const QRect groove = style()->subControlRect( const QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this); const QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
const QRect handle = style()->subControlRect(
QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (orientation() == Qt::Horizontal) { if (orientation() == Qt::Horizontal) {
pos = event->pos().x(); pos = event->pos().x();
@ -95,9 +90,8 @@ int AbsoluteSlider::posToRangeValue(QMouseEvent *event)
sliderMax = groove.bottom() - (handleLength / 2) + 1; sliderMax = groove.bottom() - (handleLength / 2) + 1;
} }
int sliderValue = style()->sliderValueFromPosition( int sliderValue = style()->sliderValueFromPosition(minimum(), maximum(), pos - sliderMin, sliderMax - sliderMin,
minimum(), maximum(), pos - sliderMin, sliderMax - sliderMin, opt.upsideDown);
opt.upsideDown);
return sliderValue; return sliderValue;
} }

View File

@ -16,11 +16,9 @@
#define MIN_DB -96.0 #define MIN_DB -96.0
#define MAX_DB 26.0 #define MAX_DB 26.0
static inline void setMixer(obs_source_t *source, const int mixerIdx, static inline void setMixer(obs_source_t *source, const int mixerIdx, const bool checked);
const bool checked);
OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) : source(source_)
: source(source_)
{ {
QHBoxLayout *hlayout; QHBoxLayout *hlayout;
signal_handler_t *handler = obs_source_get_signal_handler(source); signal_handler_t *handler = obs_source_get_signal_handler(source);
@ -54,18 +52,14 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
sigs.emplace_back(handler, "activate", OBSSourceActivated, this); sigs.emplace_back(handler, "activate", OBSSourceActivated, this);
sigs.emplace_back(handler, "deactivate", OBSSourceDeactivated, this); sigs.emplace_back(handler, "deactivate", OBSSourceDeactivated, this);
sigs.emplace_back(handler, "audio_activate", OBSSourceActivated, this); sigs.emplace_back(handler, "audio_activate", OBSSourceActivated, this);
sigs.emplace_back(handler, "audio_deactivate", OBSSourceDeactivated, sigs.emplace_back(handler, "audio_deactivate", OBSSourceDeactivated, this);
this);
sigs.emplace_back(handler, "volume", OBSSourceVolumeChanged, this); sigs.emplace_back(handler, "volume", OBSSourceVolumeChanged, this);
sigs.emplace_back(handler, "audio_sync", OBSSourceSyncChanged, this); sigs.emplace_back(handler, "audio_sync", OBSSourceSyncChanged, this);
sigs.emplace_back(handler, "update_flags", OBSSourceFlagsChanged, this); sigs.emplace_back(handler, "update_flags", OBSSourceFlagsChanged, this);
if (obs_audio_monitoring_available()) if (obs_audio_monitoring_available())
sigs.emplace_back(handler, "audio_monitoring", sigs.emplace_back(handler, "audio_monitoring", OBSSourceMonitoringTypeChanged, this);
OBSSourceMonitoringTypeChanged, this); sigs.emplace_back(handler, "audio_mixers", OBSSourceMixersChanged, this);
sigs.emplace_back(handler, "audio_mixers", OBSSourceMixersChanged, sigs.emplace_back(handler, "audio_balance", OBSSourceBalanceChanged, this);
this);
sigs.emplace_back(handler, "audio_balance", OBSSourceBalanceChanged,
this);
sigs.emplace_back(handler, "rename", OBSSourceRenamed, this); sigs.emplace_back(handler, "rename", OBSSourceRenamed, this);
hlayout = new QHBoxLayout(); hlayout = new QHBoxLayout();
@ -90,10 +84,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
SetSourceName(sourceName); SetSourceName(sourceName);
nameLabel->setAlignment(Qt::AlignVCenter); nameLabel->setAlignment(Qt::AlignVCenter);
bool isActive = obs_source_active(source) && bool isActive = obs_source_active(source) && obs_source_audio_active(source);
obs_source_audio_active(source); active->setText(isActive ? QTStr("Basic.Stats.Status.Active") : QTStr("Basic.Stats.Status.Inactive"));
active->setText(isActive ? QTStr("Basic.Stats.Status.Active")
: QTStr("Basic.Stats.Status.Inactive"));
if (isActive) if (isActive)
setClasses(active, "text-danger"); setClasses(active, "text-danger");
active->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); active->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
@ -104,8 +96,7 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
volume->setDecimals(1); volume->setDecimals(1);
volume->setSuffix(" dB"); volume->setSuffix(" dB");
volume->setValue(obs_mul_to_db(vol)); volume->setValue(obs_mul_to_db(vol));
volume->setAccessibleName( volume->setAccessibleName(QTStr("Basic.AdvAudio.VolumeSource").arg(sourceName));
QTStr("Basic.AdvAudio.VolumeSource").arg(sourceName));
if (volume->value() < MIN_DB) { if (volume->value() < MIN_DB) {
volume->setSpecialValueText("-inf dB"); volume->setSpecialValueText("-inf dB");
@ -116,34 +107,29 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
percent->setMaximum(2000); percent->setMaximum(2000);
percent->setSuffix("%"); percent->setSuffix("%");
percent->setValue((int)(obs_source_get_volume(source) * 100.0f)); percent->setValue((int)(obs_source_get_volume(source) * 100.0f));
percent->setAccessibleName( percent->setAccessibleName(QTStr("Basic.AdvAudio.VolumeSource").arg(sourceName));
QTStr("Basic.AdvAudio.VolumeSource").arg(sourceName));
stackedWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); stackedWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
stackedWidget->setFixedWidth(100); stackedWidget->setFixedWidth(100);
stackedWidget->addWidget(volume); stackedWidget->addWidget(volume);
stackedWidget->addWidget(percent); stackedWidget->addWidget(percent);
VolumeType volType = (VolumeType)config_get_int( VolumeType volType = (VolumeType)config_get_int(App()->GetUserConfig(), "BasicWindow", "AdvAudioVolumeType");
App()->GetUserConfig(), "BasicWindow", "AdvAudioVolumeType");
SetVolumeWidget(volType); SetVolumeWidget(volType);
forceMono->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); forceMono->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
forceMono->setChecked((flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0); forceMono->setChecked((flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0);
forceMono->setAccessibleName( forceMono->setAccessibleName(QTStr("Basic.AdvAudio.MonoSource").arg(sourceName));
QTStr("Basic.AdvAudio.MonoSource").arg(sourceName));
balance->setOrientation(Qt::Horizontal); balance->setOrientation(Qt::Horizontal);
balance->setMinimum(0); balance->setMinimum(0);
balance->setMaximum(100); balance->setMaximum(100);
balance->setTickPosition(QSlider::TicksAbove); balance->setTickPosition(QSlider::TicksAbove);
balance->setTickInterval(50); balance->setTickInterval(50);
balance->setAccessibleName( balance->setAccessibleName(QTStr("Basic.AdvAudio.BalanceSource").arg(sourceName));
QTStr("Basic.AdvAudio.BalanceSource").arg(sourceName));
const char *speakers = const char *speakers = config_get_string(main->Config(), "Audio", "ChannelSetup");
config_get_string(main->Config(), "Audio", "ChannelSetup");
if (strcmp(speakers, "Mono") == 0) if (strcmp(speakers, "Mono") == 0)
balance->setEnabled(false); balance->setEnabled(false);
@ -159,53 +145,40 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
syncOffset->setSuffix(" ms"); syncOffset->setSuffix(" ms");
syncOffset->setValue(int(cur_sync / NSEC_PER_MSEC)); syncOffset->setValue(int(cur_sync / NSEC_PER_MSEC));
syncOffset->setFixedWidth(100); syncOffset->setFixedWidth(100);
syncOffset->setAccessibleName( syncOffset->setAccessibleName(QTStr("Basic.AdvAudio.SyncOffsetSource").arg(sourceName));
QTStr("Basic.AdvAudio.SyncOffsetSource").arg(sourceName));
int idx; int idx;
if (obs_audio_monitoring_available()) { if (obs_audio_monitoring_available()) {
monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.None"), monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.None"), (int)OBS_MONITORING_TYPE_NONE);
(int)OBS_MONITORING_TYPE_NONE); monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.MonitorOnly"),
monitoringType->addItem( (int)OBS_MONITORING_TYPE_MONITOR_ONLY);
QTStr("Basic.AdvAudio.Monitoring.MonitorOnly"), monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.Both"),
(int)OBS_MONITORING_TYPE_MONITOR_ONLY); (int)OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
monitoringType->addItem(
QTStr("Basic.AdvAudio.Monitoring.Both"),
(int)OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
int mt = (int)obs_source_get_monitoring_type(source); int mt = (int)obs_source_get_monitoring_type(source);
idx = monitoringType->findData(mt); idx = monitoringType->findData(mt);
monitoringType->setCurrentIndex(idx); monitoringType->setCurrentIndex(idx);
monitoringType->setAccessibleName( monitoringType->setAccessibleName(QTStr("Basic.AdvAudio.MonitoringSource").arg(sourceName));
QTStr("Basic.AdvAudio.MonitoringSource") monitoringType->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
.arg(sourceName));
monitoringType->setSizePolicy(QSizePolicy::Maximum,
QSizePolicy::Fixed);
} }
mixer1->setText("1"); mixer1->setText("1");
mixer1->setChecked(mixers & (1 << 0)); mixer1->setChecked(mixers & (1 << 0));
mixer1->setAccessibleName( mixer1->setAccessibleName(QTStr("Basic.Settings.Output.Adv.Audio.Track1"));
QTStr("Basic.Settings.Output.Adv.Audio.Track1"));
mixer2->setText("2"); mixer2->setText("2");
mixer2->setChecked(mixers & (1 << 1)); mixer2->setChecked(mixers & (1 << 1));
mixer2->setAccessibleName( mixer2->setAccessibleName(QTStr("Basic.Settings.Output.Adv.Audio.Track2"));
QTStr("Basic.Settings.Output.Adv.Audio.Track2"));
mixer3->setText("3"); mixer3->setText("3");
mixer3->setChecked(mixers & (1 << 2)); mixer3->setChecked(mixers & (1 << 2));
mixer3->setAccessibleName( mixer3->setAccessibleName(QTStr("Basic.Settings.Output.Adv.Audio.Track3"));
QTStr("Basic.Settings.Output.Adv.Audio.Track3"));
mixer4->setText("4"); mixer4->setText("4");
mixer4->setChecked(mixers & (1 << 3)); mixer4->setChecked(mixers & (1 << 3));
mixer4->setAccessibleName( mixer4->setAccessibleName(QTStr("Basic.Settings.Output.Adv.Audio.Track4"));
QTStr("Basic.Settings.Output.Adv.Audio.Track4"));
mixer5->setText("5"); mixer5->setText("5");
mixer5->setChecked(mixers & (1 << 4)); mixer5->setChecked(mixers & (1 << 4));
mixer5->setAccessibleName( mixer5->setAccessibleName(QTStr("Basic.Settings.Output.Adv.Audio.Track5"));
QTStr("Basic.Settings.Output.Adv.Audio.Track5"));
mixer6->setText("6"); mixer6->setText("6");
mixer6->setChecked(mixers & (1 << 5)); mixer6->setChecked(mixers & (1 << 5));
mixer6->setAccessibleName( mixer6->setAccessibleName(QTStr("Basic.Settings.Output.Adv.Audio.Track6"));
QTStr("Basic.Settings.Output.Adv.Audio.Track6"));
balanceContainer->layout()->addWidget(labelL); balanceContainer->layout()->addWidget(labelL);
balanceContainer->layout()->addWidget(balance); balanceContainer->layout()->addWidget(balance);
@ -224,26 +197,17 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
mixerContainer->layout()->addWidget(mixer6); mixerContainer->layout()->addWidget(mixer6);
mixerContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); mixerContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
connect(volume, &QDoubleSpinBox::valueChanged, this, connect(volume, &QDoubleSpinBox::valueChanged, this, &OBSAdvAudioCtrl::volumeChanged);
&OBSAdvAudioCtrl::volumeChanged); connect(percent, &QSpinBox::valueChanged, this, &OBSAdvAudioCtrl::percentChanged);
connect(percent, &QSpinBox::valueChanged, this, connect(forceMono, &QCheckBox::clicked, this, &OBSAdvAudioCtrl::downmixMonoChanged);
&OBSAdvAudioCtrl::percentChanged); connect(balance, &BalanceSlider::valueChanged, this, &OBSAdvAudioCtrl::balanceChanged);
connect(forceMono, &QCheckBox::clicked, this, connect(balance, &BalanceSlider::doubleClicked, this, &OBSAdvAudioCtrl::ResetBalance);
&OBSAdvAudioCtrl::downmixMonoChanged); connect(syncOffset, &QSpinBox::valueChanged, this, &OBSAdvAudioCtrl::syncOffsetChanged);
connect(balance, &BalanceSlider::valueChanged, this,
&OBSAdvAudioCtrl::balanceChanged);
connect(balance, &BalanceSlider::doubleClicked, this,
&OBSAdvAudioCtrl::ResetBalance);
connect(syncOffset, &QSpinBox::valueChanged, this,
&OBSAdvAudioCtrl::syncOffsetChanged);
if (obs_audio_monitoring_available()) if (obs_audio_monitoring_available())
connect(monitoringType, &QComboBox::currentIndexChanged, this, connect(monitoringType, &QComboBox::currentIndexChanged, this, &OBSAdvAudioCtrl::monitoringTypeChanged);
&OBSAdvAudioCtrl::monitoringTypeChanged);
auto connectMixer = [this](QCheckBox *mixer, int num) { auto connectMixer = [this](QCheckBox *mixer, int num) {
connect(mixer, &QCheckBox::clicked, [this, num](bool checked) { connect(mixer, &QCheckBox::clicked, [this, num](bool checked) { setMixer(source, num, checked); });
setMixer(source, num, checked);
});
}; };
connectMixer(mixer1, 0); connectMixer(mixer1, 0);
connectMixer(mixer2, 1); connectMixer(mixer2, 1);
@ -293,67 +257,62 @@ void OBSAdvAudioCtrl::ShowAudioControl(QGridLayout *layout)
void OBSAdvAudioCtrl::OBSSourceActivated(void *param, calldata_t *) void OBSAdvAudioCtrl::OBSSourceActivated(void *param, calldata_t *)
{ {
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceActiveChanged", Q_ARG(bool, true));
"SourceActiveChanged", Q_ARG(bool, true));
} }
void OBSAdvAudioCtrl::OBSSourceDeactivated(void *param, calldata_t *) void OBSAdvAudioCtrl::OBSSourceDeactivated(void *param, calldata_t *)
{ {
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceActiveChanged",
"SourceActiveChanged", Q_ARG(bool, false)); Q_ARG(bool, false));
} }
void OBSAdvAudioCtrl::OBSSourceFlagsChanged(void *param, calldata_t *calldata) void OBSAdvAudioCtrl::OBSSourceFlagsChanged(void *param, calldata_t *calldata)
{ {
uint32_t flags = (uint32_t)calldata_int(calldata, "flags"); uint32_t flags = (uint32_t)calldata_int(calldata, "flags");
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceFlagsChanged",
"SourceFlagsChanged", Q_ARG(uint32_t, flags)); Q_ARG(uint32_t, flags));
} }
void OBSAdvAudioCtrl::OBSSourceVolumeChanged(void *param, calldata_t *calldata) void OBSAdvAudioCtrl::OBSSourceVolumeChanged(void *param, calldata_t *calldata)
{ {
float volume = (float)calldata_float(calldata, "volume"); float volume = (float)calldata_float(calldata, "volume");
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceVolumeChanged",
"SourceVolumeChanged", Q_ARG(float, volume)); Q_ARG(float, volume));
} }
void OBSAdvAudioCtrl::OBSSourceSyncChanged(void *param, calldata_t *calldata) void OBSAdvAudioCtrl::OBSSourceSyncChanged(void *param, calldata_t *calldata)
{ {
int64_t offset = calldata_int(calldata, "offset"); int64_t offset = calldata_int(calldata, "offset");
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceSyncChanged",
"SourceSyncChanged", Q_ARG(int64_t, offset)); Q_ARG(int64_t, offset));
} }
void OBSAdvAudioCtrl::OBSSourceMonitoringTypeChanged(void *param, void OBSAdvAudioCtrl::OBSSourceMonitoringTypeChanged(void *param, calldata_t *calldata)
calldata_t *calldata)
{ {
int type = calldata_int(calldata, "type"); int type = calldata_int(calldata, "type");
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceMonitoringTypeChanged",
"SourceMonitoringTypeChanged",
Q_ARG(int, type)); Q_ARG(int, type));
} }
void OBSAdvAudioCtrl::OBSSourceMixersChanged(void *param, calldata_t *calldata) void OBSAdvAudioCtrl::OBSSourceMixersChanged(void *param, calldata_t *calldata)
{ {
uint32_t mixers = (uint32_t)calldata_int(calldata, "mixers"); uint32_t mixers = (uint32_t)calldata_int(calldata, "mixers");
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceMixersChanged",
"SourceMixersChanged",
Q_ARG(uint32_t, mixers)); Q_ARG(uint32_t, mixers));
} }
void OBSAdvAudioCtrl::OBSSourceBalanceChanged(void *param, calldata_t *calldata) void OBSAdvAudioCtrl::OBSSourceBalanceChanged(void *param, calldata_t *calldata)
{ {
int balance = (float)calldata_float(calldata, "balance") * 100.0f; int balance = (float)calldata_float(calldata, "balance") * 100.0f;
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SourceBalanceChanged",
"SourceBalanceChanged", Q_ARG(int, balance)); Q_ARG(int, balance));
} }
void OBSAdvAudioCtrl::OBSSourceRenamed(void *param, calldata_t *calldata) void OBSAdvAudioCtrl::OBSSourceRenamed(void *param, calldata_t *calldata)
{ {
QString newName = QT_UTF8(calldata_string(calldata, "new_name")); QString newName = QT_UTF8(calldata_string(calldata, "new_name"));
QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), QMetaObject::invokeMethod(reinterpret_cast<OBSAdvAudioCtrl *>(param), "SetSourceName", Q_ARG(QString, newName));
"SetSourceName", Q_ARG(QString, newName));
} }
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@ -441,19 +400,16 @@ void OBSAdvAudioCtrl::volumeChanged(double db)
obs_source_set_volume(source, val); obs_source_set_volume(source, val);
auto undo_redo = [](const std::string &uuid, float val) { auto undo_redo = [](const std::string &uuid, float val) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
obs_source_set_volume(source, val); obs_source_set_volume(source, val);
}; };
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
main->undo_s.add_action( main->undo_s.add_action(QTStr("Undo.Volume.Change").arg(name),
QTStr("Undo.Volume.Change").arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
std::bind(undo_redo, std::placeholders::_1, prev), std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);
std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid,
true);
} }
void OBSAdvAudioCtrl::percentChanged(int percent) void OBSAdvAudioCtrl::percentChanged(int percent)
@ -464,18 +420,15 @@ void OBSAdvAudioCtrl::percentChanged(int percent)
obs_source_set_volume(source, val); obs_source_set_volume(source, val);
auto undo_redo = [](const std::string &uuid, float val) { auto undo_redo = [](const std::string &uuid, float val) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
obs_source_set_volume(source, val); obs_source_set_volume(source, val);
}; };
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic::Get()->undo_s.add_action( OBSBasic::Get()->undo_s.add_action(QTStr("Undo.Volume.Change").arg(name),
QTStr("Undo.Volume.Change").arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
std::bind(undo_redo, std::placeholders::_1, prev), std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);
std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid,
true);
} }
static inline void set_mono(obs_source_t *source, bool mono) static inline void set_mono(obs_source_t *source, bool mono)
@ -504,8 +457,7 @@ void OBSAdvAudioCtrl::downmixMonoChanged(bool val)
obs_source_set_flags(source, flags); obs_source_set_flags(source, flags);
auto undo_redo = [](const std::string &uuid, bool val) { auto undo_redo = [](const std::string &uuid, bool val) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
set_mono(source, val); set_mono(source, val);
}; };
@ -513,10 +465,8 @@ void OBSAdvAudioCtrl::downmixMonoChanged(bool val)
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic::Get()->undo_s.add_action( OBSBasic::Get()->undo_s.add_action(text.arg(name), std::bind(undo_redo, std::placeholders::_1, !val),
text.arg(name), std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid);
std::bind(undo_redo, std::placeholders::_1, !val),
std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid);
} }
void OBSAdvAudioCtrl::balanceChanged(int val) void OBSAdvAudioCtrl::balanceChanged(int val)
@ -534,18 +484,15 @@ void OBSAdvAudioCtrl::balanceChanged(int val)
obs_source_set_balance_value(source, bal); obs_source_set_balance_value(source, bal);
auto undo_redo = [](const std::string &uuid, float val) { auto undo_redo = [](const std::string &uuid, float val) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
obs_source_set_balance_value(source, val); obs_source_set_balance_value(source, val);
}; };
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic::Get()->undo_s.add_action( OBSBasic::Get()->undo_s.add_action(QTStr("Undo.Balance.Change").arg(name),
QTStr("Undo.Balance.Change").arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
std::bind(undo_redo, std::placeholders::_1, prev), std::bind(undo_redo, std::placeholders::_1, bal), uuid, uuid, true);
std::bind(undo_redo, std::placeholders::_1, bal), uuid, uuid,
true);
} }
void OBSAdvAudioCtrl::ResetBalance() void OBSAdvAudioCtrl::ResetBalance()
@ -564,26 +511,22 @@ void OBSAdvAudioCtrl::syncOffsetChanged(int milliseconds)
obs_source_set_sync_offset(source, val); obs_source_set_sync_offset(source, val);
auto undo_redo = [](const std::string &uuid, int64_t val) { auto undo_redo = [](const std::string &uuid, int64_t val) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
obs_source_set_sync_offset(source, val); obs_source_set_sync_offset(source, val);
}; };
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic::Get()->undo_s.add_action( OBSBasic::Get()->undo_s.add_action(QTStr("Undo.SyncOffset.Change").arg(name),
QTStr("Undo.SyncOffset.Change").arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
std::bind(undo_redo, std::placeholders::_1, prev), std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);
std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid,
true);
} }
void OBSAdvAudioCtrl::monitoringTypeChanged(int index) void OBSAdvAudioCtrl::monitoringTypeChanged(int index)
{ {
obs_monitoring_type prev = obs_source_get_monitoring_type(source); obs_monitoring_type prev = obs_source_get_monitoring_type(source);
obs_monitoring_type mt = obs_monitoring_type mt = (obs_monitoring_type)monitoringType->itemData(index).toInt();
(obs_monitoring_type)monitoringType->itemData(index).toInt();
obs_source_set_monitoring_type(source, mt); obs_source_set_monitoring_type(source, mt);
const char *type = nullptr; const char *type = nullptr;
@ -601,24 +544,20 @@ void OBSAdvAudioCtrl::monitoringTypeChanged(int index)
} }
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
blog(LOG_INFO, "User changed audio monitoring for source '%s' to: %s", blog(LOG_INFO, "User changed audio monitoring for source '%s' to: %s", name ? name : "(null)", type);
name ? name : "(null)", type);
auto undo_redo = [](const std::string &uuid, obs_monitoring_type val) { auto undo_redo = [](const std::string &uuid, obs_monitoring_type val) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
obs_source_set_monitoring_type(source, val); obs_source_set_monitoring_type(source, val);
}; };
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic::Get()->undo_s.add_action( OBSBasic::Get()->undo_s.add_action(QTStr("Undo.MonitoringType.Change").arg(name),
QTStr("Undo.MonitoringType.Change").arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
std::bind(undo_redo, std::placeholders::_1, prev), std::bind(undo_redo, std::placeholders::_1, mt), uuid, uuid);
std::bind(undo_redo, std::placeholders::_1, mt), uuid, uuid);
} }
static inline void setMixer(obs_source_t *source, const int mixerIdx, static inline void setMixer(obs_source_t *source, const int mixerIdx, const bool checked)
const bool checked)
{ {
uint32_t mixers = obs_source_get_audio_mixers(source); uint32_t mixers = obs_source_get_audio_mixers(source);
uint32_t new_mixers = mixers; uint32_t new_mixers = mixers;
@ -631,18 +570,15 @@ static inline void setMixer(obs_source_t *source, const int mixerIdx,
obs_source_set_audio_mixers(source, new_mixers); obs_source_set_audio_mixers(source, new_mixers);
auto undo_redo = [](const std::string &uuid, uint32_t mixers) { auto undo_redo = [](const std::string &uuid, uint32_t mixers) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
obs_get_source_by_uuid(uuid.c_str());
obs_source_set_audio_mixers(source, mixers); obs_source_set_audio_mixers(source, mixers);
}; };
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
OBSBasic::Get()->undo_s.add_action( OBSBasic::Get()->undo_s.add_action(QTStr("Undo.Mixers.Change").arg(name),
QTStr("Undo.Mixers.Change").arg(name), std::bind(undo_redo, std::placeholders::_1, mixers),
std::bind(undo_redo, std::placeholders::_1, mixers), std::bind(undo_redo, std::placeholders::_1, new_mixers), uuid, uuid);
std::bind(undo_redo, std::placeholders::_1, new_mixers), uuid,
uuid);
} }
void OBSAdvAudioCtrl::SetVolumeWidget(VolumeType type) void OBSAdvAudioCtrl::SetVolumeWidget(VolumeType type)

View File

@ -53,8 +53,7 @@ private:
static void OBSSourceFlagsChanged(void *param, calldata_t *calldata); static void OBSSourceFlagsChanged(void *param, calldata_t *calldata);
static void OBSSourceVolumeChanged(void *param, calldata_t *calldata); static void OBSSourceVolumeChanged(void *param, calldata_t *calldata);
static void OBSSourceSyncChanged(void *param, calldata_t *calldata); static void OBSSourceSyncChanged(void *param, calldata_t *calldata);
static void OBSSourceMonitoringTypeChanged(void *param, static void OBSSourceMonitoringTypeChanged(void *param, calldata_t *calldata);
calldata_t *calldata);
static void OBSSourceMixersChanged(void *param, calldata_t *calldata); static void OBSSourceMixersChanged(void *param, calldata_t *calldata);
static void OBSSourceBalanceChanged(void *param, calldata_t *calldata); static void OBSSourceBalanceChanged(void *param, calldata_t *calldata);
static void OBSSourceRenamed(void *param, calldata_t *calldata); static void OBSSourceRenamed(void *param, calldata_t *calldata);

View File

@ -28,19 +28,15 @@ template<typename T> struct OBSStudioCallback {
T callback; T callback;
void *private_data; void *private_data;
inline OBSStudioCallback(T cb, void *p) : callback(cb), private_data(p) inline OBSStudioCallback(T cb, void *p) : callback(cb), private_data(p) {}
{
}
}; };
template<typename T> template<typename T>
inline size_t GetCallbackIdx(vector<OBSStudioCallback<T>> &callbacks, inline size_t GetCallbackIdx(vector<OBSStudioCallback<T>> &callbacks, T callback, void *private_data)
T callback, void *private_data)
{ {
for (size_t i = 0; i < callbacks.size(); i++) { for (size_t i = 0; i < callbacks.size(); i++) {
OBSStudioCallback<T> curCB = callbacks[i]; OBSStudioCallback<T> curCB = callbacks[i];
if (curCB.callback == callback && if (curCB.callback == callback && curCB.private_data == private_data)
curCB.private_data == private_data)
return i; return i;
} }
@ -55,23 +51,13 @@ struct OBSStudioAPI : obs_frontend_callbacks {
inline OBSStudioAPI(OBSBasic *main_) : main(main_) {} inline OBSStudioAPI(OBSBasic *main_) : main(main_) {}
void *obs_frontend_get_main_window(void) override void *obs_frontend_get_main_window(void) override { return (void *)main; }
{
return (void *)main;
}
void *obs_frontend_get_main_window_handle(void) override void *obs_frontend_get_main_window_handle(void) override { return (void *)main->winId(); }
{
return (void *)main->winId();
}
void *obs_frontend_get_system_tray(void) override void *obs_frontend_get_system_tray(void) override { return (void *)main->trayIcon.data(); }
{
return (void *)main->trayIcon.data();
}
void obs_frontend_get_scenes( void obs_frontend_get_scenes(struct obs_frontend_source_list *sources) override
struct obs_frontend_source_list *sources) override
{ {
for (int i = 0; i < main->ui->scenes->count(); i++) { for (int i = 0; i < main->ui->scenes->count(); i++) {
QListWidgetItem *item = main->ui->scenes->item(i); QListWidgetItem *item = main->ui->scenes->item(i);
@ -96,23 +82,18 @@ struct OBSStudioAPI : obs_frontend_callbacks {
void obs_frontend_set_current_scene(obs_source_t *scene) override void obs_frontend_set_current_scene(obs_source_t *scene) override
{ {
if (main->IsPreviewProgramMode()) { if (main->IsPreviewProgramMode()) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(main, "TransitionToScene", WaitConnection(),
main, "TransitionToScene", WaitConnection(), Q_ARG(OBSSource, OBSSource(scene)));
Q_ARG(OBSSource, OBSSource(scene)));
} else { } else {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(main, "SetCurrentScene", WaitConnection(),
main, "SetCurrentScene", WaitConnection(), Q_ARG(OBSSource, OBSSource(scene)), Q_ARG(bool, false));
Q_ARG(OBSSource, OBSSource(scene)),
Q_ARG(bool, false));
} }
} }
void obs_frontend_get_transitions( void obs_frontend_get_transitions(struct obs_frontend_source_list *sources) override
struct obs_frontend_source_list *sources) override
{ {
for (int i = 0; i < main->ui->transitions->count(); i++) { for (int i = 0; i < main->ui->transitions->count(); i++) {
OBSSource tr = main->ui->transitions->itemData(i) OBSSource tr = main->ui->transitions->itemData(i).value<OBSSource>();
.value<OBSSource>();
if (!tr) if (!tr)
continue; continue;
@ -128,62 +109,43 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return obs_source_get_ref(tr); return obs_source_get_ref(tr);
} }
void void obs_frontend_set_current_transition(obs_source_t *transition) override
obs_frontend_set_current_transition(obs_source_t *transition) override
{ {
QMetaObject::invokeMethod(main, "SetTransition", QMetaObject::invokeMethod(main, "SetTransition", Q_ARG(OBSSource, OBSSource(transition)));
Q_ARG(OBSSource,
OBSSource(transition)));
} }
int obs_frontend_get_transition_duration(void) override int obs_frontend_get_transition_duration(void) override { return main->ui->transitionDuration->value(); }
{
return main->ui->transitionDuration->value();
}
void obs_frontend_set_transition_duration(int duration) override void obs_frontend_set_transition_duration(int duration) override
{ {
QMetaObject::invokeMethod(main->ui->transitionDuration, QMetaObject::invokeMethod(main->ui->transitionDuration, "setValue", Q_ARG(int, duration));
"setValue", Q_ARG(int, duration));
} }
void obs_frontend_release_tbar(void) override void obs_frontend_release_tbar(void) override { QMetaObject::invokeMethod(main, "TBarReleased"); }
{
QMetaObject::invokeMethod(main, "TBarReleased");
}
void obs_frontend_set_tbar_position(int position) override void obs_frontend_set_tbar_position(int position) override
{ {
QMetaObject::invokeMethod(main, "TBarChanged", QMetaObject::invokeMethod(main, "TBarChanged", Q_ARG(int, position));
Q_ARG(int, position));
} }
int obs_frontend_get_tbar_position(void) override int obs_frontend_get_tbar_position(void) override { return main->tBar->value(); }
{
return main->tBar->value();
}
void obs_frontend_get_scene_collections( void obs_frontend_get_scene_collections(std::vector<std::string> &strings) override
std::vector<std::string> &strings) override
{ {
for (auto &[collectionName, collection] : for (auto &[collectionName, collection] : main->GetSceneCollectionCache()) {
main->GetSceneCollectionCache()) {
strings.emplace_back(collectionName); strings.emplace_back(collectionName);
} }
} }
char *obs_frontend_get_current_scene_collection(void) override char *obs_frontend_get_current_scene_collection(void) override
{ {
const OBSSceneCollection &currentCollection = const OBSSceneCollection &currentCollection = main->GetCurrentSceneCollection();
main->GetCurrentSceneCollection();
return bstrdup(currentCollection.name.c_str()); return bstrdup(currentCollection.name.c_str());
} }
void obs_frontend_set_current_scene_collection( void obs_frontend_set_current_scene_collection(const char *collection) override
const char *collection) override
{ {
QList<QAction *> menuActions = QList<QAction *> menuActions = main->ui->sceneCollectionMenu->actions();
main->ui->sceneCollectionMenu->actions();
QString qstrCollection = QT_UTF8(collection); QString qstrCollection = QT_UTF8(collection);
for (int i = 0; i < menuActions.count(); i++) { for (int i = 0; i < menuActions.count(); i++) {
@ -202,15 +164,12 @@ struct OBSStudioAPI : obs_frontend_callbacks {
bool obs_frontend_add_scene_collection(const char *name) override bool obs_frontend_add_scene_collection(const char *name) override
{ {
bool success = false; bool success = false;
QMetaObject::invokeMethod(main, "CreateNewSceneCollection", QMetaObject::invokeMethod(main, "CreateNewSceneCollection", WaitConnection(),
WaitConnection(), Q_RETURN_ARG(bool, success), Q_ARG(QString, QT_UTF8(name)));
Q_RETURN_ARG(bool, success),
Q_ARG(QString, QT_UTF8(name)));
return success; return success;
} }
void void obs_frontend_get_profiles(std::vector<std::string> &strings) override
obs_frontend_get_profiles(std::vector<std::string> &strings) override
{ {
const OBSProfileCache &profiles = main->GetProfileCache(); const OBSProfileCache &profiles = main->GetProfileCache();
@ -252,69 +211,42 @@ struct OBSStudioAPI : obs_frontend_callbacks {
void obs_frontend_create_profile(const char *name) override void obs_frontend_create_profile(const char *name) override
{ {
QMetaObject::invokeMethod(main, "CreateNewProfile", QMetaObject::invokeMethod(main, "CreateNewProfile", Q_ARG(QString, name));
Q_ARG(QString, name));
} }
void obs_frontend_duplicate_profile(const char *name) override void obs_frontend_duplicate_profile(const char *name) override
{ {
QMetaObject::invokeMethod(main, "CreateDuplicateProfile", QMetaObject::invokeMethod(main, "CreateDuplicateProfile", Q_ARG(QString, name));
Q_ARG(QString, name));
} }
void obs_frontend_delete_profile(const char *profile) override void obs_frontend_delete_profile(const char *profile) override
{ {
QMetaObject::invokeMethod(main, "DeleteProfile", QMetaObject::invokeMethod(main, "DeleteProfile", Q_ARG(QString, profile));
Q_ARG(QString, profile));
} }
void obs_frontend_streaming_start(void) override void obs_frontend_streaming_start(void) override { QMetaObject::invokeMethod(main, "StartStreaming"); }
{
QMetaObject::invokeMethod(main, "StartStreaming");
}
void obs_frontend_streaming_stop(void) override void obs_frontend_streaming_stop(void) override { QMetaObject::invokeMethod(main, "StopStreaming"); }
{
QMetaObject::invokeMethod(main, "StopStreaming");
}
bool obs_frontend_streaming_active(void) override bool obs_frontend_streaming_active(void) override { return os_atomic_load_bool(&streaming_active); }
{
return os_atomic_load_bool(&streaming_active);
}
void obs_frontend_recording_start(void) override void obs_frontend_recording_start(void) override { QMetaObject::invokeMethod(main, "StartRecording"); }
{
QMetaObject::invokeMethod(main, "StartRecording");
}
void obs_frontend_recording_stop(void) override void obs_frontend_recording_stop(void) override { QMetaObject::invokeMethod(main, "StopRecording"); }
{
QMetaObject::invokeMethod(main, "StopRecording");
}
bool obs_frontend_recording_active(void) override bool obs_frontend_recording_active(void) override { return os_atomic_load_bool(&recording_active); }
{
return os_atomic_load_bool(&recording_active);
}
void obs_frontend_recording_pause(bool pause) override void obs_frontend_recording_pause(bool pause) override
{ {
QMetaObject::invokeMethod(main, pause ? "PauseRecording" QMetaObject::invokeMethod(main, pause ? "PauseRecording" : "UnpauseRecording");
: "UnpauseRecording");
} }
bool obs_frontend_recording_paused(void) override bool obs_frontend_recording_paused(void) override { return os_atomic_load_bool(&recording_paused); }
{
return os_atomic_load_bool(&recording_paused);
}
bool obs_frontend_recording_split_file(void) override bool obs_frontend_recording_split_file(void) override
{ {
if (os_atomic_load_bool(&recording_active) && if (os_atomic_load_bool(&recording_active) && !os_atomic_load_bool(&recording_paused)) {
!os_atomic_load_bool(&recording_paused)) { proc_handler_t *ph = obs_output_get_proc_handler(main->outputHandler->fileOutput);
proc_handler_t *ph = obs_output_get_proc_handler(
main->outputHandler->fileOutput);
uint8_t stack[128]; uint8_t stack[128];
calldata cd; calldata cd;
calldata_init_fixed(&cd, stack, sizeof(stack)); calldata_init_fixed(&cd, stack, sizeof(stack));
@ -328,12 +260,10 @@ struct OBSStudioAPI : obs_frontend_callbacks {
bool obs_frontend_recording_add_chapter(const char *name) override bool obs_frontend_recording_add_chapter(const char *name) override
{ {
if (!os_atomic_load_bool(&recording_active) || if (!os_atomic_load_bool(&recording_active) || os_atomic_load_bool(&recording_paused))
os_atomic_load_bool(&recording_paused))
return false; return false;
proc_handler_t *ph = obs_output_get_proc_handler( proc_handler_t *ph = obs_output_get_proc_handler(main->outputHandler->fileOutput);
main->outputHandler->fileOutput);
calldata cd; calldata cd;
calldata_init(&cd); calldata_init(&cd);
@ -343,25 +273,13 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return result; return result;
} }
void obs_frontend_replay_buffer_start(void) override void obs_frontend_replay_buffer_start(void) override { QMetaObject::invokeMethod(main, "StartReplayBuffer"); }
{
QMetaObject::invokeMethod(main, "StartReplayBuffer");
}
void obs_frontend_replay_buffer_save(void) override void obs_frontend_replay_buffer_save(void) override { QMetaObject::invokeMethod(main, "ReplayBufferSave"); }
{
QMetaObject::invokeMethod(main, "ReplayBufferSave");
}
void obs_frontend_replay_buffer_stop(void) override void obs_frontend_replay_buffer_stop(void) override { QMetaObject::invokeMethod(main, "StopReplayBuffer"); }
{
QMetaObject::invokeMethod(main, "StopReplayBuffer");
}
bool obs_frontend_replay_buffer_active(void) override bool obs_frontend_replay_buffer_active(void) override { return os_atomic_load_bool(&replaybuf_active); }
{
return os_atomic_load_bool(&replaybuf_active);
}
void *obs_frontend_add_tools_menu_qaction(const char *name) override void *obs_frontend_add_tools_menu_qaction(const char *name) override
{ {
@ -369,9 +287,7 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return (void *)main->ui->menuTools->addAction(QT_UTF8(name)); return (void *)main->ui->menuTools->addAction(QT_UTF8(name));
} }
void obs_frontend_add_tools_menu_item(const char *name, void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data) override
obs_frontend_cb callback,
void *private_data) override
{ {
main->ui->menuTools->setEnabled(true); main->ui->menuTools->setEnabled(true);
@ -389,9 +305,8 @@ struct OBSStudioAPI : obs_frontend_callbacks {
QString name = d->objectName(); QString name = d->objectName();
if (name.isEmpty() || main->IsDockObjectNameUsed(name)) { if (name.isEmpty() || main->IsDockObjectNameUsed(name)) {
blog(LOG_WARNING, blog(LOG_WARNING, "The object name of the added dock is empty or already used,"
"The object name of the added dock is empty or already used," " a temporary one will be set to avoid conflicts");
" a temporary one will be set to avoid conflicts");
char *uuid = os_generate_uuid(); char *uuid = os_generate_uuid();
name = QT_UTF8(uuid); name = QT_UTF8(uuid);
@ -404,8 +319,7 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return (void *)main->AddDockWidget(d); return (void *)main->AddDockWidget(d);
} }
bool obs_frontend_add_dock_by_id(const char *id, const char *title, bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget) override
void *widget) override
{ {
if (main->IsDockObjectNameUsed(QT_UTF8(id))) { if (main->IsDockObjectNameUsed(QT_UTF8(id))) {
blog(LOG_WARNING, blog(LOG_WARNING,
@ -428,10 +342,7 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return true; return true;
} }
void obs_frontend_remove_dock(const char *id) override void obs_frontend_remove_dock(const char *id) override { main->RemoveDockWidget(QT_UTF8(id)); }
{
main->RemoveDockWidget(QT_UTF8(id));
}
bool obs_frontend_add_custom_qdock(const char *id, void *dock) override bool obs_frontend_add_custom_qdock(const char *id, void *dock) override
{ {
@ -451,16 +362,14 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return true; return true;
} }
void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) override
void *private_data) override
{ {
size_t idx = GetCallbackIdx(callbacks, callback, private_data); size_t idx = GetCallbackIdx(callbacks, callback, private_data);
if (idx == (size_t)-1) if (idx == (size_t)-1)
callbacks.emplace_back(callback, private_data); callbacks.emplace_back(callback, private_data);
} }
void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data) override
void *private_data) override
{ {
size_t idx = GetCallbackIdx(callbacks, callback, private_data); size_t idx = GetCallbackIdx(callbacks, callback, private_data);
if (idx == (size_t)-1) if (idx == (size_t)-1)
@ -471,13 +380,8 @@ struct OBSStudioAPI : obs_frontend_callbacks {
obs_output_t *obs_frontend_get_streaming_output(void) override obs_output_t *obs_frontend_get_streaming_output(void) override
{ {
auto multitrackVideo = auto multitrackVideo = main->outputHandler->multitrackVideo.get();
main->outputHandler->multitrackVideo.get(); auto mtvOutput = multitrackVideo ? obs_output_get_ref(multitrackVideo->StreamingOutput()) : nullptr;
auto mtvOutput =
multitrackVideo
? obs_output_get_ref(
multitrackVideo->StreamingOutput())
: nullptr;
if (mtvOutput) if (mtvOutput)
return mtvOutput; return mtvOutput;
@ -497,10 +401,7 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return obs_output_get_ref(out); return obs_output_get_ref(out);
} }
config_t *obs_frontend_get_profile_config(void) override config_t *obs_frontend_get_profile_config(void) override { return main->activeConfiguration; }
{
return main->activeConfiguration;
}
config_t *obs_frontend_get_global_config(void) override config_t *obs_frontend_get_global_config(void) override
{ {
@ -509,19 +410,11 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return App()->GetAppConfig(); return App()->GetAppConfig();
} }
config_t *obs_frontend_get_app_config(void) override config_t *obs_frontend_get_app_config(void) override { return App()->GetAppConfig(); }
{
return App()->GetAppConfig();
}
config_t *obs_frontend_get_user_config(void) override config_t *obs_frontend_get_user_config(void) override { return App()->GetUserConfig(); }
{
return App()->GetUserConfig();
}
void obs_frontend_open_projector(const char *type, int monitor, void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name) override
const char *geometry,
const char *name) override
{ {
SavedProjectorInfo proj = { SavedProjectorInfo proj = {
ProjectorType::Preview, ProjectorType::Preview,
@ -539,108 +432,71 @@ struct OBSStudioAPI : obs_frontend_callbacks {
else if (astrcmpi(type, "Multiview") == 0) else if (astrcmpi(type, "Multiview") == 0)
proj.type = ProjectorType::Multiview; proj.type = ProjectorType::Multiview;
} }
QMetaObject::invokeMethod(main, "OpenSavedProjector", QMetaObject::invokeMethod(main, "OpenSavedProjector", WaitConnection(),
WaitConnection(),
Q_ARG(SavedProjectorInfo *, &proj)); Q_ARG(SavedProjectorInfo *, &proj));
} }
void obs_frontend_save(void) override { main->SaveProject(); } void obs_frontend_save(void) override { main->SaveProject(); }
void obs_frontend_defer_save_begin(void) override void obs_frontend_defer_save_begin(void) override { QMetaObject::invokeMethod(main, "DeferSaveBegin"); }
{
QMetaObject::invokeMethod(main, "DeferSaveBegin");
}
void obs_frontend_defer_save_end(void) override void obs_frontend_defer_save_end(void) override { QMetaObject::invokeMethod(main, "DeferSaveEnd"); }
{
QMetaObject::invokeMethod(main, "DeferSaveEnd");
}
void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data) override
void *private_data) override
{ {
size_t idx = size_t idx = GetCallbackIdx(saveCallbacks, callback, private_data);
GetCallbackIdx(saveCallbacks, callback, private_data);
if (idx == (size_t)-1) if (idx == (size_t)-1)
saveCallbacks.emplace_back(callback, private_data); saveCallbacks.emplace_back(callback, private_data);
} }
void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data) override
void *private_data) override
{ {
size_t idx = size_t idx = GetCallbackIdx(saveCallbacks, callback, private_data);
GetCallbackIdx(saveCallbacks, callback, private_data);
if (idx == (size_t)-1) if (idx == (size_t)-1)
return; return;
saveCallbacks.erase(saveCallbacks.begin() + idx); saveCallbacks.erase(saveCallbacks.begin() + idx);
} }
void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data) override
void *private_data) override
{ {
size_t idx = GetCallbackIdx(preloadCallbacks, callback, size_t idx = GetCallbackIdx(preloadCallbacks, callback, private_data);
private_data);
if (idx == (size_t)-1) if (idx == (size_t)-1)
preloadCallbacks.emplace_back(callback, private_data); preloadCallbacks.emplace_back(callback, private_data);
} }
void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data) override
void *private_data) override
{ {
size_t idx = GetCallbackIdx(preloadCallbacks, callback, size_t idx = GetCallbackIdx(preloadCallbacks, callback, private_data);
private_data);
if (idx == (size_t)-1) if (idx == (size_t)-1)
return; return;
preloadCallbacks.erase(preloadCallbacks.begin() + idx); preloadCallbacks.erase(preloadCallbacks.begin() + idx);
} }
void obs_frontend_push_ui_translation( void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) override
obs_frontend_translate_ui_cb translate) override
{ {
App()->PushUITranslation(translate); App()->PushUITranslation(translate);
} }
void obs_frontend_pop_ui_translation(void) override void obs_frontend_pop_ui_translation(void) override { App()->PopUITranslation(); }
{
App()->PopUITranslation();
}
void obs_frontend_set_streaming_service(obs_service_t *service) override void obs_frontend_set_streaming_service(obs_service_t *service) override { main->SetService(service); }
{
main->SetService(service);
}
obs_service_t *obs_frontend_get_streaming_service(void) override obs_service_t *obs_frontend_get_streaming_service(void) override { return main->GetService(); }
{
return main->GetService();
}
void obs_frontend_save_streaming_service(void) override void obs_frontend_save_streaming_service(void) override { main->SaveService(); }
{
main->SaveService();
}
bool obs_frontend_preview_program_mode_active(void) override bool obs_frontend_preview_program_mode_active(void) override { return main->IsPreviewProgramMode(); }
{
return main->IsPreviewProgramMode();
}
void obs_frontend_set_preview_program_mode(bool enable) override void obs_frontend_set_preview_program_mode(bool enable) override { main->SetPreviewProgramMode(enable); }
{
main->SetPreviewProgramMode(enable);
}
void obs_frontend_preview_program_trigger_transition(void) override void obs_frontend_preview_program_trigger_transition(void) override
{ {
QMetaObject::invokeMethod(main, "TransitionClicked"); QMetaObject::invokeMethod(main, "TransitionClicked");
} }
bool obs_frontend_preview_enabled(void) override bool obs_frontend_preview_enabled(void) override { return main->previewEnabled; }
{
return main->previewEnabled;
}
void obs_frontend_set_preview_enabled(bool enable) override void obs_frontend_set_preview_enabled(bool enable) override
{ {
@ -658,26 +514,19 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return nullptr; return nullptr;
} }
void void obs_frontend_set_current_preview_scene(obs_source_t *scene) override
obs_frontend_set_current_preview_scene(obs_source_t *scene) override
{ {
if (main->IsPreviewProgramMode()) { if (main->IsPreviewProgramMode()) {
QMetaObject::invokeMethod(main, "SetCurrentScene", QMetaObject::invokeMethod(main, "SetCurrentScene", Q_ARG(OBSSource, OBSSource(scene)),
Q_ARG(OBSSource,
OBSSource(scene)),
Q_ARG(bool, false)); Q_ARG(bool, false));
} }
} }
void obs_frontend_take_screenshot(void) override void obs_frontend_take_screenshot(void) override { QMetaObject::invokeMethod(main, "Screenshot"); }
{
QMetaObject::invokeMethod(main, "Screenshot");
}
void obs_frontend_take_source_screenshot(obs_source_t *source) override void obs_frontend_take_source_screenshot(obs_source_t *source) override
{ {
QMetaObject::invokeMethod(main, "Screenshot", QMetaObject::invokeMethod(main, "Screenshot", Q_ARG(OBSSource, OBSSource(source)));
Q_ARG(OBSSource, OBSSource(source)));
} }
obs_output_t *obs_frontend_get_virtualcam_output(void) override obs_output_t *obs_frontend_get_virtualcam_output(void) override
@ -686,47 +535,32 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return obs_output_get_ref(output); return obs_output_get_ref(output);
} }
void obs_frontend_start_virtualcam(void) override void obs_frontend_start_virtualcam(void) override { QMetaObject::invokeMethod(main, "StartVirtualCam"); }
{
QMetaObject::invokeMethod(main, "StartVirtualCam");
}
void obs_frontend_stop_virtualcam(void) override void obs_frontend_stop_virtualcam(void) override { QMetaObject::invokeMethod(main, "StopVirtualCam"); }
{
QMetaObject::invokeMethod(main, "StopVirtualCam");
}
bool obs_frontend_virtualcam_active(void) override bool obs_frontend_virtualcam_active(void) override { return os_atomic_load_bool(&virtualcam_active); }
{
return os_atomic_load_bool(&virtualcam_active);
}
void obs_frontend_reset_video(void) override { main->ResetVideo(); } void obs_frontend_reset_video(void) override { main->ResetVideo(); }
void obs_frontend_open_source_properties(obs_source_t *source) override void obs_frontend_open_source_properties(obs_source_t *source) override
{ {
QMetaObject::invokeMethod(main, "OpenProperties", QMetaObject::invokeMethod(main, "OpenProperties", Q_ARG(OBSSource, OBSSource(source)));
Q_ARG(OBSSource, OBSSource(source)));
} }
void obs_frontend_open_source_filters(obs_source_t *source) override void obs_frontend_open_source_filters(obs_source_t *source) override
{ {
QMetaObject::invokeMethod(main, "OpenFilters", QMetaObject::invokeMethod(main, "OpenFilters", Q_ARG(OBSSource, OBSSource(source)));
Q_ARG(OBSSource, OBSSource(source)));
} }
void obs_frontend_open_source_interaction(obs_source_t *source) override void obs_frontend_open_source_interaction(obs_source_t *source) override
{ {
QMetaObject::invokeMethod(main, "OpenInteraction", QMetaObject::invokeMethod(main, "OpenInteraction", Q_ARG(OBSSource, OBSSource(source)));
Q_ARG(OBSSource, OBSSource(source)));
} }
void obs_frontend_open_sceneitem_edit_transform( void obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item) override
obs_sceneitem_t *item) override
{ {
QMetaObject::invokeMethod(main, "OpenEditTransform", QMetaObject::invokeMethod(main, "OpenEditTransform", Q_ARG(OBSSceneItem, OBSSceneItem(item)));
Q_ARG(OBSSceneItem,
OBSSceneItem(item)));
} }
char *obs_frontend_get_current_record_output_path(void) override char *obs_frontend_get_current_record_output_path(void) override
@ -736,43 +570,25 @@ struct OBSStudioAPI : obs_frontend_callbacks {
return bstrdup(recordOutputPath); return bstrdup(recordOutputPath);
} }
const char *obs_frontend_get_locale_string(const char *string) override const char *obs_frontend_get_locale_string(const char *string) override { return Str(string); }
{
return Str(string);
}
bool obs_frontend_is_theme_dark(void) override bool obs_frontend_is_theme_dark(void) override { return App()->IsThemeDark(); }
{
return App()->IsThemeDark();
}
char *obs_frontend_get_last_recording(void) override char *obs_frontend_get_last_recording(void) override
{ {
return bstrdup(main->outputHandler->lastRecordingPath.c_str()); return bstrdup(main->outputHandler->lastRecordingPath.c_str());
} }
char *obs_frontend_get_last_screenshot(void) override char *obs_frontend_get_last_screenshot(void) override { return bstrdup(main->lastScreenshot.c_str()); }
{
return bstrdup(main->lastScreenshot.c_str());
}
char *obs_frontend_get_last_replay(void) override char *obs_frontend_get_last_replay(void) override { return bstrdup(main->lastReplay.c_str()); }
{
return bstrdup(main->lastReplay.c_str());
}
void obs_frontend_add_undo_redo_action(const char *name, void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,
const undo_redo_cb undo, const char *undo_data, const char *redo_data, bool repeatable) override
const undo_redo_cb redo,
const char *undo_data,
const char *redo_data,
bool repeatable) override
{ {
main->undo_s.add_action( main->undo_s.add_action(
name, name, [undo](const std::string &data) { undo(data.c_str()); },
[undo](const std::string &data) { undo(data.c_str()); }, [redo](const std::string &data) { redo(data.c_str()); }, undo_data, redo_data, repeatable);
[redo](const std::string &data) { redo(data.c_str()); },
undo_data, redo_data, repeatable);
} }
void on_load(obs_data_t *settings) override void on_load(obs_data_t *settings) override
@ -801,8 +617,7 @@ struct OBSStudioAPI : obs_frontend_callbacks {
void on_event(enum obs_frontend_event event) override void on_event(enum obs_frontend_event event) override
{ {
if (main->disableSaving && if (main->disableSaving && event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP &&
event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP &&
event != OBS_FRONTEND_EVENT_EXIT) event != OBS_FRONTEND_EVENT_EXIT)
return; return;

View File

@ -32,8 +32,7 @@ static void HandleIntProperty(obs_property_t *prop, std::vector<int> &bitrates)
bitrates.push_back(i); bitrates.push_back(i);
} }
static void HandleListProperty(obs_property_t *prop, const char *id, static void HandleListProperty(obs_property_t *prop, const char *id, std::vector<int> &bitrates)
std::vector<int> &bitrates)
{ {
obs_combo_format format = obs_property_list_format(prop); obs_combo_format format = obs_property_list_format(prop);
if (format != OBS_COMBO_FORMAT_INT) { if (format != OBS_COMBO_FORMAT_INT) {
@ -50,8 +49,7 @@ static void HandleListProperty(obs_property_t *prop, const char *id,
if (obs_property_list_item_disabled(prop, i)) if (obs_property_list_item_disabled(prop, i))
continue; continue;
int bitrate = int bitrate = static_cast<int>(obs_property_list_item_int(prop, i));
static_cast<int>(obs_property_list_item_int(prop, i));
bitrates.push_back(bitrate); bitrates.push_back(bitrate);
} }
} }
@ -61,8 +59,7 @@ static void HandleSampleRate(obs_property_t *prop, const char *id)
auto ReleaseData = [](obs_data_t *data) { auto ReleaseData = [](obs_data_t *data) {
obs_data_release(data); obs_data_release(data);
}; };
std::unique_ptr<obs_data_t, decltype(ReleaseData)> data{ std::unique_ptr<obs_data_t, decltype(ReleaseData)> data{obs_encoder_defaults(id), ReleaseData};
obs_encoder_defaults(id), ReleaseData};
if (!data) { if (!data) {
blog(LOG_ERROR, blog(LOG_ERROR,
@ -79,8 +76,7 @@ static void HandleSampleRate(obs_property_t *prop, const char *id)
return; return;
} }
uint32_t sampleRate = uint32_t sampleRate = config_get_uint(main->Config(), "Audio", "SampleRate");
config_get_uint(main->Config(), "Audio", "SampleRate");
obs_data_set_int(data.get(), "samplerate", sampleRate); obs_data_set_int(data.get(), "samplerate", sampleRate);
@ -92,8 +88,8 @@ static void HandleEncoderProperties(const char *id, std::vector<int> &bitrates)
auto DestroyProperties = [](obs_properties_t *props) { auto DestroyProperties = [](obs_properties_t *props) {
obs_properties_destroy(props); obs_properties_destroy(props);
}; };
std::unique_ptr<obs_properties_t, decltype(DestroyProperties)> props{ std::unique_ptr<obs_properties_t, decltype(DestroyProperties)> props{obs_get_encoder_properties(id),
obs_get_encoder_properties(id), DestroyProperties}; DestroyProperties};
if (!props) { if (!props) {
blog(LOG_ERROR, blog(LOG_ERROR,
@ -103,8 +99,7 @@ static void HandleEncoderProperties(const char *id, std::vector<int> &bitrates)
return; return;
} }
obs_property_t *samplerate = obs_property_t *samplerate = obs_properties_get(props.get(), "samplerate");
obs_properties_get(props.get(), "samplerate");
if (samplerate) if (samplerate)
HandleSampleRate(samplerate, id); HandleSampleRate(samplerate, id);
@ -156,16 +151,14 @@ static void PopulateBitrateLists()
for (auto &bitrate : fallbackBitrates) for (auto &bitrate : fallbackBitrates)
ss << "\n " << setw(3) << bitrate << " kbit/s:"; ss << "\n " << setw(3) << bitrate << " kbit/s:";
blog(LOG_DEBUG, "Fallback encoder bitrates:%s", blog(LOG_DEBUG, "Fallback encoder bitrates:%s", ss.str().c_str());
ss.str().c_str());
const char *id = nullptr; const char *id = nullptr;
for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) { for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
if (obs_get_encoder_type(id) != OBS_ENCODER_AUDIO) if (obs_get_encoder_type(id) != OBS_ENCODER_AUDIO)
continue; continue;
if (strcmp(id, "ffmpeg_aac") == 0 || if (strcmp(id, "ffmpeg_aac") == 0 || strcmp(id, "ffmpeg_opus") == 0)
strcmp(id, "ffmpeg_opus") == 0)
continue; continue;
std::string encoder = id; std::string encoder = id;
@ -180,11 +173,9 @@ static void PopulateBitrateLists()
ostringstream ss; ostringstream ss;
for (auto &bitrate : encoderBitrates[encoder]) for (auto &bitrate : encoderBitrates[encoder])
ss << "\n " << setw(3) << bitrate ss << "\n " << setw(3) << bitrate << " kbit/s";
<< " kbit/s";
blog(LOG_DEBUG, "%s (%s) encoder bitrates:%s", blog(LOG_DEBUG, "%s (%s) encoder bitrates:%s", EncoderName(id), id, ss.str().c_str());
EncoderName(id), id, ss.str().c_str());
} }
if (encoderBitrates.empty() && fallbackBitrates.empty()) if (encoderBitrates.empty() && fallbackBitrates.empty())
@ -222,8 +213,7 @@ static void PopulateSimpleAACBitrateMap()
return val == NullToEmpty(id); return val == NullToEmpty(id);
}; };
if (find_if(begin(encoders), end(encoders), Compare) != if (find_if(begin(encoders), end(encoders), Compare) != end(encoders))
end(encoders))
continue; continue;
if (strcmp(GetCodec(id), "aac") != 0) if (strcmp(GetCodec(id), "aac") != 0)
@ -256,12 +246,10 @@ static void PopulateSimpleAACBitrateMap()
ostringstream ss; ostringstream ss;
for (auto &entry : simpleAACBitrateMap) for (auto &entry : simpleAACBitrateMap)
ss << "\n " << setw(3) << entry.first ss << "\n " << setw(3) << entry.first << " kbit/s: '" << EncoderName(entry.second)
<< " kbit/s: '" << EncoderName(entry.second) << "' (" << "' (" << entry.second << ')';
<< entry.second << ')';
blog(LOG_DEBUG, "AAC simple encoder bitrate mapping:%s", blog(LOG_DEBUG, "AAC simple encoder bitrate mapping:%s", ss.str().c_str());
ss.str().c_str());
}); });
} }
@ -301,12 +289,10 @@ static void PopulateSimpleOpusBitrateMap()
ostringstream ss; ostringstream ss;
for (auto &entry : simpleOpusBitrateMap) for (auto &entry : simpleOpusBitrateMap)
ss << "\n " << setw(3) << entry.first ss << "\n " << setw(3) << entry.first << " kbit/s: '" << EncoderName(entry.second)
<< " kbit/s: '" << EncoderName(entry.second) << "' (" << "' (" << entry.second << ')';
<< entry.second << ')';
blog(LOG_DEBUG, "Opus simple encoder bitrate mapping:%s", blog(LOG_DEBUG, "Opus simple encoder bitrate mapping:%s", ss.str().c_str());
ss.str().c_str());
}); });
} }
@ -342,8 +328,7 @@ const char *GetSimpleOpusEncoderForBitrate(int bitrate)
#define INVALID_BITRATE 10000 #define INVALID_BITRATE 10000
static int FindClosestAvailableSimpleBitrate(int bitrate, static int FindClosestAvailableSimpleBitrate(int bitrate, const map<int, std::string> &map)
const map<int, std::string> &map)
{ {
int prev = 0; int prev = 0;
int next = INVALID_BITRATE; int next = INVALID_BITRATE;
@ -369,14 +354,12 @@ static int FindClosestAvailableSimpleBitrate(int bitrate,
int FindClosestAvailableSimpleAACBitrate(int bitrate) int FindClosestAvailableSimpleAACBitrate(int bitrate)
{ {
return FindClosestAvailableSimpleBitrate( return FindClosestAvailableSimpleBitrate(bitrate, GetSimpleAACEncoderBitrateMap());
bitrate, GetSimpleAACEncoderBitrateMap());
} }
int FindClosestAvailableSimpleOpusBitrate(int bitrate) int FindClosestAvailableSimpleOpusBitrate(int bitrate)
{ {
return FindClosestAvailableSimpleBitrate( return FindClosestAvailableSimpleBitrate(bitrate, GetSimpleOpusEncoderBitrateMap());
bitrate, GetSimpleOpusEncoderBitrateMap());
} }
const std::vector<int> &GetAudioEncoderBitrates(const char *id) const std::vector<int> &GetAudioEncoderBitrates(const char *id)
@ -396,9 +379,7 @@ int FindClosestAvailableAudioBitrate(const char *id, int bitrate)
int next = INVALID_BITRATE; int next = INVALID_BITRATE;
std::string encoder = id; std::string encoder = id;
for (auto val : encoderBitrates[encoder].empty() for (auto val : encoderBitrates[encoder].empty() ? fallbackBitrates : encoderBitrates[encoder]) {
? fallbackBitrates
: encoderBitrates[encoder]) {
if (next > val) { if (next > val) {
if (val == bitrate) if (val == bitrate)
return bitrate; return bitrate;

View File

@ -61,8 +61,7 @@ void Auth::Load()
if (main->auth) { if (main->auth) {
if (main->auth->LoadInternal()) { if (main->auth->LoadInternal()) {
main->auth->LoadUI(); main->auth->LoadUI();
main->SetBroadcastFlowEnabled( main->SetBroadcastFlowEnabled(main->auth->broadcastFlow());
main->auth->broadcastFlow());
} }
} else { } else {
main->SetBroadcastFlowEnabled(false); main->SetBroadcastFlowEnabled(false);

View File

@ -17,11 +17,7 @@ protected:
std::string message; std::string message;
std::string error; std::string error;
ErrorInfo(std::string message_, std::string error_) ErrorInfo(std::string message_, std::string error_) : message(message_), error(error_) {}
: message(message_),
error(error_)
{
}
}; };
public: public:

View File

@ -10,33 +10,29 @@
#define LOGO_URL "https://obsproject.com/assets/images/new_icon_small-r.png" #define LOGO_URL "https://obsproject.com/assets/images/new_icon_small-r.png"
static const QString serverResponseHeader = static const QString serverResponseHeader = QStringLiteral("HTTP/1.0 200 OK\n"
QStringLiteral("HTTP/1.0 200 OK\n" "Connection: close\n"
"Connection: close\n" "Content-Type: text/html; charset=UTF-8\n"
"Content-Type: text/html; charset=UTF-8\n" "Server: OBS Studio\n"
"Server: OBS Studio\n" "\n"
"\n" "<html><head><title>OBS Studio"
"<html><head><title>OBS Studio" "</title></head>");
"</title></head>");
static const QString responseTemplate = static const QString responseTemplate = "<center>"
"<center>" "<img src=\"" LOGO_URL
"<img src=\"" LOGO_URL "\" alt=\"OBS\" class=\"center\" height=\"60\" width=\"60\">"
"\" alt=\"OBS\" class=\"center\" height=\"60\" width=\"60\">" "</center>"
"</center>" "<center><p style=\"font-family:verdana; font-size:13pt\">%1</p></center>";
"<center><p style=\"font-family:verdana; font-size:13pt\">%1</p></center>";
AuthListener::AuthListener(QObject *parent) : QObject(parent) AuthListener::AuthListener(QObject *parent) : QObject(parent)
{ {
server = new QTcpServer(this); server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, connect(server, &QTcpServer::newConnection, this, &AuthListener::NewConnection);
&AuthListener::NewConnection);
if (!server->listen(QHostAddress::LocalHost, 0)) { if (!server->listen(QHostAddress::LocalHost, 0)) {
blog(LOG_DEBUG, "Server could not start"); blog(LOG_DEBUG, "Server could not start");
emit fail(); emit fail();
} else { } else {
blog(LOG_DEBUG, "Server started at port %d", blog(LOG_DEBUG, "Server started at port %d", server->serverPort());
server->serverPort());
} }
} }
@ -54,8 +50,7 @@ void AuthListener::NewConnection()
{ {
QTcpSocket *socket = server->nextPendingConnection(); QTcpSocket *socket = server->nextPendingConnection();
if (socket) { if (socket) {
connect(socket, &QTcpSocket::disconnected, socket, connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
&QTcpSocket::deleteLater);
connect(socket, &QTcpSocket::readyRead, socket, [&, socket]() { connect(socket, &QTcpSocket::readyRead, socket, [&, socket]() {
QByteArray buffer; QByteArray buffer;
while (socket->bytesAvailable() > 0) { while (socket->bytesAvailable() > 0) {
@ -65,13 +60,10 @@ void AuthListener::NewConnection()
QString redirect = QString::fromLatin1(buffer); QString redirect = QString::fromLatin1(buffer);
blog(LOG_DEBUG, "redirect: %s", QT_TO_UTF8(redirect)); blog(LOG_DEBUG, "redirect: %s", QT_TO_UTF8(redirect));
QRegularExpression re_state( QRegularExpression re_state("(&|\\?)state=(?<state>[^&]+)");
"(&|\\?)state=(?<state>[^&]+)"); QRegularExpression re_code("(&|\\?)code=(?<code>[^&]+)");
QRegularExpression re_code(
"(&|\\?)code=(?<code>[^&]+)");
QRegularExpressionMatch match = QRegularExpressionMatch match = re_state.match(redirect);
re_state.match(redirect);
QString code; QString code;
@ -95,13 +87,11 @@ void AuthListener::NewConnection()
} }
if (code.isEmpty()) { if (code.isEmpty()) {
auto data = responseTemplate.arg( auto data = responseTemplate.arg(QTStr("YouTube.Auth.NoCode"));
QTStr("YouTube.Auth.NoCode"));
socket->write(QT_TO_UTF8(data)); socket->write(QT_TO_UTF8(data));
emit fail(); emit fail();
} else { } else {
auto data = responseTemplate.arg( auto data = responseTemplate.arg(QTStr("YouTube.Auth.Ok"));
QTStr("YouTube.Auth.Ok"));
socket->write(QT_TO_UTF8(data)); socket->write(QT_TO_UTF8(data));
emit ok(code); emit ok(code);
} }

View File

@ -26,9 +26,7 @@ extern QCefCookieManager *panel_cookies;
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
OAuthLogin::OAuthLogin(QWidget *parent, const std::string &url, bool token) OAuthLogin::OAuthLogin(QWidget *parent, const std::string &url, bool token) : QDialog(parent), get_token(token)
: QDialog(parent),
get_token(token)
{ {
#ifdef BROWSER_AVAILABLE #ifdef BROWSER_AVAILABLE
if (!cef) { if (!cef) {
@ -51,10 +49,8 @@ OAuthLogin::OAuthLogin(QWidget *parent, const std::string &url, bool token)
return; return;
} }
connect(cefWidget, &QCefWidget::titleChanged, this, connect(cefWidget, &QCefWidget::titleChanged, this, &OAuthLogin::setWindowTitle);
&OAuthLogin::setWindowTitle); connect(cefWidget, &QCefWidget::urlChanged, this, &OAuthLogin::urlChanged);
connect(cefWidget, &QCefWidget::urlChanged, this,
&OAuthLogin::urlChanged);
QPushButton *close = new QPushButton(QTStr("Cancel")); QPushButton *close = new QPushButton(QTStr("Cancel"));
connect(close, &QAbstractButton::clicked, this, &QDialog::reject); connect(close, &QAbstractButton::clicked, this, &QDialog::reject);
@ -131,8 +127,7 @@ struct OAuthInfo {
static std::vector<OAuthInfo> loginCBs; static std::vector<OAuthInfo> loginCBs;
void OAuth::RegisterOAuth(const Def &d, create_cb create, login_cb login, void OAuth::RegisterOAuth(const Def &d, create_cb create, login_cb login, delete_cookies_cb delete_cookies)
delete_cookies_cb delete_cookies)
{ {
OAuthInfo info = {d, login, delete_cookies}; OAuthInfo info = {d, login, delete_cookies};
loginCBs.push_back(info); loginCBs.push_back(info);
@ -162,15 +157,13 @@ void OAuth::DeleteCookies(const std::string &service)
void OAuth::SaveInternal() void OAuth::SaveInternal()
{ {
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
config_set_string(main->Config(), service(), "RefreshToken", config_set_string(main->Config(), service(), "RefreshToken", refresh_token.c_str());
refresh_token.c_str());
config_set_string(main->Config(), service(), "Token", token.c_str()); config_set_string(main->Config(), service(), "Token", token.c_str());
config_set_uint(main->Config(), service(), "ExpireTime", expire_time); config_set_uint(main->Config(), service(), "ExpireTime", expire_time);
config_set_int(main->Config(), service(), "ScopeVer", currentScopeVer); config_set_int(main->Config(), service(), "ScopeVer", currentScopeVer);
} }
static inline std::string get_config_str(OBSBasic *main, const char *section, static inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)
const char *name)
{ {
const char *val = config_get_string(main->Config(), section, name); const char *val = config_get_string(main->Config(), section, name);
return val ? val : ""; return val ? val : "";
@ -182,8 +175,7 @@ bool OAuth::LoadInternal()
refresh_token = get_config_str(main, service(), "RefreshToken"); refresh_token = get_config_str(main, service(), "RefreshToken");
token = get_config_str(main, service(), "Token"); token = get_config_str(main, service(), "Token");
expire_time = config_get_uint(main->Config(), service(), "ExpireTime"); expire_time = config_get_uint(main->Config(), service(), "ExpireTime");
currentScopeVer = currentScopeVer = (int)config_get_int(main->Config(), service(), "ScopeVer");
(int)config_get_int(main->Config(), service(), "ScopeVer");
return implicit ? !token.empty() : !refresh_token.empty(); return implicit ? !token.empty() : !refresh_token.empty();
} }
@ -196,25 +188,20 @@ bool OAuth::TokenExpired()
return false; return false;
} }
bool OAuth::GetToken(const char *url, const std::string &client_id, bool OAuth::GetToken(const char *url, const std::string &client_id, const std::string &secret,
const std::string &secret, const std::string &redirect_uri, const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry)
int scope_ver, const std::string &auth_code, bool retry)
{ {
return GetTokenInternal(url, client_id, secret, redirect_uri, scope_ver, return GetTokenInternal(url, client_id, secret, redirect_uri, scope_ver, auth_code, retry);
auth_code, retry);
} }
bool OAuth::GetToken(const char *url, const std::string &client_id, bool OAuth::GetToken(const char *url, const std::string &client_id, int scope_ver, const std::string &auth_code,
int scope_ver, const std::string &auth_code, bool retry) bool retry)
{ {
return GetTokenInternal(url, client_id, {}, {}, scope_ver, auth_code, return GetTokenInternal(url, client_id, {}, {}, scope_ver, auth_code, retry);
retry);
} }
bool OAuth::GetTokenInternal(const char *url, const std::string &client_id, bool OAuth::GetTokenInternal(const char *url, const std::string &client_id, const std::string &secret,
const std::string &secret, const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry)
const std::string &redirect_uri, int scope_ver,
const std::string &auth_code, bool retry)
try { try {
std::string output; std::string output;
std::string error; std::string error;
@ -225,8 +212,7 @@ try {
return true; return true;
} else { } else {
QString title = QTStr("Auth.InvalidScope.Title"); QString title = QTStr("Auth.InvalidScope.Title");
QString text = QString text = QTStr("Auth.InvalidScope.Text").arg(service());
QTStr("Auth.InvalidScope.Text").arg(service());
QMessageBox::warning(OBSBasic::Get(), title, text); QMessageBox::warning(OBSBasic::Get(), title, text);
} }
@ -259,14 +245,11 @@ try {
bool success = false; bool success = false;
auto func = [&]() { auto func = [&]() {
success = GetRemoteFile(url, output, error, nullptr, success = GetRemoteFile(url, output, error, nullptr, "application/x-www-form-urlencoded", "",
"application/x-www-form-urlencoded", "", post_data.c_str(), std::vector<std::string>(), nullptr, 5);
post_data.c_str(),
std::vector<std::string>(), nullptr, 5);
}; };
ExecThreadedWithoutBlocking(func, QTStr("Auth.Authing.Title"), ExecThreadedWithoutBlocking(func, QTStr("Auth.Authing.Title"), QTStr("Auth.Authing.Text").arg(service()));
QTStr("Auth.Authing.Text").arg(service()));
if (!success || output.empty()) if (!success || output.empty())
throw ErrorInfo("Failed to get token from remote", error); throw ErrorInfo("Failed to get token from remote", error);
@ -284,8 +267,7 @@ try {
} }
} }
if (!error.empty()) if (!error.empty())
throw ErrorInfo(error, throw ErrorInfo(error, json["error_description"].string_value());
json["error_description"].string_value());
/* -------------------------- */ /* -------------------------- */
/* success! */ /* success! */
@ -310,15 +292,12 @@ try {
} catch (ErrorInfo &info) { } catch (ErrorInfo &info) {
if (!retry) { if (!retry) {
QString title = QTStr("Auth.AuthFailure.Title"); QString title = QTStr("Auth.AuthFailure.Title");
QString text = QTStr("Auth.AuthFailure.Text") QString text = QTStr("Auth.AuthFailure.Text").arg(service(), info.message.c_str(), info.error.c_str());
.arg(service(), info.message.c_str(),
info.error.c_str());
QMessageBox::warning(OBSBasic::Get(), title, text); QMessageBox::warning(OBSBasic::Get(), title, text);
} }
blog(LOG_WARNING, "%s: %s: %s", __FUNCTION__, info.message.c_str(), blog(LOG_WARNING, "%s: %s: %s", __FUNCTION__, info.message.c_str(), info.error.c_str());
info.error.c_str());
return false; return false;
} }
@ -335,8 +314,7 @@ void OAuthStreamKey::OnStreamConfig()
bool bwtest = obs_data_get_bool(settings, "bwtest"); bool bwtest = obs_data_get_bool(settings, "bwtest");
if (bwtest && strcmp(this->service(), "Twitch") == 0) if (bwtest && strcmp(this->service(), "Twitch") == 0)
obs_data_set_string(settings, "key", obs_data_set_string(settings, "key", (key_ + "?bandwidthtest=true").c_str());
(key_ + "?bandwidthtest=true").c_str());
else else
obs_data_set_string(settings, "key", key_.c_str()); obs_data_set_string(settings, "key", key_.c_str());

View File

@ -37,18 +37,13 @@ class OAuth : public Auth {
public: public:
inline OAuth(const Def &d) : Auth(d) {} inline OAuth(const Def &d) : Auth(d) {}
typedef std::function<std::shared_ptr<Auth>( typedef std::function<std::shared_ptr<Auth>(QWidget *, const std::string &service_name)> login_cb;
QWidget *, const std::string &service_name)>
login_cb;
typedef std::function<void()> delete_cookies_cb; typedef std::function<void()> delete_cookies_cb;
static std::shared_ptr<Auth> Login(QWidget *parent, static std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service);
const std::string &service);
static void DeleteCookies(const std::string &service); static void DeleteCookies(const std::string &service);
static void RegisterOAuth(const Def &d, create_cb create, static void RegisterOAuth(const Def &d, create_cb create, login_cb login, delete_cookies_cb delete_cookies);
login_cb login,
delete_cookies_cb delete_cookies);
protected: protected:
std::string refresh_token; std::string refresh_token;
@ -62,20 +57,14 @@ protected:
virtual bool RetryLogin() = 0; virtual bool RetryLogin() = 0;
bool TokenExpired(); bool TokenExpired();
bool GetToken(const char *url, const std::string &client_id, bool GetToken(const char *url, const std::string &client_id, int scope_ver,
int scope_ver, const std::string &auth_code = std::string(), bool retry = false);
const std::string &auth_code = std::string(), bool GetToken(const char *url, const std::string &client_id, const std::string &secret,
bool retry = false); const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry);
bool GetToken(const char *url, const std::string &client_id,
const std::string &secret,
const std::string &redirect_uri, int scope_ver,
const std::string &auth_code, bool retry);
private: private:
bool GetTokenInternal(const char *url, const std::string &client_id, bool GetTokenInternal(const char *url, const std::string &client_id, const std::string &secret,
const std::string &secret, const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry);
const std::string &redirect_uri, int scope_ver,
const std::string &auth_code, bool retry);
}; };
class OAuthStreamKey : public OAuth { class OAuthStreamKey : public OAuth {

View File

@ -72,14 +72,12 @@ try {
bool success; bool success;
auto func = [&]() { auto func = [&]() {
success = GetRemoteFile(RESTREAM_STREAMKEY_URL, output, error, success = GetRemoteFile(RESTREAM_STREAMKEY_URL, output, error, nullptr, "application/json", "", nullptr,
nullptr, "application/json", "", headers, nullptr, 5);
nullptr, headers, nullptr, 5);
}; };
ExecThreadedWithoutBlocking( ExecThreadedWithoutBlocking(func, QTStr("Auth.LoadingChannel.Title"),
func, QTStr("Auth.LoadingChannel.Title"), QTStr("Auth.LoadingChannel.Text").arg(service()));
QTStr("Auth.LoadingChannel.Text").arg(service()));
if (!success || output.empty()) if (!success || output.empty())
throw ErrorInfo("Failed to get stream key from remote", error); throw ErrorInfo("Failed to get stream key from remote", error);
@ -89,35 +87,29 @@ try {
error = json["error"].string_value(); error = json["error"].string_value();
if (!error.empty()) if (!error.empty())
throw ErrorInfo(error, throw ErrorInfo(error, json["error_description"].string_value());
json["error_description"].string_value());
key_ = json["streamKey"].string_value(); key_ = json["streamKey"].string_value();
return true; return true;
} catch (ErrorInfo info) { } catch (ErrorInfo info) {
QString title = QTStr("Auth.ChannelFailure.Title"); QString title = QTStr("Auth.ChannelFailure.Title");
QString text = QTStr("Auth.ChannelFailure.Text") QString text = QTStr("Auth.ChannelFailure.Text").arg(service(), info.message.c_str(), info.error.c_str());
.arg(service(), info.message.c_str(),
info.error.c_str());
QMessageBox::warning(OBSBasic::Get(), title, text); QMessageBox::warning(OBSBasic::Get(), title, text);
blog(LOG_WARNING, "%s: %s: %s", __FUNCTION__, info.message.c_str(), blog(LOG_WARNING, "%s: %s: %s", __FUNCTION__, info.message.c_str(), info.error.c_str());
info.error.c_str());
return false; return false;
} }
void RestreamAuth::SaveInternal() void RestreamAuth::SaveInternal()
{ {
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
config_set_string(main->Config(), service(), "DockState", config_set_string(main->Config(), service(), "DockState", main->saveState().toBase64().constData());
main->saveState().toBase64().constData());
OAuthStreamKey::SaveInternal(); OAuthStreamKey::SaveInternal();
} }
static inline std::string get_config_str(OBSBasic *main, const char *section, static inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)
const char *name)
{ {
const char *val = config_get_string(main->Config(), section, name); const char *val = config_get_string(main->Config(), section, name);
return val ? val : ""; return val ? val : "";
@ -209,10 +201,8 @@ void RestreamAuth::LoadUI()
info->setVisible(true); info->setVisible(true);
channels->setVisible(true); channels->setVisible(true);
} else { } else {
const char *dockStateStr = config_get_string( const char *dockStateStr = config_get_string(main->Config(), service(), "DockState");
main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));
QByteArray dockState =
QByteArray::fromBase64(QByteArray(dockStateStr));
if (main->isVisible() || !main->isMaximized()) if (main->isVisible() || !main->isMaximized())
main->restoreState(dockState); main->restoreState(dockState);
@ -229,14 +219,12 @@ bool RestreamAuth::RetryLogin()
return false; return false;
} }
std::shared_ptr<RestreamAuth> auth = std::shared_ptr<RestreamAuth> auth = std::make_shared<RestreamAuth>(restreamDef);
std::make_shared<RestreamAuth>(restreamDef);
std::string client_id = RESTREAM_CLIENTID; std::string client_id = RESTREAM_CLIENTID;
deobfuscate_str(&client_id[0], RESTREAM_HASH); deobfuscate_str(&client_id[0], RESTREAM_HASH);
return GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION, return GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()), true);
QT_TO_UTF8(login.GetCode()), true);
} }
std::shared_ptr<Auth> RestreamAuth::Login(QWidget *parent, const std::string &) std::shared_ptr<Auth> RestreamAuth::Login(QWidget *parent, const std::string &)
@ -248,15 +236,12 @@ std::shared_ptr<Auth> RestreamAuth::Login(QWidget *parent, const std::string &)
return nullptr; return nullptr;
} }
std::shared_ptr<RestreamAuth> auth = std::shared_ptr<RestreamAuth> auth = std::make_shared<RestreamAuth>(restreamDef);
std::make_shared<RestreamAuth>(restreamDef);
std::string client_id = RESTREAM_CLIENTID; std::string client_id = RESTREAM_CLIENTID;
deobfuscate_str(&client_id[0], RESTREAM_HASH); deobfuscate_str(&client_id[0], RESTREAM_HASH);
if (!auth->GetToken(RESTREAM_TOKEN_URL, client_id, if (!auth->GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()))) {
RESTREAM_SCOPE_VERSION,
QT_TO_UTF8(login.GetCode()))) {
return nullptr; return nullptr;
} }
@ -287,6 +272,5 @@ void RegisterRestreamAuth()
return; return;
#endif #endif
OAuth::RegisterOAuth(restreamDef, CreateRestreamAuth, OAuth::RegisterOAuth(restreamDef, CreateRestreamAuth, RestreamAuth::Login, DeleteCookies);
RestreamAuth::Login, DeleteCookies);
} }

View File

@ -22,6 +22,5 @@ public:
RestreamAuth(const Def &d); RestreamAuth(const Def &d);
~RestreamAuth(); ~RestreamAuth();
static std::shared_ptr<Auth> Login(QWidget *parent, static std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service_name);
const std::string &service_name);
}; };

View File

@ -41,17 +41,14 @@ TwitchAuth::TwitchAuth(const Def &d) : OAuthStreamKey(d)
if (!cef) if (!cef)
return; return;
cef->add_popup_whitelist_url( cef->add_popup_whitelist_url("https://twitch.tv/popout/frankerfacez/chat?ffz-settings", this);
"https://twitch.tv/popout/frankerfacez/chat?ffz-settings",
this);
/* enables javascript-based popups. basically bttv popups */ /* enables javascript-based popups. basically bttv popups */
cef->add_popup_whitelist_url("about:blank#blocked", this); cef->add_popup_whitelist_url("about:blank#blocked", this);
uiLoadTimer.setSingleShot(true); uiLoadTimer.setSingleShot(true);
uiLoadTimer.setInterval(500); uiLoadTimer.setInterval(500);
connect(&uiLoadTimer, &QTimer::timeout, this, connect(&uiLoadTimer, &QTimer::timeout, this, &TwitchAuth::TryLoadSecondaryUIPanes);
&TwitchAuth::TryLoadSecondaryUIPanes);
} }
TwitchAuth::~TwitchAuth() TwitchAuth::~TwitchAuth()
@ -86,19 +83,15 @@ bool TwitchAuth::MakeApiRequest(const char *path, Json &json_out)
bool success = false; bool success = false;
auto func = [&]() { auto func = [&]() {
success = GetRemoteFile(url.c_str(), output, error, &error_code, success = GetRemoteFile(url.c_str(), output, error, &error_code, "application/json", "", nullptr,
"application/json", "", nullptr,
headers, nullptr, 5); headers, nullptr, 5);
}; };
ExecThreadedWithoutBlocking( ExecThreadedWithoutBlocking(func, QTStr("Auth.LoadingChannel.Title"),
func, QTStr("Auth.LoadingChannel.Title"), QTStr("Auth.LoadingChannel.Text").arg(service()));
QTStr("Auth.LoadingChannel.Text").arg(service()));
if (error_code == 403) { if (error_code == 403) {
OBSMessageBox::warning(OBSBasic::Get(), OBSMessageBox::warning(OBSBasic::Get(), Str("TwitchAuth.TwoFactorFail.Title"),
Str("TwitchAuth.TwoFactorFail.Title"), Str("TwitchAuth.TwoFactorFail.Text"), true);
Str("TwitchAuth.TwoFactorFail.Text"),
true);
blog(LOG_WARNING, "%s: %s", __FUNCTION__, blog(LOG_WARNING, "%s: %s", __FUNCTION__,
"Got 403 from Twitch, user probably does not " "Got 403 from Twitch, user probably does not "
"have two-factor authentication enabled on " "have two-factor authentication enabled on "
@ -140,8 +133,7 @@ try {
name = json["data"][0]["login"].string_value(); name = json["data"][0]["login"].string_value();
std::string path = "streams/key?broadcaster_id=" + std::string path = "streams/key?broadcaster_id=" + json["data"][0]["id"].string_value();
json["data"][0]["id"].string_value();
success = MakeApiRequest(path.c_str(), json); success = MakeApiRequest(path.c_str(), json);
if (!success) if (!success)
return false; return false;
@ -151,14 +143,11 @@ try {
return true; return true;
} catch (ErrorInfo info) { } catch (ErrorInfo info) {
QString title = QTStr("Auth.ChannelFailure.Title"); QString title = QTStr("Auth.ChannelFailure.Title");
QString text = QTStr("Auth.ChannelFailure.Text") QString text = QTStr("Auth.ChannelFailure.Text").arg(service(), info.message.c_str(), info.error.c_str());
.arg(service(), info.message.c_str(),
info.error.c_str());
QMessageBox::warning(OBSBasic::Get(), title, text); QMessageBox::warning(OBSBasic::Get(), title, text);
blog(LOG_WARNING, "%s: %s: %s", __FUNCTION__, info.message.c_str(), blog(LOG_WARNING, "%s: %s: %s", __FUNCTION__, info.message.c_str(), info.error.c_str());
info.error.c_str());
return false; return false;
} }
@ -169,14 +158,12 @@ void TwitchAuth::SaveInternal()
config_set_string(main->Config(), service(), "UUID", uuid.c_str()); config_set_string(main->Config(), service(), "UUID", uuid.c_str());
if (uiLoaded) { if (uiLoaded) {
config_set_string(main->Config(), service(), "DockState", config_set_string(main->Config(), service(), "DockState", main->saveState().toBase64().constData());
main->saveState().toBase64().constData());
} }
OAuthStreamKey::SaveInternal(); OAuthStreamKey::SaveInternal();
} }
static inline std::string get_config_str(OBSBasic *main, const char *section, static inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)
const char *name)
{ {
const char *val = config_get_string(main->Config(), section, name); const char *val = config_get_string(main->Config(), section, name);
return val ? val : ""; return val ? val : "";
@ -268,8 +255,7 @@ void TwitchAuth::LoadUI()
script = "localStorage.setItem('twilight.theme', 0);"; script = "localStorage.setItem('twilight.theme', 0);";
} }
const int twAddonChoice = const int twAddonChoice = config_get_int(main->Config(), service(), "AddonChoice");
config_get_int(main->Config(), service(), "AddonChoice");
if (twAddonChoice) { if (twAddonChoice) {
if (twAddonChoice & 0x1) if (twAddonChoice & 0x1)
script += bttv_script; script += bttv_script;
@ -289,10 +275,8 @@ void TwitchAuth::LoadUI()
if (firstLoad) { if (firstLoad) {
chat->setVisible(true); chat->setVisible(true);
} else { } else {
const char *dockStateStr = config_get_string( const char *dockStateStr = config_get_string(main->Config(), service(), "DockState");
main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));
QByteArray dockState =
QByteArray::fromBase64(QByteArray(dockStateStr));
if (main->isVisible() || !main->isMaximized()) if (main->isVisible() || !main->isMaximized())
main->restoreState(dockState); main->restoreState(dockState);
@ -325,8 +309,7 @@ void TwitchAuth::LoadSecondaryUIPanes()
script += "/dashboard/live"; script += "/dashboard/live";
script += referrer_script2; script += referrer_script2;
const int twAddonChoice = const int twAddonChoice = config_get_int(main->Config(), service(), "AddonChoice");
config_get_int(main->Config(), service(), "AddonChoice");
if (twAddonChoice) { if (twAddonChoice) {
if (twAddonChoice & 0x1) if (twAddonChoice & 0x1)
script += bttv_script; script += bttv_script;
@ -410,17 +393,14 @@ void TwitchAuth::LoadSecondaryUIPanes()
stats->setVisible(false); stats->setVisible(false);
feed->setVisible(false); feed->setVisible(false);
} else { } else {
uint32_t lastVersion = config_get_int(App()->GetAppConfig(), uint32_t lastVersion = config_get_int(App()->GetAppConfig(), "General", "LastVersion");
"General", "LastVersion");
if (lastVersion <= MAKE_SEMANTIC_VERSION(23, 0, 2)) { if (lastVersion <= MAKE_SEMANTIC_VERSION(23, 0, 2)) {
feed->setVisible(false); feed->setVisible(false);
} }
const char *dockStateStr = config_get_string( const char *dockStateStr = config_get_string(main->Config(), service(), "DockState");
main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));
QByteArray dockState =
QByteArray::fromBase64(QByteArray(dockStateStr));
if (main->isVisible() || !main->isMaximized()) if (main->isVisible() || !main->isMaximized())
main->restoreState(dockState); main->restoreState(dockState);
@ -449,13 +429,11 @@ void TwitchAuth::TryLoadSecondaryUIPanes()
if (!found) { if (!found) {
QMetaObject::invokeMethod(&this_->uiLoadTimer, "start"); QMetaObject::invokeMethod(&this_->uiLoadTimer, "start");
} else { } else {
QMetaObject::invokeMethod(this_, QMetaObject::invokeMethod(this_, "LoadSecondaryUIPanes");
"LoadSecondaryUIPanes");
} }
}; };
panel_cookies->CheckForCookie("https://www.twitch.tv", "auth-token", panel_cookies->CheckForCookie("https://www.twitch.tv", "auth-token", cb);
cb);
} }
bool TwitchAuth::RetryLogin() bool TwitchAuth::RetryLogin()
@ -465,13 +443,11 @@ bool TwitchAuth::RetryLogin()
return false; return false;
} }
std::shared_ptr<TwitchAuth> auth = std::shared_ptr<TwitchAuth> auth = std::make_shared<TwitchAuth>(twitchDef);
std::make_shared<TwitchAuth>(twitchDef);
std::string client_id = TWITCH_CLIENTID; std::string client_id = TWITCH_CLIENTID;
deobfuscate_str(&client_id[0], TWITCH_HASH); deobfuscate_str(&client_id[0], TWITCH_HASH);
return GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, return GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()), true);
QT_TO_UTF8(login.GetCode()), true);
} }
std::shared_ptr<Auth> TwitchAuth::Login(QWidget *parent, const std::string &) std::shared_ptr<Auth> TwitchAuth::Login(QWidget *parent, const std::string &)
@ -481,14 +457,12 @@ std::shared_ptr<Auth> TwitchAuth::Login(QWidget *parent, const std::string &)
return nullptr; return nullptr;
} }
std::shared_ptr<TwitchAuth> auth = std::shared_ptr<TwitchAuth> auth = std::make_shared<TwitchAuth>(twitchDef);
std::make_shared<TwitchAuth>(twitchDef);
std::string client_id = TWITCH_CLIENTID; std::string client_id = TWITCH_CLIENTID;
deobfuscate_str(&client_id[0], TWITCH_HASH); deobfuscate_str(&client_id[0], TWITCH_HASH);
if (!auth->GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, if (!auth->GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()))) {
QT_TO_UTF8(login.GetCode()))) {
return nullptr; return nullptr;
} }
@ -517,6 +491,5 @@ void RegisterTwitchAuth()
return; return;
#endif #endif
OAuth::RegisterOAuth(twitchDef, CreateTwitchAuth, TwitchAuth::Login, OAuth::RegisterOAuth(twitchDef, CreateTwitchAuth, TwitchAuth::Login, DeleteCookies);
DeleteCookies);
} }

View File

@ -32,8 +32,7 @@ public:
TwitchAuth(const Def &d); TwitchAuth(const Def &d);
~TwitchAuth(); ~TwitchAuth();
static std::shared_ptr<Auth> Login(QWidget *parent, static std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service_name);
const std::string &service_name);
QTimer uiLoadTimer; QTimer uiLoadTimer;

View File

@ -37,15 +37,12 @@ using namespace json11;
#define YOUTUBE_API_STATE_LENGTH 32 #define YOUTUBE_API_STATE_LENGTH 32
#define SECTION_NAME "YouTube" #define SECTION_NAME "YouTube"
#define YOUTUBE_CHAT_PLACEHOLDER_URL \ #define YOUTUBE_CHAT_PLACEHOLDER_URL "https://obsproject.com/placeholders/youtube-chat"
"https://obsproject.com/placeholders/youtube-chat" #define YOUTUBE_CHAT_POPOUT_URL "https://www.youtube.com/live_chat?is_popout=1&dark_theme=1&v=%1"
#define YOUTUBE_CHAT_POPOUT_URL \
"https://www.youtube.com/live_chat?is_popout=1&dark_theme=1&v=%1"
#define YOUTUBE_CHAT_DOCK_NAME "ytChat" #define YOUTUBE_CHAT_DOCK_NAME "ytChat"
static const char allowedChars[] = static const char allowedChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static const int allowedCount = static_cast<int>(sizeof(allowedChars) - 1); static const int allowedCount = static_cast<int>(sizeof(allowedChars) - 1);
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@ -67,20 +64,12 @@ void RegisterYoutubeAuth()
{ {
for (auto &service : youtubeServices) { for (auto &service : youtubeServices) {
OAuth::RegisterOAuth( OAuth::RegisterOAuth(
service, service, [service]() { return std::make_shared<YoutubeApiWrappers>(service); },
[service]() {
return std::make_shared<YoutubeApiWrappers>(
service);
},
YoutubeAuth::Login, DeleteCookies); YoutubeAuth::Login, DeleteCookies);
} }
} }
YoutubeAuth::YoutubeAuth(const Def &d) YoutubeAuth::YoutubeAuth(const Def &d) : OAuthStreamKey(d), section(SECTION_NAME) {}
: OAuthStreamKey(d),
section(SECTION_NAME)
{
}
YoutubeAuth::~YoutubeAuth() YoutubeAuth::~YoutubeAuth()
{ {
@ -103,21 +92,16 @@ bool YoutubeAuth::RetryLogin()
void YoutubeAuth::SaveInternal() void YoutubeAuth::SaveInternal()
{ {
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
config_set_string(main->Config(), service(), "DockState", config_set_string(main->Config(), service(), "DockState", main->saveState().toBase64().constData());
main->saveState().toBase64().constData());
const char *section_name = section.c_str(); const char *section_name = section.c_str();
config_set_string(main->Config(), section_name, "RefreshToken", config_set_string(main->Config(), section_name, "RefreshToken", refresh_token.c_str());
refresh_token.c_str());
config_set_string(main->Config(), section_name, "Token", token.c_str()); config_set_string(main->Config(), section_name, "Token", token.c_str());
config_set_uint(main->Config(), section_name, "ExpireTime", config_set_uint(main->Config(), section_name, "ExpireTime", expire_time);
expire_time); config_set_int(main->Config(), section_name, "ScopeVer", currentScopeVer);
config_set_int(main->Config(), section_name, "ScopeVer",
currentScopeVer);
} }
static inline std::string get_config_str(OBSBasic *main, const char *section, static inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)
const char *name)
{ {
const char *val = config_get_string(main->Config(), section, name); const char *val = config_get_string(main->Config(), section, name);
return val ? val : ""; return val ? val : "";
@ -130,10 +114,8 @@ bool YoutubeAuth::LoadInternal()
const char *section_name = section.c_str(); const char *section_name = section.c_str();
refresh_token = get_config_str(main, section_name, "RefreshToken"); refresh_token = get_config_str(main, section_name, "RefreshToken");
token = get_config_str(main, section_name, "Token"); token = get_config_str(main, section_name, "Token");
expire_time = expire_time = config_get_uint(main->Config(), section_name, "ExpireTime");
config_get_uint(main->Config(), section_name, "ExpireTime"); currentScopeVer = (int)config_get_int(main->Config(), section_name, "ScopeVer");
currentScopeVer =
(int)config_get_int(main->Config(), section_name, "ScopeVer");
firstLoad = false; firstLoad = false;
return implicit ? !token.empty() : !refresh_token.empty(); return implicit ? !token.empty() : !refresh_token.empty();
} }
@ -161,8 +143,7 @@ void YoutubeAuth::LoadUI()
chat->setMinimumSize(200, 300); chat->setMinimumSize(200, 300);
chat->setAllowedAreas(Qt::AllDockWidgetAreas); chat->setAllowedAreas(Qt::AllDockWidgetAreas);
browser = cef->create_widget(chat, YOUTUBE_CHAT_PLACEHOLDER_URL, browser = cef->create_widget(chat, YOUTUBE_CHAT_PLACEHOLDER_URL, panel_cookies);
panel_cookies);
chat->SetWidget(browser); chat->SetWidget(browser);
main->AddDockWidget(chat, Qt::RightDockWidgetArea); main->AddDockWidget(chat, Qt::RightDockWidgetArea);
@ -178,10 +159,8 @@ void YoutubeAuth::LoadUI()
main->NewYouTubeAppDock(); main->NewYouTubeAppDock();
if (!firstLoad) { if (!firstLoad) {
const char *dockStateStr = config_get_string( const char *dockStateStr = config_get_string(main->Config(), service(), "DockState");
main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));
QByteArray dockState =
QByteArray::fromBase64(QByteArray(dockStateStr));
if (main->isVisible() || !main->isMaximized()) if (main->isVisible() || !main->isMaximized())
main->restoreState(dockState); main->restoreState(dockState);
@ -190,8 +169,7 @@ void YoutubeAuth::LoadUI()
uiLoaded = true; uiLoaded = true;
} }
void YoutubeAuth::SetChatId(const QString &chat_id, void YoutubeAuth::SetChatId(const QString &chat_id, const std::string &api_chat_id)
const std::string &api_chat_id)
{ {
#ifdef BROWSER_AVAILABLE #ifdef BROWSER_AVAILABLE
QString chat_url = QString(YOUTUBE_CHAT_POPOUT_URL).arg(chat_id); QString chat_url = QString(YOUTUBE_CHAT_POPOUT_URL).arg(chat_id);
@ -239,23 +217,19 @@ QString YoutubeAuth::GenerateState()
} }
// Static. // Static.
std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner, std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner, const std::string &service)
const std::string &service)
{ {
QString auth_code; QString auth_code;
AuthListener server; AuthListener server;
auto it = std::find_if(youtubeServices.begin(), youtubeServices.end(), auto it = std::find_if(youtubeServices.begin(), youtubeServices.end(),
[service](auto &item) { [service](auto &item) { return service == item.service; });
return service == item.service;
});
if (it == youtubeServices.end()) { if (it == youtubeServices.end()) {
return nullptr; return nullptr;
} }
const auto auth = std::make_shared<YoutubeApiWrappers>(*it); const auto auth = std::make_shared<YoutubeApiWrappers>(*it);
QString redirect_uri = QString redirect_uri = QString("http://127.0.0.1:%1").arg(server.GetPort());
QString("http://127.0.0.1:%1").arg(server.GetPort());
QMessageBox dlg(owner); QMessageBox dlg(owner);
dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowCloseButtonHint); dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowCloseButtonHint);
@ -277,12 +251,10 @@ std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner,
url_template += "&redirect_uri=%3"; url_template += "&redirect_uri=%3";
url_template += "&state=%4"; url_template += "&state=%4";
url_template += "&scope=https://www.googleapis.com/auth/youtube"; url_template += "&scope=https://www.googleapis.com/auth/youtube";
QString url = url_template.arg(YOUTUBE_AUTH_URL, clientid.c_str(), QString url = url_template.arg(YOUTUBE_AUTH_URL, clientid.c_str(), redirect_uri, state);
redirect_uri, state);
QString text = QTStr("YouTube.Auth.WaitingAuth.Text"); QString text = QTStr("YouTube.Auth.WaitingAuth.Text");
text = text.arg( text = text.arg(QString("<a href='%1'>Google OAuth Service</a>").arg(url));
QString("<a href='%1'>Google OAuth Service</a>").arg(url));
dlg.setText(text); dlg.setText(text);
dlg.setTextFormat(Qt::RichText); dlg.setTextFormat(Qt::RichText);
@ -293,25 +265,22 @@ std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner,
dlg.setOption(QMessageBox::Option::DontUseNativeDialog); dlg.setOption(QMessageBox::Option::DontUseNativeDialog);
#endif #endif
connect(&dlg, &QMessageBox::buttonClicked, &dlg, connect(&dlg, &QMessageBox::buttonClicked, &dlg, [&](QAbstractButton *) {
[&](QAbstractButton *) {
#ifdef _DEBUG #ifdef _DEBUG
blog(LOG_DEBUG, "Action Cancelled."); blog(LOG_DEBUG, "Action Cancelled.");
#endif #endif
// TODO: Stop server. // TODO: Stop server.
dlg.reject(); dlg.reject();
}); });
// Async Login. // Async Login.
connect(&server, &AuthListener::ok, &dlg, connect(&server, &AuthListener::ok, &dlg, [&dlg, &auth_code](QString code) {
[&dlg, &auth_code](QString code) {
#ifdef _DEBUG #ifdef _DEBUG
blog(LOG_DEBUG, "Got youtube redirected answer: %s", blog(LOG_DEBUG, "Got youtube redirected answer: %s", QT_TO_UTF8(code));
QT_TO_UTF8(code));
#endif #endif
auth_code = code; auth_code = code;
dlg.accept(); dlg.accept();
}); });
connect(&server, &AuthListener::fail, &dlg, [&dlg]() { connect(&server, &AuthListener::fail, &dlg, [&dlg]() {
#ifdef _DEBUG #ifdef _DEBUG
blog(LOG_DEBUG, "No access granted"); blog(LOG_DEBUG, "No access granted");
@ -325,10 +294,8 @@ std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner,
QScopedPointer<QThread> thread(CreateQThread(open_external_browser)); QScopedPointer<QThread> thread(CreateQThread(open_external_browser));
thread->start(); thread->start();
#if defined(__APPLE__) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && \ #if defined(__APPLE__) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
QT_VERSION < QT_VERSION_CHECK(6, 6, 0) const bool nativeDialogs = qApp->testAttribute(Qt::AA_DontUseNativeDialogs);
const bool nativeDialogs =
qApp->testAttribute(Qt::AA_DontUseNativeDialogs);
App()->setAttribute(Qt::AA_DontUseNativeDialogs, true); App()->setAttribute(Qt::AA_DontUseNativeDialogs, true);
dlg.exec(); dlg.exec();
App()->setAttribute(Qt::AA_DontUseNativeDialogs, nativeDialogs); App()->setAttribute(Qt::AA_DontUseNativeDialogs, nativeDialogs);
@ -336,12 +303,10 @@ std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner,
dlg.exec(); dlg.exec();
#endif #endif
if (dlg.result() == QMessageBox::Cancel || if (dlg.result() == QMessageBox::Cancel || dlg.result() == QDialog::Rejected)
dlg.result() == QDialog::Rejected)
return nullptr; return nullptr;
if (!auth->GetToken(YOUTUBE_TOKEN_URL, clientid, secret, if (!auth->GetToken(YOUTUBE_TOKEN_URL, clientid, secret, QT_TO_UTF8(redirect_uri), YOUTUBE_SCOPE_VERSION,
QT_TO_UTF8(redirect_uri), YOUTUBE_SCOPE_VERSION,
QT_TO_UTF8(auth_code), true)) { QT_TO_UTF8(auth_code), true)) {
return nullptr; return nullptr;
} }
@ -351,8 +316,7 @@ std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner,
ChannelDescription cd; ChannelDescription cd;
if (auth->GetChannelDescription(cd)) if (auth->GetChannelDescription(cd))
config_set_string(config, "YouTube", "ChannelName", config_set_string(config, "YouTube", "ChannelName", QT_TO_UTF8(cd.title));
QT_TO_UTF8(cd.title));
config_save_safe(config, "tmp", nullptr); config_save_safe(config, "tmp", nullptr);
return auth; return auth;
@ -373,10 +337,8 @@ YoutubeChatDock::YoutubeChatDock(const QString &title) : BrowserDock(title)
chatLayout->addWidget(lineEdit, 1); chatLayout->addWidget(lineEdit, 1);
chatLayout->addWidget(sendButton); chatLayout->addWidget(sendButton);
QWidget::connect(lineEdit, &LineEditAutoResize::returnPressed, this, QWidget::connect(lineEdit, &LineEditAutoResize::returnPressed, this, &YoutubeChatDock::SendChatMessage);
&YoutubeChatDock::SendChatMessage); QWidget::connect(sendButton, &QPushButton::pressed, this, &YoutubeChatDock::SendChatMessage);
QWidget::connect(sendButton, &QPushButton::pressed, this,
&YoutubeChatDock::SendChatMessage);
} }
void YoutubeChatDock::SetWidget(QCefWidget *widget_) void YoutubeChatDock::SetWidget(QCefWidget *widget_)
@ -392,15 +354,13 @@ void YoutubeChatDock::SetWidget(QCefWidget *widget_)
cefWidget.reset(widget_); cefWidget.reset(widget_);
QWidget::connect(cefWidget.get(), &QCefWidget::urlChanged, this, QWidget::connect(cefWidget.get(), &QCefWidget::urlChanged, this, &YoutubeChatDock::YoutubeCookieCheck);
&YoutubeChatDock::YoutubeCookieCheck);
} }
void YoutubeChatDock::SetApiChatId(const std::string &id) void YoutubeChatDock::SetApiChatId(const std::string &id)
{ {
this->apiChatId = id; this->apiChatId = id;
QMetaObject::invokeMethod(this, "EnableChatInput", Qt::QueuedConnection, QMetaObject::invokeMethod(this, "EnableChatInput", Qt::QueuedConnection, Q_ARG(bool, !id.empty()));
Q_ARG(bool, !id.empty()));
} }
void YoutubeChatDock::YoutubeCookieCheck() void YoutubeChatDock::YoutubeCookieCheck()
@ -409,25 +369,20 @@ void YoutubeChatDock::YoutubeCookieCheck()
auto cb = [this_](bool currentlyLoggedIn) { auto cb = [this_](bool currentlyLoggedIn) {
bool previouslyLoggedIn = this_->isLoggedIn; bool previouslyLoggedIn = this_->isLoggedIn;
this_->isLoggedIn = currentlyLoggedIn; this_->isLoggedIn = currentlyLoggedIn;
bool loginStateChanged = bool loginStateChanged = (currentlyLoggedIn && !previouslyLoggedIn) ||
(currentlyLoggedIn && !previouslyLoggedIn) || (!currentlyLoggedIn && previouslyLoggedIn);
(!currentlyLoggedIn && previouslyLoggedIn);
if (loginStateChanged) { if (loginStateChanged) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(this_, "EnableChatInput", Qt::QueuedConnection,
this_, "EnableChatInput", Qt::QueuedConnection, Q_ARG(bool, !currentlyLoggedIn));
Q_ARG(bool, !currentlyLoggedIn));
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
if (main->GetYouTubeAppDock() != nullptr) { if (main->GetYouTubeAppDock() != nullptr) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(main->GetYouTubeAppDock(), "SettingsUpdated",
main->GetYouTubeAppDock(), Qt::QueuedConnection, Q_ARG(bool, !currentlyLoggedIn));
"SettingsUpdated", Qt::QueuedConnection,
Q_ARG(bool, !currentlyLoggedIn));
} }
} }
}; };
if (panel_cookies) { if (panel_cookies) {
panel_cookies->CheckForCookie("https://www.youtube.com", "SID", panel_cookies->CheckForCookie("https://www.youtube.com", "SID", cb);
cb);
} }
} }
@ -438,31 +393,26 @@ void YoutubeChatDock::SendChatMessage()
return; return;
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
YoutubeApiWrappers *apiYouTube( YoutubeApiWrappers *apiYouTube(dynamic_cast<YoutubeApiWrappers *>(main->GetAuth()));
dynamic_cast<YoutubeApiWrappers *>(main->GetAuth()));
ExecuteFuncSafeBlock([&]() { ExecuteFuncSafeBlock([&]() {
lineEdit->setText(""); lineEdit->setText("");
lineEdit->setPlaceholderText( lineEdit->setPlaceholderText(QTStr("YouTube.Chat.Input.Sending"));
QTStr("YouTube.Chat.Input.Sending"));
if (apiYouTube->SendChatMessage(apiChatId, message)) { if (apiYouTube->SendChatMessage(apiChatId, message)) {
os_sleep_ms(3000); os_sleep_ms(3000);
} else { } else {
QString error = apiYouTube->GetLastError(); QString error = apiYouTube->GetLastError();
apiYouTube->GetTranslatedError(error); apiYouTube->GetTranslatedError(error);
QMetaObject::invokeMethod( QMetaObject::invokeMethod(this, "ShowErrorMessage", Qt::QueuedConnection,
this, "ShowErrorMessage", Qt::QueuedConnection, Q_ARG(const QString &, error));
Q_ARG(const QString &, error));
} }
lineEdit->setPlaceholderText( lineEdit->setPlaceholderText(QTStr("YouTube.Chat.Input.Placeholder"));
QTStr("YouTube.Chat.Input.Placeholder"));
}); });
} }
void YoutubeChatDock::ShowErrorMessage(const QString &error) void YoutubeChatDock::ShowErrorMessage(const QString &error)
{ {
QMessageBox::warning(this, QTStr("YouTube.Chat.Error.Title"), QMessageBox::warning(this, QTStr("YouTube.Chat.Error.Title"), QTStr("YouTube.Chat.Error.Text").arg(error));
QTStr("YouTube.Chat.Error.Text").arg(error));
} }
void YoutubeChatDock::EnableChatInput(bool visible) void YoutubeChatDock::EnableChatInput(bool visible)

View File

@ -34,10 +34,9 @@ private slots:
}; };
#endif #endif
inline const std::vector<Auth::Def> youtubeServices = { inline const std::vector<Auth::Def> youtubeServices = {{"YouTube - RTMP", Auth::Type::OAuth_LinkedAccount, true, true},
{"YouTube - RTMP", Auth::Type::OAuth_LinkedAccount, true, true}, {"YouTube - RTMPS", Auth::Type::OAuth_LinkedAccount, true, true},
{"YouTube - RTMPS", Auth::Type::OAuth_LinkedAccount, true, true}, {"YouTube - HLS", Auth::Type::OAuth_LinkedAccount, true, true}};
{"YouTube - HLS", Auth::Type::OAuth_LinkedAccount, true, true}};
class YoutubeAuth : public OAuthStreamKey { class YoutubeAuth : public OAuthStreamKey {
Q_OBJECT Q_OBJECT
@ -64,6 +63,5 @@ public:
void ResetChat(); void ResetChat();
void ReloadChat(); void ReloadChat();
static std::shared_ptr<Auth> Login(QWidget *parent, static std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service);
const std::string &service);
}; };

View File

@ -2,80 +2,61 @@
#include "window-basic-main.hpp" #include "window-basic-main.hpp"
OBSBasicControls::OBSBasicControls(OBSBasic *main) OBSBasicControls::OBSBasicControls(OBSBasic *main) : QFrame(nullptr), ui(new Ui::OBSBasicControls)
: QFrame(nullptr),
ui(new Ui::OBSBasicControls)
{ {
/* Create UI elements */ /* Create UI elements */
ui->setupUi(this); ui->setupUi(this);
streamButtonMenu.reset(new QMenu()); streamButtonMenu.reset(new QMenu());
startStreamAction = startStreamAction = streamButtonMenu->addAction(QTStr("Basic.Main.StartStreaming"));
streamButtonMenu->addAction(QTStr("Basic.Main.StartStreaming")); stopStreamAction = streamButtonMenu->addAction(QTStr("Basic.Main.StopStreaming"));
stopStreamAction = QAction *forceStopStreamAction = streamButtonMenu->addAction(QTStr("Basic.Main.ForceStopStreaming"));
streamButtonMenu->addAction(QTStr("Basic.Main.StopStreaming"));
QAction *forceStopStreamAction = streamButtonMenu->addAction(
QTStr("Basic.Main.ForceStopStreaming"));
/* Transfer buttons signals as OBSBasicControls signals */ /* Transfer buttons signals as OBSBasicControls signals */
connect( connect(
ui->streamButton, &QPushButton::clicked, this, ui->streamButton, &QPushButton::clicked, this, [this]() { emit this->StreamButtonClicked(); },
[this]() { emit this->StreamButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->broadcastButton, &QPushButton::clicked, this, ui->broadcastButton, &QPushButton::clicked, this, [this]() { emit this->BroadcastButtonClicked(); },
[this]() { emit this->BroadcastButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->recordButton, &QPushButton::clicked, this, ui->recordButton, &QPushButton::clicked, this, [this]() { emit this->RecordButtonClicked(); },
[this]() { emit this->RecordButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->pauseRecordButton, &QPushButton::clicked, this, ui->pauseRecordButton, &QPushButton::clicked, this, [this]() { emit this->PauseRecordButtonClicked(); },
[this]() { emit this->PauseRecordButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->replayBufferButton, &QPushButton::clicked, this, ui->replayBufferButton, &QPushButton::clicked, this,
[this]() { emit this->ReplayBufferButtonClicked(); }, [this]() { emit this->ReplayBufferButtonClicked(); }, Qt::DirectConnection);
Qt::DirectConnection);
connect( connect(
ui->saveReplayButton, &QPushButton::clicked, this, ui->saveReplayButton, &QPushButton::clicked, this,
[this]() { emit this->SaveReplayBufferButtonClicked(); }, [this]() { emit this->SaveReplayBufferButtonClicked(); }, Qt::DirectConnection);
Qt::DirectConnection);
connect( connect(
ui->virtualCamButton, &QPushButton::clicked, this, ui->virtualCamButton, &QPushButton::clicked, this, [this]() { emit this->VirtualCamButtonClicked(); },
[this]() { emit this->VirtualCamButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->virtualCamConfigButton, &QPushButton::clicked, this, ui->virtualCamConfigButton, &QPushButton::clicked, this,
[this]() { emit this->VirtualCamConfigButtonClicked(); }, [this]() { emit this->VirtualCamConfigButtonClicked(); }, Qt::DirectConnection);
connect(
ui->modeSwitch, &QPushButton::clicked, this, [this]() { emit this->StudioModeButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->modeSwitch, &QPushButton::clicked, this, ui->settingsButton, &QPushButton::clicked, this, [this]() { emit this->SettingsButtonClicked(); },
[this]() { emit this->StudioModeButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
ui->settingsButton, &QPushButton::clicked, this, ui->exitButton, &QPushButton::clicked, this, [this]() { emit this->ExitButtonClicked(); },
[this]() { emit this->SettingsButtonClicked(); },
Qt::DirectConnection);
connect(
ui->exitButton, &QPushButton::clicked, this,
[this]() { emit this->ExitButtonClicked(); },
Qt::DirectConnection); Qt::DirectConnection);
/* Transfer menu actions signals as OBSBasicControls signals */ /* Transfer menu actions signals as OBSBasicControls signals */
connect( connect(
startStreamAction.get(), &QAction::triggered, this, startStreamAction.get(), &QAction::triggered, this,
[this]() { emit this->StartStreamMenuActionClicked(); }, [this]() { emit this->StartStreamMenuActionClicked(); }, Qt::DirectConnection);
Qt::DirectConnection);
connect( connect(
stopStreamAction.get(), &QAction::triggered, this, stopStreamAction.get(), &QAction::triggered, this,
[this]() { emit this->StopStreamMenuActionClicked(); }, [this]() { emit this->StopStreamMenuActionClicked(); }, Qt::DirectConnection);
Qt::DirectConnection);
connect( connect(
forceStopStreamAction, &QAction::triggered, this, forceStopStreamAction, &QAction::triggered, this,
[this]() { emit this->ForceStopStreamMenuActionClicked(); }, [this]() { emit this->ForceStopStreamMenuActionClicked(); }, Qt::DirectConnection);
Qt::DirectConnection);
/* Set up default visibilty */ /* Set up default visibilty */
ui->broadcastButton->setVisible(false); ui->broadcastButton->setVisible(false);
@ -86,57 +67,35 @@ OBSBasicControls::OBSBasicControls(OBSBasic *main)
ui->virtualCamConfigButton->setVisible(false); ui->virtualCamConfigButton->setVisible(false);
/* Set up state update connections */ /* Set up state update connections */
connect(main, &OBSBasic::StreamingPreparing, this, connect(main, &OBSBasic::StreamingPreparing, this, &OBSBasicControls::StreamingPreparing);
&OBSBasicControls::StreamingPreparing); connect(main, &OBSBasic::StreamingStarting, this, &OBSBasicControls::StreamingStarting);
connect(main, &OBSBasic::StreamingStarting, this, connect(main, &OBSBasic::StreamingStarted, this, &OBSBasicControls::StreamingStarted);
&OBSBasicControls::StreamingStarting); connect(main, &OBSBasic::StreamingStopping, this, &OBSBasicControls::StreamingStopping);
connect(main, &OBSBasic::StreamingStarted, this, connect(main, &OBSBasic::StreamingStopped, this, &OBSBasicControls::StreamingStopped);
&OBSBasicControls::StreamingStarted);
connect(main, &OBSBasic::StreamingStopping, this,
&OBSBasicControls::StreamingStopping);
connect(main, &OBSBasic::StreamingStopped, this,
&OBSBasicControls::StreamingStopped);
connect(main, &OBSBasic::BroadcastStreamReady, this, connect(main, &OBSBasic::BroadcastStreamReady, this, &OBSBasicControls::BroadcastStreamReady);
&OBSBasicControls::BroadcastStreamReady); connect(main, &OBSBasic::BroadcastStreamActive, this, &OBSBasicControls::BroadcastStreamActive);
connect(main, &OBSBasic::BroadcastStreamActive, this, connect(main, &OBSBasic::BroadcastStreamStarted, this, &OBSBasicControls::BroadcastStreamStarted);
&OBSBasicControls::BroadcastStreamActive);
connect(main, &OBSBasic::BroadcastStreamStarted, this,
&OBSBasicControls::BroadcastStreamStarted);
connect(main, &OBSBasic::RecordingStarted, this, connect(main, &OBSBasic::RecordingStarted, this, &OBSBasicControls::RecordingStarted);
&OBSBasicControls::RecordingStarted); connect(main, &OBSBasic::RecordingPaused, this, &OBSBasicControls::RecordingPaused);
connect(main, &OBSBasic::RecordingPaused, this, connect(main, &OBSBasic::RecordingUnpaused, this, &OBSBasicControls::RecordingUnpaused);
&OBSBasicControls::RecordingPaused); connect(main, &OBSBasic::RecordingStopping, this, &OBSBasicControls::RecordingStopping);
connect(main, &OBSBasic::RecordingUnpaused, this, connect(main, &OBSBasic::RecordingStopped, this, &OBSBasicControls::RecordingStopped);
&OBSBasicControls::RecordingUnpaused);
connect(main, &OBSBasic::RecordingStopping, this,
&OBSBasicControls::RecordingStopping);
connect(main, &OBSBasic::RecordingStopped, this,
&OBSBasicControls::RecordingStopped);
connect(main, &OBSBasic::ReplayBufStarted, this, connect(main, &OBSBasic::ReplayBufStarted, this, &OBSBasicControls::ReplayBufferStarted);
&OBSBasicControls::ReplayBufferStarted); connect(main, &OBSBasic::ReplayBufStopping, this, &OBSBasicControls::ReplayBufferStopping);
connect(main, &OBSBasic::ReplayBufStopping, this, connect(main, &OBSBasic::ReplayBufStopped, this, &OBSBasicControls::ReplayBufferStopped);
&OBSBasicControls::ReplayBufferStopping);
connect(main, &OBSBasic::ReplayBufStopped, this,
&OBSBasicControls::ReplayBufferStopped);
connect(main, &OBSBasic::VirtualCamStarted, this, connect(main, &OBSBasic::VirtualCamStarted, this, &OBSBasicControls::VirtualCamStarted);
&OBSBasicControls::VirtualCamStarted); connect(main, &OBSBasic::VirtualCamStopped, this, &OBSBasicControls::VirtualCamStopped);
connect(main, &OBSBasic::VirtualCamStopped, this,
&OBSBasicControls::VirtualCamStopped);
connect(main, &OBSBasic::PreviewProgramModeChanged, this, connect(main, &OBSBasic::PreviewProgramModeChanged, this, &OBSBasicControls::UpdateStudioModeState);
&OBSBasicControls::UpdateStudioModeState);
/* Set up enablement connection */ /* Set up enablement connection */
connect(main, &OBSBasic::BroadcastFlowEnabled, this, connect(main, &OBSBasic::BroadcastFlowEnabled, this, &OBSBasicControls::EnableBroadcastFlow);
&OBSBasicControls::EnableBroadcastFlow); connect(main, &OBSBasic::ReplayBufEnabled, this, &OBSBasicControls::EnableReplayBufferButtons);
connect(main, &OBSBasic::ReplayBufEnabled, this, connect(main, &OBSBasic::VirtualCamEnabled, this, &OBSBasicControls::EnableVirtualCamButtons);
&OBSBasicControls::EnableReplayBufferButtons);
connect(main, &OBSBasic::VirtualCamEnabled, this,
&OBSBasicControls::EnableVirtualCamButtons);
} }
void OBSBasicControls::StreamingPreparing() void OBSBasicControls::StreamingPreparing()
@ -153,8 +112,7 @@ void OBSBasicControls::StreamingStarting(bool broadcastAutoStart)
// well, we need to disable button while stream is not active // well, we need to disable button while stream is not active
ui->broadcastButton->setEnabled(false); ui->broadcastButton->setEnabled(false);
ui->broadcastButton->setText( ui->broadcastButton->setText(QTStr("Basic.Main.StartBroadcast"));
QTStr("Basic.Main.StartBroadcast"));
ui->broadcastButton->setProperty("broadcastState", "ready"); ui->broadcastButton->setProperty("broadcastState", "ready");
ui->broadcastButton->style()->unpolish(ui->broadcastButton); ui->broadcastButton->style()->unpolish(ui->broadcastButton);
@ -209,9 +167,7 @@ void OBSBasicControls::BroadcastStreamActive()
void OBSBasicControls::BroadcastStreamStarted(bool autoStop) void OBSBasicControls::BroadcastStreamStarted(bool autoStop)
{ {
ui->broadcastButton->setText( ui->broadcastButton->setText(QTStr(autoStop ? "Basic.Main.AutoStopEnabled" : "Basic.Main.StopBroadcast"));
QTStr(autoStop ? "Basic.Main.AutoStopEnabled"
: "Basic.Main.StopBroadcast"));
if (autoStop) if (autoStop)
ui->broadcastButton->setEnabled(false); ui->broadcastButton->setEnabled(false);
@ -276,8 +232,7 @@ void OBSBasicControls::ReplayBufferStarted()
void OBSBasicControls::ReplayBufferStopping() void OBSBasicControls::ReplayBufferStopping()
{ {
ui->replayBufferButton->setText( ui->replayBufferButton->setText(QTStr("Basic.Main.StoppingReplayBuffer"));
QTStr("Basic.Main.StoppingReplayBuffer"));
} }
void OBSBasicControls::ReplayBufferStopped() void OBSBasicControls::ReplayBufferStopped()

View File

@ -56,16 +56,12 @@ void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
if (!currentSceneSource) if (!currentSceneSource)
return; return;
std::string scene_uuid = obs_source_get_uuid(currentSceneSource); std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
auto undo_redo = [scene_uuid = std::move(scene_uuid), auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
main](const std::string &data) { OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
OBSDataAutoRelease settings = OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
obs_data_create_from_json(data.c_str());
OBSSourceAutoRelease source = obs_get_source_by_uuid(
obs_data_get_string(settings, "undo_suuid"));
obs_source_reset_settings(source, settings); obs_source_reset_settings(source, settings);
OBSSourceAutoRelease scene_source = OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
obs_get_source_by_uuid(scene_uuid.c_str());
main->SetCurrentScene(scene_source.Get(), true); main->SetCurrentScene(scene_source.Get(), true);
main->UpdateContextBar(); main->UpdateContextBar();
@ -74,17 +70,14 @@ void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
OBSDataAutoRelease new_settings = obs_data_create(); OBSDataAutoRelease new_settings = obs_data_create();
OBSDataAutoRelease curr_settings = obs_source_get_settings(source); OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
obs_data_apply(new_settings, curr_settings); obs_data_apply(new_settings, curr_settings);
obs_data_set_string(new_settings, "undo_suuid", obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
obs_source_get_uuid(source));
std::string undo_data(obs_data_get_json(oldData)); std::string undo_data(obs_data_get_json(oldData));
std::string redo_data(obs_data_get_json(new_settings)); std::string redo_data(obs_data_get_json(new_settings));
if (undo_data.compare(redo_data) != 0) if (undo_data.compare(redo_data) != 0)
main->undo_s.add_action( main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
QTStr("Undo.Properties") undo_data, redo_data, repeatable);
.arg(obs_source_get_name(source)),
undo_redo, undo_redo, undo_data, redo_data, repeatable);
oldData = nullptr; oldData = nullptr;
} }
@ -122,8 +115,7 @@ ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
ComboSelectToolbar::~ComboSelectToolbar() {} ComboSelectToolbar::~ComboSelectToolbar() {}
static int FillPropertyCombo(QComboBox *c, obs_property_t *p, static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
const std::string &cur_id, bool is_int = false)
{ {
size_t count = obs_property_list_item_count(p); size_t count = obs_property_list_item_count(p);
int cur_idx = -1; int cur_idx = -1;
@ -148,8 +140,7 @@ static int FillPropertyCombo(QComboBox *c, obs_property_t *p,
return cur_idx; return cur_idx;
} }
void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
obs_properties_t *props,
const char *prop_name, bool is_int) const char *prop_name, bool is_int)
{ {
std::string cur_id; std::string cur_id;
@ -168,9 +159,7 @@ void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source,
if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) { if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
if (cur_idx == -1) { if (cur_idx == -1) {
combo->insertItem( combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
0,
QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
cur_idx = 0; cur_idx = 0;
} }
@ -188,12 +177,10 @@ void ComboSelectToolbar::Init()
return; return;
} }
UpdateSourceComboToolbarProperties(ui->device, source, props.get(), UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
prop_name, is_int);
} }
void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
const char *prop_name, bool is_int)
{ {
QString id = combo->itemData(idx).toString(); QString id = combo->itemData(idx).toString();
@ -214,28 +201,22 @@ void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
} }
SaveOldProperties(source); SaveOldProperties(source);
UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
is_int);
SetUndoProperties(source); SetUndoProperties(source);
} }
AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
: ComboSelectToolbar(parent, source)
{
}
void AudioCaptureToolbar::Init() void AudioCaptureToolbar::Init()
{ {
delete ui->activateButton; delete ui->activateButton;
ui->activateButton = nullptr; ui->activateButton = nullptr;
obs_module_t *mod = obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
if (!mod) if (!mod)
return; return;
const char *device_str = const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
get_os_text(mod, "Device", "CoreAudio.Device", "Device");
ui->deviceLabel->setText(device_str); ui->deviceLabel->setText(device_str);
prop_name = "device_id"; prop_name = "device_id";
@ -243,23 +224,18 @@ void AudioCaptureToolbar::Init()
ComboSelectToolbar::Init(); ComboSelectToolbar::Init();
} }
WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
: ComboSelectToolbar(parent, source)
{
}
void WindowCaptureToolbar::Init() void WindowCaptureToolbar::Init()
{ {
delete ui->activateButton; delete ui->activateButton;
ui->activateButton = nullptr; ui->activateButton = nullptr;
obs_module_t *mod = obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
get_os_module("win-capture", "mac-capture", "linux-capture");
if (!mod) if (!mod)
return; return;
const char *device_str = get_os_text(mod, "WindowCapture.Window", const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
"WindowUtils.Window", "Window");
ui->deviceLabel->setText(device_str); ui->deviceLabel->setText(device_str);
#if !defined(_WIN32) && !defined(__APPLE__) //linux #if !defined(_WIN32) && !defined(__APPLE__) //linux
@ -275,8 +251,7 @@ void WindowCaptureToolbar::Init()
ComboSelectToolbar::Init(); ComboSelectToolbar::Init();
} }
ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
OBSSource source)
: ComboSelectToolbar(parent, source) : ComboSelectToolbar(parent, source)
{ {
} }
@ -295,23 +270,18 @@ void ApplicationAudioCaptureToolbar::Init()
ComboSelectToolbar::Init(); ComboSelectToolbar::Init();
} }
DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
: ComboSelectToolbar(parent, source)
{
}
void DisplayCaptureToolbar::Init() void DisplayCaptureToolbar::Init()
{ {
delete ui->activateButton; delete ui->activateButton;
ui->activateButton = nullptr; ui->activateButton = nullptr;
obs_module_t *mod = obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
get_os_module("win-capture", "mac-capture", "linux-capture");
if (!mod) if (!mod)
return; return;
const char *device_str = const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
ui->deviceLabel->setText(device_str); ui->deviceLabel->setText(device_str);
#ifdef _WIN32 #ifdef _WIN32
@ -399,8 +369,7 @@ GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
return; return;
ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode")); ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
ui->windowLabel->setText( ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
obs_module_get_locale_text(mod, "WindowCapture.Window"));
OBSDataAutoRelease settings = obs_source_get_settings(source); OBSDataAutoRelease settings = obs_source_get_settings(source);
std::string cur_mode = obs_data_get_string(settings, "capture_mode"); std::string cur_mode = obs_data_get_string(settings, "capture_mode");
@ -522,8 +491,7 @@ void ImageSourceToolbar::on_browse_clicked()
static inline QColor color_from_int(long long val) static inline QColor color_from_int(long long val)
{ {
return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
(val >> 24) & 0xff);
} }
static inline long long color_to_int(QColor color) static inline long long color_to_int(QColor color)
@ -532,8 +500,7 @@ static inline long long color_to_int(QColor color)
return ((val & 0xff) << shift); return ((val & 0xff) << shift);
}; };
return shift(color.red(), 0) | shift(color.green(), 8) | return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
shift(color.blue(), 16) | shift(color.alpha(), 24);
} }
ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source) ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
@ -557,12 +524,9 @@ void ColorSourceToolbar::UpdateColor()
ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel); ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
ui->color->setText(color.name(QColor::HexRgb)); ui->color->setText(color.name(QColor::HexRgb));
ui->color->setPalette(palette); ui->color->setPalette(palette);
ui->color->setStyleSheet( ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
QString("background-color :%1; color: %2;") .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
.arg(palette.color(QPalette::Window) .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
.name(QColor::HexRgb))
.arg(palette.color(QPalette::WindowText)
.name(QColor::HexRgb)));
ui->color->setAutoFillBackground(true); ui->color->setAutoFillBackground(true);
ui->color->setAlignment(Qt::AlignCenter); ui->color->setAlignment(Qt::AlignCenter);
} }
@ -616,25 +580,20 @@ TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
const char *id = obs_source_get_unversioned_id(source); const char *id = obs_source_get_unversioned_id(source);
bool ft2 = strcmp(id, "text_ft2_source") == 0; bool ft2 = strcmp(id, "text_ft2_source") == 0;
bool read_from_file = obs_data_get_bool( bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
settings, ft2 ? "from_file" : "read_from_file");
OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font"); OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
MakeQFont(font_obj, font); MakeQFont(font_obj, font);
// Use "color1" if it's a freetype source and "color" elsewise // Use "color1" if it's a freetype source and "color" elsewise
unsigned int val = (unsigned int)obs_data_get_int( unsigned int val = (unsigned int)obs_data_get_int(
settings, settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
(strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0)
? "color1"
: "color");
color = color_from_int(val); color = color_from_int(val);
const char *text = obs_data_get_string(settings, "text"); const char *text = obs_data_get_string(settings, "text");
bool single_line = !read_from_file && bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
(!text || (strchr(text, '\n') == nullptr));
ui->emptySpace->setVisible(!single_line); ui->emptySpace->setVisible(!single_line);
ui->text->setVisible(single_line); ui->text->setVisible(single_line);
if (single_line) if (single_line)
@ -658,10 +617,8 @@ void TextSourceToolbar::on_selectFont_clicked()
options = QFontDialog::DontUseNativeDialog; options = QFontDialog::DontUseNativeDialog;
#endif #endif
font = QFontDialog::getFont( font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
&success, font, this, options);
QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
options);
if (!success) { if (!success) {
return; return;
} }
@ -695,11 +652,9 @@ void TextSourceToolbar::on_selectColor_clicked()
return; return;
} }
bool freetype = bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
obs_property_t *p = obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
obs_properties_get(props.get(), freetype ? "color1" : "color");
const char *desc = obs_property_description(p); const char *desc = obs_property_description(p);

View File

@ -18,8 +18,7 @@ class SourceToolbar : public QWidget {
protected: protected:
using properties_delete_t = decltype(&obs_properties_destroy); using properties_delete_t = decltype(&obs_properties_destroy);
using properties_t = using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
std::unique_ptr<obs_properties_t, properties_delete_t>;
properties_t props; properties_t props;
OBSDataAutoRelease oldData; OBSDataAutoRelease oldData;

View File

@ -20,8 +20,7 @@
#include <graphics/vec4.h> #include <graphics/vec4.h>
#include <graphics/matrix4.h> #include <graphics/matrix4.h>
static inline void GetScaleAndCenterPos(int baseCX, int baseCY, int windowCX, static inline void GetScaleAndCenterPos(int baseCX, int baseCY, int windowCX, int windowCY, int &x, int &y,
int windowCY, int &x, int &y,
float &scale) float &scale)
{ {
double windowAspect, baseAspect; double windowAspect, baseAspect;
@ -44,9 +43,8 @@ static inline void GetScaleAndCenterPos(int baseCX, int baseCY, int windowCX,
y = windowCY / 2 - newCY / 2; y = windowCY / 2 - newCY / 2;
} }
static inline void GetCenterPosFromFixedScale(int baseCX, int baseCY, static inline void GetCenterPosFromFixedScale(int baseCX, int baseCY, int windowCX, int windowCY, int &x, int &y,
int windowCX, int windowCY, float scale)
int &x, int &y, float scale)
{ {
x = (float(windowCX) - float(baseCX) * scale) / 2.0f; x = (float(windowCX) - float(baseCX) * scale) / 2.0f;
y = (float(windowCY) - float(baseCY) * scale) / 2.0f; y = (float(windowCY) - float(baseCY) * scale) / 2.0f;
@ -65,12 +63,9 @@ static inline QSize GetPixelSize(QWidget *widget)
#define GRAPHICS_SAFE_PERCENT 0.05f // 5.0% #define GRAPHICS_SAFE_PERCENT 0.05f // 5.0%
#define FOURBYTHREE_SAFE_PERCENT 0.1625f // 16.25% #define FOURBYTHREE_SAFE_PERCENT 0.1625f // 16.25%
static inline void InitSafeAreas(gs_vertbuffer_t **actionSafeMargin, static inline void InitSafeAreas(gs_vertbuffer_t **actionSafeMargin, gs_vertbuffer_t **graphicsSafeMargin,
gs_vertbuffer_t **graphicsSafeMargin, gs_vertbuffer_t **fourByThreeSafeMargin, gs_vertbuffer_t **leftLine,
gs_vertbuffer_t **fourByThreeSafeMargin, gs_vertbuffer_t **topLine, gs_vertbuffer_t **rightLine)
gs_vertbuffer_t **leftLine,
gs_vertbuffer_t **topLine,
gs_vertbuffer_t **rightLine)
{ {
obs_enter_graphics(); obs_enter_graphics();

View File

@ -26,8 +26,7 @@ extern "C" {
using namespace std; using namespace std;
vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format, vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format, bool ignore_compatibility)
bool ignore_compatibility)
{ {
vector<FFmpegCodec> codecs; vector<FFmpegCodec> codecs;
const AVCodec *codec; const AVCodec *codec;
@ -38,8 +37,7 @@ vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format,
if (!av_codec_is_encoder(codec)) if (!av_codec_is_encoder(codec))
continue; continue;
// Skip if not supported and compatibility check not disabled // Skip if not supported and compatibility check not disabled
if (!ignore_compatibility && if (!ignore_compatibility && !av_codec_get_tag(format.codec_tags, codec->id)) {
!av_codec_get_tag(format.codec_tags, codec->id)) {
continue; continue;
} }
@ -82,8 +80,7 @@ vector<FFmpegFormat> GetSupportedFormats()
FFmpegCodec FFmpegFormat::GetDefaultEncoder(FFmpegCodecType codec_type) const FFmpegCodec FFmpegFormat::GetDefaultEncoder(FFmpegCodecType codec_type) const
{ {
const AVCodecID codec_id = codec_type == VIDEO ? video_codec const AVCodecID codec_id = codec_type == VIDEO ? video_codec : audio_codec;
: audio_codec;
if (codec_type == UNKNOWN || codec_id == AV_CODEC_ID_NONE) if (codec_type == UNKNOWN || codec_id == AV_CODEC_ID_NONE)
return {}; return {};
@ -100,23 +97,19 @@ bool FFCodecAndFormatCompatible(const char *codec, const char *format)
if (!codec || !format) if (!codec || !format)
return false; return false;
const AVOutputFormat *output_format = const AVOutputFormat *output_format = av_guess_format(format, nullptr, nullptr);
av_guess_format(format, nullptr, nullptr);
if (!output_format) if (!output_format)
return false; return false;
const AVCodecDescriptor *codec_desc = const AVCodecDescriptor *codec_desc = avcodec_descriptor_get_by_name(codec);
avcodec_descriptor_get_by_name(codec);
if (!codec_desc) if (!codec_desc)
return false; return false;
return avformat_query_codec(output_format, codec_desc->id, return avformat_query_codec(output_format, codec_desc->id, FF_COMPLIANCE_NORMAL) == 1;
FF_COMPLIANCE_NORMAL) == 1;
} }
static const unordered_set<string> builtin_codecs = { static const unordered_set<string> builtin_codecs = {
"h264", "hevc", "av1", "prores", "aac", "opus", "h264", "hevc", "av1", "prores", "aac", "opus", "alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le",
"alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le",
}; };
bool IsBuiltinCodec(const char *codec) bool IsBuiltinCodec(const char *codec)

View File

@ -114,10 +114,7 @@ struct FFmpegCodec {
{ {
} }
FFmpegCodec(const AVCodec *codec) FFmpegCodec(const AVCodec *codec) : name(codec->name), long_name(codec->long_name), id(codec->id)
: name(codec->name),
long_name(codec->long_name),
id(codec->id)
{ {
switch (codec->type) { switch (codec->type) {
case AVMEDIA_TYPE_AUDIO: case AVMEDIA_TYPE_AUDIO:
@ -142,10 +139,8 @@ struct FFmpegCodec {
Q_DECLARE_METATYPE(FFmpegCodec) Q_DECLARE_METATYPE(FFmpegCodec)
std::vector<FFmpegFormat> GetSupportedFormats(); std::vector<FFmpegFormat> GetSupportedFormats();
std::vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format, std::vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format, bool ignore_compatibility);
bool ignore_compatibility);
bool FFCodecAndFormatCompatible(const char *codec, const char *format); bool FFCodecAndFormatCompatible(const char *codec, const char *format);
bool IsBuiltinCodec(const char *codec); bool IsBuiltinCodec(const char *codec);
bool ContainerSupportsCodec(const std::string &container, bool ContainerSupportsCodec(const std::string &container, const std::string &codec);
const std::string &codec);

View File

@ -15,8 +15,7 @@ void FocusList::dragMoveEvent(QDragMoveEvent *event)
QPoint pos = event->position().toPoint(); QPoint pos = event->position().toPoint();
int itemRow = row(itemAt(pos)); int itemRow = row(itemAt(pos));
if ((itemRow == currentRow() + 1) || if ((itemRow == currentRow() + 1) || (currentRow() == count() - 1 && itemRow == -1))
(currentRow() == count() - 1 && itemRow == -1))
event->ignore(); event->ignore();
else else
QListWidget::dragMoveEvent(event); QListWidget::dragMoveEvent(event);

View File

@ -46,46 +46,36 @@ void AJAOutputUI::SetupPropertiesView()
obs_data_apply(settings, data); obs_data_apply(settings, data);
} else { } else {
// apply default settings // apply default settings
obs_data_set_default_int( obs_data_set_default_int(settings, kUIPropOutput.id, static_cast<long long>(IOSelection::Invalid));
settings, kUIPropOutput.id, obs_data_set_default_int(settings, kUIPropVideoFormatSelect.id,
static_cast<long long>(IOSelection::Invalid)); static_cast<long long>(kDefaultAJAVideoFormat));
obs_data_set_default_int( obs_data_set_default_int(settings, kUIPropPixelFormatSelect.id,
settings, kUIPropVideoFormatSelect.id, static_cast<long long>(kDefaultAJAPixelFormat));
static_cast<long long>(kDefaultAJAVideoFormat)); obs_data_set_default_int(settings, kUIPropSDITransport.id,
obs_data_set_default_int( static_cast<long long>(kDefaultAJASDITransport));
settings, kUIPropPixelFormatSelect.id, obs_data_set_default_int(settings, kUIPropSDITransport4K.id,
static_cast<long long>(kDefaultAJAPixelFormat)); static_cast<long long>(kDefaultAJASDITransport4K));
obs_data_set_default_int(
settings, kUIPropSDITransport.id,
static_cast<long long>(kDefaultAJASDITransport));
obs_data_set_default_int(
settings, kUIPropSDITransport4K.id,
static_cast<long long>(kDefaultAJASDITransport4K));
} }
// Assign an ID to the program output plugin instance for channel usage tracking // Assign an ID to the program output plugin instance for channel usage tracking
obs_data_set_string(settings, kUIPropAJAOutputID.id, kProgramOutputID); obs_data_set_string(settings, kUIPropAJAOutputID.id, kProgramOutputID);
propertiesView = new OBSPropertiesView( propertiesView =
settings, "aja_output", new OBSPropertiesView(settings, "aja_output", (PropertiesReloadCallback)obs_get_output_properties, 170);
(PropertiesReloadCallback)obs_get_output_properties, 170);
ui->propertiesLayout->addWidget(propertiesView); ui->propertiesLayout->addWidget(propertiesView);
obs_data_release(settings); obs_data_release(settings);
connect(propertiesView, &OBSPropertiesView::Changed, this, connect(propertiesView, &OBSPropertiesView::Changed, this, &AJAOutputUI::PropertiesChanged);
&AJAOutputUI::PropertiesChanged);
} }
void AJAOutputUI::SaveSettings(const char *filename, obs_data_t *settings) void AJAOutputUI::SaveSettings(const char *filename, obs_data_t *settings)
{ {
BPtr<char> modulePath = BPtr<char> modulePath = obs_module_get_config_path(obs_current_module(), "");
obs_module_get_config_path(obs_current_module(), "");
os_mkdirs(modulePath); os_mkdirs(modulePath);
BPtr<char> path = BPtr<char> path = obs_module_get_config_path(obs_current_module(), filename);
obs_module_get_config_path(obs_current_module(), filename);
if (settings) if (settings)
obs_data_save_json_safe(settings, path, "tmp", "bak"); obs_data_save_json_safe(settings, path, "tmp", "bak");
@ -103,35 +93,27 @@ void AJAOutputUI::SetupPreviewPropertiesView()
obs_data_apply(settings, data); obs_data_apply(settings, data);
} else { } else {
// apply default settings // apply default settings
obs_data_set_default_int( obs_data_set_default_int(settings, kUIPropOutput.id, static_cast<long long>(IOSelection::Invalid));
settings, kUIPropOutput.id, obs_data_set_default_int(settings, kUIPropVideoFormatSelect.id,
static_cast<long long>(IOSelection::Invalid)); static_cast<long long>(kDefaultAJAVideoFormat));
obs_data_set_default_int( obs_data_set_default_int(settings, kUIPropPixelFormatSelect.id,
settings, kUIPropVideoFormatSelect.id, static_cast<long long>(kDefaultAJAPixelFormat));
static_cast<long long>(kDefaultAJAVideoFormat)); obs_data_set_default_int(settings, kUIPropSDITransport.id,
obs_data_set_default_int( static_cast<long long>(kDefaultAJASDITransport));
settings, kUIPropPixelFormatSelect.id, obs_data_set_default_int(settings, kUIPropSDITransport4K.id,
static_cast<long long>(kDefaultAJAPixelFormat)); static_cast<long long>(kDefaultAJASDITransport4K));
obs_data_set_default_int(
settings, kUIPropSDITransport.id,
static_cast<long long>(kDefaultAJASDITransport));
obs_data_set_default_int(
settings, kUIPropSDITransport4K.id,
static_cast<long long>(kDefaultAJASDITransport4K));
} }
// Assign an ID to the program output plugin instance for channel usage tracking // Assign an ID to the program output plugin instance for channel usage tracking
obs_data_set_string(settings, kUIPropAJAOutputID.id, kPreviewOutputID); obs_data_set_string(settings, kUIPropAJAOutputID.id, kPreviewOutputID);
previewPropertiesView = new OBSPropertiesView( previewPropertiesView =
settings, "aja_output", new OBSPropertiesView(settings, "aja_output", (PropertiesReloadCallback)obs_get_output_properties, 170);
(PropertiesReloadCallback)obs_get_output_properties, 170);
ui->previewPropertiesLayout->addWidget(previewPropertiesView); ui->previewPropertiesLayout->addWidget(previewPropertiesView);
obs_data_release(settings); obs_data_release(settings);
connect(previewPropertiesView, &OBSPropertiesView::Changed, this, connect(previewPropertiesView, &OBSPropertiesView::Changed, this, &AJAOutputUI::PreviewPropertiesChanged);
&AJAOutputUI::PreviewPropertiesChanged);
} }
void AJAOutputUI::on_outputButton_clicked() void AJAOutputUI::on_outputButton_clicked()
@ -160,15 +142,13 @@ void AJAOutputUI::OutputStateChanged(bool active)
void AJAOutputUI::on_previewOutputButton_clicked() void AJAOutputUI::on_previewOutputButton_clicked()
{ {
SaveSettings(kPreviewPropsFilename, SaveSettings(kPreviewPropsFilename, previewPropertiesView->GetSettings());
previewPropertiesView->GetSettings());
preview_output_toggle(); preview_output_toggle();
} }
void AJAOutputUI::PreviewPropertiesChanged() void AJAOutputUI::PreviewPropertiesChanged()
{ {
SaveSettings(kPreviewPropsFilename, SaveSettings(kPreviewPropsFilename, previewPropertiesView->GetSettings());
previewPropertiesView->GetSettings());
} }
void AJAOutputUI::PreviewOutputStateChanged(bool active) void AJAOutputUI::PreviewOutputStateChanged(bool active)
@ -205,15 +185,13 @@ static obs_properties_t *create_misc_props_ui(void *vp)
} }
obs_properties_t *props = obs_properties_create(); obs_properties_t *props = obs_properties_create();
obs_property_t *deviceList = obs_properties_add_list( obs_property_t *deviceList = obs_properties_add_list(props, kUIPropDevice.id,
props, kUIPropDevice.id, obs_module_text(kUIPropDevice.text), obs_module_text(kUIPropDevice.text), OBS_COMBO_TYPE_LIST,
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); OBS_COMBO_FORMAT_STRING);
obs_property_t *multiViewEnable = obs_properties_add_bool( obs_property_t *multiViewEnable =
props, kUIPropMultiViewEnable.id, obs_properties_add_bool(props, kUIPropMultiViewEnable.id, obs_module_text(kUIPropMultiViewEnable.text));
obs_module_text(kUIPropMultiViewEnable.text));
obs_property_t *multiViewAudioSources = obs_properties_add_list( obs_property_t *multiViewAudioSources = obs_properties_add_list(
props, kUIPropMultiViewAudioSource.id, props, kUIPropMultiViewAudioSource.id, obs_module_text(kUIPropMultiViewAudioSource.text),
obs_module_text(kUIPropMultiViewAudioSource.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_clear(deviceList); obs_property_list_clear(deviceList);
@ -222,12 +200,9 @@ static obs_properties_t *create_misc_props_ui(void *vp)
NTV2DeviceID firstDeviceID = DEVICE_ID_NOTFOUND; NTV2DeviceID firstDeviceID = DEVICE_ID_NOTFOUND;
populate_misc_device_list(deviceList, cardManager, firstDeviceID); populate_misc_device_list(deviceList, cardManager, firstDeviceID);
populate_multi_view_audio_sources(multiViewAudioSources, firstDeviceID); populate_multi_view_audio_sources(multiViewAudioSources, firstDeviceID);
obs_property_set_modified_callback2(deviceList, on_misc_device_selected, obs_property_set_modified_callback2(deviceList, on_misc_device_selected, cardManager);
cardManager); obs_property_set_modified_callback2(multiViewEnable, on_multi_view_toggle, cardManager);
obs_property_set_modified_callback2(multiViewEnable, obs_property_set_modified_callback2(multiViewAudioSources, on_multi_view_toggle, cardManager);
on_multi_view_toggle, cardManager);
obs_property_set_modified_callback2(multiViewAudioSources,
on_multi_view_toggle, cardManager);
outputUI->ui->label_3->setVisible(haveMultiView); outputUI->ui->label_3->setVisible(haveMultiView);
obs_property_set_visible(deviceList, haveMultiView); obs_property_set_visible(deviceList, haveMultiView);
@ -263,12 +238,10 @@ void AJAOutputUI::SetupMiscPropertiesView()
obs_data_apply(settings, data); obs_data_apply(settings, data);
} }
miscPropertiesView = new OBSPropertiesView( miscPropertiesView = new OBSPropertiesView(settings, this, (PropertiesReloadCallback)create_misc_props_ui,
settings, this, (PropertiesReloadCallback)create_misc_props_ui, nullptr, nullptr, 170);
nullptr, nullptr, 170);
ui->miscPropertiesLayout->addWidget(miscPropertiesView); ui->miscPropertiesLayout->addWidget(miscPropertiesView);
obs_data_release(settings); obs_data_release(settings);
connect(miscPropertiesView, &OBSPropertiesView::Changed, this, connect(miscPropertiesView, &OBSPropertiesView::Changed, this, &AJAOutputUI::MiscPropertiesChanged);
&AJAOutputUI::MiscPropertiesChanged);
} }

View File

@ -43,8 +43,7 @@ static struct preview_output context = {0};
OBSData load_settings(const char *filename) OBSData load_settings(const char *filename)
{ {
BPtr<char> path = BPtr<char> path = obs_module_get_config_path(obs_current_module(), filename);
obs_module_get_config_path(obs_current_module(), filename);
BPtr<char> jsonData = os_quick_read_utf8_file(path); BPtr<char> jsonData = os_quick_read_utf8_file(path);
if (!!jsonData) { if (!!jsonData) {
obs_data_t *data = obs_data_create_from_json(jsonData); obs_data_t *data = obs_data_create_from_json(jsonData);
@ -70,8 +69,7 @@ void output_start()
OBSData settings = load_settings(kProgramPropsFilename); OBSData settings = load_settings(kProgramPropsFilename);
if (settings != nullptr) { if (settings != nullptr) {
output = obs_output_create("aja_output", kProgramOutputID, output = obs_output_create("aja_output", kProgramOutputID, settings, NULL);
settings, NULL);
bool started = obs_output_start(output); bool started = obs_output_start(output);
obs_data_release(settings); obs_data_release(settings);
@ -122,8 +120,7 @@ void preview_output_start()
OBSData settings = load_settings(kPreviewPropsFilename); OBSData settings = load_settings(kPreviewPropsFilename);
if (settings != nullptr) { if (settings != nullptr) {
context.output = obs_output_create( context.output = obs_output_create("aja_output", kPreviewOutputID, settings, NULL);
"aja_output", kPreviewOutputID, settings, NULL);
obs_get_video_info(&context.ovi); obs_get_video_info(&context.ovi);
@ -132,12 +129,10 @@ void preview_output_start()
obs_enter_graphics(); obs_enter_graphics();
context.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE); context.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);
context.stagesurface = context.stagesurface = gs_stagesurface_create(width, height, GS_BGRA);
gs_stagesurface_create(width, height, GS_BGRA);
obs_leave_graphics(); obs_leave_graphics();
const video_output_info *mainVOI = const video_output_info *mainVOI = video_output_get_info(obs_get_video());
video_output_get_info(obs_get_video());
video_output_info vi = {0}; video_output_info vi = {0};
vi.format = VIDEO_FORMAT_BGRA; vi.format = VIDEO_FORMAT_BGRA;
@ -152,19 +147,15 @@ void preview_output_start()
video_output_open(&context.video_queue, &vi); video_output_open(&context.video_queue, &vi);
obs_frontend_add_event_callback(on_preview_scene_changed, obs_frontend_add_event_callback(on_preview_scene_changed, &context);
&context);
if (obs_frontend_preview_program_mode_active()) { if (obs_frontend_preview_program_mode_active()) {
context.current_source = context.current_source = obs_frontend_get_current_preview_scene();
obs_frontend_get_current_preview_scene();
} else { } else {
context.current_source = context.current_source = obs_frontend_get_current_scene();
obs_frontend_get_current_scene();
} }
obs_add_main_render_callback(render_preview_source, &context); obs_add_main_render_callback(render_preview_source, &context);
obs_output_set_media(context.output, context.video_queue, obs_output_set_media(context.output, context.video_queue, obs_get_audio());
obs_get_audio());
bool started = obs_output_start(context.output); bool started = obs_output_start(context.output);
obs_data_release(settings); obs_data_release(settings);
@ -185,18 +176,15 @@ void preview_output_toggle()
preview_output_start(); preview_output_start();
} }
void populate_misc_device_list(obs_property_t *list, void populate_misc_device_list(obs_property_t *list, aja::CardManager *cardManager, NTV2DeviceID &firstDeviceID)
aja::CardManager *cardManager,
NTV2DeviceID &firstDeviceID)
{ {
for (const auto &iter : *cardManager) { for (const auto &iter : *cardManager) {
if (!iter.second) if (!iter.second)
continue; continue;
if (firstDeviceID == DEVICE_ID_NOTFOUND) if (firstDeviceID == DEVICE_ID_NOTFOUND)
firstDeviceID = iter.second->GetDeviceID(); firstDeviceID = iter.second->GetDeviceID();
obs_property_list_add_string( obs_property_list_add_string(list, iter.second->GetDisplayName().c_str(),
list, iter.second->GetDisplayName().c_str(), iter.second->GetCardID().c_str());
iter.second->GetCardID().c_str());
} }
} }
@ -211,16 +199,13 @@ void populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id)
}; };
for (const auto &inp : kMultiViewAudioInputs) { for (const auto &inp : kMultiViewAudioInputs) {
if (NTV2DeviceCanDoInputSource(id, inp)) { if (NTV2DeviceCanDoInputSource(id, inp)) {
std::string inputSourceStr = std::string inputSourceStr = NTV2InputSourceToString(inp, true);
NTV2InputSourceToString(inp, true); obs_property_list_add_int(list, inputSourceStr.c_str(), (long long)inp);
obs_property_list_add_int(list, inputSourceStr.c_str(),
(long long)inp);
} }
} }
} }
bool on_misc_device_selected(void *data, obs_properties_t *props, bool on_misc_device_selected(void *data, obs_properties_t *props, obs_property_t *, obs_data_t *settings)
obs_property_t *, obs_data_t *settings)
{ {
const char *cardID = obs_data_get_string(settings, kUIPropDevice.id); const char *cardID = obs_data_get_string(settings, kUIPropDevice.id);
if (!cardID || !cardID[0]) if (!cardID || !cardID[0])
@ -234,10 +219,8 @@ bool on_misc_device_selected(void *data, obs_properties_t *props,
NTV2DeviceID deviceID = cardEntry->GetDeviceID(); NTV2DeviceID deviceID = cardEntry->GetDeviceID();
bool enableMultiViewUI = NTV2DeviceCanDoHDMIMultiView(deviceID); bool enableMultiViewUI = NTV2DeviceCanDoHDMIMultiView(deviceID);
obs_property_t *multiViewCheckbox = obs_property_t *multiViewCheckbox = obs_properties_get(props, kUIPropMultiViewEnable.id);
obs_properties_get(props, kUIPropMultiViewEnable.id); obs_property_t *multiViewAudioSource = obs_properties_get(props, kUIPropMultiViewAudioSource.id);
obs_property_t *multiViewAudioSource =
obs_properties_get(props, kUIPropMultiViewAudioSource.id);
populate_multi_view_audio_sources(multiViewAudioSource, deviceID); populate_multi_view_audio_sources(multiViewAudioSource, deviceID);
obs_property_set_enabled(multiViewCheckbox, enableMultiViewUI); obs_property_set_enabled(multiViewCheckbox, enableMultiViewUI);
obs_property_set_enabled(multiViewAudioSource, enableMultiViewUI); obs_property_set_enabled(multiViewAudioSource, enableMultiViewUI);
@ -249,8 +232,7 @@ static void toggle_multi_view(CNTV2Card *card, NTV2InputSource src, bool enable)
std::ostringstream oss; std::ostringstream oss;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
std::string datastream = std::to_string(i); std::string datastream = std::to_string(i);
oss << "sdi[" << datastream << "][0]->hdmi[0][" << datastream oss << "sdi[" << datastream << "][0]->hdmi[0][" << datastream << "];";
<< "];";
} }
NTV2DeviceID deviceId = card->GetDeviceID(); NTV2DeviceID deviceId = card->GetDeviceID();
@ -262,29 +244,19 @@ static void toggle_multi_view(CNTV2Card *card, NTV2InputSource src, bool enable)
if (enable) { if (enable) {
card->ApplySignalRoute(cnx, false); card->ApplySignalRoute(cnx, false);
if (NTV2DeviceCanDoAudioMixer(deviceId)) { if (NTV2DeviceCanDoAudioMixer(deviceId)) {
card->SetAudioMixerInputAudioSystem( card->SetAudioMixerInputAudioSystem(NTV2_AudioMixerInputMain, audioSys);
NTV2_AudioMixerInputMain, card->SetAudioMixerInputChannelSelect(NTV2_AudioMixerInputMain,
audioSys); NTV2_AudioChannel1_2);
card->SetAudioMixerInputChannelSelect( card->SetAudioMixerInputChannelsMute(NTV2_AudioMixerInputAux1,
NTV2_AudioMixerInputMain, NTV2AudioChannelsMuteAll);
NTV2_AudioChannel1_2); card->SetAudioMixerInputChannelsMute(NTV2_AudioMixerInputAux2,
card->SetAudioMixerInputChannelsMute( NTV2AudioChannelsMuteAll);
NTV2_AudioMixerInputAux1,
NTV2AudioChannelsMuteAll);
card->SetAudioMixerInputChannelsMute(
NTV2_AudioMixerInputAux2,
NTV2AudioChannelsMuteAll);
} }
card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_ON, card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_ON, audioSys);
audioSys); card->SetAudioOutputMonitorSource(NTV2_AudioChannel1_2, audioSys);
card->SetAudioOutputMonitorSource( card->SetHDMIOutAudioChannels(NTV2_HDMIAudio8Channels);
NTV2_AudioChannel1_2, audioSys); card->SetHDMIOutAudioSource2Channel(NTV2_AudioChannel1_2, audioSys);
card->SetHDMIOutAudioChannels( card->SetHDMIOutAudioSource8Channel(NTV2_AudioChannel1_8, audioSys);
NTV2_HDMIAudio8Channels);
card->SetHDMIOutAudioSource2Channel(
NTV2_AudioChannel1_2, audioSys);
card->SetHDMIOutAudioSource8Channel(
NTV2_AudioChannel1_8, audioSys);
} else { } else {
card->RemoveConnections(cnx); card->RemoveConnections(cnx);
} }
@ -292,14 +264,11 @@ static void toggle_multi_view(CNTV2Card *card, NTV2InputSource src, bool enable)
} }
} }
bool on_multi_view_toggle(void *data, obs_properties_t *, obs_property_t *, bool on_multi_view_toggle(void *data, obs_properties_t *, obs_property_t *, obs_data_t *settings)
obs_data_t *settings)
{ {
bool multiViewEnabled = bool multiViewEnabled = obs_data_get_bool(settings, kUIPropMultiViewEnable.id) && !main_output_running &&
obs_data_get_bool(settings, kUIPropMultiViewEnable.id) && !preview_output_running;
!main_output_running && !preview_output_running; const int audioInputSource = obs_data_get_int(settings, kUIPropMultiViewAudioSource.id);
const int audioInputSource =
obs_data_get_int(settings, kUIPropMultiViewAudioSource.id);
const char *cardID = obs_data_get_string(settings, kUIPropDevice.id); const char *cardID = obs_data_get_string(settings, kUIPropDevice.id);
if (!cardID || !cardID[0]) if (!cardID || !cardID[0])
return false; return false;
@ -359,8 +328,7 @@ void render_preview_source(void *param, uint32_t, uint32_t)
vec4_zero(&background); vec4_zero(&background);
gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0); gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0);
gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);
100.0f);
gs_blend_state_push(); gs_blend_state_push();
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
@ -371,24 +339,15 @@ void render_preview_source(void *param, uint32_t, uint32_t)
gs_texrender_end(ctx->texrender); gs_texrender_end(ctx->texrender);
struct video_frame output_frame; struct video_frame output_frame;
if (video_output_lock_frame(ctx->video_queue, &output_frame, 1, if (video_output_lock_frame(ctx->video_queue, &output_frame, 1, os_gettime_ns())) {
os_gettime_ns())) { gs_stage_texture(ctx->stagesurface, gs_texrender_get_texture(ctx->texrender));
gs_stage_texture(
ctx->stagesurface,
gs_texrender_get_texture(ctx->texrender));
if (gs_stagesurface_map(ctx->stagesurface, if (gs_stagesurface_map(ctx->stagesurface, &ctx->video_data, &ctx->video_linesize)) {
&ctx->video_data,
&ctx->video_linesize)) {
uint32_t linesize = output_frame.linesize[0]; uint32_t linesize = output_frame.linesize[0];
for (uint32_t i = 0; i < ctx->ovi.base_height; for (uint32_t i = 0; i < ctx->ovi.base_height; i++) {
i++) {
uint32_t dst_offset = linesize * i; uint32_t dst_offset = linesize * i;
uint32_t src_offset = uint32_t src_offset = ctx->video_linesize * i;
ctx->video_linesize * i; memcpy(output_frame.data[0] + dst_offset, ctx->video_data + src_offset,
memcpy(output_frame.data[0] +
dst_offset,
ctx->video_data + src_offset,
linesize); linesize);
} }
@ -403,8 +362,7 @@ void render_preview_source(void *param, uint32_t, uint32_t)
void addOutputUI(void) void addOutputUI(void)
{ {
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction("AJA I/O Device Output");
"AJA I/O Device Output");
QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window(); QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();
@ -423,20 +381,16 @@ static void OBSEvent(enum obs_frontend_event event, void *)
{ {
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
OBSData settings = load_settings(kProgramPropsFilename); OBSData settings = load_settings(kProgramPropsFilename);
if (settings && if (settings && obs_data_get_bool(settings, kUIPropAutoStartOutput.id))
obs_data_get_bool(settings, kUIPropAutoStartOutput.id))
output_start(); output_start();
OBSData previewSettings = load_settings(kPreviewPropsFilename); OBSData previewSettings = load_settings(kPreviewPropsFilename);
if (previewSettings && if (previewSettings && obs_data_get_bool(previewSettings, kUIPropAutoStartOutput.id))
obs_data_get_bool(previewSettings,
kUIPropAutoStartOutput.id))
preview_output_start(); preview_output_start();
OBSData miscSettings = load_settings(kMiscPropsFilename); OBSData miscSettings = load_settings(kMiscPropsFilename);
if (miscSettings && ajaOutputUI) { if (miscSettings && ajaOutputUI) {
on_multi_view_toggle(ajaOutputUI->GetCardManager(), on_multi_view_toggle(ajaOutputUI->GetCardManager(), nullptr, nullptr, miscSettings);
nullptr, nullptr, miscSettings);
} }
} else if (event == OBS_FRONTEND_EVENT_EXIT) { } else if (event == OBS_FRONTEND_EVENT_EXIT) {
if (main_output_running) if (main_output_running)
@ -459,16 +413,14 @@ bool obs_module_load(void)
CNTV2DeviceScanner scanner; CNTV2DeviceScanner scanner;
auto numDevices = scanner.GetNumDevices(); auto numDevices = scanner.GetNumDevices();
if (numDevices == 0) { if (numDevices == 0) {
blog(LOG_WARNING, blog(LOG_WARNING, "No AJA devices found, skipping loading AJA UI plugin");
"No AJA devices found, skipping loading AJA UI plugin");
return false; return false;
} }
// Signal to wait for AJA plugin to finish loading so we can access the CardManager instance // Signal to wait for AJA plugin to finish loading so we can access the CardManager instance
auto signal_handler = obs_get_signal_handler(); auto signal_handler = obs_get_signal_handler();
signal_handler_add(signal_handler, "void aja_loaded(ptr card_manager)"); signal_handler_add(signal_handler, "void aja_loaded(ptr card_manager)");
signal_handler_connect(signal_handler, "aja_loaded", aja_loaded, signal_handler_connect(signal_handler, "aja_loaded", aja_loaded, nullptr);
nullptr);
addOutputUI(); addOutputUI();

View File

@ -14,11 +14,7 @@ static const char *kMiscPropsFilename = "ajaMiscProps.json";
OBSData load_settings(const char *filename); OBSData load_settings(const char *filename);
void output_toggle(); void output_toggle();
void preview_output_toggle(); void preview_output_toggle();
void populate_misc_device_list(obs_property_t *list, void populate_misc_device_list(obs_property_t *list, aja::CardManager *cardManager, NTV2DeviceID &firstDeviceID);
aja::CardManager *cardManager,
NTV2DeviceID &firstDeviceID);
void populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id); void populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id);
bool on_misc_device_selected(void *data, obs_properties_t *props, bool on_misc_device_selected(void *data, obs_properties_t *props, obs_property_t *list, obs_data_t *settings);
obs_property_t *list, obs_data_t *settings); bool on_multi_view_toggle(void *data, obs_properties_t *props, obs_property_t *list, obs_data_t *settings);
bool on_multi_view_toggle(void *data, obs_properties_t *props,
obs_property_t *list, obs_data_t *settings);

View File

@ -22,9 +22,7 @@ obs_captions::obs_captions() {}
static obs_captions *captions = nullptr; static obs_captions *captions = nullptr;
DecklinkCaptionsUI::DecklinkCaptionsUI(QWidget *parent) DecklinkCaptionsUI::DecklinkCaptionsUI(QWidget *parent) : QDialog(parent), ui(new Ui_CaptionsDialog)
: QDialog(parent),
ui(new Ui_CaptionsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -50,11 +48,7 @@ DecklinkCaptionsUI::DecklinkCaptionsUI(QWidget *parent)
ui->source->blockSignals(true); ui->source->blockSignals(true);
ui->source->addItem(QStringLiteral("")); ui->source->addItem(QStringLiteral(""));
ui->source->setCurrentIndex(0); ui->source->setCurrentIndex(0);
obs_enum_sources( obs_enum_sources([](void *data, obs_source_t *source) { return (*static_cast<cb_t *>(data))(source); }, &cb);
[](void *data, obs_source_t *source) {
return (*static_cast<cb_t *>(data))(source);
},
&cb);
ui->source->blockSignals(false); ui->source->blockSignals(false);
} }
@ -68,13 +62,11 @@ void DecklinkCaptionsUI::on_source_currentIndexChanged(int)
captions->start(); captions->start();
} }
static void caption_callback(void * /* param */, obs_source_t * /* source */, static void caption_callback(void * /* param */, obs_source_t * /* source */, const struct obs_source_cea_708 *captions)
const struct obs_source_cea_708 *captions)
{ {
obs_output *output = obs_frontend_get_streaming_output(); obs_output *output = obs_frontend_get_streaming_output();
if (output) { if (output) {
if (obs_frontend_streaming_active() && if (obs_frontend_streaming_active() && obs_output_active(output)) {
obs_output_active(output)) {
obs_output_caption(output, captions); obs_output_caption(output, captions);
} }
obs_output_release(output); obs_output_release(output);
@ -95,32 +87,27 @@ void obs_captions::stop()
{ {
OBSSource s = OBSGetStrongRef(source); OBSSource s = OBSGetStrongRef(source);
if (s) if (s)
obs_source_remove_caption_callback(s, caption_callback, obs_source_remove_caption_callback(s, caption_callback, nullptr);
nullptr);
} }
static void save_decklink_caption_data(obs_data_t *save_data, bool saving, static void save_decklink_caption_data(obs_data_t *save_data, bool saving, void *)
void *)
{ {
if (saving) { if (saving) {
obs_data_t *obj = obs_data_create(); obs_data_t *obj = obs_data_create();
obs_data_set_string(obj, "source", obs_data_set_string(obj, "source", captions->source_name.c_str());
captions->source_name.c_str());
obs_data_set_obj(save_data, "decklink_captions", obj); obs_data_set_obj(save_data, "decklink_captions", obj);
obs_data_release(obj); obs_data_release(obj);
} else { } else {
captions->stop(); captions->stop();
obs_data_t *obj = obs_data_t *obj = obs_data_get_obj(save_data, "decklink_captions");
obs_data_get_obj(save_data, "decklink_captions");
if (!obj) if (!obj)
obj = obs_data_create(); obj = obs_data_create();
captions->source_name = obs_data_get_string(obj, "source"); captions->source_name = obs_data_get_string(obj, "source");
captions->source = captions->source = GetWeakSourceByName(captions->source_name.c_str());
GetWeakSourceByName(captions->source_name.c_str());
obs_data_release(obj); obs_data_release(obj);
captions->start(); captions->start();
@ -129,8 +116,7 @@ static void save_decklink_caption_data(obs_data_t *save_data, bool saving,
void addOutputUI(void) void addOutputUI(void)
{ {
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("Decklink Captions"));
obs_module_text("Decklink Captions"));
captions = new obs_captions; captions = new obs_captions;

View File

@ -4,9 +4,7 @@
#include <util/util.hpp> #include <util/util.hpp>
#include "decklink-ui-main.h" #include "decklink-ui-main.h"
DecklinkOutputUI::DecklinkOutputUI(QWidget *parent) DecklinkOutputUI::DecklinkOutputUI(QWidget *parent) : QDialog(parent), ui(new Ui_Output)
: QDialog(parent),
ui(new Ui_Output)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -37,26 +35,22 @@ void DecklinkOutputUI::SetupPropertiesView()
if (data) if (data)
obs_data_apply(settings, data); obs_data_apply(settings, data);
propertiesView = new OBSPropertiesView( propertiesView = new OBSPropertiesView(settings, "decklink_output",
settings, "decklink_output", (PropertiesReloadCallback)obs_get_output_properties, 170);
(PropertiesReloadCallback)obs_get_output_properties, 170);
ui->propertiesLayout->addWidget(propertiesView); ui->propertiesLayout->addWidget(propertiesView);
obs_data_release(settings); obs_data_release(settings);
connect(propertiesView, &OBSPropertiesView::Changed, this, connect(propertiesView, &OBSPropertiesView::Changed, this, &DecklinkOutputUI::PropertiesChanged);
&DecklinkOutputUI::PropertiesChanged);
} }
void DecklinkOutputUI::SaveSettings() void DecklinkOutputUI::SaveSettings()
{ {
BPtr<char> modulePath = BPtr<char> modulePath = obs_module_get_config_path(obs_current_module(), "");
obs_module_get_config_path(obs_current_module(), "");
os_mkdirs(modulePath); os_mkdirs(modulePath);
BPtr<char> path = obs_module_get_config_path( BPtr<char> path = obs_module_get_config_path(obs_current_module(), "decklinkOutputProps.json");
obs_current_module(), "decklinkOutputProps.json");
obs_data_t *settings = propertiesView->GetSettings(); obs_data_t *settings = propertiesView->GetSettings();
if (settings) if (settings)
@ -74,26 +68,22 @@ void DecklinkOutputUI::SetupPreviewPropertiesView()
if (data) if (data)
obs_data_apply(settings, data); obs_data_apply(settings, data);
previewPropertiesView = new OBSPropertiesView( previewPropertiesView = new OBSPropertiesView(settings, "decklink_output",
settings, "decklink_output", (PropertiesReloadCallback)obs_get_output_properties, 170);
(PropertiesReloadCallback)obs_get_output_properties, 170);
ui->previewPropertiesLayout->addWidget(previewPropertiesView); ui->previewPropertiesLayout->addWidget(previewPropertiesView);
obs_data_release(settings); obs_data_release(settings);
connect(previewPropertiesView, &OBSPropertiesView::Changed, this, connect(previewPropertiesView, &OBSPropertiesView::Changed, this, &DecklinkOutputUI::PreviewPropertiesChanged);
&DecklinkOutputUI::PreviewPropertiesChanged);
} }
void DecklinkOutputUI::SavePreviewSettings() void DecklinkOutputUI::SavePreviewSettings()
{ {
BPtr<char> modulePath = BPtr<char> modulePath = obs_module_get_config_path(obs_current_module(), "");
obs_module_get_config_path(obs_current_module(), "");
os_mkdirs(modulePath); os_mkdirs(modulePath);
BPtr<char> path = obs_module_get_config_path( BPtr<char> path = obs_module_get_config_path(obs_current_module(), "decklinkPreviewOutputProps.json");
obs_current_module(), "decklinkPreviewOutputProps.json");
obs_data_t *settings = previewPropertiesView->GetSettings(); obs_data_t *settings = previewPropertiesView->GetSettings();
if (settings) if (settings)

View File

@ -43,8 +43,7 @@ static struct decklink_ui_output context_preview = {0};
OBSData load_settings() OBSData load_settings()
{ {
BPtr<char> path = obs_module_get_config_path( BPtr<char> path = obs_module_get_config_path(obs_current_module(), "decklinkOutputProps.json");
obs_current_module(), "decklinkOutputProps.json");
BPtr<char> jsonData = os_quick_read_utf8_file(path); BPtr<char> jsonData = os_quick_read_utf8_file(path);
if (!!jsonData) { if (!!jsonData) {
obs_data_t *data = obs_data_create_from_json(jsonData); obs_data_t *data = obs_data_create_from_json(jsonData);
@ -90,10 +89,8 @@ void output_start()
OBSData settings = load_settings(); OBSData settings = load_settings();
if (settings != nullptr) { if (settings != nullptr) {
obs_output_t *const output = obs_output_create( obs_output_t *const output = obs_output_create("decklink_output", "decklink_output", settings, NULL);
"decklink_output", "decklink_output", settings, NULL); const struct video_scale_info *const conversion = obs_output_get_video_conversion(output);
const struct video_scale_info *const conversion =
obs_output_get_video_conversion(output);
if (conversion != nullptr) { if (conversion != nullptr) {
context.output = output; context.output = output;
obs_add_tick_callback(decklink_ui_tick, &context); obs_add_tick_callback(decklink_ui_tick, &context);
@ -105,11 +102,9 @@ void output_start()
obs_enter_graphics(); obs_enter_graphics();
context.texrender_premultiplied = nullptr; context.texrender_premultiplied = nullptr;
context.texrender = context.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);
gs_texrender_create(GS_BGRA, GS_ZS_NONE);
for (gs_stagesurf_t *&surf : context.stagesurfaces) for (gs_stagesurf_t *&surf : context.stagesurfaces)
surf = gs_stagesurface_create(width, height, surf = gs_stagesurface_create(width, height, GS_BGRA);
GS_BGRA);
obs_leave_graphics(); obs_leave_graphics();
for (bool &written : context.surf_written) for (bool &written : context.surf_written)
@ -131,12 +126,9 @@ void output_start()
video_output_open(&context.video_queue, &vi); video_output_open(&context.video_queue, &vi);
context.current_source = nullptr; context.current_source = nullptr;
obs_add_main_rendered_callback(decklink_ui_render, obs_add_main_rendered_callback(decklink_ui_render, &context);
&context);
obs_output_set_media(context.output, obs_output_set_media(context.output, context.video_queue, obs_get_audio());
context.video_queue,
obs_get_audio());
bool started = obs_output_start(context.output); bool started = obs_output_start(context.output);
main_output_running = started; main_output_running = started;
@ -162,8 +154,7 @@ void output_toggle()
OBSData load_preview_settings() OBSData load_preview_settings()
{ {
BPtr<char> path = obs_module_get_config_path( BPtr<char> path = obs_module_get_config_path(obs_current_module(), "decklinkPreviewOutputProps.json");
obs_current_module(), "decklinkPreviewOutputProps.json");
BPtr<char> jsonData = os_quick_read_utf8_file(path); BPtr<char> jsonData = os_quick_read_utf8_file(path);
if (!!jsonData) { if (!!jsonData) {
obs_data_t *data = obs_data_create_from_json(jsonData); obs_data_t *data = obs_data_create_from_json(jsonData);
@ -191,8 +182,7 @@ static void decklink_ui_tick(void *param, float /* sec */)
void preview_output_stop() void preview_output_stop()
{ {
obs_remove_main_rendered_callback(decklink_ui_render, &context_preview); obs_remove_main_rendered_callback(decklink_ui_render, &context_preview);
obs_frontend_remove_event_callback(on_preview_scene_changed, obs_frontend_remove_event_callback(on_preview_scene_changed, &context_preview);
&context_preview);
obs_output_stop(context_preview.output); obs_output_stop(context_preview.output);
obs_output_release(context_preview.output); obs_output_release(context_preview.output);
@ -224,14 +214,11 @@ void preview_output_start()
OBSData settings = load_preview_settings(); OBSData settings = load_preview_settings();
if (settings != nullptr) { if (settings != nullptr) {
obs_output_t *const output = obs_output_create( obs_output_t *const output = obs_output_create("decklink_output", "decklink_output", settings, NULL);
"decklink_output", "decklink_output", settings, NULL); const struct video_scale_info *const conversion = obs_output_get_video_conversion(output);
const struct video_scale_info *const conversion =
obs_output_get_video_conversion(output);
if (conversion != nullptr) { if (conversion != nullptr) {
context_preview.output = output; context_preview.output = output;
obs_add_tick_callback(decklink_ui_tick, obs_add_tick_callback(decklink_ui_tick, &context_preview);
&context_preview);
obs_get_video_info(&context_preview.ovi); obs_get_video_info(&context_preview.ovi);
@ -239,14 +226,10 @@ void preview_output_start()
const uint32_t height = conversion->height; const uint32_t height = conversion->height;
obs_enter_graphics(); obs_enter_graphics();
context_preview.texrender_premultiplied = context_preview.texrender_premultiplied = gs_texrender_create(GS_BGRA, GS_ZS_NONE);
gs_texrender_create(GS_BGRA, GS_ZS_NONE); context_preview.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);
context_preview.texrender = for (gs_stagesurf_t *&surf : context_preview.stagesurfaces)
gs_texrender_create(GS_BGRA, GS_ZS_NONE); surf = gs_stagesurface_create(width, height, GS_BGRA);
for (gs_stagesurf_t *&surf :
context_preview.stagesurfaces)
surf = gs_stagesurface_create(width, height,
GS_BGRA);
obs_leave_graphics(); obs_leave_graphics();
for (bool &written : context_preview.surf_written) for (bool &written : context_preview.surf_written)
@ -267,21 +250,15 @@ void preview_output_start()
video_output_open(&context_preview.video_queue, &vi); video_output_open(&context_preview.video_queue, &vi);
obs_frontend_add_event_callback( obs_frontend_add_event_callback(on_preview_scene_changed, &context_preview);
on_preview_scene_changed, &context_preview);
if (obs_frontend_preview_program_mode_active()) { if (obs_frontend_preview_program_mode_active()) {
context_preview.current_source = context_preview.current_source = obs_frontend_get_current_preview_scene();
obs_frontend_get_current_preview_scene();
} else { } else {
context_preview.current_source = context_preview.current_source = obs_frontend_get_current_scene();
obs_frontend_get_current_scene();
} }
obs_add_main_rendered_callback(decklink_ui_render, obs_add_main_rendered_callback(decklink_ui_render, &context_preview);
&context_preview);
obs_output_set_media(context_preview.output, obs_output_set_media(context_preview.output, context_preview.video_queue, obs_get_audio());
context_preview.video_queue,
obs_get_audio());
bool started = obs_output_start(context_preview.output); bool started = obs_output_start(context_preview.output);
preview_output_running = started; preview_output_running = started;
@ -356,8 +333,7 @@ static void decklink_ui_render(void *param)
width = obs_source_get_base_width(ctx->current_source); width = obs_source_get_base_width(ctx->current_source);
height = obs_source_get_base_height(ctx->current_source); height = obs_source_get_base_height(ctx->current_source);
gs_texrender_t *const texrender_premultiplied = gs_texrender_t *const texrender_premultiplied = ctx->texrender_premultiplied;
ctx->texrender_premultiplied;
if (!gs_texrender_begin(texrender_premultiplied, width, height)) if (!gs_texrender_begin(texrender_premultiplied, width, height))
return; return;
@ -365,8 +341,7 @@ static void decklink_ui_render(void *param)
vec4_zero(&background); vec4_zero(&background);
gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0); gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0);
gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);
100.0f);
gs_blend_state_push(); gs_blend_state_push();
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
@ -381,8 +356,7 @@ static void decklink_ui_render(void *param)
return; return;
} }
const struct video_scale_info *const conversion = const struct video_scale_info *const conversion = obs_output_get_video_conversion(ctx->output);
obs_output_get_video_conversion(ctx->output);
const uint32_t scaled_width = conversion->width; const uint32_t scaled_width = conversion->width;
const uint32_t scaled_height = conversion->height; const uint32_t scaled_height = conversion->height;
@ -390,23 +364,17 @@ static void decklink_ui_render(void *param)
return; return;
const bool previous = gs_framebuffer_srgb_enabled(); const bool previous = gs_framebuffer_srgb_enabled();
const bool source_hdr = (ctx->ovi.colorspace == VIDEO_CS_2100_PQ) || const bool source_hdr = (ctx->ovi.colorspace == VIDEO_CS_2100_PQ) || (ctx->ovi.colorspace == VIDEO_CS_2100_HLG);
(ctx->ovi.colorspace == VIDEO_CS_2100_HLG); const bool target_hdr = source_hdr && (conversion->colorspace == VIDEO_CS_2100_PQ);
const bool target_hdr = source_hdr &&
(conversion->colorspace == VIDEO_CS_2100_PQ);
gs_enable_framebuffer_srgb(!target_hdr); gs_enable_framebuffer_srgb(!target_hdr);
gs_enable_blending(false); gs_enable_blending(false);
gs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); gs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"), gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"), tex);
tex); const char *const tech_name = target_hdr ? "DrawAlphaDivideR10L"
const char *const tech_name = : (source_hdr ? "DrawAlphaDivideTonemap" : "DrawAlphaDivide");
target_hdr ? "DrawAlphaDivideR10L"
: (source_hdr ? "DrawAlphaDivideTonemap"
: "DrawAlphaDivide");
while (gs_effect_loop(effect, tech_name)) { while (gs_effect_loop(effect, tech_name)) {
gs_effect_set_float(gs_effect_get_param_by_name(effect, gs_effect_set_float(gs_effect_get_param_by_name(effect, "multiplier"),
"multiplier"),
obs_get_video_sdr_white_level() / 10000.f); obs_get_video_sdr_white_level() / 10000.f);
gs_draw_sprite(tex, 0, 0, 0); gs_draw_sprite(tex, 0, 0, 0);
} }
@ -417,28 +385,20 @@ static void decklink_ui_render(void *param)
gs_texrender_end(ctx->texrender); gs_texrender_end(ctx->texrender);
const size_t write_stage_index = ctx->stage_index; const size_t write_stage_index = ctx->stage_index;
gs_stage_texture(ctx->stagesurfaces[write_stage_index], gs_stage_texture(ctx->stagesurfaces[write_stage_index], gs_texrender_get_texture(ctx->texrender));
gs_texrender_get_texture(ctx->texrender));
ctx->surf_written[write_stage_index] = true; ctx->surf_written[write_stage_index] = true;
const size_t read_stage_index = const size_t read_stage_index = (write_stage_index + 1) % STAGE_BUFFER_COUNT;
(write_stage_index + 1) % STAGE_BUFFER_COUNT;
if (ctx->surf_written[read_stage_index]) { if (ctx->surf_written[read_stage_index]) {
struct video_frame output_frame; struct video_frame output_frame;
if (video_output_lock_frame(ctx->video_queue, &output_frame, 1, if (video_output_lock_frame(ctx->video_queue, &output_frame, 1, os_gettime_ns())) {
os_gettime_ns())) { gs_stagesurf_t *const read_surf = ctx->stagesurfaces[read_stage_index];
gs_stagesurf_t *const read_surf = if (gs_stagesurface_map(read_surf, &ctx->video_data, &ctx->video_linesize)) {
ctx->stagesurfaces[read_stage_index];
if (gs_stagesurface_map(read_surf, &ctx->video_data,
&ctx->video_linesize)) {
uint32_t linesize = output_frame.linesize[0]; uint32_t linesize = output_frame.linesize[0];
for (uint32_t i = 0; i < scaled_height; i++) { for (uint32_t i = 0; i < scaled_height; i++) {
uint32_t dst_offset = linesize * i; uint32_t dst_offset = linesize * i;
uint32_t src_offset = uint32_t src_offset = ctx->video_linesize * i;
ctx->video_linesize * i; memcpy(output_frame.data[0] + dst_offset, ctx->video_data + src_offset,
memcpy(output_frame.data[0] +
dst_offset,
ctx->video_data + src_offset,
linesize); linesize);
} }
@ -455,8 +415,7 @@ static void decklink_ui_render(void *param)
void addOutputUI(void) void addOutputUI(void)
{ {
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("Decklink Output"));
obs_module_text("Decklink Output"));
QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window(); QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();
@ -481,8 +440,7 @@ static void OBSEvent(enum obs_frontend_event event, void *)
OBSData previewSettings = load_preview_settings(); OBSData previewSettings = load_preview_settings();
if (previewSettings && if (previewSettings && obs_data_get_bool(previewSettings, "auto_start"))
obs_data_get_bool(previewSettings, "auto_start"))
preview_output_start(); preview_output_start();
} else if (event == OBS_FRONTEND_EVENT_EXIT) { } else if (event == OBS_FRONTEND_EVENT_EXIT) {
shutting_down = true; shutting_down = true;

View File

@ -39,8 +39,7 @@ void CleanupSceneSwitcher()
static bool ewmhIsSupported() static bool ewmhIsSupported()
{ {
Display *display = disp(); Display *display = disp();
Atom netSupportingWmCheck = Atom netSupportingWmCheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
Atom actualType; Atom actualType;
int format = 0; int format = 0;
unsigned long num = 0, bytes = 0; unsigned long num = 0, bytes = 0;
@ -53,10 +52,8 @@ static bool ewmhIsSupported()
return false; return false;
} }
int status = XGetWindowProperty(display, root_window, int status = XGetWindowProperty(display, root_window, netSupportingWmCheck, 0L, 1L, false, XA_WINDOW,
netSupportingWmCheck, 0L, 1L, false, &actualType, &format, &num, &bytes, &data);
XA_WINDOW, &actualType, &format, &num,
&bytes, &data);
if (status == Success) { if (status == Success) {
if (num > 0) { if (num > 0) {
@ -69,12 +66,9 @@ static bool ewmhIsSupported()
} }
if (ewmh_window) { if (ewmh_window) {
status = XGetWindowProperty(display, ewmh_window, status = XGetWindowProperty(display, ewmh_window, netSupportingWmCheck, 0L, 1L, false, XA_WINDOW,
netSupportingWmCheck, 0L, 1L, false, &actualType, &format, &num, &bytes, &data);
XA_WINDOW, &actualType, &format, if (status != Success || num == 0 || ewmh_window != ((Window *)data)[0]) {
&num, &bytes, &data);
if (status != Success || num == 0 ||
ewmh_window != ((Window *)data)[0]) {
ewmh_window = 0; ewmh_window = 0;
} }
if (status == Success && data) { if (status == Success && data) {
@ -107,10 +101,8 @@ static std::vector<Window> getTopLevelWindows()
continue; continue;
} }
int status = XGetWindowProperty(disp(), rootWin, netClList, 0L, int status = XGetWindowProperty(disp(), rootWin, netClList, 0L, ~0L, false, AnyPropertyType,
~0L, false, AnyPropertyType, &actualType, &format, &num, &bytes, (uint8_t **)&data);
&actualType, &format, &num,
&bytes, (uint8_t **)&data);
if (status != Success) { if (status != Success) {
continue; continue;
@ -141,8 +133,7 @@ static std::string GetWindowTitle(size_t i)
XFree(name); XFree(name);
} else { } else {
XTextProperty xtp_new_name; XTextProperty xtp_new_name;
if (XGetWMName(disp(), w, &xtp_new_name) != 0 && if (XGetWMName(disp(), w, &xtp_new_name) != 0 && xtp_new_name.value != nullptr) {
xtp_new_name.value != nullptr) {
std::string str((const char *)xtp_new_name.value); std::string str((const char *)xtp_new_name.value);
windowTitle = str; windowTitle = str;
XFree(xtp_new_name.value); XFree(xtp_new_name.value);
@ -180,8 +171,7 @@ void GetCurrentWindowTitle(string &title)
return; return;
} }
XGetWindowProperty(disp(), rootWin, active, 0L, ~0L, false, XGetWindowProperty(disp(), rootWin, active, 0L, ~0L, false, AnyPropertyType, &actualType, &format, &num, &bytes,
AnyPropertyType, &actualType, &format, &num, &bytes,
(uint8_t **)&data); (uint8_t **)&data);
if (!data[0]) { if (!data[0]) {
@ -194,8 +184,7 @@ void GetCurrentWindowTitle(string &title)
title = str; title = str;
} else { } else {
XTextProperty xtp_new_name; XTextProperty xtp_new_name;
if (XGetWMName(disp(), data[0], &xtp_new_name) != 0 && if (XGetWMName(disp(), data[0], &xtp_new_name) != 0 && xtp_new_name.value != nullptr) {
xtp_new_name.value != nullptr) {
std::string str((const char *)xtp_new_name.value); std::string str((const char *)xtp_new_name.value);
title = str; title = str;
XFree(xtp_new_name.value); XFree(xtp_new_name.value);

View File

@ -25,12 +25,7 @@ struct SceneSwitch {
string window; string window;
regex re; regex re;
inline SceneSwitch(OBSWeakSource scene_, const char *window_) inline SceneSwitch(OBSWeakSource scene_, const char *window_) : scene(scene_), window(window_), re(window_) {}
: scene(scene_),
window(window_),
re(window_)
{
}
}; };
static inline bool WeakSourceValid(obs_weak_source_t *ws) static inline bool WeakSourceValid(obs_weak_source_t *ws)
@ -73,15 +68,12 @@ struct SwitcherData {
static SwitcherData *switcher = nullptr; static SwitcherData *switcher = nullptr;
static inline QString MakeSwitchName(const QString &scene, static inline QString MakeSwitchName(const QString &scene, const QString &window)
const QString &window)
{ {
return QStringLiteral("[") + scene + QStringLiteral("]: ") + window; return QStringLiteral("[") + scene + QStringLiteral("]: ") + window;
} }
SceneSwitcher::SceneSwitcher(QWidget *parent) SceneSwitcher::SceneSwitcher(QWidget *parent) : QDialog(parent), ui(new Ui_SceneSwitcher)
: QDialog(parent),
ui(new Ui_SceneSwitcher)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -105,8 +97,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
else else
ui->noMatchDontSwitch->setChecked(true); ui->noMatchDontSwitch->setChecked(true);
ui->noMatchSwitchScene->setCurrentText( ui->noMatchSwitchScene->setCurrentText(GetWeakSourceName(switcher->nonMatchingScene).c_str());
GetWeakSourceName(switcher->nonMatchingScene).c_str());
ui->checkInterval->setValue(switcher->interval); ui->checkInterval->setValue(switcher->interval);
vector<string> windows; vector<string> windows;
@ -117,8 +108,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
for (auto &s : switcher->switches) { for (auto &s : switcher->switches) {
string sceneName = GetWeakSourceName(s.scene); string sceneName = GetWeakSourceName(s.scene);
QString text = QString text = MakeSwitchName(sceneName.c_str(), s.window.c_str());
MakeSwitchName(sceneName.c_str(), s.window.c_str());
QListWidgetItem *item = new QListWidgetItem(text, ui->switches); QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
item->setData(Qt::UserRole, s.window.c_str()); item->setData(Qt::UserRole, s.window.c_str());
@ -201,16 +191,13 @@ void SceneSwitcher::on_add_clicked()
if (idx == -1) { if (idx == -1) {
try { try {
lock_guard<mutex> lock(switcher->m); lock_guard<mutex> lock(switcher->m);
switcher->switches.emplace_back( switcher->switches.emplace_back(source, windowName.toUtf8().constData());
source, windowName.toUtf8().constData());
QListWidgetItem *item = QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
new QListWidgetItem(text, ui->switches);
item->setData(Qt::UserRole, v); item->setData(Qt::UserRole, v);
} catch (const regex_error &) { } catch (const regex_error &) {
QMessageBox::warning( QMessageBox::warning(this, obs_module_text("InvalidRegex.Title"),
this, obs_module_text("InvalidRegex.Title"), obs_module_text("InvalidRegex.Text"));
obs_module_text("InvalidRegex.Text"));
} }
} else { } else {
QListWidgetItem *item = ui->switches->item(idx); QListWidgetItem *item = ui->switches->item(idx);
@ -238,8 +225,7 @@ void SceneSwitcher::on_remove_clicked()
if (!item) if (!item)
return; return;
string window = string window = item->data(Qt::UserRole).toString().toUtf8().constData();
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
lock_guard<mutex> lock(switcher->m); lock_guard<mutex> lock(switcher->m);
@ -260,8 +246,7 @@ void SceneSwitcher::on_remove_clicked()
void SceneSwitcher::UpdateNonMatchingScene(const QString &name) void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
{ {
OBSSourceAutoRelease scene = OBSSourceAutoRelease scene = obs_get_source_by_name(name.toUtf8().constData());
obs_get_source_by_name(name.toUtf8().constData());
OBSWeakSourceAutoRelease ws = obs_source_get_weak_source(scene); OBSWeakSourceAutoRelease ws = obs_source_get_weak_source(scene);
switcher->nonMatchingScene = ws.Get(); switcher->nonMatchingScene = ws.Get();
@ -339,25 +324,20 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
for (SceneSwitch &s : switcher->switches) { for (SceneSwitch &s : switcher->switches) {
OBSDataAutoRelease array_obj = obs_data_create(); OBSDataAutoRelease array_obj = obs_data_create();
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_weak_source_get_source(s.scene);
obs_weak_source_get_source(s.scene);
if (source) { if (source) {
const char *n = obs_source_get_name(source); const char *n = obs_source_get_name(source);
obs_data_set_string(array_obj, "scene", n); obs_data_set_string(array_obj, "scene", n);
obs_data_set_string(array_obj, "window_title", obs_data_set_string(array_obj, "window_title", s.window.c_str());
s.window.c_str());
obs_data_array_push_back(array, array_obj); obs_data_array_push_back(array, array_obj);
} }
} }
string nonMatchingSceneName = string nonMatchingSceneName = GetWeakSourceName(switcher->nonMatchingScene);
GetWeakSourceName(switcher->nonMatchingScene);
obs_data_set_int(obj, "interval", switcher->interval); obs_data_set_int(obj, "interval", switcher->interval);
obs_data_set_string(obj, "non_matching_scene", obs_data_set_string(obj, "non_matching_scene", nonMatchingSceneName.c_str());
nonMatchingSceneName.c_str()); obs_data_set_bool(obj, "switch_if_not_matching", switcher->switchIfNotMatching);
obs_data_set_bool(obj, "switch_if_not_matching",
switcher->switchIfNotMatching);
obs_data_set_bool(obj, "active", switcher->th.joinable()); obs_data_set_bool(obj, "active", switcher->th.joinable());
obs_data_set_array(obj, "switches", array); obs_data_set_array(obj, "switches", array);
@ -365,10 +345,8 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
} else { } else {
switcher->m.lock(); switcher->m.lock();
OBSDataAutoRelease obj = OBSDataAutoRelease obj = obs_data_get_obj(save_data, "auto-scene-switcher");
obs_data_get_obj(save_data, "auto-scene-switcher"); OBSDataArrayAutoRelease array = obs_data_get_array(obj, "switches");
OBSDataArrayAutoRelease array =
obs_data_get_array(obj, "switches");
size_t count = obs_data_array_count(array); size_t count = obs_data_array_count(array);
if (!obj) if (!obj)
@ -377,28 +355,21 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL); obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL);
switcher->interval = obs_data_get_int(obj, "interval"); switcher->interval = obs_data_get_int(obj, "interval");
switcher->switchIfNotMatching = switcher->switchIfNotMatching = obs_data_get_bool(obj, "switch_if_not_matching");
obs_data_get_bool(obj, "switch_if_not_matching"); string nonMatchingScene = obs_data_get_string(obj, "non_matching_scene");
string nonMatchingScene =
obs_data_get_string(obj, "non_matching_scene");
bool active = obs_data_get_bool(obj, "active"); bool active = obs_data_get_bool(obj, "active");
switcher->nonMatchingScene = switcher->nonMatchingScene = GetWeakSourceByName(nonMatchingScene.c_str());
GetWeakSourceByName(nonMatchingScene.c_str());
switcher->switches.clear(); switcher->switches.clear();
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease array_obj = OBSDataAutoRelease array_obj = obs_data_array_item(array, i);
obs_data_array_item(array, i);
const char *scene = const char *scene = obs_data_get_string(array_obj, "scene");
obs_data_get_string(array_obj, "scene"); const char *window = obs_data_get_string(array_obj, "window_title");
const char *window =
obs_data_get_string(array_obj, "window_title");
switcher->switches.emplace_back( switcher->switches.emplace_back(GetWeakSourceByName(scene), window);
GetWeakSourceByName(scene), window);
} }
switcher->m.unlock(); switcher->m.unlock();
@ -412,8 +383,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
void SwitcherData::Thread() void SwitcherData::Thread()
{ {
chrono::duration<long long, milli> duration = chrono::duration<long long, milli> duration = chrono::milliseconds(interval);
chrono::milliseconds(interval);
string lastTitle; string lastTitle;
string title; string title;
@ -447,8 +417,7 @@ void SwitcherData::Thread()
if (!match) { if (!match) {
for (SceneSwitch &s : switches) { for (SceneSwitch &s : switches) {
try { try {
bool matches = regex_match( bool matches = regex_match(title, s.re);
title, s.re);
if (matches) { if (matches) {
match = true; match = true;
scene = s.scene; scene = s.scene;
@ -465,10 +434,8 @@ void SwitcherData::Thread()
} }
if (match) { if (match) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_weak_source_get_source(scene);
obs_weak_source_get_source(scene); OBSSourceAutoRelease currentSource = obs_frontend_get_current_scene();
OBSSourceAutoRelease currentSource =
obs_frontend_get_current_scene();
if (source && source != currentSource) if (source && source != currentSource)
obs_frontend_set_current_scene(source); obs_frontend_set_current_scene(source);
@ -518,16 +485,14 @@ extern "C" void InitSceneSwitcher()
return; return;
#endif #endif
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("SceneSwitcher"));
obs_module_text("SceneSwitcher"));
switcher = new SwitcherData; switcher = new SwitcherData;
auto cb = []() { auto cb = []() {
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QMainWindow *window = QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();
(QMainWindow *)obs_frontend_get_main_window();
SceneSwitcher ss(window); SceneSwitcher ss(window);
ss.exec(); ss.exec();

View File

@ -1,23 +1,18 @@
#include "captions-handler.hpp" #include "captions-handler.hpp"
captions_handler::captions_handler(captions_cb callback, captions_handler::captions_handler(captions_cb callback, enum audio_format format, uint32_t sample_rate) : cb(callback)
enum audio_format format,
uint32_t sample_rate)
: cb(callback)
{ {
if (!reset_resampler(format, sample_rate)) if (!reset_resampler(format, sample_rate))
throw CAPTIONS_ERROR_GENERIC_FAIL; throw CAPTIONS_ERROR_GENERIC_FAIL;
} }
bool captions_handler::reset_resampler(enum audio_format format, bool captions_handler::reset_resampler(enum audio_format format, uint32_t sample_rate)
uint32_t sample_rate)
try { try {
obs_audio_info ai; obs_audio_info ai;
if (!obs_get_audio_info(&ai)) if (!obs_get_audio_info(&ai))
throw std::string("Failed to get OBS audio info"); throw std::string("Failed to get OBS audio info");
resample_info src = {ai.samples_per_sec, AUDIO_FORMAT_FLOAT_PLANAR, resample_info src = {ai.samples_per_sec, AUDIO_FORMAT_FLOAT_PLANAR, ai.speakers};
ai.speakers};
resample_info dst = {sample_rate, format, SPEAKERS_MONO}; resample_info dst = {sample_rate, format, SPEAKERS_MONO};
if (!resampler.reset(dst, src)) if (!resampler.reset(dst, src))
@ -37,8 +32,7 @@ void captions_handler::push_audio(const audio_data *audio)
uint64_t ts_offset; uint64_t ts_offset;
bool success; bool success;
success = audio_resampler_resample(resampler, out, &frames, &ts_offset, success = audio_resampler_resample(resampler, out, &frames, &ts_offset, (const uint8_t *const *)audio->data,
(const uint8_t *const *)audio->data,
audio->frames); audio->frames);
if (success) if (success)
pcm_data(out[0], frames); pcm_data(out[0], frames);

View File

@ -25,8 +25,7 @@ public:
typedef std::function<void(const std::string &)> captions_cb; typedef std::function<void(const std::string &)> captions_cb;
#define CAPTIONS_ERROR_GENERIC_FAIL \ #define CAPTIONS_ERROR_GENERIC_FAIL std::string(obs_module_text("Captions.Error.GenericFail"))
std::string(obs_module_text("Captions.Error.GenericFail"))
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@ -44,8 +43,7 @@ protected:
public: public:
/* throw std::string for errors shown to users */ /* throw std::string for errors shown to users */
captions_handler(captions_cb callback, enum audio_format format, captions_handler(captions_cb callback, enum audio_format format, uint32_t sample_rate);
uint32_t sample_rate);
virtual ~captions_handler() {} virtual ~captions_handler() {}
void push_audio(const audio_data *audio); void push_audio(const audio_data *audio);

View File

@ -8,9 +8,7 @@
using namespace std; using namespace std;
#if 0 #if 0
#define debugfunc(format, ...) \ #define debugfunc(format, ...) blog(LOG_DEBUG, "[Captions] %s(" format ")", __FUNCTION__, ##__VA_ARGS__)
blog(LOG_DEBUG, "[Captions] %s(" format ")", __FUNCTION__, \
##__VA_ARGS__)
#else #else
#define debugfunc(format, ...) #define debugfunc(format, ...)
#endif #endif
@ -144,8 +142,7 @@ STDMETHODIMP CaptionStream::Write(const void *, ULONG bytes, ULONG *)
// IStream methods // IStream methods
STDMETHODIMP CaptionStream::Seek(LARGE_INTEGER move, DWORD origin, STDMETHODIMP CaptionStream::Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos)
ULARGE_INTEGER *new_pos)
{ {
debugfunc("%lld, %lx, new_pos", move, origin); debugfunc("%lld, %lx, new_pos", move, origin);
@ -165,8 +162,7 @@ STDMETHODIMP CaptionStream::SetSize(ULARGE_INTEGER new_size)
return STG_E_INVALIDFUNCTION; return STG_E_INVALIDFUNCTION;
} }
STDMETHODIMP CaptionStream::CopyTo(IStream *stream, ULARGE_INTEGER bytes, STDMETHODIMP CaptionStream::CopyTo(IStream *stream, ULARGE_INTEGER bytes, ULARGE_INTEGER *read_bytes,
ULARGE_INTEGER *read_bytes,
ULARGE_INTEGER *written_bytes) ULARGE_INTEGER *written_bytes)
{ {
HRESULT hr; HRESULT hr;
@ -207,16 +203,14 @@ STDMETHODIMP CaptionStream::Revert(void)
return S_OK; return S_OK;
} }
STDMETHODIMP CaptionStream::LockRegion(ULARGE_INTEGER offset, STDMETHODIMP CaptionStream::LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type)
ULARGE_INTEGER size, DWORD type)
{ {
debugfunc("%llu, %llu, %ld", offset, size, type); debugfunc("%llu, %llu, %ld", offset, size, type);
/* TODO? */ /* TODO? */
return STG_E_INVALIDFUNCTION; return STG_E_INVALIDFUNCTION;
} }
STDMETHODIMP CaptionStream::UnlockRegion(ULARGE_INTEGER offset, STDMETHODIMP CaptionStream::UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type)
ULARGE_INTEGER size, DWORD type)
{ {
debugfunc("%llu, %llu, %ld", offset, size, type); debugfunc("%llu, %llu, %ld", offset, size, type);
/* TODO? */ /* TODO? */
@ -255,8 +249,7 @@ STDMETHODIMP CaptionStream::Clone(IStream **stream)
// ISpStreamFormat methods // ISpStreamFormat methods
STDMETHODIMP CaptionStream::GetFormat(GUID *guid, STDMETHODIMP CaptionStream::GetFormat(GUID *guid, WAVEFORMATEX **co_mem_wfex_out)
WAVEFORMATEX **co_mem_wfex_out)
{ {
debugfunc("guid, co_mem_wfex_out"); debugfunc("guid, co_mem_wfex_out");
@ -284,8 +277,7 @@ STDMETHODIMP CaptionStream::SetState(SPAUDIOSTATE state_, ULONGLONG)
return S_OK; return S_OK;
} }
STDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref, STDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref, const WAVEFORMATEX *wfex)
const WAVEFORMATEX *wfex)
{ {
debugfunc("guid, wfex"); debugfunc("guid, wfex");
if (!wfex) if (!wfex)
@ -294,8 +286,7 @@ STDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref,
if (guid_ref == SPDFID_WaveFormatEx) { if (guid_ref == SPDFID_WaveFormatEx) {
lock_guard<mutex> lock(m); lock_guard<mutex> lock(m);
memcpy(&format, wfex, sizeof(format)); memcpy(&format, wfex, sizeof(format));
if (!handler->reset_resampler(AUDIO_FORMAT_16BIT, if (!handler->reset_resampler(AUDIO_FORMAT_16BIT, wfex->nSamplesPerSec))
wfex->nSamplesPerSec))
return E_FAIL; return E_FAIL;
/* 50 msec */ /* 50 msec */
@ -342,8 +333,7 @@ STDMETHODIMP CaptionStream::GetBufferInfo(SPAUDIOBUFFERINFO *buf_info_)
return S_OK; return S_OK;
} }
STDMETHODIMP CaptionStream::GetDefaultFormat(GUID *format, STDMETHODIMP CaptionStream::GetDefaultFormat(GUID *format, WAVEFORMATEX **co_mem_wfex_out)
WAVEFORMATEX **co_mem_wfex_out)
{ {
debugfunc("format, co_mem_wfex_out"); debugfunc("format, co_mem_wfex_out");

View File

@ -55,38 +55,30 @@ public:
// ISequentialStream methods // ISequentialStream methods
STDMETHODIMP Read(void *data, ULONG bytes, ULONG *read_bytes) override; STDMETHODIMP Read(void *data, ULONG bytes, ULONG *read_bytes) override;
STDMETHODIMP Write(const void *data, ULONG bytes, STDMETHODIMP Write(const void *data, ULONG bytes, ULONG *written_bytes) override;
ULONG *written_bytes) override;
// IStream methods // IStream methods
STDMETHODIMP Seek(LARGE_INTEGER move, DWORD origin, STDMETHODIMP Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos) override;
ULARGE_INTEGER *new_pos) override;
STDMETHODIMP SetSize(ULARGE_INTEGER new_size) override; STDMETHODIMP SetSize(ULARGE_INTEGER new_size) override;
STDMETHODIMP CopyTo(IStream *stream, ULARGE_INTEGER bytes, STDMETHODIMP CopyTo(IStream *stream, ULARGE_INTEGER bytes, ULARGE_INTEGER *read_bytes,
ULARGE_INTEGER *read_bytes,
ULARGE_INTEGER *written_bytes) override; ULARGE_INTEGER *written_bytes) override;
STDMETHODIMP Commit(DWORD commit_flags) override; STDMETHODIMP Commit(DWORD commit_flags) override;
STDMETHODIMP Revert(void) override; STDMETHODIMP Revert(void) override;
STDMETHODIMP LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, STDMETHODIMP LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) override;
DWORD type) override; STDMETHODIMP UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) override;
STDMETHODIMP UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size,
DWORD type) override;
STDMETHODIMP Stat(STATSTG *stg, DWORD flags) override; STDMETHODIMP Stat(STATSTG *stg, DWORD flags) override;
STDMETHODIMP Clone(IStream **stream) override; STDMETHODIMP Clone(IStream **stream) override;
// ISpStreamFormat methods // ISpStreamFormat methods
STDMETHODIMP GetFormat(GUID *guid, STDMETHODIMP GetFormat(GUID *guid, WAVEFORMATEX **co_mem_wfex_out) override;
WAVEFORMATEX **co_mem_wfex_out) override;
// ISpAudio methods // ISpAudio methods
STDMETHODIMP SetState(SPAUDIOSTATE state, ULONGLONG reserved) override; STDMETHODIMP SetState(SPAUDIOSTATE state, ULONGLONG reserved) override;
STDMETHODIMP SetFormat(REFGUID guid_ref, STDMETHODIMP SetFormat(REFGUID guid_ref, const WAVEFORMATEX *wfex) override;
const WAVEFORMATEX *wfex) override;
STDMETHODIMP GetStatus(SPAUDIOSTATUS *status) override; STDMETHODIMP GetStatus(SPAUDIOSTATUS *status) override;
STDMETHODIMP SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info) override; STDMETHODIMP SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info) override;
STDMETHODIMP GetBufferInfo(SPAUDIOBUFFERINFO *buf_info) override; STDMETHODIMP GetBufferInfo(SPAUDIOBUFFERINFO *buf_info) override;
STDMETHODIMP GetDefaultFormat(GUID *format, STDMETHODIMP GetDefaultFormat(GUID *format, WAVEFORMATEX **co_mem_wfex_out) override;
WAVEFORMATEX **co_mem_wfex_out) override;
STDMETHODIMP_(HANDLE) EventHandle(void) override; STDMETHODIMP_(HANDLE) EventHandle(void) override;
STDMETHODIMP GetVolumeLevel(ULONG *level) override; STDMETHODIMP GetVolumeLevel(ULONG *level) override;
STDMETHODIMP SetVolumeLevel(ULONG level) override; STDMETHODIMP SetVolumeLevel(ULONG level) override;

View File

@ -1,7 +1,6 @@
#include "captions-mssapi.hpp" #include "captions-mssapi.hpp"
#define do_log(type, format, ...) \ #define do_log(type, format, ...) blog(type, "[Captions] " format, ##__VA_ARGS__)
blog(type, "[Captions] " format, ##__VA_ARGS__)
#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__) #define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__)
#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
@ -29,8 +28,8 @@ try : captions_handler(callback, AUDIO_FORMAT_16BIT, 16000) {
if (FAILED(hr)) if (FAILED(hr))
throw HRError("SpFindBestToken failed", hr); throw HRError("SpFindBestToken failed", hr);
hr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_ALL, hr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_ALL, __uuidof(ISpRecognizer),
__uuidof(ISpRecognizer), (void **)&recognizer); (void **)&recognizer);
if (FAILED(hr)) if (FAILED(hr))
throw HRError("CoCreateInstance for recognizer failed", hr); throw HRError("CoCreateInstance for recognizer failed", hr);
@ -46,8 +45,7 @@ try : captions_handler(callback, AUDIO_FORMAT_16BIT, 16000) {
if (FAILED(hr)) if (FAILED(hr))
throw HRError("CreateRecoContext failed", hr); throw HRError("CreateRecoContext failed", hr);
ULONGLONG interest = SPFEI(SPEI_RECOGNITION) | ULONGLONG interest = SPFEI(SPEI_RECOGNITION) | SPFEI(SPEI_END_SR_STREAM);
SPFEI(SPEI_END_SR_STREAM);
hr = context->SetInterest(interest, interest); hr = context->SetInterest(interest, interest);
if (FAILED(hr)) if (FAILED(hr))
throw HRError("SetInterest failed", hr); throw HRError("SetInterest failed", hr);
@ -130,8 +128,7 @@ try {
ISpRecoResult *result = event.RecoResult(); ISpRecoResult *result = event.RecoResult();
CoTaskMemPtr<wchar_t> text; CoTaskMemPtr<wchar_t> text;
hr = result->GetText((ULONG)-1, (ULONG)-1, true, hr = result->GetText((ULONG)-1, (ULONG)-1, true, &text, nullptr);
&text, nullptr);
if (FAILED(hr)) if (FAILED(hr))
continue; continue;
@ -164,8 +161,7 @@ void mssapi_captions::pcm_data(const void *data, size_t frames)
audio->PushAudio(data, frames); audio->PushAudio(data, frames);
} }
captions_handler_info mssapi_info = { captions_handler_info mssapi_info = {[]() -> std::string { return "Microsoft Speech-to-Text"; },
[]() -> std::string { return "Microsoft Speech-to-Text"; }, [](captions_cb cb, const std::string &lang) -> captions_handler * {
[](captions_cb cb, const std::string &lang) -> captions_handler * { return new mssapi_captions(cb, lang);
return new mssapi_captions(cb, lang); }};
}};

View File

@ -31,8 +31,7 @@
#include "captions-mssapi.hpp" #include "captions-mssapi.hpp"
#define do_log(type, format, ...) \ #define do_log(type, format, ...) blog(type, "[Captions] " format, ##__VA_ARGS__)
blog(type, "[Captions] " format, ##__VA_ARGS__)
#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
@ -50,11 +49,7 @@ struct obs_captions {
std::unordered_map<std::string, captions_handler_info &> handler_types; std::unordered_map<std::string, captions_handler_info &> handler_types;
inline void register_handler(const char *id, inline void register_handler(const char *id, captions_handler_info &info) { handler_types.emplace(id, info); }
captions_handler_info &info)
{
handler_types.emplace(id, info);
}
void start(); void start();
void stop(); void stop();
@ -73,11 +68,7 @@ struct locale_info {
inline locale_info() {} inline locale_info() {}
inline locale_info(const locale_info &) = delete; inline locale_info(const locale_info &) = delete;
inline locale_info(locale_info &&li) inline locale_info(locale_info &&li) : name(std::move(li.name)), id(li.id) {}
: name(std::move(li.name)),
id(li.id)
{
}
}; };
static void get_valid_locale_names(vector<locale_info> &names); static void get_valid_locale_names(vector<locale_info> &names);
@ -85,9 +76,7 @@ static bool valid_lang(LANGID id);
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
CaptionsDialog::CaptionsDialog(QWidget *parent) CaptionsDialog::CaptionsDialog(QWidget *parent) : QDialog(parent), ui(new Ui_CaptionsDialog)
: QDialog(parent),
ui(new Ui_CaptionsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -111,11 +100,7 @@ CaptionsDialog::CaptionsDialog(QWidget *parent)
ui->source->blockSignals(true); ui->source->blockSignals(true);
ui->source->addItem(QStringLiteral("")); ui->source->addItem(QStringLiteral(""));
ui->source->setCurrentIndex(0); ui->source->setCurrentIndex(0);
obs_enum_sources( obs_enum_sources([](void *data, obs_source_t *source) { return (*static_cast<cb_t *>(data))(source); }, &cb);
[](void *data, obs_source_t *source) {
return (*static_cast<cb_t *>(data))(source);
},
&cb);
ui->source->blockSignals(false); ui->source->blockSignals(false);
for (auto &ht : captions->handler_types) { for (auto &ht : captions->handler_types) {
@ -217,8 +202,7 @@ void CaptionsDialog::on_provider_currentIndexChanged(int idx)
if (started) if (started)
captions->stop(); captions->stop();
captions->handler_id = captions->handler_id = ui->provider->itemData(idx).toString().toUtf8().constData();
ui->provider->itemData(idx).toString().toUtf8().constData();
if (started) if (started)
captions->start(); captions->start();
@ -234,8 +218,7 @@ static void caption_text(const std::string &text)
} }
} }
static void audio_capture(void *, obs_source_t *, static void audio_capture(void *, obs_source_t *, const struct audio_data *audio, bool)
const struct audio_data *audio, bool)
{ {
captions->handler->push_audio(audio); captions->handler->push_audio(audio);
} }
@ -252,8 +235,7 @@ void obs_captions::start()
} }
if (!LCIDToLocaleName(lang_id, wname, 256, 0)) { if (!LCIDToLocaleName(lang_id, wname, 256, 0)) {
warn("Failed to get locale name: %d", warn("Failed to get locale name: %d", (int)GetLastError());
(int)GetLastError());
return; return;
} }
@ -272,24 +254,18 @@ void obs_captions::start()
} }
try { try {
captions_handler *h = captions_handler *h = pair->second.create(caption_text, lang_name);
pair->second.create(caption_text, lang_name);
handler.reset(h); handler.reset(h);
OBSSource s = OBSGetStrongRef(source); OBSSource s = OBSGetStrongRef(source);
obs_source_add_audio_capture_callback(s, audio_capture, obs_source_add_audio_capture_callback(s, audio_capture, nullptr);
nullptr);
} catch (std::string text) { } catch (std::string text) {
QWidget *window = QWidget *window = (QWidget *)obs_frontend_get_main_window();
(QWidget *)obs_frontend_get_main_window();
warn("Failed to create handler: %s", text.c_str()); warn("Failed to create handler: %s", text.c_str());
QMessageBox::warning( QMessageBox::warning(window, obs_module_text("Captions.Error.GenericFail"), text.c_str());
window,
obs_module_text("Captions.Error.GenericFail"),
text.c_str());
} }
} }
} }
@ -298,8 +274,7 @@ void obs_captions::stop()
{ {
OBSSource s = OBSGetStrongRef(source); OBSSource s = OBSGetStrongRef(source);
if (s) if (s)
obs_source_remove_audio_capture_callback(s, audio_capture, obs_source_remove_audio_capture_callback(s, audio_capture, nullptr);
nullptr);
handler.reset(); handler.reset();
} }
@ -332,19 +307,16 @@ static void get_valid_locale_names(vector<locale_info> &locales)
locale_info cur; locale_info cur;
char locale_name[256]; char locale_name[256];
static const LANGID default_locales[] = { static const LANGID default_locales[] = {0x0409, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408,
0x0409, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, 0x0410, 0x0411, 0x0412,
0x0407, 0x0408, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0};
0x040f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415,
0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0};
/* ---------------------------------- */ /* ---------------------------------- */
LANGID def_id = GetUserDefaultUILanguage(); LANGID def_id = GetUserDefaultUILanguage();
LANGID id = def_id; LANGID id = def_id;
if (valid_lang(id) && get_locale_name(id, locale_name)) { if (valid_lang(id) && get_locale_name(id, locale_name)) {
dstr_copy(cur.name, dstr_copy(cur.name, obs_module_text("Captions.CurrentSystemLanguage"));
obs_module_text("Captions.CurrentSystemLanguage"));
dstr_replace(cur.name, "%1", locale_name); dstr_replace(cur.name, "%1", locale_name);
cur.id = id; cur.id = id;
@ -358,8 +330,7 @@ static void get_valid_locale_names(vector<locale_info> &locales)
while (*locale) { while (*locale) {
id = *locale; id = *locale;
if (id != def_id && valid_lang(id) && if (id != def_id && valid_lang(id) && get_locale_name(id, locale_name)) {
get_locale_name(id, locale_name)) {
dstr_copy(cur.name, locale_name); dstr_copy(cur.name, locale_name);
cur.id = id; cur.id = id;
@ -399,32 +370,27 @@ static void save_caption_data(obs_data_t *save_data, bool saving, void *)
if (saving) { if (saving) {
OBSDataAutoRelease obj = obs_data_create(); OBSDataAutoRelease obj = obs_data_create();
obs_data_set_string(obj, "source", obs_data_set_string(obj, "source", captions->source_name.c_str());
captions->source_name.c_str());
obs_data_set_bool(obj, "enabled", !!captions->handler); obs_data_set_bool(obj, "enabled", !!captions->handler);
obs_data_set_int(obj, "lang_id", captions->lang_id); obs_data_set_int(obj, "lang_id", captions->lang_id);
obs_data_set_string(obj, "provider", obs_data_set_string(obj, "provider", captions->handler_id.c_str());
captions->handler_id.c_str());
obs_data_set_obj(save_data, "captions", obj); obs_data_set_obj(save_data, "captions", obj);
} else { } else {
captions->stop(); captions->stop();
OBSDataAutoRelease obj = OBSDataAutoRelease obj = obs_data_get_obj(save_data, "captions");
obs_data_get_obj(save_data, "captions");
if (!obj) if (!obj)
obj = obs_data_create(); obj = obs_data_create();
obs_data_set_default_int(obj, "lang_id", obs_data_set_default_int(obj, "lang_id", GetUserDefaultUILanguage());
GetUserDefaultUILanguage());
obs_data_set_default_string(obj, "provider", DEFAULT_HANDLER); obs_data_set_default_string(obj, "provider", DEFAULT_HANDLER);
bool enabled = obs_data_get_bool(obj, "enabled"); bool enabled = obs_data_get_bool(obj, "enabled");
captions->source_name = obs_data_get_string(obj, "source"); captions->source_name = obs_data_get_string(obj, "source");
captions->lang_id = (int)obs_data_get_int(obj, "lang_id"); captions->lang_id = (int)obs_data_get_int(obj, "lang_id");
captions->handler_id = obs_data_get_string(obj, "provider"); captions->handler_id = obs_data_get_string(obj, "provider");
captions->source = captions->source = GetWeakSourceByName(captions->source_name.c_str());
GetWeakSourceByName(captions->source_name.c_str());
if (enabled) if (enabled)
captions->start(); captions->start();
@ -433,8 +399,7 @@ static void save_caption_data(obs_data_t *save_data, bool saving, void *)
extern "C" void InitCaptions() extern "C" void InitCaptions()
{ {
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("Captions"));
obs_module_text("Captions"));
captions = new obs_captions; captions = new obs_captions;

View File

@ -12,20 +12,16 @@ using namespace std;
OutputTimer *ot; OutputTimer *ot;
OutputTimer::OutputTimer(QWidget *parent) OutputTimer::OutputTimer(QWidget *parent) : QDialog(parent), ui(new Ui_OutputTimer)
: QDialog(parent),
ui(new Ui_OutputTimer)
{ {
ui->setupUi(this); ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
QObject::connect(ui->outputTimerStream, &QPushButton::clicked, this, QObject::connect(ui->outputTimerStream, &QPushButton::clicked, this, &OutputTimer::StreamingTimerButton);
&OutputTimer::StreamingTimerButton); QObject::connect(ui->outputTimerRecord, &QPushButton::clicked, this, &OutputTimer::RecordingTimerButton);
QObject::connect(ui->outputTimerRecord, &QPushButton::clicked, this, QObject::connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this,
&OutputTimer::RecordingTimerButton); &OutputTimer::hide);
QObject::connect(ui->buttonBox->button(QDialogButtonBox::Close),
&QPushButton::clicked, this, &OutputTimer::hide);
streamingTimer = new QTimer(this); streamingTimer = new QTimer(this);
streamingTimerDisplay = new QTimer(this); streamingTimerDisplay = new QTimer(this);
@ -33,14 +29,10 @@ OutputTimer::OutputTimer(QWidget *parent)
recordingTimer = new QTimer(this); recordingTimer = new QTimer(this);
recordingTimerDisplay = new QTimer(this); recordingTimerDisplay = new QTimer(this);
QObject::connect(streamingTimer, &QTimer::timeout, this, QObject::connect(streamingTimer, &QTimer::timeout, this, &OutputTimer::EventStopStreaming);
&OutputTimer::EventStopStreaming); QObject::connect(streamingTimerDisplay, &QTimer::timeout, this, &OutputTimer::UpdateStreamTimerDisplay);
QObject::connect(streamingTimerDisplay, &QTimer::timeout, this, QObject::connect(recordingTimer, &QTimer::timeout, this, &OutputTimer::EventStopRecording);
&OutputTimer::UpdateStreamTimerDisplay); QObject::connect(recordingTimerDisplay, &QTimer::timeout, this, &OutputTimer::UpdateRecordTimerDisplay);
QObject::connect(recordingTimer, &QTimer::timeout, this,
&OutputTimer::EventStopRecording);
QObject::connect(recordingTimerDisplay, &QTimer::timeout, this,
&OutputTimer::UpdateRecordTimerDisplay);
} }
void OutputTimer::closeEvent(QCloseEvent *) void OutputTimer::closeEvent(QCloseEvent *)
@ -178,8 +170,7 @@ void OutputTimer::UpdateStreamTimerDisplay()
int minutes = (remainingTime % 3600) / 60; int minutes = (remainingTime % 3600) / 60;
int hours = remainingTime / 3600; int hours = remainingTime / 3600;
QString text = QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
ui->streamTime->setText(text); ui->streamTime->setText(text);
} }
@ -187,8 +178,7 @@ void OutputTimer::UpdateRecordTimerDisplay()
{ {
int remainingTime = 0; int remainingTime = 0;
if (obs_frontend_recording_paused() && if (obs_frontend_recording_paused() && ui->pauseRecordTimer->isChecked())
ui->pauseRecordTimer->isChecked())
remainingTime = recordingTimeLeft / 1000; remainingTime = recordingTimeLeft / 1000;
else else
remainingTime = recordingTimer->remainingTime() / 1000; remainingTime = recordingTimer->remainingTime() / 1000;
@ -197,8 +187,7 @@ void OutputTimer::UpdateRecordTimerDisplay()
int minutes = (remainingTime % 3600) / 60; int minutes = (remainingTime % 3600) / 60;
int hours = remainingTime / 3600; int hours = remainingTime / 3600;
QString text = QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
ui->recordTime->setText(text); ui->recordTime->setText(text);
} }
@ -250,57 +239,38 @@ static void SaveOutputTimer(obs_data_t *save_data, bool saving, void *)
if (saving) { if (saving) {
OBSDataAutoRelease obj = obs_data_create(); OBSDataAutoRelease obj = obs_data_create();
obs_data_set_int(obj, "streamTimerHours", obs_data_set_int(obj, "streamTimerHours", ot->ui->streamingTimerHours->value());
ot->ui->streamingTimerHours->value()); obs_data_set_int(obj, "streamTimerMinutes", ot->ui->streamingTimerMinutes->value());
obs_data_set_int(obj, "streamTimerMinutes", obs_data_set_int(obj, "streamTimerSeconds", ot->ui->streamingTimerSeconds->value());
ot->ui->streamingTimerMinutes->value());
obs_data_set_int(obj, "streamTimerSeconds",
ot->ui->streamingTimerSeconds->value());
obs_data_set_int(obj, "recordTimerHours", obs_data_set_int(obj, "recordTimerHours", ot->ui->recordingTimerHours->value());
ot->ui->recordingTimerHours->value()); obs_data_set_int(obj, "recordTimerMinutes", ot->ui->recordingTimerMinutes->value());
obs_data_set_int(obj, "recordTimerMinutes", obs_data_set_int(obj, "recordTimerSeconds", ot->ui->recordingTimerSeconds->value());
ot->ui->recordingTimerMinutes->value());
obs_data_set_int(obj, "recordTimerSeconds",
ot->ui->recordingTimerSeconds->value());
obs_data_set_bool(obj, "autoStartStreamTimer", obs_data_set_bool(obj, "autoStartStreamTimer", ot->ui->autoStartStreamTimer->isChecked());
ot->ui->autoStartStreamTimer->isChecked()); obs_data_set_bool(obj, "autoStartRecordTimer", ot->ui->autoStartRecordTimer->isChecked());
obs_data_set_bool(obj, "autoStartRecordTimer",
ot->ui->autoStartRecordTimer->isChecked());
obs_data_set_bool(obj, "pauseRecordTimer", obs_data_set_bool(obj, "pauseRecordTimer", ot->ui->pauseRecordTimer->isChecked());
ot->ui->pauseRecordTimer->isChecked());
obs_data_set_obj(save_data, "output-timer", obj); obs_data_set_obj(save_data, "output-timer", obj);
} else { } else {
OBSDataAutoRelease obj = OBSDataAutoRelease obj = obs_data_get_obj(save_data, "output-timer");
obs_data_get_obj(save_data, "output-timer");
if (!obj) if (!obj)
obj = obs_data_create(); obj = obs_data_create();
ot->ui->streamingTimerHours->setValue( ot->ui->streamingTimerHours->setValue(obs_data_get_int(obj, "streamTimerHours"));
obs_data_get_int(obj, "streamTimerHours")); ot->ui->streamingTimerMinutes->setValue(obs_data_get_int(obj, "streamTimerMinutes"));
ot->ui->streamingTimerMinutes->setValue( ot->ui->streamingTimerSeconds->setValue(obs_data_get_int(obj, "streamTimerSeconds"));
obs_data_get_int(obj, "streamTimerMinutes"));
ot->ui->streamingTimerSeconds->setValue(
obs_data_get_int(obj, "streamTimerSeconds"));
ot->ui->recordingTimerHours->setValue( ot->ui->recordingTimerHours->setValue(obs_data_get_int(obj, "recordTimerHours"));
obs_data_get_int(obj, "recordTimerHours")); ot->ui->recordingTimerMinutes->setValue(obs_data_get_int(obj, "recordTimerMinutes"));
ot->ui->recordingTimerMinutes->setValue( ot->ui->recordingTimerSeconds->setValue(obs_data_get_int(obj, "recordTimerSeconds"));
obs_data_get_int(obj, "recordTimerMinutes"));
ot->ui->recordingTimerSeconds->setValue(
obs_data_get_int(obj, "recordTimerSeconds"));
ot->ui->autoStartStreamTimer->setChecked( ot->ui->autoStartStreamTimer->setChecked(obs_data_get_bool(obj, "autoStartStreamTimer"));
obs_data_get_bool(obj, "autoStartStreamTimer")); ot->ui->autoStartRecordTimer->setChecked(obs_data_get_bool(obj, "autoStartRecordTimer"));
ot->ui->autoStartRecordTimer->setChecked(
obs_data_get_bool(obj, "autoStartRecordTimer"));
ot->ui->pauseRecordTimer->setChecked( ot->ui->pauseRecordTimer->setChecked(obs_data_get_bool(obj, "pauseRecordTimer"));
obs_data_get_bool(obj, "pauseRecordTimer"));
} }
} }
@ -328,8 +298,7 @@ static void OBSEvent(enum obs_frontend_event event, void *)
extern "C" void InitOutputTimer() extern "C" void InitOutputTimer()
{ {
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("OutputTimer"));
obs_module_text("OutputTimer"));
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);

View File

@ -93,8 +93,7 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr)
QHBoxLayout *buttonLayout = new QHBoxLayout(); QHBoxLayout *buttonLayout = new QHBoxLayout();
QPushButton *clearButton = new QPushButton(tr("Clear")); QPushButton *clearButton = new QPushButton(tr("Clear"));
connect(clearButton, &QPushButton::clicked, this, connect(clearButton, &QPushButton::clicked, this, &ScriptLogWindow::ClearWindow);
&ScriptLogWindow::ClearWindow);
QPushButton *closeButton = new QPushButton(tr("Close")); QPushButton *closeButton = new QPushButton(tr("Close"));
connect(closeButton, &QPushButton::clicked, this, &QDialog::hide); connect(closeButton, &QPushButton::clicked, this, &QDialog::hide);
@ -114,8 +113,7 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr)
resize(600, 400); resize(600, 400);
config_t *global_config = obs_frontend_get_user_config(); config_t *global_config = obs_frontend_get_user_config();
const char *geom = const char *geom = config_get_string(global_config, "ScriptLogWindow", "geometry");
config_get_string(global_config, "ScriptLogWindow", "geometry");
if (geom != nullptr) { if (geom != nullptr) {
QByteArray ba = QByteArray::fromBase64(QByteArray(geom)); QByteArray ba = QByteArray::fromBase64(QByteArray(geom));
restoreGeometry(ba); restoreGeometry(ba);
@ -123,15 +121,13 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr)
setWindowTitle(obs_module_text("ScriptLogWindow")); setWindowTitle(obs_module_text("ScriptLogWindow"));
connect(edit->verticalScrollBar(), &QAbstractSlider::sliderMoved, this, connect(edit->verticalScrollBar(), &QAbstractSlider::sliderMoved, this, &ScriptLogWindow::ScrollChanged);
&ScriptLogWindow::ScrollChanged);
} }
ScriptLogWindow::~ScriptLogWindow() ScriptLogWindow::~ScriptLogWindow()
{ {
config_t *global_config = obs_frontend_get_user_config(); config_t *global_config = obs_frontend_get_user_config();
config_set_string(global_config, "ScriptLogWindow", "geometry", config_set_string(global_config, "ScriptLogWindow", "geometry", saveGeometry().toBase64().constData());
saveGeometry().toBase64().constData());
} }
void ScriptLogWindow::ScrollChanged(int val) void ScriptLogWindow::ScrollChanged(int val)
@ -190,8 +186,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)
#if PYTHON_UI #if PYTHON_UI
config_t *config = obs_frontend_get_user_config(); config_t *config = obs_frontend_get_user_config();
const char *path = const char *path = config_get_string(config, "Python", "Path" ARCH_NAME);
config_get_string(config, "Python", "Path" ARCH_NAME);
ui->pythonPath->setText(path); ui->pythonPath->setText(path);
ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT)); ui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT));
updatePythonVersionLabel(); updatePythonVersionLabel();
@ -203,8 +198,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)
delete propertiesView; delete propertiesView;
propertiesView = new QWidget(); propertiesView = new QWidget();
propertiesView->setSizePolicy(QSizePolicy::Expanding, propertiesView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QSizePolicy::Expanding);
ui->propertiesLayout->addWidget(propertiesView); ui->propertiesLayout->addWidget(propertiesView);
config_t *user_config = obs_frontend_get_user_config(); config_t *user_config = obs_frontend_get_user_config();
@ -215,8 +209,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)
ScriptsTool::~ScriptsTool() ScriptsTool::~ScriptsTool()
{ {
config_t *user_config = obs_frontend_get_user_config(); config_t *user_config = obs_frontend_get_user_config();
config_set_int(user_config, "scripts-tool", "prevScriptRow", config_set_int(user_config, "scripts-tool", "prevScriptRow", ui->scripts->currentRow());
ui->scripts->currentRow());
} }
void ScriptsTool::updatePythonVersionLabel() void ScriptsTool::updatePythonVersionLabel()
@ -225,8 +218,7 @@ void ScriptsTool::updatePythonVersionLabel()
if (obs_scripting_python_loaded()) { if (obs_scripting_python_loaded()) {
char version[8]; char version[8];
obs_scripting_python_version(version, sizeof(version)); obs_scripting_python_version(version, sizeof(version));
label = QString(obs_module_text("PythonSettings.PythonVersion")) label = QString(obs_module_text("PythonSettings.PythonVersion")).arg(version);
.arg(version);
} else { } else {
label = obs_module_text("PythonSettings.PythonNotLoaded"); label = obs_module_text("PythonSettings.PythonNotLoaded");
} }
@ -240,8 +232,7 @@ void ScriptsTool::RemoveScript(const char *path)
const char *script_path = obs_script_get_path(script); const char *script_path = obs_script_get_path(script);
if (strcmp(script_path, path) == 0) { if (strcmp(script_path, path) == 0) {
scriptData->scripts.erase(scriptData->scripts.begin() + scriptData->scripts.erase(scriptData->scripts.begin() + i);
i);
break; break;
} }
} }
@ -256,8 +247,7 @@ void ScriptsTool::ReloadScript(const char *path)
OBSDataAutoRelease settings = obs_data_create(); OBSDataAutoRelease settings = obs_data_create();
obs_properties_t *prop = obs_properties_t *prop = obs_script_get_properties(script);
obs_script_get_properties(script);
obs_properties_apply_settings(prop, settings); obs_properties_apply_settings(prop, settings);
obs_properties_destroy(prop); obs_properties_destroy(prop);
@ -285,8 +275,7 @@ void ScriptsTool::SetScriptDefaults(const char *path)
for (OBSScript &script : scriptData->scripts) { for (OBSScript &script : scriptData->scripts) {
const char *script_path = obs_script_get_path(script); const char *script_path = obs_script_get_path(script);
if (strcmp(script_path, path) == 0) { if (strcmp(script_path, path) == 0) {
OBSDataAutoRelease settings = OBSDataAutoRelease settings = obs_script_get_settings(script);
obs_script_get_settings(script);
obs_data_clear(settings); obs_data_clear(settings);
obs_script_update(script, nullptr); obs_script_update(script, nullptr);
@ -335,15 +324,13 @@ void ScriptsTool::on_addScripts_clicked()
lastBrowsedDir = baseScriptPath; lastBrowsedDir = baseScriptPath;
} }
QStringList files = OpenFiles(this, QStringList files =
QT_UTF8(obs_module_text("AddScripts")), OpenFiles(this, QT_UTF8(obs_module_text("AddScripts")), QT_UTF8(lastBrowsedDir.c_str()), filter);
QT_UTF8(lastBrowsedDir.c_str()), filter);
if (!files.count()) if (!files.count())
return; return;
for (const QString &file : files) { for (const QString &file : files) {
lastBrowsedDir = lastBrowsedDir = QFileInfo(file).absolutePath().toUtf8().constData();
QFileInfo(file).absolutePath().toUtf8().constData();
QByteArray pathBytes = file.toUtf8(); QByteArray pathBytes = file.toUtf8();
const char *path = pathBytes.constData(); const char *path = pathBytes.constData();
@ -358,15 +345,13 @@ void ScriptsTool::on_addScripts_clicked()
scriptData->scripts.emplace_back(script); scriptData->scripts.emplace_back(script);
QListWidgetItem *item = QListWidgetItem *item = new QListWidgetItem(script_file);
new QListWidgetItem(script_file);
item->setData(Qt::UserRole, QString(file)); item->setData(Qt::UserRole, QString(file));
ui->scripts->addItem(item); ui->scripts->addItem(item);
OBSDataAutoRelease settings = obs_data_create(); OBSDataAutoRelease settings = obs_data_create();
obs_properties_t *prop = obs_properties_t *prop = obs_script_get_properties(script);
obs_script_get_properties(script);
obs_properties_apply_settings(prop, settings); obs_properties_apply_settings(prop, settings);
obs_properties_destroy(prop); obs_properties_destroy(prop);
@ -380,10 +365,7 @@ void ScriptsTool::on_removeScripts_clicked()
QList<QListWidgetItem *> items = ui->scripts->selectedItems(); QList<QListWidgetItem *> items = ui->scripts->selectedItems();
for (QListWidgetItem *item : items) for (QListWidgetItem *item : items)
RemoveScript(item->data(Qt::UserRole) RemoveScript(item->data(Qt::UserRole).toString().toUtf8().constData());
.toString()
.toUtf8()
.constData());
RefreshLists(); RefreshLists();
} }
@ -391,10 +373,7 @@ void ScriptsTool::on_reloadScripts_clicked()
{ {
QList<QListWidgetItem *> items = ui->scripts->selectedItems(); QList<QListWidgetItem *> items = ui->scripts->selectedItems();
for (QListWidgetItem *item : items) for (QListWidgetItem *item : items)
ReloadScript(item->data(Qt::UserRole) ReloadScript(item->data(Qt::UserRole).toString().toUtf8().constData());
.toString()
.toUtf8()
.constData());
on_scripts_currentRowChanged(ui->scripts->currentRow()); on_scripts_currentRowChanged(ui->scripts->currentRow());
} }
@ -405,8 +384,7 @@ void ScriptsTool::OpenScriptParentDirectory()
for (QListWidgetItem *item : items) { for (QListWidgetItem *item : items) {
QDir dir(item->data(Qt::UserRole).toString()); QDir dir(item->data(Qt::UserRole).toString());
dir.cdUp(); dir.cdUp();
QDesktopServices::openUrl( QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
QUrl::fromLocalFile(dir.absolutePath()));
} }
} }
@ -423,13 +401,10 @@ void ScriptsTool::on_scripts_customContextMenuRequested(const QPoint &pos)
if (item) { if (item) {
popup.addSeparator(); popup.addSeparator();
popup.addAction(obs_module_text("Reload"), this, popup.addAction(obs_module_text("Reload"), this, &ScriptsTool::on_reloadScripts_clicked);
&ScriptsTool::on_reloadScripts_clicked); popup.addAction(obs_module_text("OpenFileLocation"), this, &ScriptsTool::OpenScriptParentDirectory);
popup.addAction(obs_module_text("OpenFileLocation"), this,
&ScriptsTool::OpenScriptParentDirectory);
popup.addSeparator(); popup.addSeparator();
popup.addAction(tr("Remove"), this, popup.addAction(tr("Remove"), this, &ScriptsTool::on_removeScripts_clicked);
&ScriptsTool::on_removeScripts_clicked);
} }
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
@ -441,8 +416,7 @@ void ScriptsTool::on_editScript_clicked()
int row = ui->scripts->currentRow(); int row = ui->scripts->currentRow();
if (row == -1) if (row == -1)
return; return;
QUrl url = QUrl::fromLocalFile( QUrl url = QUrl::fromLocalFile(ui->scripts->item(row)->data(Qt::UserRole).toString());
ui->scripts->item(row)->data(Qt::UserRole).toString());
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
} }
@ -455,8 +429,7 @@ void ScriptsTool::on_scriptLog_clicked()
void ScriptsTool::on_pythonPathBrowse_clicked() void ScriptsTool::on_pythonPathBrowse_clicked()
{ {
QString curPath = ui->pythonPath->text(); QString curPath = ui->pythonPath->text();
QString newPath = QString newPath = SelectDirectory(this, ui->pythonPathLabel->text(), curPath);
SelectDirectory(this, ui->pythonPathLabel->text(), curPath);
if (newPath.isEmpty()) if (newPath.isEmpty())
return; return;
@ -474,14 +447,8 @@ void ScriptsTool::on_pythonPathBrowse_clicked()
if (loaded && !newPath.isEmpty() && curPath.compare(newPath) != 0) { if (loaded && !newPath.isEmpty() && curPath.compare(newPath) != 0) {
char version[8]; char version[8];
obs_scripting_python_version(version, sizeof(version)); obs_scripting_python_version(version, sizeof(version));
QString message = QString message = QString(obs_module_text("PythonSettings.AlreadyLoaded.Message")).arg(version);
QString(obs_module_text( OBSMessageBox::information(this, obs_module_text("PythonSettings.AlreadyLoaded.Title"), message);
"PythonSettings.AlreadyLoaded.Message"))
.arg(version);
OBSMessageBox::information(
this,
obs_module_text("PythonSettings.AlreadyLoaded.Title"),
message);
return; return;
} else if (loaded) { } else if (loaded) {
return; return;
@ -509,15 +476,13 @@ void ScriptsTool::on_scripts_currentRowChanged(int row)
if (row == -1) { if (row == -1) {
propertiesView = new QWidget(); propertiesView = new QWidget();
propertiesView->setSizePolicy(QSizePolicy::Expanding, propertiesView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QSizePolicy::Expanding);
ui->propertiesLayout->addWidget(propertiesView); ui->propertiesLayout->addWidget(propertiesView);
ui->description->setText(QString()); ui->description->setText(QString());
return; return;
} }
QByteArray array = QByteArray array = ui->scripts->item(row)->data(Qt::UserRole).toString().toUtf8();
ui->scripts->item(row)->data(Qt::UserRole).toString().toUtf8();
const char *path = array.constData(); const char *path = array.constData();
obs_script_t *script = scriptData->FindScript(path); obs_script_t *script = scriptData->FindScript(path);
@ -528,10 +493,9 @@ void ScriptsTool::on_scripts_currentRowChanged(int row)
OBSDataAutoRelease settings = obs_script_get_settings(script); OBSDataAutoRelease settings = obs_script_get_settings(script);
OBSPropertiesView *view = new OBSPropertiesView( OBSPropertiesView *view = new OBSPropertiesView(settings.Get(), script,
settings.Get(), script, (PropertiesReloadCallback)obs_script_get_properties, nullptr,
(PropertiesReloadCallback)obs_script_get_properties, nullptr, (PropertiesVisualUpdateCb)obs_script_update);
(PropertiesVisualUpdateCb)obs_script_update);
view->SetDeferrable(false); view->SetDeferrable(false);
propertiesView = view; propertiesView = view;
@ -546,33 +510,26 @@ void ScriptsTool::on_defaults_clicked()
if (!item) if (!item)
return; return;
SetScriptDefaults( SetScriptDefaults(item->data(Qt::UserRole).toString().toUtf8().constData());
item->data(Qt::UserRole).toString().toUtf8().constData());
} }
void ScriptsTool::on_description_linkActivated(const QString &link) void ScriptsTool::on_description_linkActivated(const QString &link)
{ {
QUrl url(link, QUrl::StrictMode); QUrl url(link, QUrl::StrictMode);
if (url.isValid() && (url.scheme().compare("http") == 0 || if (url.isValid() && (url.scheme().compare("http") == 0 || url.scheme().compare("https") == 0)) {
url.scheme().compare("https") == 0)) {
QString msg(obs_module_text("ScriptDescriptionLink.Text")); QString msg(obs_module_text("ScriptDescriptionLink.Text"));
msg += "\n\n"; msg += "\n\n";
msg += QString(obs_module_text( msg += QString(obs_module_text("ScriptDescriptionLink.Text.Url")).arg(link);
"ScriptDescriptionLink.Text.Url"))
.arg(link);
const char *open = const char *open = obs_module_text("ScriptDescriptionLink.OpenURL");
obs_module_text("ScriptDescriptionLink.OpenURL");
QMessageBox messageBox(this); QMessageBox messageBox(this);
messageBox.setWindowTitle(open); messageBox.setWindowTitle(open);
messageBox.setText(msg); messageBox.setText(msg);
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QPushButton *yesButton = QPushButton *yesButton = messageBox.addButton(open, QMessageBox::YesRole);
messageBox.addButton(open, QMessageBox::YesRole); QPushButton *noButton = messageBox.addButton(tr("Cancel"), QMessageBox::NoRole);
QPushButton *noButton =
messageBox.addButton(tr("Cancel"), QMessageBox::NoRole);
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
messageBox.setDefaultButton(yesButton); messageBox.setDefaultButton(yesButton);
messageBox.setEscapeButton(noButton); messageBox.setEscapeButton(noButton);
@ -615,8 +572,7 @@ static void obs_event(enum obs_frontend_event event, void *)
static void load_script_data(obs_data_t *load_data, bool, void *) static void load_script_data(obs_data_t *load_data, bool, void *)
{ {
OBSDataArrayAutoRelease array = OBSDataArrayAutoRelease array = obs_data_get_array(load_data, "scripts-tool");
obs_data_get_array(load_data, "scripts-tool");
delete scriptData; delete scriptData;
scriptData = new ScriptData; scriptData = new ScriptData;
@ -657,20 +613,17 @@ static void save_script_data(obs_data_t *save_data, bool saving, void *)
obs_data_set_array(save_data, "scripts-tool", array); obs_data_set_array(save_data, "scripts-tool", array);
} }
static void script_log(void *, obs_script_t *script, int log_level, static void script_log(void *, obs_script_t *script, int log_level, const char *message)
const char *message)
{ {
QString qmsg; QString qmsg;
if (script) { if (script) {
qmsg = QStringLiteral("[%1] %2").arg( qmsg = QStringLiteral("[%1] %2").arg(obs_script_get_file(script), message);
obs_script_get_file(script), message);
} else { } else {
qmsg = QStringLiteral("[Unknown Script] %1").arg(message); qmsg = QStringLiteral("[Unknown Script] %1").arg(message);
} }
QMetaObject::invokeMethod(scriptLogWindow, "AddLogMsg", QMetaObject::invokeMethod(scriptLogWindow, "AddLogMsg", Q_ARG(int, log_level), Q_ARG(QString, qmsg));
Q_ARG(int, log_level), Q_ARG(QString, qmsg));
} }
extern "C" void InitScripts() extern "C" void InitScripts()
@ -680,24 +633,20 @@ extern "C" void InitScripts()
obs_scripting_load(); obs_scripting_load();
obs_scripting_set_log_callback(script_log, nullptr); obs_scripting_set_log_callback(script_log, nullptr);
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("Scripts"));
obs_module_text("Scripts"));
#if PYTHON_UI #if PYTHON_UI
config_t *config = obs_frontend_get_user_config(); config_t *config = obs_frontend_get_user_config();
const char *python_path = const char *python_path = config_get_string(config, "Python", "Path" ARCH_NAME);
config_get_string(config, "Python", "Path" ARCH_NAME);
#ifdef __APPLE__ #ifdef __APPLE__
if (python_path && *python_path) { if (python_path && *python_path) {
std::string _python_path(python_path); std::string _python_path(python_path);
std::size_t pos = std::size_t pos = _python_path.find("/Python.framework/Versions");
_python_path.find("/Python.framework/Versions");
if (pos != std::string::npos) { if (pos != std::string::npos) {
std::string _temp = _python_path.substr(0, pos); std::string _temp = _python_path.substr(0, pos);
config_set_string(config, "Python", "Path" ARCH_NAME, config_set_string(config, "Python", "Path" ARCH_NAME, _temp.c_str());
_temp.c_str());
config_save(config); config_save(config);
python_path = _temp.c_str(); python_path = _temp.c_str();
} }

View File

@ -23,8 +23,7 @@ void censorRecurse(obs_data_t *data)
censorRecurse(child_data); censorRecurse(child_data);
obs_data_release(child_data); obs_data_release(child_data);
} else if (typ == OBS_DATA_ARRAY) { } else if (typ == OBS_DATA_ARRAY) {
obs_data_array_t *child_array = obs_data_array_t *child_array = obs_data_item_get_array(item);
obs_data_item_get_array(item);
censorRecurseArray(child_array); censorRecurseArray(child_array);
obs_data_array_release(child_array); obs_data_array_release(child_array);
} }
@ -55,8 +54,7 @@ QString censoredJson(obs_data_t *data, bool pretty)
censorRecurse(clone); censorRecurse(clone);
// Turn our copy into JSON // Turn our copy into JSON
QString s = pretty ? obs_data_get_json_pretty(clone) QString s = pretty ? obs_data_get_json_pretty(clone) : obs_data_get_json(clone);
: obs_data_get_json(clone);
// Eliminate our copy // Eliminate our copy
obs_data_release(clone); obs_data_release(clone);

View File

@ -17,8 +17,7 @@ using json = nlohmann::json;
Qt::ConnectionType BlockingConnectionTypeFor(QObject *object); Qt::ConnectionType BlockingConnectionTypeFor(QObject *object);
void HandleGoLiveApiErrors(QWidget *parent, const json &raw_json, void HandleGoLiveApiErrors(QWidget *parent, const json &raw_json, const GoLiveApi::Config &config)
const GoLiveApi::Config &config)
{ {
using GoLiveApi::StatusResult; using GoLiveApi::StatusResult;
@ -36,17 +35,12 @@ void HandleGoLiveApiErrors(QWidget *parent, const json &raw_json,
[=] { [=] {
QMessageBox mb(parent); QMessageBox mb(parent);
mb.setIcon(QMessageBox::Warning); mb.setIcon(QMessageBox::Warning);
mb.setWindowTitle(QTStr( mb.setWindowTitle(QTStr("ConfigDownload.WarningMessageTitle"));
"ConfigDownload.WarningMessageTitle"));
mb.setTextFormat(Qt::RichText); mb.setTextFormat(Qt::RichText);
mb.setText( mb.setText(message + QTStr("FailedToStartStream.WarningRetry"));
message + mb.setStandardButtons(QMessageBox::StandardButton::Yes |
QTStr("FailedToStartStream.WarningRetry")); QMessageBox::StandardButton::No);
mb.setStandardButtons( return mb.exec() == QMessageBox::StandardButton::No;
QMessageBox::StandardButton::Yes |
QMessageBox::StandardButton::No);
return mb.exec() ==
QMessageBox::StandardButton::No;
}, },
BlockingConnectionTypeFor(parent), &ret); BlockingConnectionTypeFor(parent), &ret);
if (ret) if (ret)
@ -54,66 +48,52 @@ void HandleGoLiveApiErrors(QWidget *parent, const json &raw_json,
}; };
auto missing_html = [] { auto missing_html = [] {
return QTStr("FailedToStartStream.StatusMissingHTML") return QTStr("FailedToStartStream.StatusMissingHTML").toStdString();
.toStdString();
}; };
if (status.result == StatusResult::Unknown) { if (status.result == StatusResult::Unknown) {
return warn_continue( return warn_continue(QTStr("FailedToStartStream.WarningUnknownStatus")
QTStr("FailedToStartStream.WarningUnknownStatus") .arg(raw_json["status"]["result"].dump().c_str()));
.arg(raw_json["status"]["result"]
.dump()
.c_str()));
} else if (status.result == StatusResult::Warning) { } else if (status.result == StatusResult::Warning) {
if (config.encoder_configurations.empty()) { if (config.encoder_configurations.empty()) {
throw MultitrackVideoError::warning( throw MultitrackVideoError::warning(status.html_en_us.value_or(missing_html()).c_str());
status.html_en_us.value_or(missing_html())
.c_str());
} }
return warn_continue( return warn_continue(status.html_en_us.value_or(missing_html()).c_str());
status.html_en_us.value_or(missing_html()).c_str());
} else if (status.result == StatusResult::Error) { } else if (status.result == StatusResult::Error) {
throw MultitrackVideoError::critical( throw MultitrackVideoError::critical(status.html_en_us.value_or(missing_html()).c_str());
status.html_en_us.value_or(missing_html()).c_str());
} }
} }
GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, const GoLiveApi::PostData &post_data,
const GoLiveApi::PostData &post_data,
const QString &multitrack_video_name) const QString &multitrack_video_name)
{ {
json post_data_json = post_data; json post_data_json = post_data;
blog(LOG_INFO, "Go live POST data: %s", blog(LOG_INFO, "Go live POST data: %s", censoredJson(post_data_json).toUtf8().constData());
censoredJson(post_data_json).toUtf8().constData());
if (url.isEmpty()) if (url.isEmpty())
throw MultitrackVideoError::critical( throw MultitrackVideoError::critical(QTStr("FailedToStartStream.MissingConfigURL"));
QTStr("FailedToStartStream.MissingConfigURL"));
std::string encodeConfigText; std::string encodeConfigText;
std::string libraryError; std::string libraryError;
std::vector<std::string> headers; std::vector<std::string> headers;
headers.push_back("Content-Type: application/json"); headers.push_back("Content-Type: application/json");
bool encodeConfigDownloadedOk = GetRemoteFile( bool encodeConfigDownloadedOk = GetRemoteFile(url.toLocal8Bit(), encodeConfigText,
url.toLocal8Bit(), encodeConfigText, libraryError, // out params
libraryError, // out params nullptr,
nullptr, nullptr, // out params (response code and content type)
nullptr, // out params (response code and content type) "POST", post_data_json.dump().c_str(), headers,
"POST", post_data_json.dump().c_str(), headers, nullptr, // signature
nullptr, // signature 5); // timeout in seconds
5); // timeout in seconds
if (!encodeConfigDownloadedOk) if (!encodeConfigDownloadedOk)
throw MultitrackVideoError::warning( throw MultitrackVideoError::warning(
QTStr("FailedToStartStream.ConfigRequestFailed") QTStr("FailedToStartStream.ConfigRequestFailed").arg(url, libraryError.c_str()));
.arg(url, libraryError.c_str()));
try { try {
auto data = json::parse(encodeConfigText); auto data = json::parse(encodeConfigText);
blog(LOG_INFO, "Go live response data: %s", blog(LOG_INFO, "Go live response data: %s", censoredJson(data, true).toUtf8().constData());
censoredJson(data, true).toUtf8().constData());
GoLiveApi::Config config = data; GoLiveApi::Config config = data;
HandleGoLiveApiErrors(parent, data, config); HandleGoLiveApiErrors(parent, data, config);
return config; return config;
@ -121,19 +101,16 @@ GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url,
} catch (const json::exception &e) { } catch (const json::exception &e) {
blog(LOG_INFO, "Failed to parse go live config: %s", e.what()); blog(LOG_INFO, "Failed to parse go live config: %s", e.what());
throw MultitrackVideoError::warning( throw MultitrackVideoError::warning(
QTStr("FailedToStartStream.FallbackToDefault") QTStr("FailedToStartStream.FallbackToDefault").arg(multitrack_video_name));
.arg(multitrack_video_name));
} }
} }
QString MultitrackVideoAutoConfigURL(obs_service_t *service) QString MultitrackVideoAutoConfigURL(obs_service_t *service)
{ {
static const std::optional<QString> cli_url = static const std::optional<QString> cli_url = []() -> std::optional<QString> {
[]() -> std::optional<QString> {
auto args = qApp->arguments(); auto args = qApp->arguments();
for (int i = 0; i < args.length() - 1; i++) { for (int i = 0; i < args.length() - 1; i++) {
if (args[i] == "--config-url" && if (args[i] == "--config-url" && args.length() > (i + 1)) {
args.length() > (i + 1)) {
return args[i + 1]; return args[i + 1];
} }
} }
@ -145,8 +122,7 @@ QString MultitrackVideoAutoConfigURL(obs_service_t *service)
url = *cli_url; url = *cli_url;
} else { } else {
OBSDataAutoRelease settings = obs_service_get_settings(service); OBSDataAutoRelease settings = obs_service_get_settings(service);
url = obs_data_get_string(settings, url = obs_data_get_string(settings, "multitrack_video_configuration_url");
"multitrack_video_configuration_url");
} }
blog(LOG_INFO, "Go live URL: %s", url.toUtf8().constData()); blog(LOG_INFO, "Go live URL: %s", url.toUtf8().constData());

View File

@ -10,6 +10,5 @@ QString MultitrackVideoAutoConfigURL(obs_service_t *service);
class QWidget; class QWidget;
GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, const GoLiveApi::PostData &post_data,
const GoLiveApi::PostData &post_data,
const QString &multitrack_video_name); const QString &multitrack_video_name);

View File

@ -6,11 +6,8 @@
#include "models/multitrack-video.hpp" #include "models/multitrack-video.hpp"
GoLiveApi::PostData GoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<uint64_t> &maximum_aggregate_bitrate,
constructGoLivePost(QString streamKey, const std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled)
const std::optional<uint64_t> &maximum_aggregate_bitrate,
const std::optional<uint32_t> &maximum_video_tracks,
bool vod_track_enabled)
{ {
GoLiveApi::PostData post_data{}; GoLiveApi::PostData post_data{};
post_data.service = "IVS"; post_data.service = "IVS";
@ -25,8 +22,7 @@ constructGoLivePost(QString streamKey,
client.version = obs_get_version_string(); client.version = obs_get_version_string();
auto add_codec = [&](const char *codec) { auto add_codec = [&](const char *codec) {
auto it = std::find(std::begin(client.supported_codecs), auto it = std::find(std::begin(client.supported_codecs), std::end(client.supported_codecs), codec);
std::end(client.supported_codecs), codec);
if (it != std::end(client.supported_codecs)) if (it != std::end(client.supported_codecs))
return; return;
@ -67,8 +63,7 @@ constructGoLivePost(QString streamKey,
} }
if (maximum_aggregate_bitrate.has_value()) if (maximum_aggregate_bitrate.has_value())
preferences.maximum_aggregate_bitrate = preferences.maximum_aggregate_bitrate = maximum_aggregate_bitrate.value();
maximum_aggregate_bitrate.value();
if (maximum_video_tracks.has_value()) if (maximum_video_tracks.has_value())
preferences.maximum_video_tracks = maximum_video_tracks.value(); preferences.maximum_video_tracks = maximum_video_tracks.value();

View File

@ -5,8 +5,5 @@
#include <QString> #include <QString>
#include "models/multitrack-video.hpp" #include "models/multitrack-video.hpp"
GoLiveApi::PostData GoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<uint64_t> &maximum_aggregate_bitrate,
constructGoLivePost(QString streamKey, const std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled);
const std::optional<uint64_t> &maximum_aggregate_bitrate,
const std::optional<uint32_t> &maximum_video_tracks,
bool vod_track_enabled);

View File

@ -49,12 +49,10 @@ void OBSHotkeyEdit::keyPressEvent(QKeyEvent *event)
#endif #endif
default: default:
new_key.key = new_key.key = obs_key_from_virtual_key(event->nativeVirtualKey());
obs_key_from_virtual_key(event->nativeVirtualKey());
} }
new_key.modifiers = new_key.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());
TranslateQtKeyboardEventModifiers(event->modifiers());
HandleNewKey(new_key); HandleNewKey(new_key);
} }
@ -81,8 +79,7 @@ void OBSHotkeyEdit::keyReleaseEvent(QKeyEvent *event)
// kVK_CapsLock == 57 // kVK_CapsLock == 57
new_key.key = obs_key_from_virtual_key(57); new_key.key = obs_key_from_virtual_key(57);
new_key.modifiers = new_key.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());
TranslateQtKeyboardEventModifiers(event->modifiers());
HandleNewKey(new_key); HandleNewKey(new_key);
} }
@ -135,8 +132,7 @@ void OBSHotkeyEdit::mousePressEvent(QMouseEvent *event)
#undef MAP_BUTTON #undef MAP_BUTTON
} }
new_key.modifiers = new_key.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());
TranslateQtKeyboardEventModifiers(event->modifiers());
HandleNewKey(new_key); HandleNewKey(new_key);
} }
@ -198,22 +194,19 @@ void OBSHotkeyEdit::UpdateDuplicationState()
void OBSHotkeyEdit::InitSignalHandler() void OBSHotkeyEdit::InitSignalHandler()
{ {
layoutChanged = { layoutChanged = {obs_get_signal_handler(), "hotkey_layout_change",
obs_get_signal_handler(), "hotkey_layout_change", [](void *this_, calldata_t *) {
[](void *this_, calldata_t *) { auto edit = static_cast<OBSHotkeyEdit *>(this_);
auto edit = static_cast<OBSHotkeyEdit *>(this_); QMetaObject::invokeMethod(edit, "ReloadKeyLayout");
QMetaObject::invokeMethod(edit, "ReloadKeyLayout"); },
}, this};
this};
} }
void OBSHotkeyEdit::CreateDupeIcon() void OBSHotkeyEdit::CreateDupeIcon()
{ {
dupeIcon = addAction(settings->GetHotkeyConflictIcon(), dupeIcon = addAction(settings->GetHotkeyConflictIcon(), ActionPosition::TrailingPosition);
ActionPosition::TrailingPosition);
dupeIcon->setToolTip(QTStr("Basic.Settings.Hotkeys.DuplicateWarning")); dupeIcon->setToolTip(QTStr("Basic.Settings.Hotkeys.DuplicateWarning"));
QObject::connect(dupeIcon, &QAction::triggered, QObject::connect(dupeIcon, &QAction::triggered, [=] { emit SearchKey(key); });
[=] { emit SearchKey(key); });
dupeIcon->setVisible(false); dupeIcon->setVisible(false);
} }
@ -222,8 +215,7 @@ void OBSHotkeyEdit::ReloadKeyLayout()
RenderKey(); RenderKey();
} }
void OBSHotkeyWidget::SetKeyCombinations( void OBSHotkeyWidget::SetKeyCombinations(const std::vector<obs_key_combination_t> &combos)
const std::vector<obs_key_combination_t> &combos)
{ {
if (combos.empty()) if (combos.empty())
AddEdit({0, OBS_KEY_NONE}); AddEdit({0, OBS_KEY_NONE});
@ -234,9 +226,7 @@ void OBSHotkeyWidget::SetKeyCombinations(
bool OBSHotkeyWidget::Changed() const bool OBSHotkeyWidget::Changed() const
{ {
return changed || return changed || std::any_of(begin(edits), end(edits), [](OBSHotkeyEdit *edit) { return edit->changed; });
std::any_of(begin(edits), end(edits),
[](OBSHotkeyEdit *edit) { return edit->changed; });
} }
void OBSHotkeyWidget::Apply() void OBSHotkeyWidget::Apply()
@ -252,8 +242,7 @@ void OBSHotkeyWidget::Apply()
revertButton->setEnabled(false); revertButton->setEnabled(false);
} }
void OBSHotkeyWidget::GetCombinations( void OBSHotkeyWidget::GetCombinations(std::vector<obs_key_combination_t> &combinations) const
std::vector<obs_key_combination_t> &combinations) const
{ {
combinations.clear(); combinations.clear();
for (auto &edit : edits) for (auto &edit : edits)
@ -275,16 +264,14 @@ void OBSHotkeyWidget::Save(std::vector<obs_key_combination_t> &combinations)
auto AtomicUpdate = [&]() { auto AtomicUpdate = [&]() {
ignoreChangedBindings = true; ignoreChangedBindings = true;
obs_hotkey_load_bindings(id, combinations.data(), obs_hotkey_load_bindings(id, combinations.data(), combinations.size());
combinations.size());
ignoreChangedBindings = false; ignoreChangedBindings = false;
}; };
using AtomicUpdate_t = decltype(&AtomicUpdate); using AtomicUpdate_t = decltype(&AtomicUpdate);
obs_hotkey_update_atomic( obs_hotkey_update_atomic([](void *d) { (*static_cast<AtomicUpdate_t>(d))(); },
[](void *d) { (*static_cast<AtomicUpdate_t>(d))(); }, static_cast<void *>(&AtomicUpdate));
static_cast<void *>(&AtomicUpdate));
} }
void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx) void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
@ -302,13 +289,10 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
clear->setToolTip(QTStr("Clear")); clear->setToolTip(QTStr("Clear"));
clear->setEnabled(!obs_key_combination_is_empty(combo)); clear->setEnabled(!obs_key_combination_is_empty(combo));
QObject::connect( QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, [=](obs_key_combination_t new_combo) {
edit, &OBSHotkeyEdit::KeyChanged, clear->setEnabled(!obs_key_combination_is_empty(new_combo));
[=](obs_key_combination_t new_combo) { revert->setEnabled(edit->original != new_combo);
clear->setEnabled( });
!obs_key_combination_is_empty(new_combo));
revert->setEnabled(edit->original != new_combo);
});
auto add = new QPushButton; auto add = new QPushButton;
add->setProperty("class", "icon-plus"); add->setProperty("class", "icon-plus");
@ -320,8 +304,7 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
remove->setEnabled(removeButtons.size() > 0); remove->setEnabled(removeButtons.size() > 0);
auto CurrentIndex = [&, remove] { auto CurrentIndex = [&, remove] {
auto res = std::find(begin(removeButtons), end(removeButtons), auto res = std::find(begin(removeButtons), end(removeButtons), remove);
remove);
return std::distance(begin(removeButtons), res); return std::distance(begin(removeButtons), res);
}; };
@ -329,8 +312,7 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
AddEdit({0, OBS_KEY_NONE}, CurrentIndex() + 1); AddEdit({0, OBS_KEY_NONE}, CurrentIndex() + 1);
}); });
QObject::connect(remove, &QPushButton::clicked, QObject::connect(remove, &QPushButton::clicked, [&, CurrentIndex] { RemoveEdit(CurrentIndex()); });
[&, CurrentIndex] { RemoveEdit(CurrentIndex()); });
QHBoxLayout *subLayout = new QHBoxLayout; QHBoxLayout *subLayout = new QHBoxLayout;
subLayout->setContentsMargins(0, 2, 0, 2); subLayout->setContentsMargins(0, 2, 0, 2);
@ -355,17 +337,11 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
layout()->insertLayout(idx, subLayout); layout()->insertLayout(idx, subLayout);
QObject::connect(revert, &QPushButton::clicked, edit, QObject::connect(revert, &QPushButton::clicked, edit, &OBSHotkeyEdit::ResetKey);
&OBSHotkeyEdit::ResetKey); QObject::connect(clear, &QPushButton::clicked, edit, &OBSHotkeyEdit::ClearKey);
QObject::connect(clear, &QPushButton::clicked, edit,
&OBSHotkeyEdit::ClearKey);
QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, [&](obs_key_combination) { emit KeyChanged(); });
[&](obs_key_combination) { emit KeyChanged(); }); QObject::connect(edit, &OBSHotkeyEdit::SearchKey, [=](obs_key_combination combo) { emit SearchKey(combo); });
QObject::connect(edit, &OBSHotkeyEdit::SearchKey,
[=](obs_key_combination combo) {
emit SearchKey(combo);
});
} }
void OBSHotkeyWidget::RemoveEdit(size_t idx, bool signal) void OBSHotkeyWidget::RemoveEdit(size_t idx, bool signal)
@ -398,8 +374,7 @@ void OBSHotkeyWidget::BindingsChanged(void *data, calldata_t *param)
auto widget = static_cast<OBSHotkeyWidget *>(data); auto widget = static_cast<OBSHotkeyWidget *>(data);
auto key = static_cast<obs_hotkey_t *>(calldata_ptr(param, "key")); auto key = static_cast<obs_hotkey_t *>(calldata_ptr(param, "key"));
QMetaObject::invokeMethod(widget, "HandleChangedBindings", QMetaObject::invokeMethod(widget, "HandleChangedBindings", Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key)));
Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key)));
} }
void OBSHotkeyWidget::HandleChangedBindings(obs_hotkey_id id_) void OBSHotkeyWidget::HandleChangedBindings(obs_hotkey_id id_)

View File

@ -27,14 +27,12 @@
#include <obs.hpp> #include <obs.hpp>
static inline bool operator!=(const obs_key_combination_t &c1, static inline bool operator!=(const obs_key_combination_t &c1, const obs_key_combination_t &c2)
const obs_key_combination_t &c2)
{ {
return c1.modifiers != c2.modifiers || c1.key != c2.key; return c1.modifiers != c2.modifiers || c1.key != c2.key;
} }
static inline bool operator==(const obs_key_combination_t &c1, static inline bool operator==(const obs_key_combination_t &c1, const obs_key_combination_t &c2)
const obs_key_combination_t &c2)
{ {
return !(c1 != c2); return !(c1 != c2);
} }
@ -58,8 +56,7 @@ class OBSHotkeyEdit : public QLineEdit {
Q_OBJECT; Q_OBJECT;
public: public:
OBSHotkeyEdit(QWidget *parent, obs_key_combination_t original, OBSHotkeyEdit(QWidget *parent, obs_key_combination_t original, OBSBasicSettings *settings)
OBSBasicSettings *settings)
: QLineEdit(parent), : QLineEdit(parent),
original(original), original(original),
settings(settings) settings(settings)
@ -74,10 +71,7 @@ public:
InitSignalHandler(); InitSignalHandler();
ResetKey(); ResetKey();
} }
OBSHotkeyEdit(QWidget *parent = nullptr) OBSHotkeyEdit(QWidget *parent = nullptr) : QLineEdit(parent), original({}), settings(nullptr)
: QLineEdit(parent),
original({}),
settings(nullptr)
{ {
#ifdef __APPLE__ #ifdef __APPLE__
// disable the input cursor on OSX, focus should be clear // disable the input cursor on OSX, focus should be clear
@ -129,14 +123,12 @@ class OBSHotkeyWidget : public QWidget {
Q_OBJECT; Q_OBJECT;
public: public:
OBSHotkeyWidget(QWidget *parent, obs_hotkey_id id, std::string name, OBSHotkeyWidget(QWidget *parent, obs_hotkey_id id, std::string name, OBSBasicSettings *settings,
OBSBasicSettings *settings,
const std::vector<obs_key_combination_t> &combos = {}) const std::vector<obs_key_combination_t> &combos = {})
: QWidget(parent), : QWidget(parent),
id(id), id(id),
name(name), name(name),
bindingsChanged(obs_get_signal_handler(), bindingsChanged(obs_get_signal_handler(), "hotkey_bindings_changed",
"hotkey_bindings_changed",
&OBSHotkeyWidget::BindingsChanged, this), &OBSHotkeyWidget::BindingsChanged, this),
settings(settings) settings(settings)
{ {
@ -187,10 +179,7 @@ private:
bool ignoreChangedBindings = false; bool ignoreChangedBindings = false;
OBSBasicSettings *settings; OBSBasicSettings *settings;
QVBoxLayout *layout() const QVBoxLayout *layout() const { return dynamic_cast<QVBoxLayout *>(QWidget::layout()); }
{
return dynamic_cast<QVBoxLayout *>(QWidget::layout());
}
private slots: private slots:
void HandleChangedBindings(obs_hotkey_id id_); void HandleChangedBindings(obs_hotkey_id id_);

View File

@ -33,11 +33,9 @@ static bool source_name_exists(const Json::array &sources, const string &name)
return false; return false;
} }
#define translate_int(in_key, in, out_key, out, off) \ #define translate_int(in_key, in, out_key, out, off) out[out_key] = in[in_key].int_value() + off;
out[out_key] = in[in_key].int_value() + off;
#define translate_string(in_key, in, out_key, out) out[out_key] = in[in_key]; #define translate_string(in_key, in, out_key, out) out[out_key] = in[in_key];
#define translate_bool(in_key, in, out_key, out) \ #define translate_bool(in_key, in, out_key, out) out[out_key] = in[in_key].int_value() == 1;
out[out_key] = in[in_key].int_value() == 1;
static Json::object translate_scene_item(const Json &in, const Json &source) static Json::object translate_scene_item(const Json &in, const Json &source)
{ {
@ -122,22 +120,15 @@ static Json::object translate_source(const Json &in, const Json &sources)
settings["outline_color"] = color; settings["outline_color"] = color;
translate_string("text", in_settings, "text", settings); translate_string("text", in_settings, "text", settings);
translate_int("backgroundOpacity", in_settings, "bk_opacity", translate_int("backgroundOpacity", in_settings, "bk_opacity", settings, 0);
settings, 0);
translate_bool("vertical", in_settings, "vertical", settings); translate_bool("vertical", in_settings, "vertical", settings);
translate_int("textOpacity", in_settings, "opacity", settings, translate_int("textOpacity", in_settings, "opacity", settings, 0);
0);
translate_bool("useOutline", in_settings, "outline", settings); translate_bool("useOutline", in_settings, "outline", settings);
translate_int("outlineOpacity", in_settings, "outline_opacity", translate_int("outlineOpacity", in_settings, "outline_opacity", settings, 0);
settings, 0); translate_int("outlineSize", in_settings, "outline_size", settings, 0);
translate_int("outlineSize", in_settings, "outline_size", translate_bool("useTextExtents", in_settings, "extents", settings);
settings, 0); translate_int("extentWidth", in_settings, "extents_cx", settings, 0);
translate_bool("useTextExtents", in_settings, "extents", translate_int("extentHeight", in_settings, "extents_cy", settings, 0);
settings);
translate_int("extentWidth", in_settings, "extents_cx",
settings, 0);
translate_int("extentHeight", in_settings, "extents_cy",
settings, 0);
translate_bool("mode", in_settings, "read_from_file", settings); translate_bool("mode", in_settings, "read_from_file", settings);
translate_bool("wrap", in_settings, "extents_wrap", settings); translate_bool("wrap", in_settings, "extents_wrap", settings);
@ -145,9 +136,7 @@ static Json::object translate_source(const Json &in, const Json &sources)
settings["file"] = StringReplace(str, "\\\\", "/"); settings["file"] = StringReplace(str, "\\\\", "/");
int in_align = in_settings["align"].int_value(); int in_align = in_settings["align"].int_value();
string align = in_align == 0 string align = in_align == 0 ? "left" : (in_align == 1 ? "center" : "right");
? "left"
: (in_align == 1 ? "center" : "right");
settings["align"] = align; settings["align"] = align;
@ -181,8 +170,7 @@ static Json::object translate_source(const Json &in, const Json &sources)
out["id"] = "monitor_capture"; out["id"] = "monitor_capture";
translate_int("monitor", in_settings, "monitor", settings, 0); translate_int("monitor", in_settings, "monitor", settings, 0);
translate_bool("captureMouse", in_settings, "capture_cursor", translate_bool("captureMouse", in_settings, "capture_cursor", settings);
settings);
} else if (id == "BitmapImageSource") { } else if (id == "BitmapImageSource") {
out["id"] = "image_source"; out["id"] = "image_source";
@ -214,10 +202,7 @@ static Json::object translate_source(const Json &in, const Json &sources)
out["id"] = "browser_source"; out["id"] = "browser_source";
string browser_dec = string browser_dec =
QByteArray::fromBase64(in_settings["sourceSettings"] QByteArray::fromBase64(in_settings["sourceSettings"].string_value().c_str()).toStdString();
.string_value()
.c_str())
.toStdString();
string err; string err;
@ -258,8 +243,7 @@ static Json::object translate_source(const Json &in, const Json &sources)
settings["window"] = ":" + winClass + ":" + exec; settings["window"] = ":" + winClass + ":" + exec;
translate_bool("captureMouse", in_settings, "capture_cursor", translate_bool("captureMouse", in_settings, "capture_cursor", settings);
settings);
} }
out["settings"] = settings; out["settings"] = settings;
@ -303,10 +287,8 @@ static void translate_sc(const Json &in, Json &out)
for (size_t x = sources.size(); x > 0; x--) { for (size_t x = sources.size(); x > 0; x--) {
Json source = sources[x - 1]; Json source = sources[x - 1];
Json::object out_source = Json::object out_source = translate_source(source, out_sources);
translate_source(source, out_sources); Json::object out_item = translate_scene_item(source, out_source);
Json::object out_item =
translate_scene_item(source, out_source);
out_item["id"] = (int)x - 1; out_item["id"] = (int)x - 1;
@ -316,12 +298,10 @@ static void translate_sc(const Json &in, Json &out)
out_sources.push_back(out_source); out_sources.push_back(out_source);
} }
out_sources.push_back(Json::object{ out_sources.push_back(
{"id", "scene"}, Json::object{{"id", "scene"},
{"name", in_scene["name"]}, {"name", in_scene["name"]},
{"settings", {"settings", Json::object{{"items", items}, {"id_counter", (int)items.size()}}}});
Json::object{{"items", items},
{"id_counter", (int)items.size()}}}});
} }
res["current_scene"] = first_name; res["current_scene"] = first_name;
@ -332,8 +312,7 @@ static void translate_sc(const Json &in, Json &out)
out = res; out = res;
} }
static void create_string(const string &name, Json::object &out, static void create_string(const string &name, Json::object &out, const string &data)
const string &data)
{ {
string str = StringReplace(data, "\\\\", "/"); string str = StringReplace(data, "\\\\", "/");
out[name] = str; out[name] = str;
@ -346,15 +325,13 @@ static void create_string_obj(const string &data, Json::array &arr)
arr.push_back(obj); arr.push_back(obj);
} }
static void create_double(const string &name, Json::object &out, static void create_double(const string &name, Json::object &out, const string &data)
const string &data)
{ {
double d = atof(data.c_str()); double d = atof(data.c_str());
out[name] = d; out[name] = d;
} }
static void create_int(const string &name, Json::object &out, static void create_int(const string &name, Json::object &out, const string &data)
const string &data)
{ {
int i = atoi(data.c_str()); int i = atoi(data.c_str());
out[name] = i; out[name] = i;
@ -376,8 +353,7 @@ static void create_data_item(Json::object &out, const string &line)
string first = line.substr(end_pos + 3); string first = line.substr(end_pos + 3);
if ((first[0] >= 'A' && first[0] <= 'Z') || if ((first[0] >= 'A' && first[0] <= 'Z') || (first[0] >= 'a' && first[0] <= 'z') || first[0] == '\\' ||
(first[0] >= 'a' && first[0] <= 'z') || first[0] == '\\' ||
first[0] == '/') { first[0] == '/') {
if (out.find(c_name) != out.end()) { if (out.find(c_name) != out.end()) {
Json::array arr = out[c_name].array_items(); Json::array arr = out[c_name].array_items();
@ -515,8 +491,7 @@ int ClassicImporter::ImportScenes(const string &path, string &name, Json &res)
string line = ReadLine(file); string line = ReadLine(file);
while (!line.empty() && line[0] != '\0') { while (!line.empty() && line[0] != '\0') {
string key = line != "global sources : {" ? "scenes" string key = line != "global sources : {" ? "scenes" : "globals";
: "globals";
Json::array arr = create_sources(data, line, file); Json::array arr = create_sources(data, line, file);
data[key] = arr; data[key] = arr;

View File

@ -32,8 +32,7 @@ void ImportersInit()
importers.push_back(make_unique<XSplitImporter>()); importers.push_back(make_unique<XSplitImporter>());
} }
int ImportSCFromProg(const string &path, string &name, const string &program, int ImportSCFromProg(const string &path, string &name, const string &program, Json &res)
Json &res)
{ {
if (!os_file_exists(path.c_str())) { if (!os_file_exists(path.c_str())) {
return IMPORTER_FILE_NOT_FOUND; return IMPORTER_FILE_NOT_FOUND;

View File

@ -41,8 +41,7 @@ class Importer {
public: public:
virtual ~Importer() {} virtual ~Importer() {}
virtual std::string Prog() { return "Null"; }; virtual std::string Prog() { return "Null"; };
virtual int ImportScenes(const std::string &path, std::string &name, virtual int ImportScenes(const std::string &path, std::string &name, json11::Json &res) = 0;
json11::Json &res) = 0;
virtual bool Check(const std::string &path) = 0; virtual bool Check(const std::string &path) = 0;
virtual std::string Name(const std::string &path) = 0; virtual std::string Name(const std::string &path) = 0;
virtual OBSImporterFiles FindFiles() virtual OBSImporterFiles FindFiles()
@ -55,8 +54,7 @@ public:
class ClassicImporter : public Importer { class ClassicImporter : public Importer {
public: public:
std::string Prog() { return "OBSClassic"; }; std::string Prog() { return "OBSClassic"; };
int ImportScenes(const std::string &path, std::string &name, int ImportScenes(const std::string &path, std::string &name, json11::Json &res);
json11::Json &res);
bool Check(const std::string &path); bool Check(const std::string &path);
std::string Name(const std::string &path); std::string Name(const std::string &path);
OBSImporterFiles FindFiles(); OBSImporterFiles FindFiles();
@ -65,8 +63,7 @@ public:
class StudioImporter : public Importer { class StudioImporter : public Importer {
public: public:
std::string Prog() { return "OBSStudio"; }; std::string Prog() { return "OBSStudio"; };
int ImportScenes(const std::string &path, std::string &name, int ImportScenes(const std::string &path, std::string &name, json11::Json &res);
json11::Json &res);
bool Check(const std::string &path); bool Check(const std::string &path);
std::string Name(const std::string &path); std::string Name(const std::string &path);
}; };
@ -74,8 +71,7 @@ public:
class SLImporter : public Importer { class SLImporter : public Importer {
public: public:
std::string Prog() { return "Streamlabs"; }; std::string Prog() { return "Streamlabs"; };
int ImportScenes(const std::string &path, std::string &name, int ImportScenes(const std::string &path, std::string &name, json11::Json &res);
json11::Json &res);
bool Check(const std::string &path); bool Check(const std::string &path);
std::string Name(const std::string &path); std::string Name(const std::string &path);
OBSImporterFiles FindFiles(); OBSImporterFiles FindFiles();
@ -84,8 +80,7 @@ public:
class XSplitImporter : public Importer { class XSplitImporter : public Importer {
public: public:
std::string Prog() { return "XSplitBroadcaster"; }; std::string Prog() { return "XSplitBroadcaster"; };
int ImportScenes(const std::string &path, std::string &name, int ImportScenes(const std::string &path, std::string &name, json11::Json &res);
json11::Json &res);
bool Check(const std::string &path); bool Check(const std::string &path);
std::string Name(const std::string &) { return "XSplit Import"; }; std::string Name(const std::string &) { return "XSplit Import"; };
OBSImporterFiles FindFiles(); OBSImporterFiles FindFiles();
@ -96,8 +91,7 @@ void ImportersInit();
std::string DetectProgram(const std::string &path); std::string DetectProgram(const std::string &path);
std::string GetSCName(const std::string &path, const std::string &prog); std::string GetSCName(const std::string &path, const std::string &prog);
int ImportSCFromProg(const std::string &path, std::string &name, int ImportSCFromProg(const std::string &path, std::string &name, const std::string &program, json11::Json &res);
const std::string &program, json11::Json &res);
int ImportSC(const std::string &path, std::string &name, json11::Json &res); int ImportSC(const std::string &path, std::string &name, json11::Json &res);
OBSImporterFiles ImportersFindFiles(); OBSImporterFiles ImportersFindFiles();
@ -135,9 +129,7 @@ static inline std::string GetFolderFromPath(const std::string &path)
return path.substr(0, pos + 1); return path.substr(0, pos + 1);
} }
static inline std::string StringReplace(const std::string &in, static inline std::string StringReplace(const std::string &in, const std::string &search, const std::string &rep)
const std::string &search,
const std::string &rep)
{ {
std::string res = in; std::string res = in;
size_t pos; size_t pos;

View File

@ -90,10 +90,8 @@ static string translate_hotkey(const Json &hotkey, const string &source)
return out + source; \ return out + source; \
} }
add_translation(name, "TOGGLE_SOURCE_VISIBILITY_SHOW", add_translation(name, "TOGGLE_SOURCE_VISIBILITY_SHOW", "libobs.show_scene_item.", source);
"libobs.show_scene_item.", source); add_translation(name, "TOGGLE_SOURCE_VISIBILITY_HIDE", "libobs.hide_scene_item.", source);
add_translation(name, "TOGGLE_SOURCE_VISIBILITY_HIDE",
"libobs.hide_scene_item.", source);
string empty = ""; string empty = "";
@ -103,8 +101,7 @@ static string translate_hotkey(const Json &hotkey, const string &source)
add_translation(name, "TOGGLE_UNMUTE", "libobs.unmute", empty); add_translation(name, "TOGGLE_UNMUTE", "libobs.unmute", empty);
add_translation(name, "PUSH_TO_MUTE", "libobs.push-to-mute", empty); add_translation(name, "PUSH_TO_MUTE", "libobs.push-to-mute", empty);
add_translation(name, "PUSH_TO_TALK", "libobs.push-to-talk", empty); add_translation(name, "PUSH_TO_TALK", "libobs.push-to-talk", empty);
add_translation(name, "GAME_CAPTURE_HOTKEY_START", "hotkey_start", add_translation(name, "GAME_CAPTURE_HOTKEY_START", "hotkey_start", empty);
empty);
add_translation(name, "GAME_CAPTURE_HOTKEY_STOP", "hotkey_stop", empty); add_translation(name, "GAME_CAPTURE_HOTKEY_STOP", "hotkey_stop", empty);
return ""; return "";
@ -124,9 +121,7 @@ static bool source_name_exists(const Json::array &sources, const string &name)
return false; return false;
} }
static string get_source_name_from_id(const Json &root, static string get_source_name_from_id(const Json &root, const Json::array &sources, const string &id)
const Json::array &sources,
const string &id)
{ {
for (size_t i = 0; i < sources.size(); i++) { for (size_t i = 0; i < sources.size(); i++) {
Json item = sources[i]; Json item = sources[i];
@ -158,8 +153,7 @@ static string get_source_name_from_id(const Json &root,
return ""; return "";
} }
static void get_hotkey_bindings(Json::object &out_hotkeys, static void get_hotkey_bindings(Json::object &out_hotkeys, const Json &in_hotkeys, const string &name)
const Json &in_hotkeys, const string &name)
{ {
Json::array hot_arr = in_hotkeys.array_items(); Json::array hot_arr = in_hotkeys.array_items();
for (size_t i = 0; i < hot_arr.size(); i++) { for (size_t i = 0; i < hot_arr.size(); i++) {
@ -173,26 +167,24 @@ static void get_hotkey_bindings(Json::object &out_hotkeys,
Json binding = bindings[x]; Json binding = bindings[x];
Json modifiers = binding["modifiers"]; Json modifiers = binding["modifiers"];
string key = string key = translate_key(binding["key"].string_value());
translate_key(binding["key"].string_value());
if (key == "IGNORE") if (key == "IGNORE")
continue; continue;
out_hotkey.push_back( out_hotkey.push_back(Json::object{{"control", modifiers["ctrl"]},
Json::object{{"control", modifiers["ctrl"]}, {"shift", modifiers["shift"]},
{"shift", modifiers["shift"]}, {"command", modifiers["meta"]},
{"command", modifiers["meta"]}, {"alt", modifiers["alt"]},
{"alt", modifiers["alt"]}, {"key", key}});
{"key", key}});
} }
out_hotkeys[hotkey_name] = out_hotkey; out_hotkeys[hotkey_name] = out_hotkey;
} }
} }
static void get_scene_items(const Json &root, const Json::array &out_sources, static void get_scene_items(const Json &root, const Json::array &out_sources, Json::object &scene,
Json::object &scene, const Json::array &in) const Json::array &in)
{ {
int length = 0; int length = 0;
@ -206,35 +198,29 @@ static void get_scene_items(const Json &root, const Json::array &out_sources,
string id = item["sourceId"].string_value(); string id = item["sourceId"].string_value();
string name = get_source_name_from_id(root, out_sources, id); string name = get_source_name_from_id(root, out_sources, id);
Json::array hotkey_items = Json::array hotkey_items = item["hotkeys"]["items"].array_items();
item["hotkeys"]["items"].array_items();
get_hotkey_bindings(hotkeys, hotkey_items, name); get_hotkey_bindings(hotkeys, hotkey_items, name);
out_items.push_back(Json::object{ out_items.push_back(Json::object{{"name", name},
{"name", name}, {"id", length++},
{"id", length++}, {"pos", Json::object{{"x", item["x"]}, {"y", item["y"]}}},
{"pos", {"scale", Json::object{{"x", item["scaleX"]}, {"y", item["scaleY"]}}},
Json::object{{"x", item["x"]}, {"y", item["y"]}}}, {"rot", item["rotation"]},
{"scale", Json::object{{"x", item["scaleX"]}, {"visible", item["visible"]},
{"y", item["scaleY"]}}}, {"crop_top", in_crop["top"]},
{"rot", item["rotation"]}, {"crop_bottom", in_crop["bottom"]},
{"visible", item["visible"]}, {"crop_left", in_crop["left"]},
{"crop_top", in_crop["top"]}, {"crop_right", in_crop["right"]}});
{"crop_bottom", in_crop["bottom"]},
{"crop_left", in_crop["left"]},
{"crop_right", in_crop["right"]}});
} }
scene["hotkeys"] = hotkeys; scene["hotkeys"] = hotkeys;
scene["settings"] = scene["settings"] = Json::object{{"items", out_items}, {"id_counter", length}};
Json::object{{"items", out_items}, {"id_counter", length}};
} }
static void translate_screen_capture(Json::object &out_settings, string &type) static void translate_screen_capture(Json::object &out_settings, string &type)
{ {
string subtype_info = string subtype_info = out_settings["capture_source_list"].string_value();
out_settings["capture_source_list"].string_value();
size_t pos = subtype_info.find(':'); size_t pos = subtype_info.find(':');
string subtype = subtype_info.substr(0, pos); string subtype = subtype_info.substr(0, pos);
@ -246,8 +232,7 @@ static void translate_screen_capture(Json::object &out_settings, string &type)
} else if (subtype == "window") { } else if (subtype == "window") {
type = "window_capture"; type = "window_capture";
out_settings["cursor"] = out_settings["capture_cursor"]; out_settings["cursor"] = out_settings["capture_cursor"];
out_settings["window"] = out_settings["window"] = out_settings["capture_window_line"].string_value();
out_settings["capture_window_line"].string_value();
out_settings["capture_cursor"] = nullptr; out_settings["capture_cursor"] = nullptr;
} }
out_settings["auto_capture_rules_path"] = nullptr; out_settings["auto_capture_rules_path"] = nullptr;
@ -272,16 +257,14 @@ static int attempt_import(const Json &root, const string &name, Json &res)
Json source = source_arr[i]; Json source = source_arr[i];
Json in_hotkeys = source["hotkeys"]; Json in_hotkeys = source["hotkeys"];
Json::array hotkey_items = Json::array hotkey_items = source["hotkeys"]["items"].array_items();
source["hotkeys"]["items"].array_items();
Json in_filters = source["filters"]; Json in_filters = source["filters"];
Json::array filter_items = in_filters["items"].array_items(); Json::array filter_items = in_filters["items"].array_items();
Json in_settings = source["settings"]; Json in_settings = source["settings"];
Json in_sync = source["syncOffset"]; Json in_sync = source["syncOffset"];
int sync = (int)(in_sync["sec"].number_value() * 1000000000 + int sync = (int)(in_sync["sec"].number_value() * 1000000000 + in_sync["nsec"].number_value());
in_sync["nsec"].number_value());
double vol = source["volume"].number_value(); double vol = source["volume"].number_value();
bool muted = source["muted"].bool_value(); bool muted = source["muted"].bool_value();
@ -313,17 +296,16 @@ static int attempt_import(const Json &root, const string &name, Json &res)
translate_screen_capture(out_settings, type); translate_screen_capture(out_settings, type);
} }
out_sources.push_back( out_sources.push_back(Json::object{{"filters", out_filters},
Json::object{{"filters", out_filters}, {"hotkeys", out_hotkeys},
{"hotkeys", out_hotkeys}, {"id", type},
{"id", type}, {"sl_id", sl_id},
{"sl_id", sl_id}, {"settings", out_settings},
{"settings", out_settings}, {"sync", sync},
{"sync", sync}, {"volume", vol},
{"volume", vol}, {"muted", muted},
{"muted", muted}, {"name", out_name},
{"name", out_name}, {"monitoring_type", monitoring}});
{"monitoring_type", monitoring}});
} }
string scene_name = ""; string scene_name = "";
@ -362,15 +344,10 @@ static int attempt_import(const Json &root, const string &name, Json &res)
string sl_id = scene["id"].string_value(); string sl_id = scene["id"].string_value();
Json::object out = Json::object out = Json::object{{"filters", out_filters}, {"hotkeys", out_hotkeys},
Json::object{{"filters", out_filters}, {"id", "scene"}, {"sl_id", sl_id},
{"hotkeys", out_hotkeys}, {"settings", in_settings}, {"volume", 1.0},
{"id", "scene"}, {"name", out_name}, {"private_settings", Json::object{}}};
{"sl_id", sl_id},
{"settings", in_settings},
{"volume", 1.0},
{"name", out_name},
{"private_settings", Json::object{}}};
Json in_items = scene["sceneItems"]; Json in_items = scene["sceneItems"];
Json::array items_arr = in_items["items"].array_items(); Json::array items_arr = in_items["items"].array_items();
@ -394,11 +371,10 @@ static int attempt_import(const Json &root, const string &name, Json &res)
if (id == t_id) if (id == t_id)
transition_name = name; transition_name = name;
out_transitions.push_back( out_transitions.push_back(Json::object{{"id", transition["type"]},
Json::object{{"id", transition["type"]}, {"settings", in_settings},
{"settings", in_settings}, {"name", name},
{"name", name}, {"duration", duration}});
{"duration", duration}});
} }
res = Json::object{{"sources", out_sources}, res = Json::object{{"sources", out_sources},
@ -420,15 +396,13 @@ string SLImporter::Name(const string &path)
string manifest_path = folder + "manifest.json"; string manifest_path = folder + "manifest.json";
if (os_file_exists(manifest_path.c_str())) { if (os_file_exists(manifest_path.c_str())) {
BPtr<char> file_data = BPtr<char> file_data = os_quick_read_utf8_file(manifest_path.c_str());
os_quick_read_utf8_file(manifest_path.c_str());
string err; string err;
Json data = Json::parse(file_data, err); Json data = Json::parse(file_data, err);
if (err == "") { if (err == "") {
Json::array collections = Json::array collections = data["collections"].array_items();
data["collections"].array_items();
bool name_set = false; bool name_set = false;
@ -513,8 +487,7 @@ OBSImporterFiles SLImporter::FindFiles()
#if defined(_WIN32) || defined(__APPLE__) #if defined(_WIN32) || defined(__APPLE__)
char dst[512]; char dst[512];
int found = int found = os_get_config_path(dst, 512, "slobs-client/SceneCollections/");
os_get_config_path(dst, 512, "slobs-client/SceneCollections/");
if (found == -1) if (found == -1)
return res; return res;

View File

@ -49,19 +49,13 @@ void TranslateOSStudio(Json &res)
ClearTranslation("game_capture", "syphon-input"); ClearTranslation("game_capture", "syphon-input");
ClearTranslation("wasapi_input_capture", ClearTranslation("wasapi_input_capture", "coreaudio_input_capture");
"coreaudio_input_capture"); ClearTranslation("wasapi_output_capture", "coreaudio_output_capture");
ClearTranslation("wasapi_output_capture", ClearTranslation("pulse_input_capture", "coreaudio_input_capture");
"coreaudio_output_capture"); ClearTranslation("pulse_output_capture", "coreaudio_output_capture");
ClearTranslation("pulse_input_capture",
"coreaudio_input_capture");
ClearTranslation("pulse_output_capture",
"coreaudio_output_capture");
ClearTranslation("jack_output_capture", ClearTranslation("jack_output_capture", "coreaudio_output_capture");
"coreaudio_output_capture"); ClearTranslation("alsa_input_capture", "coreaudio_input_capture");
ClearTranslation("alsa_input_capture",
"coreaudio_input_capture");
ClearTranslation("dshow_input", "av_capture_input"); ClearTranslation("dshow_input", "av_capture_input");
ClearTranslation("v4l2_input", "av_capture_input"); ClearTranslation("v4l2_input", "av_capture_input");
@ -69,10 +63,8 @@ void TranslateOSStudio(Json &res)
ClearTranslation("xcomposite_input", "window_capture"); ClearTranslation("xcomposite_input", "window_capture");
if (id == "monitor_capture") { if (id == "monitor_capture") {
if (settings["show_cursor"].is_null() && if (settings["show_cursor"].is_null() && !settings["capture_cursor"].is_null()) {
!settings["capture_cursor"].is_null()) { bool cursor = settings["capture_cursor"].bool_value();
bool cursor =
settings["capture_cursor"].bool_value();
settings["show_cursor"] = cursor; settings["show_cursor"] = cursor;
} }
@ -84,16 +76,12 @@ void TranslateOSStudio(Json &res)
ClearTranslation("syphon-input", "game_capture"); ClearTranslation("syphon-input", "game_capture");
ClearTranslation("coreaudio_input_capture", ClearTranslation("coreaudio_input_capture", "wasapi_input_capture");
"wasapi_input_capture"); ClearTranslation("coreaudio_output_capture", "wasapi_output_capture");
ClearTranslation("coreaudio_output_capture",
"wasapi_output_capture");
ClearTranslation("pulse_input_capture", "wasapi_input_capture"); ClearTranslation("pulse_input_capture", "wasapi_input_capture");
ClearTranslation("pulse_output_capture", ClearTranslation("pulse_output_capture", "wasapi_output_capture");
"wasapi_output_capture");
ClearTranslation("jack_output_capture", ClearTranslation("jack_output_capture", "wasapi_output_capture");
"wasapi_output_capture");
ClearTranslation("alsa_input_capture", "wasapi_input_capture"); ClearTranslation("alsa_input_capture", "wasapi_input_capture");
ClearTranslation("av_capture_input", "dshow_input"); ClearTranslation("av_capture_input", "dshow_input");
@ -103,8 +91,7 @@ void TranslateOSStudio(Json &res)
if (id == "monitor_capture" || id == "xshm_input") { if (id == "monitor_capture" || id == "xshm_input") {
if (!settings["show_cursor"].is_null()) { if (!settings["show_cursor"].is_null()) {
bool cursor = bool cursor = settings["show_cursor"].bool_value();
settings["show_cursor"].bool_value();
settings["capture_cursor"] = cursor; settings["capture_cursor"] = cursor;
} }
@ -114,13 +101,10 @@ void TranslateOSStudio(Json &res)
#else #else
DirectTranslation("text_gdiplus", "text_ft2_source"); DirectTranslation("text_gdiplus", "text_ft2_source");
ClearTranslation("coreaudio_input_capture", ClearTranslation("coreaudio_input_capture", "pulse_input_capture");
"pulse_input_capture"); ClearTranslation("coreaudio_output_capture", "pulse_output_capture");
ClearTranslation("coreaudio_output_capture",
"pulse_output_capture");
ClearTranslation("wasapi_input_capture", "pulse_input_capture"); ClearTranslation("wasapi_input_capture", "pulse_input_capture");
ClearTranslation("wasapi_output_capture", ClearTranslation("wasapi_output_capture", "pulse_output_capture");
"pulse_output_capture");
ClearTranslation("av_capture_input", "v4l2_input"); ClearTranslation("av_capture_input", "v4l2_input");
ClearTranslation("dshow_input", "v4l2_input"); ClearTranslation("dshow_input", "v4l2_input");
@ -130,10 +114,8 @@ void TranslateOSStudio(Json &res)
if (id == "monitor_capture") { if (id == "monitor_capture") {
source["id"] = "xshm_input"; source["id"] = "xshm_input";
if (settings["show_cursor"].is_null() && if (settings["show_cursor"].is_null() && !settings["capture_cursor"].is_null()) {
!settings["capture_cursor"].is_null()) { bool cursor = settings["capture_cursor"].bool_value();
bool cursor =
settings["capture_cursor"].bool_value();
settings["show_cursor"] = cursor; settings["show_cursor"] = cursor;
} }
@ -158,8 +140,7 @@ static string CheckPath(const string &path, const string &rootDir)
char absPath[512]; char absPath[512];
*absPath = 0; *absPath = 0;
size_t len = os_get_abs_path((rootDir + path).c_str(), absPath, size_t len = os_get_abs_path((rootDir + path).c_str(), absPath, sizeof(absPath));
sizeof(absPath));
if (len == 0) if (len == 0)
return path; return path;
@ -185,8 +166,7 @@ void TranslatePaths(Json &res, const string &rootDir)
if (val.string_value().rfind("./", 0) != 0) if (val.string_value().rfind("./", 0) != 0)
continue; continue;
out[it->first] = out[it->first] = CheckPath(val.string_value(), rootDir);
CheckPath(val.string_value(), rootDir);
} else if (val.is_array() || val.is_object()) { } else if (val.is_array() || val.is_object()) {
TranslatePaths(val, rootDir); TranslatePaths(val, rootDir);
out[it->first] = val; out[it->first] = val;

View File

@ -74,17 +74,15 @@ static Json::object parse_text(QString &config)
Json font = Json::object{{"face", data["fontStyle"]}, {"size", 200}}; Json font = Json::object{{"face", data["fontStyle"]}, {"size", 200}};
return Json::object{ return Json::object{{"text", data["text"]},
{"text", data["text"]}, {"font", font},
{"font", font}, {"outline", out > 0},
{"outline", out > 0}, {"outline_size", out},
{"outline_size", out}, {"outline_color", hex_string_to_int(data["outlineColor"].string_value())},
{"outline_color", {"color", hex_string_to_int(data["color"].string_value())},
hex_string_to_int(data["outlineColor"].string_value())}, {"align", data["textAlign"]},
{"color", hex_string_to_int(data["color"].string_value())}, {"valign", valign},
{"align", data["textAlign"]}, {"alpha", data["opacity"]}};
{"valign", valign},
{"alpha", data["opacity"]}};
} }
static Json::array parse_playlist(QString &playlist) static Json::array parse_playlist(QString &playlist)
@ -107,8 +105,7 @@ static Json::array parse_playlist(QString &playlist)
return out; return out;
} }
static void parse_media_types(QDomNamedNodeMap &attr, Json::object &source, static void parse_media_types(QDomNamedNodeMap &attr, Json::object &source, Json::object &settings)
Json::object &settings)
{ {
QString playlist = attr.namedItem("FilePlaylist").nodeValue(); QString playlist = attr.namedItem("FilePlaylist").nodeValue();
@ -143,8 +140,7 @@ static void parse_media_types(QDomNamedNodeMap &attr, Json::object &source,
} }
} else { } else {
source["id"] = "ffmpeg_source"; source["id"] = "ffmpeg_source";
settings["local_file"] = settings["local_file"] = url.replace("\\", "/").toStdString();
url.replace("\\", "/").toStdString();
settings["is_local_file"] = true; settings["is_local_file"] = true;
} }
} }
@ -186,8 +182,7 @@ static Json::object parse_slideshow(QString &config)
return Json::object{}; return Json::object{};
return Json::object{{"randomize", opt["random"]}, return Json::object{{"randomize", opt["random"]},
{"slide_time", {"slide_time", opt["delay"].number_value() * 1000 + 700},
opt["delay"].number_value() * 1000 + 700},
{"files", files_out}}; {"files", files_out}};
} }
@ -211,8 +206,7 @@ static Json get_source_with_id(const string &src_id, const Json::array &sources)
return nullptr; return nullptr;
} }
static void parse_items(QDomNode &item, Json::array &items, static void parse_items(QDomNode &item, Json::array &items, Json::array &sources)
Json::array &sources)
{ {
while (!item.isNull()) { while (!item.isNull()) {
QDomNamedNodeMap attr = item.attributes(); QDomNamedNodeMap attr = item.attributes();
@ -245,9 +239,7 @@ static void parse_items(QDomNode &item, Json::array &items,
name = temp_name; name = temp_name;
settings = Json::object{}; settings = Json::object{};
source = Json::object{{"name", name}, source = Json::object{{"name", name}, {"src_id", srcid.toStdString()}, {"volume", vol}};
{"src_id", srcid.toStdString()},
{"volume", vol}};
/** type=1 means Media of some kind (Video Playlist, RTSP, /** type=1 means Media of some kind (Video Playlist, RTSP,
RTMP, NDI or Media File). RTMP, NDI or Media File).
@ -270,8 +262,7 @@ static void parse_items(QDomNode &item, Json::array &items,
source["id"] = "wasapi_input_capture"; source["id"] = "wasapi_input_capture";
int dev = audio.indexOf("\\wave:") + 6; int dev = audio.indexOf("\\wave:") + 6;
QString res = QString res = "{0.0.1.00000000}." + audio.mid(dev);
"{0.0.1.00000000}." + audio.mid(dev);
res = res.toLower(); res = res.toLower();
settings["device_id"] = res.toStdString(); settings["device_id"] = res.toStdString();
@ -291,24 +282,18 @@ static void parse_items(QDomNode &item, Json::array &items,
QDomNode el = options.documentElement(); QDomNode el = options.documentElement();
QDomNamedNodeMap o_attr = el.attributes(); QDomNamedNodeMap o_attr = el.attributes();
QString display = QString display = o_attr.namedItem("desktop").nodeValue();
o_attr.namedItem("desktop").nodeValue();
if (!display.isEmpty()) { if (!display.isEmpty()) {
source["id"] = "monitor_capture"; source["id"] = "monitor_capture";
int cursor = attr.namedItem("ScrCapShowMouse") int cursor = attr.namedItem("ScrCapShowMouse").nodeValue().toInt();
.nodeValue()
.toInt();
settings["capture_cursor"] = cursor == 1; settings["capture_cursor"] = cursor == 1;
} else { } else {
source["id"] = "window_capture"; source["id"] = "window_capture";
QString exec = QString exec = o_attr.namedItem("module").nodeValue();
o_attr.namedItem("module").nodeValue(); QString window = o_attr.namedItem("window").nodeValue();
QString window = QString _class = o_attr.namedItem("class").nodeValue();
o_attr.namedItem("window").nodeValue();
QString _class =
o_attr.namedItem("class").nodeValue();
int pos = exec.lastIndexOf('\\'); int pos = exec.lastIndexOf('\\');
@ -316,8 +301,7 @@ static void parse_items(QDomNode &item, Json::array &items,
_class = "class"; _class = "class";
} }
QString res = window + ":" + _class + ":" + QString res = window + ":" + _class + ":" + exec.mid(pos + 1);
exec.mid(pos + 1);
settings["window"] = res.toStdString(); settings["window"] = res.toStdString();
settings["priority"] = 2; settings["priority"] = 2;
@ -335,8 +319,7 @@ static void parse_items(QDomNode &item, Json::array &items,
QDomNamedNodeMap o_attr = el.attributes(); QDomNamedNodeMap o_attr = el.attributes();
QString name = o_attr.namedItem("wndname").nodeValue(); QString name = o_attr.namedItem("wndname").nodeValue();
QString exec = QString exec = o_attr.namedItem("imagename").nodeValue();
o_attr.namedItem("imagename").nodeValue();
QString res = name = "::" + exec; QString res = name = "::" + exec;
@ -346,8 +329,7 @@ static void parse_items(QDomNode &item, Json::array &items,
} else if (type == 8) { } else if (type == 8) {
QString plugin = attr.namedItem("item").nodeValue(); QString plugin = attr.namedItem("item").nodeValue();
if (plugin.startsWith( if (plugin.startsWith("html:plugin:imageslideshowplg*")) {
"html:plugin:imageslideshowplg*")) {
source["id"] = "slideshow"; source["id"] = "slideshow";
settings = parse_slideshow(plugin); settings = parse_slideshow(plugin);
} else if (plugin.startsWith("html:plugin:titleplg")) { } else if (plugin.startsWith("html:plugin:titleplg")) {
@ -356,13 +338,11 @@ static void parse_items(QDomNode &item, Json::array &items,
} else if (plugin.startsWith("http")) { } else if (plugin.startsWith("http")) {
source["id"] = "browser_source"; source["id"] = "browser_source";
int end = plugin.indexOf('*'); int end = plugin.indexOf('*');
settings["url"] = settings["url"] = plugin.left(end).toStdString();
plugin.left(end).toStdString();
} }
} else if (type == 11) { } else if (type == 11) {
QString id = attr.namedItem("item").nodeValue(); QString id = attr.namedItem("item").nodeValue();
Json source = Json source = get_source_with_id(id.toStdString(), sources);
get_source_with_id(id.toStdString(), sources);
name = source["name"].string_value(); name = source["name"].string_value();
goto skip; goto skip;
@ -378,26 +358,19 @@ static void parse_items(QDomNode &item, Json::array &items,
int width = ovi.base_width; int width = ovi.base_width;
int height = ovi.base_height; int height = ovi.base_height;
double pos_left = double pos_left = attr.namedItem("pos_left").nodeValue().toDouble();
attr.namedItem("pos_left").nodeValue().toDouble(); double pos_right = attr.namedItem("pos_right").nodeValue().toDouble();
double pos_right = double pos_top = attr.namedItem("pos_top").nodeValue().toDouble();
attr.namedItem("pos_right").nodeValue().toDouble(); double pos_bottom = attr.namedItem("pos_bottom").nodeValue().toDouble();
double pos_top =
attr.namedItem("pos_top").nodeValue().toDouble();
double pos_bottom =
attr.namedItem("pos_bottom").nodeValue().toDouble();
bool visible = attr.namedItem("visible").nodeValue() == "1"; bool visible = attr.namedItem("visible").nodeValue() == "1";
Json out_item = Json::object{ Json out_item = Json::object{{"bounds_type", 2},
{"bounds_type", 2}, {"pos", Json::object{{"x", pos_left * width}, {"y", pos_top * height}}},
{"pos", Json::object{{"x", pos_left * width}, {"bounds", Json::object{{"x", (pos_right - pos_left) * width},
{"y", pos_top * height}}}, {"y", (pos_bottom - pos_top) * height}}},
{"bounds", {"name", name},
Json::object{{"x", (pos_right - pos_left) * width}, {"visible", visible}};
{"y", (pos_bottom - pos_top) * height}}},
{"name", name},
{"visible", visible}};
items.push_back(out_item); items.push_back(out_item);
@ -424,10 +397,9 @@ static Json::object parse_scenes(QDomElement &scenes)
if (first.isEmpty()) if (first.isEmpty())
first = name; first = name;
Json out = Json::object{ Json out = Json::object{{"id", "scene"},
{"id", "scene"}, {"name", name.toStdString().c_str()},
{"name", name.toStdString().c_str()}, {"src_id", id.toStdString().c_str()}};
{"src_id", id.toStdString().c_str()}};
sources.push_back(out); sources.push_back(out);
} }
@ -442,8 +414,7 @@ static Json::object parse_scenes(QDomElement &scenes)
parse_items(firstChild, items, sources); parse_items(firstChild, items, sources);
Json settings = Json::object{{"items", items}, Json settings = Json::object{{"items", items}, {"id_counter", (int)items.size()}};
{"id_counter", (int)items.size()}};
source["settings"] = settings; source["settings"] = settings;
sources[i] = source; sources[i] = source;
@ -456,8 +427,7 @@ static Json::object parse_scenes(QDomElement &scenes)
{"current_program_scene", first.toStdString()}}; {"current_program_scene", first.toStdString()}};
} }
int XSplitImporter::ImportScenes(const string &path, string &name, int XSplitImporter::ImportScenes(const string &path, string &name, json11::Json &res)
json11::Json &res)
{ {
if (name == "") if (name == "")
name = "XSplit Import"; name = "XSplit Import";
@ -516,8 +486,7 @@ OBSImporterFiles XSplitImporter::FindFiles()
OBSImporterFiles res; OBSImporterFiles res;
#ifdef _WIN32 #ifdef _WIN32
char dst[512]; char dst[512];
int found = os_get_program_data_path( int found = os_get_program_data_path(dst, 512, "SplitMediaLabs\\XSplit\\Presentation2.0\\");
dst, 512, "SplitMediaLabs\\XSplit\\Presentation2.0\\");
if (found == -1) if (found == -1)
return res; return res;

View File

@ -29,8 +29,7 @@ QListWidgetItem *TakeListItem(QListWidget *widget, int row);
void DeleteListItem(QListWidget *widget, QListWidgetItem *item); void DeleteListItem(QListWidget *widget, QListWidgetItem *item);
void ClearListItems(QListWidget *widget); void ClearListItems(QListWidget *widget);
template<typename QObjectPtr> template<typename QObjectPtr> void InsertQObjectByName(std::vector<QObjectPtr> &controls, QObjectPtr control)
void InsertQObjectByName(std::vector<QObjectPtr> &controls, QObjectPtr control)
{ {
QString name = control->objectName(); QString name = control->objectName();
auto finder = [name](QObjectPtr elem) { auto finder = [name](QObjectPtr elem) {

View File

@ -2,10 +2,8 @@
LineEditAutoResize::LineEditAutoResize() : m_maxLength(32767) LineEditAutoResize::LineEditAutoResize() : m_maxLength(32767)
{ {
connect(this, &LineEditAutoResize::textChanged, this, connect(this, &LineEditAutoResize::textChanged, this, &LineEditAutoResize::checkTextLength);
&LineEditAutoResize::checkTextLength); connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this,
connect(document()->documentLayout(),
&QAbstractTextDocumentLayout::documentSizeChanged, this,
&LineEditAutoResize::resizeVertically); &LineEditAutoResize::resizeVertically);
} }
@ -63,8 +61,7 @@ void LineEditAutoResize::keyPressEvent(QKeyEvent *event)
* allow all. Actions that will still exceed the limit (like * allow all. Actions that will still exceed the limit (like
* Paste) can be caught in a later step. */ * Paste) can be caught in a later step. */
default: default:
if (toPlainText().length() >= m_maxLength && if (toPlainText().length() >= m_maxLength && event->modifiers() == Qt::NoModifier &&
event->modifiers() == Qt::NoModifier &&
!textCursor().hasSelection()) !textCursor().hasSelection())
event->ignore(); event->ignore();
else else
@ -86,8 +83,7 @@ QString LineEditAutoResize::text()
void LineEditAutoResize::setText(const QString &text) void LineEditAutoResize::setText(const QString &text)
{ {
QMetaObject::invokeMethod(this, "SetPlainText", Qt::QueuedConnection, QMetaObject::invokeMethod(this, "SetPlainText", Qt::QueuedConnection, Q_ARG(const QString &, text));
Q_ARG(const QString &, text));
} }
void LineEditAutoResize::SetPlainText(const QString &text) void LineEditAutoResize::SetPlainText(const QString &text)

View File

@ -12,23 +12,18 @@
#include "moc_log-viewer.cpp" #include "moc_log-viewer.cpp"
OBSLogViewer::OBSLogViewer(QWidget *parent) OBSLogViewer::OBSLogViewer(QWidget *parent) : QDialog(parent), ui(new Ui::OBSLogViewer)
: QDialog(parent),
ui(new Ui::OBSLogViewer)
{ {
setWindowFlags(windowFlags() & Qt::WindowMaximizeButtonHint & setWindowFlags(windowFlags() & Qt::WindowMaximizeButtonHint & ~Qt::WindowContextHelpButtonHint);
~Qt::WindowContextHelpButtonHint);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this); ui->setupUi(this);
bool showLogViewerOnStartup = config_get_bool( bool showLogViewerOnStartup = config_get_bool(App()->GetUserConfig(), "LogViewer", "ShowLogStartup");
App()->GetUserConfig(), "LogViewer", "ShowLogStartup");
ui->showStartup->setChecked(showLogViewerOnStartup); ui->showStartup->setChecked(showLogViewerOnStartup);
const char *geom = config_get_string(App()->GetUserConfig(), const char *geom = config_get_string(App()->GetUserConfig(), "LogViewer", "geometry");
"LogViewer", "geometry");
if (geom != nullptr) { if (geom != nullptr) {
QByteArray ba = QByteArray::fromBase64(QByteArray(geom)); QByteArray ba = QByteArray::fromBase64(QByteArray(geom));
@ -40,14 +35,12 @@ OBSLogViewer::OBSLogViewer(QWidget *parent)
OBSLogViewer::~OBSLogViewer() OBSLogViewer::~OBSLogViewer()
{ {
config_set_string(App()->GetUserConfig(), "LogViewer", "geometry", config_set_string(App()->GetUserConfig(), "LogViewer", "geometry", saveGeometry().toBase64().constData());
saveGeometry().toBase64().constData());
} }
void OBSLogViewer::on_showStartup_clicked(bool checked) void OBSLogViewer::on_showStartup_clicked(bool checked)
{ {
config_set_bool(App()->GetUserConfig(), "LogViewer", "ShowLogStartup", config_set_bool(App()->GetUserConfig(), "LogViewer", "ShowLogStartup", checked);
checked);
} }
extern QPointer<OBSLogViewer> obsLogViewer; extern QPointer<OBSLogViewer> obsLogViewer;

View File

@ -43,9 +43,7 @@ void MediaControls::OBSMediaPrevious(void *data, calldata_t *)
QMetaObject::invokeMethod(media, "UpdateSlideCounter"); QMetaObject::invokeMethod(media, "UpdateSlideCounter");
} }
MediaControls::MediaControls(QWidget *parent) MediaControls::MediaControls(QWidget *parent) : QWidget(parent), ui(new Ui::MediaControls)
: QWidget(parent),
ui(new Ui::MediaControls)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->playPauseButton->setProperty("class", "icon-media-play"); ui->playPauseButton->setProperty("class", "icon-media-play");
@ -54,47 +52,36 @@ MediaControls::MediaControls(QWidget *parent)
ui->stopButton->setProperty("class", "icon-media-stop"); ui->stopButton->setProperty("class", "icon-media-stop");
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
connect(&mediaTimer, &QTimer::timeout, this, connect(&mediaTimer, &QTimer::timeout, this, &MediaControls::SetSliderPosition);
&MediaControls::SetSliderPosition); connect(&seekTimer, &QTimer::timeout, this, &MediaControls::SeekTimerCallback);
connect(&seekTimer, &QTimer::timeout, this, connect(ui->slider, &AbsoluteSlider::sliderPressed, this, &MediaControls::AbsoluteSliderClicked);
&MediaControls::SeekTimerCallback); connect(ui->slider, &AbsoluteSlider::absoluteSliderHovered, this, &MediaControls::AbsoluteSliderHovered);
connect(ui->slider, &AbsoluteSlider::sliderPressed, this, connect(ui->slider, &AbsoluteSlider::sliderReleased, this, &MediaControls::AbsoluteSliderReleased);
&MediaControls::AbsoluteSliderClicked); connect(ui->slider, &AbsoluteSlider::sliderMoved, this, &MediaControls::AbsoluteSliderMoved);
connect(ui->slider, &AbsoluteSlider::absoluteSliderHovered, this,
&MediaControls::AbsoluteSliderHovered);
connect(ui->slider, &AbsoluteSlider::sliderReleased, this,
&MediaControls::AbsoluteSliderReleased);
connect(ui->slider, &AbsoluteSlider::sliderMoved, this,
&MediaControls::AbsoluteSliderMoved);
countDownTimer = config_get_bool(App()->GetUserConfig(), "BasicWindow", countDownTimer = config_get_bool(App()->GetUserConfig(), "BasicWindow", "MediaControlsCountdownTimer");
"MediaControlsCountdownTimer");
QAction *restartAction = new QAction(this); QAction *restartAction = new QAction(this);
restartAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); restartAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
restartAction->setShortcut({Qt::Key_R}); restartAction->setShortcut({Qt::Key_R});
connect(restartAction, &QAction::triggered, this, connect(restartAction, &QAction::triggered, this, &MediaControls::RestartMedia);
&MediaControls::RestartMedia);
addAction(restartAction); addAction(restartAction);
QAction *sliderFoward = new QAction(this); QAction *sliderFoward = new QAction(this);
sliderFoward->setShortcutContext(Qt::WidgetWithChildrenShortcut); sliderFoward->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(sliderFoward, &QAction::triggered, this, connect(sliderFoward, &QAction::triggered, this, &MediaControls::MoveSliderFoward);
&MediaControls::MoveSliderFoward);
sliderFoward->setShortcut({Qt::Key_Right}); sliderFoward->setShortcut({Qt::Key_Right});
addAction(sliderFoward); addAction(sliderFoward);
QAction *sliderBack = new QAction(this); QAction *sliderBack = new QAction(this);
sliderBack->setShortcutContext(Qt::WidgetWithChildrenShortcut); sliderBack->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(sliderBack, &QAction::triggered, this, connect(sliderBack, &QAction::triggered, this, &MediaControls::MoveSliderBackwards);
&MediaControls::MoveSliderBackwards);
sliderBack->setShortcut({Qt::Key_Left}); sliderBack->setShortcut({Qt::Key_Left});
addAction(sliderBack); addAction(sliderBack);
QAction *playPause = new QAction(this); QAction *playPause = new QAction(this);
playPause->setShortcutContext(Qt::WidgetWithChildrenShortcut); playPause->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(playPause, &QAction::triggered, this, connect(playPause, &QAction::triggered, this, &MediaControls::on_playPauseButton_clicked);
&MediaControls::on_playPauseButton_clicked);
playPause->setShortcut({Qt::Key_Space}); playPause->setShortcut({Qt::Key_Space});
addAction(playPause); addAction(playPause);
} }
@ -216,8 +203,7 @@ void MediaControls::SetPlayingState()
ui->playPauseButton->setProperty("class", "icon-media-pause"); ui->playPauseButton->setProperty("class", "icon-media-pause");
ui->playPauseButton->style()->unpolish(ui->playPauseButton); ui->playPauseButton->style()->unpolish(ui->playPauseButton);
ui->playPauseButton->style()->polish(ui->playPauseButton); ui->playPauseButton->style()->polish(ui->playPauseButton);
ui->playPauseButton->setToolTip( ui->playPauseButton->setToolTip(QTStr("ContextBar.MediaControls.PauseMedia"));
QTStr("ContextBar.MediaControls.PauseMedia"));
prevPaused = false; prevPaused = false;
@ -230,8 +216,7 @@ void MediaControls::SetPausedState()
ui->playPauseButton->setProperty("class", "icon-media-play"); ui->playPauseButton->setProperty("class", "icon-media-play");
ui->playPauseButton->style()->unpolish(ui->playPauseButton); ui->playPauseButton->style()->unpolish(ui->playPauseButton);
ui->playPauseButton->style()->polish(ui->playPauseButton); ui->playPauseButton->style()->polish(ui->playPauseButton);
ui->playPauseButton->setToolTip( ui->playPauseButton->setToolTip(QTStr("ContextBar.MediaControls.PlayMedia"));
QTStr("ContextBar.MediaControls.PlayMedia"));
StopMediaTimer(); StopMediaTimer();
} }
@ -241,8 +226,7 @@ void MediaControls::SetRestartState()
ui->playPauseButton->setProperty("class", "icon-media-restart"); ui->playPauseButton->setProperty("class", "icon-media-restart");
ui->playPauseButton->style()->unpolish(ui->playPauseButton); ui->playPauseButton->style()->unpolish(ui->playPauseButton);
ui->playPauseButton->style()->polish(ui->playPauseButton); ui->playPauseButton->style()->polish(ui->playPauseButton);
ui->playPauseButton->setToolTip( ui->playPauseButton->setToolTip(QTStr("ContextBar.MediaControls.RestartMedia"));
QTStr("ContextBar.MediaControls.RestartMedia"));
ui->slider->setValue(0); ui->slider->setValue(0);
@ -354,8 +338,7 @@ void MediaControls::SetSliderPosition()
float sliderPosition; float sliderPosition;
if (duration) if (duration)
sliderPosition = sliderPosition = (time / duration) * (float)ui->slider->maximum();
(time / duration) * (float)ui->slider->maximum();
else else
sliderPosition = 0.0f; sliderPosition = 0.0f;
@ -465,8 +448,7 @@ void MediaControls::on_durationLabel_clicked()
{ {
countDownTimer = !countDownTimer; countDownTimer = !countDownTimer;
config_set_bool(App()->GetUserConfig(), "BasicWindow", config_set_bool(App()->GetUserConfig(), "BasicWindow", "MediaControlsCountdownTimer", countDownTimer);
"MediaControlsCountdownTimer", countDownTimer);
if (MediaPaused()) if (MediaPaused())
SetSliderPosition(); SetSliderPosition();
@ -544,10 +526,7 @@ void MediaControls::UpdateLabels(int val)
ui->timerLabel->setText(FormatSeconds((int)(time / 1000.0f))); ui->timerLabel->setText(FormatSeconds((int)(time / 1000.0f)));
if (!countDownTimer) if (!countDownTimer)
ui->durationLabel->setText( ui->durationLabel->setText(FormatSeconds((int)(duration / 1000.0f)));
FormatSeconds((int)(duration / 1000.0f)));
else else
ui->durationLabel->setText( ui->durationLabel->setText(QString("-") + FormatSeconds((int)((duration - time) / 1000.0f)));
QString("-") +
FormatSeconds((int)((duration - time) / 1000.0f)));
} }

View File

@ -10,14 +10,7 @@ protected:
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;
public: public:
explicit inline MenuButton(QWidget *parent = nullptr) explicit inline MenuButton(QWidget *parent = nullptr) : QPushButton(parent) {}
: QPushButton(parent)
{
}
explicit inline MenuButton(const QString &text, explicit inline MenuButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent) {}
QWidget *parent = nullptr)
: QPushButton(text, parent)
{
}
}; };

View File

@ -47,18 +47,16 @@ template<typename T> struct nlohmann::adl_serializer<std::optional<T>> {
} }
}; };
NLOHMANN_JSON_SERIALIZE_ENUM(obs_scale_type, NLOHMANN_JSON_SERIALIZE_ENUM(obs_scale_type, {
{ {OBS_SCALE_DISABLE, "OBS_SCALE_DISABLE"},
{OBS_SCALE_DISABLE, "OBS_SCALE_DISABLE"}, {OBS_SCALE_POINT, "OBS_SCALE_POINT"},
{OBS_SCALE_POINT, "OBS_SCALE_POINT"}, {OBS_SCALE_BICUBIC, "OBS_SCALE_BICUBIC"},
{OBS_SCALE_BICUBIC, "OBS_SCALE_BICUBIC"}, {OBS_SCALE_BILINEAR, "OBS_SCALE_BILINEAR"},
{OBS_SCALE_BILINEAR, "OBS_SCALE_BILINEAR"}, {OBS_SCALE_LANCZOS, "OBS_SCALE_LANCZOS"},
{OBS_SCALE_LANCZOS, "OBS_SCALE_LANCZOS"}, {OBS_SCALE_AREA, "OBS_SCALE_AREA"},
{OBS_SCALE_AREA, "OBS_SCALE_AREA"}, })
})
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(media_frames_per_second, numerator, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(media_frames_per_second, numerator, denominator)
denominator)
namespace GoLiveApi { namespace GoLiveApi {
using std::string; using std::string;
@ -79,8 +77,7 @@ struct Cpu {
optional<uint32_t> speed; optional<uint32_t> speed;
optional<string> name; optional<string> name;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Cpu, physical_cores, logical_cores, NLOHMANN_DEFINE_TYPE_INTRUSIVE(Cpu, physical_cores, logical_cores, speed, name)
speed, name)
}; };
struct Memory { struct Memory {
@ -98,9 +95,8 @@ struct Gpu {
uint64_t shared_system_memory; uint64_t shared_system_memory;
optional<string> driver_version; optional<string> driver_version;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Gpu, model, vendor_id, device_id, NLOHMANN_DEFINE_TYPE_INTRUSIVE(Gpu, model, vendor_id, device_id, dedicated_video_memory, shared_system_memory,
dedicated_video_memory, driver_version)
shared_system_memory, driver_version)
}; };
struct GamingFeatures { struct GamingFeatures {
@ -111,10 +107,8 @@ struct GamingFeatures {
optional<bool> game_mode_enabled; optional<bool> game_mode_enabled;
optional<bool> hags_enabled; optional<bool> hags_enabled;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GamingFeatures, game_bar_enabled, NLOHMANN_DEFINE_TYPE_INTRUSIVE(GamingFeatures, game_bar_enabled, game_dvr_allowed, game_dvr_enabled,
game_dvr_allowed, game_dvr_enabled, game_dvr_bg_recording, game_mode_enabled, hags_enabled)
game_dvr_bg_recording, game_mode_enabled,
hags_enabled)
}; };
struct System { struct System {
@ -127,8 +121,7 @@ struct System {
bool arm; bool arm;
bool armEmulation; bool armEmulation;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(System, version, name, build, release, NLOHMANN_DEFINE_TYPE_INTRUSIVE(System, version, name, build, release, revision, bits, arm, armEmulation)
revision, bits, arm, armEmulation)
}; };
struct Capabilities { struct Capabilities {
@ -138,8 +131,7 @@ struct Capabilities {
System system; System system;
optional<std::vector<Gpu>> gpu; optional<std::vector<Gpu>> gpu;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, cpu, memory, NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, cpu, memory, gaming_features, system, gpu)
gaming_features, system, gpu)
}; };
struct Preferences { struct Preferences {
@ -153,10 +145,8 @@ struct Preferences {
uint32_t canvas_height; uint32_t canvas_height;
optional<uint32_t> composition_gpu_index; optional<uint32_t> composition_gpu_index;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate, NLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate, maximum_video_tracks, vod_track_audio,
maximum_video_tracks, vod_track_audio, width, height, framerate, canvas_width, canvas_height, composition_gpu_index)
width, height, framerate, canvas_width,
canvas_height, composition_gpu_index)
}; };
struct PostData { struct PostData {
@ -168,8 +158,7 @@ struct PostData {
Capabilities capabilities; Capabilities capabilities;
Preferences preferences; Preferences preferences;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(PostData, service, schema_version, NLOHMANN_DEFINE_TYPE_INTRUSIVE(PostData, service, schema_version, authentication, client, capabilities,
authentication, client, capabilities,
preferences) preferences)
}; };
@ -190,13 +179,12 @@ enum struct StatusResult {
Error, Error,
}; };
NLOHMANN_JSON_SERIALIZE_ENUM(StatusResult, NLOHMANN_JSON_SERIALIZE_ENUM(StatusResult, {
{ {StatusResult::Unknown, nullptr},
{StatusResult::Unknown, nullptr}, {StatusResult::Success, "success"},
{StatusResult::Success, "success"}, {StatusResult::Warning, "warning"},
{StatusResult::Warning, "warning"}, {StatusResult::Error, "error"},
{StatusResult::Error, "error"}, })
})
struct Status { struct Status {
StatusResult result = StatusResult::Unknown; StatusResult result = StatusResult::Unknown;
@ -209,9 +197,7 @@ struct IngestEndpoint {
string url_template; string url_template;
optional<string> authentication; optional<string> authentication;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IngestEndpoint, protocol, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IngestEndpoint, protocol, url_template, authentication)
url_template,
authentication)
}; };
struct VideoEncoderConfiguration { struct VideoEncoderConfiguration {
@ -222,10 +208,8 @@ struct VideoEncoderConfiguration {
optional<obs_scale_type> gpu_scale_type; optional<obs_scale_type> gpu_scale_type;
json settings; json settings;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VideoEncoderConfiguration, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VideoEncoderConfiguration, type, width, height, framerate,
type, width, height, gpu_scale_type, settings)
framerate, gpu_scale_type,
settings)
}; };
struct AudioEncoderConfiguration { struct AudioEncoderConfiguration {
@ -234,16 +218,14 @@ struct AudioEncoderConfiguration {
uint32_t channels; uint32_t channels;
json settings; json settings;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(AudioEncoderConfiguration, codec, NLOHMANN_DEFINE_TYPE_INTRUSIVE(AudioEncoderConfiguration, codec, track_id, channels, settings)
track_id, channels, settings)
}; };
struct AudioConfigurations { struct AudioConfigurations {
std::vector<AudioEncoderConfiguration> live; std::vector<AudioEncoderConfiguration> live;
optional<std::vector<AudioEncoderConfiguration>> vod; optional<std::vector<AudioEncoderConfiguration>> vod;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live, vod)
vod)
}; };
struct Config { struct Config {
@ -253,9 +235,7 @@ struct Config {
std::vector<VideoEncoderConfiguration> encoder_configurations; std::vector<VideoEncoderConfiguration> encoder_configurations;
AudioConfigurations audio_configurations; AudioConfigurations audio_configurations;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, meta, status, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, meta, status, ingest_endpoints, encoder_configurations,
ingest_endpoints,
encoder_configurations,
audio_configurations) audio_configurations)
}; };
} // namespace GoLiveApi } // namespace GoLiveApi

View File

@ -19,21 +19,17 @@ MultitrackVideoError MultitrackVideoError::cancel()
return {Type::Cancel, {}}; return {Type::Cancel, {}};
} }
bool MultitrackVideoError::ShowDialog( bool MultitrackVideoError::ShowDialog(QWidget *parent, const QString &multitrack_video_name) const
QWidget *parent, const QString &multitrack_video_name) const
{ {
QMessageBox mb(parent); QMessageBox mb(parent);
mb.setTextFormat(Qt::RichText); mb.setTextFormat(Qt::RichText);
mb.setWindowTitle(QTStr("Output.StartStreamFailed")); mb.setWindowTitle(QTStr("Output.StartStreamFailed"));
if (type == Type::Warning) { if (type == Type::Warning) {
mb.setText( mb.setText(error +
error + QTStr("FailedToStartStream.WarningRetryNonMultitrackVideo").arg(multitrack_video_name));
QTStr("FailedToStartStream.WarningRetryNonMultitrackVideo")
.arg(multitrack_video_name));
mb.setIcon(QMessageBox::Warning); mb.setIcon(QMessageBox::Warning);
QAbstractButton *yesButton = QAbstractButton *yesButton = mb.addButton(QTStr("Yes"), QMessageBox::YesRole);
mb.addButton(QTStr("Yes"), QMessageBox::YesRole);
mb.addButton(QTStr("No"), QMessageBox::NoRole); mb.addButton(QTStr("No"), QMessageBox::NoRole);
mb.exec(); mb.exec();
@ -41,8 +37,7 @@ bool MultitrackVideoError::ShowDialog(
} else if (type == Type::Critical) { } else if (type == Type::Critical) {
mb.setText(error); mb.setText(error);
mb.setIcon(QMessageBox::Critical); mb.setIcon(QMessageBox::Critical);
mb.setStandardButtons( mb.setStandardButtons(QMessageBox::StandardButton::Ok); // cannot continue
QMessageBox::StandardButton::Ok); // cannot continue
mb.exec(); mb.exec();
} }

View File

@ -8,8 +8,7 @@ struct MultitrackVideoError {
static MultitrackVideoError warning(QString error); static MultitrackVideoError warning(QString error);
static MultitrackVideoError cancel(); static MultitrackVideoError cancel();
bool ShowDialog(QWidget *parent, bool ShowDialog(QWidget *parent, const QString &multitrack_video_name) const;
const QString &multitrack_video_name) const;
enum struct Type { enum struct Type {
Critical, Critical,

File diff suppressed because it is too large Load Diff

View File

@ -25,24 +25,17 @@ bool MultitrackVideoDeveloperModeEnabled();
struct MultitrackVideoOutput { struct MultitrackVideoOutput {
public: public:
void PrepareStreaming(QWidget *parent, const char *service_name, void PrepareStreaming(QWidget *parent, const char *service_name, obs_service_t *service,
obs_service_t *service, const std::optional<std::string> &rtmp_url, const QString &stream_key,
const std::optional<std::string> &rtmp_url, const char *audio_encoder_id, std::optional<uint32_t> maximum_aggregate_bitrate,
const QString &stream_key, std::optional<uint32_t> maximum_video_tracks, std::optional<std::string> custom_config,
const char *audio_encoder_id, obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer,
std::optional<uint32_t> maximum_aggregate_bitrate,
std::optional<uint32_t> maximum_video_tracks,
std::optional<std::string> custom_config,
obs_data_t *dump_stream_to_file_config,
size_t main_audio_mixer,
std::optional<size_t> vod_track_mixer); std::optional<size_t> vod_track_mixer);
signal_handler_t *StreamingSignalHandler(); signal_handler_t *StreamingSignalHandler();
void StartedStreaming(); void StartedStreaming();
void StopStreaming(); void StopStreaming();
bool HandleIncompatibleSettings(QWidget *parent, config_t *config, bool HandleIncompatibleSettings(QWidget *parent, config_t *config, obs_service_t *service, bool &useDelay,
obs_service_t *service, bool &useDelay, bool &enableNewSocketLoop, bool &enableDynBitrate);
bool &enableNewSocketLoop,
bool &enableDynBitrate);
OBSOutputAutoRelease StreamingOutput() OBSOutputAutoRelease StreamingOutput()
{ {
@ -62,8 +55,7 @@ private:
std::optional<OBSOutputObjects> take_current(); std::optional<OBSOutputObjects> take_current();
std::optional<OBSOutputObjects> take_current_stream_dump(); std::optional<OBSOutputObjects> take_current_stream_dump();
static void static void ReleaseOnMainThread(std::optional<OBSOutputObjects> objects);
ReleaseOnMainThread(std::optional<OBSOutputObjects> objects);
std::mutex current_mutex; std::mutex current_mutex;
std::optional<OBSOutputObjects> current; std::optional<OBSOutputObjects> current;

View File

@ -6,8 +6,7 @@
Multiview::Multiview() Multiview::Multiview()
{ {
InitSafeAreas(&actionSafeMargin, &graphicsSafeMargin, InitSafeAreas(&actionSafeMargin, &graphicsSafeMargin, &fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);
&fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);
} }
Multiview::~Multiview() Multiview::~Multiview()
@ -59,14 +58,12 @@ static OBSSource CreateLabel(const char *name, size_t h)
const char *text_source_id = "text_ft2_source"; const char *text_source_id = "text_ft2_source";
#endif #endif
OBSSourceAutoRelease txtSource = OBSSourceAutoRelease txtSource = obs_source_create_private(text_source_id, name, settings);
obs_source_create_private(text_source_id, name, settings);
return txtSource.Get(); return txtSource.Get();
} }
void Multiview::Update(MultiviewLayout multiviewLayout, bool drawLabel, void Multiview::Update(MultiviewLayout multiviewLayout, bool drawLabel, bool drawSafeArea)
bool drawSafeArea)
{ {
this->multiviewLayout = multiviewLayout; this->multiviewLayout = multiviewLayout;
this->drawLabel = drawLabel; this->drawLabel = drawLabel;
@ -87,10 +84,8 @@ void Multiview::Update(MultiviewLayout multiviewLayout, bool drawLabel,
struct obs_frontend_source_list scenes = {}; struct obs_frontend_source_list scenes = {};
obs_frontend_get_scenes(&scenes); obs_frontend_get_scenes(&scenes);
multiviewLabels.emplace_back( multiviewLabels.emplace_back(CreateLabel(Str("StudioMode.Preview"), h / 2));
CreateLabel(Str("StudioMode.Preview"), h / 2)); multiviewLabels.emplace_back(CreateLabel(Str("StudioMode.Program"), h / 2));
multiviewLabels.emplace_back(
CreateLabel(Str("StudioMode.Program"), h / 2));
switch (multiviewLayout) { switch (multiviewLayout) {
case MultiviewLayout::HORIZONTAL_TOP_18_SCENES: case MultiviewLayout::HORIZONTAL_TOP_18_SCENES:
@ -175,15 +170,13 @@ void Multiview::Update(MultiviewLayout multiviewLayout, bool drawLabel,
multiviewScenes.emplace_back(OBSGetWeakRef(src)); multiviewScenes.emplace_back(OBSGetWeakRef(src));
obs_source_inc_showing(src); obs_source_inc_showing(src);
multiviewLabels.emplace_back( multiviewLabels.emplace_back(CreateLabel(obs_source_get_name(src), h / 3));
CreateLabel(obs_source_get_name(src), h / 3));
} }
obs_frontend_source_list_free(&scenes); obs_frontend_source_list_free(&scenes);
} }
static inline uint32_t labelOffset(MultiviewLayout multiviewLayout, static inline uint32_t labelOffset(MultiviewLayout multiviewLayout, obs_source_t *label, uint32_t cx)
obs_source_t *label, uint32_t cx)
{ {
uint32_t w = obs_source_get_width(label); uint32_t w = obs_source_get_width(label);
@ -232,8 +225,7 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
auto drawBox = [&](float cx, float cy, uint32_t colorVal) { auto drawBox = [&](float cx, float cy, uint32_t colorVal) {
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
gs_eparam_t *color = gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
gs_effect_get_param_by_name(solid, "color");
gs_effect_set_color(color, colorVal); gs_effect_set_color(color, colorVal);
while (gs_effect_loop(solid, "Solid")) while (gs_effect_loop(solid, "Solid"))
@ -375,8 +367,7 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
} }
}; };
auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, uint32_t color) {
uint32_t color) {
gs_matrix_push(); gs_matrix_push();
gs_matrix_translate3f(tx, ty, 0.0f); gs_matrix_translate3f(tx, ty, 0.0f);
drawBox(cx, cy, color); drawBox(cx, cy, color);
@ -384,8 +375,7 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
}; };
// Define the whole usable region for the multiview // Define the whole usable region for the multiview
startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, fw, 0.0f, startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, fw, 0.0f, fh);
fh);
// Change the background color to highlight all sources // Change the background color to highlight all sources
drawBox(fw, fh, outerColor); drawBox(fw, fh, outerColor);
@ -399,10 +389,8 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
if (i >= numSrcs) { if (i >= numSrcs) {
// Just paint the background and continue // Just paint the background and continue
paintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, paintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, outerColor);
outerColor); paintAreaWithColor(siX, siY, siCX, siCY, backgroundColor);
paintAreaWithColor(siX, siY, siCX, siCY,
backgroundColor);
continue; continue;
} }
@ -416,8 +404,7 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
colorVal = studioMode ? previewColor : programColor; colorVal = studioMode ? previewColor : programColor;
// Paint the background // Paint the background
paintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, paintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, colorVal);
colorVal);
paintAreaWithColor(siX, siY, siCX, siCY, backgroundColor); paintAreaWithColor(siX, siY, siCX, siCY, backgroundColor);
/* ----------- */ /* ----------- */
@ -444,15 +431,11 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
offset = labelOffset(multiviewLayout, label, scenesCX); offset = labelOffset(multiviewLayout, label, scenesCX);
gs_matrix_push(); gs_matrix_push();
gs_matrix_translate3f( gs_matrix_translate3f(sourceX + offset,
sourceX + offset, sourceY + scenesCY - (obs_source_get_height(label) * ppiScaleY) - (thickness * 3),
sourceY + scenesCY - 0.0f);
(obs_source_get_height(label) * ppiScaleY) -
(thickness * 3),
0.0f);
gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f); gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
drawBox(obs_source_get_width(label), drawBox(obs_source_get_width(label), obs_source_get_height(label) + thicknessx2, labelColor);
obs_source_get_height(label) + thicknessx2, labelColor);
gs_matrix_translate3f(0, thickness, 0.0f); gs_matrix_translate3f(0, thickness, 0.0f);
obs_source_video_render(label); obs_source_video_render(label);
gs_matrix_pop(); gs_matrix_pop();
@ -504,15 +487,9 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
if (drawLabel) { if (drawLabel) {
gs_matrix_push(); gs_matrix_push();
gs_matrix_translate3f( gs_matrix_translate3f(
labelX, labelX, labelY - (obs_source_get_height(previewLabel) * ppiScaleY) - (thickness * 3), 0.0f);
labelY -
(obs_source_get_height(previewLabel) *
ppiScaleY) -
(thickness * 3),
0.0f);
gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f); gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
drawBox(obs_source_get_width(previewLabel), drawBox(obs_source_get_width(previewLabel), obs_source_get_height(previewLabel) + thicknessx2,
obs_source_get_height(previewLabel) + thicknessx2,
labelColor); labelColor);
gs_matrix_translate3f(0, thickness, 0.0f); gs_matrix_translate3f(0, thickness, 0.0f);
obs_source_video_render(previewLabel); obs_source_video_render(previewLabel);
@ -543,15 +520,9 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
if (drawLabel) { if (drawLabel) {
gs_matrix_push(); gs_matrix_push();
gs_matrix_translate3f( gs_matrix_translate3f(
labelX, labelX, labelY - (obs_source_get_height(programLabel) * ppiScaleY) - (thickness * 3), 0.0f);
labelY -
(obs_source_get_height(programLabel) *
ppiScaleY) -
(thickness * 3),
0.0f);
gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f); gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);
drawBox(obs_source_get_width(programLabel), drawBox(obs_source_get_width(programLabel), obs_source_get_height(programLabel) + thicknessx2,
obs_source_get_height(programLabel) + thicknessx2,
labelColor); labelColor);
gs_matrix_translate3f(0, thickness, 0.0f); gs_matrix_translate3f(0, thickness, 0.0f);
obs_source_video_render(programLabel); obs_source_video_render(programLabel);
@ -561,10 +532,8 @@ void Multiview::Render(uint32_t cx, uint32_t cy)
// Region for future usage with additional info. // Region for future usage with additional info.
if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) { if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) {
// Just paint the background for now // Just paint the background for now
paintAreaWithColor(thickness, thickness, siCX, paintAreaWithColor(thickness, thickness, siCX, siCY * 2 + thicknessx2, backgroundColor);
siCY * 2 + thicknessx2, backgroundColor); paintAreaWithColor(thickness + 2.5 * (thicknessx2 + ppiCX), thickness, siCX, siCY * 2 + thicknessx2,
paintAreaWithColor(thickness + 2.5 * (thicknessx2 + ppiCX),
thickness, siCX, siCY * 2 + thicknessx2,
backgroundColor); backgroundColor);
} }

View File

@ -20,8 +20,7 @@ class Multiview {
public: public:
Multiview(); Multiview();
~Multiview(); ~Multiview();
void Update(MultiviewLayout multiviewLayout, bool drawLabel, void Update(MultiviewLayout multiviewLayout, bool drawLabel, bool drawSafeArea);
bool drawSafeArea);
void Render(uint32_t cx, uint32_t cy); void Render(uint32_t cx, uint32_t cy);
OBSSource GetSourceByPosition(int x, int y); OBSSource GetSourceByPosition(int x, int y);
@ -41,10 +40,9 @@ private:
// Multiview position helpers // Multiview position helpers
float thickness = 6; float thickness = 6;
float offset, thicknessx2 = thickness * 2, pvwprgCX, pvwprgCY, sourceX, float offset, thicknessx2 = thickness * 2, pvwprgCX, pvwprgCY, sourceX, sourceY, labelX, labelY, scenesCX,
sourceY, labelX, labelY, scenesCX, scenesCY, ppiCX, ppiCY, scenesCY, ppiCX, ppiCY, siX, siY, siCX, siCY, ppiScaleX, ppiScaleY, siScaleX, siScaleY, fw, fh,
siX, siY, siCX, siCY, ppiScaleX, ppiScaleY, siScaleX, ratio;
siScaleY, fw, fh, ratio;
// argb colors // argb colors
static const uint32_t outerColor = 0xFF999999; static const uint32_t outerColor = 0xFF999999;
@ -54,8 +52,7 @@ private:
static const uint32_t programColor = 0xFFD00000; static const uint32_t programColor = 0xFFD00000;
}; };
static inline void startRegion(int vX, int vY, int vCX, int vCY, float oL, static inline void startRegion(int vX, int vY, int vCX, int vCY, float oL, float oR, float oT, float oB)
float oR, float oT, float oB)
{ {
gs_projection_push(); gs_projection_push();
gs_viewport_push(); gs_viewport_push();

View File

@ -11,13 +11,6 @@ class NonCheckableButton : public QPushButton {
inline void nextCheckState() override {} inline void nextCheckState() override {}
public: public:
inline NonCheckableButton(QWidget *parent = nullptr) inline NonCheckableButton(QWidget *parent = nullptr) : QPushButton(parent) {}
: QPushButton(parent) inline NonCheckableButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent) {}
{
}
inline NonCheckableButton(const QString &text,
QWidget *parent = nullptr)
: QPushButton(text, parent)
{
}
}; };

View File

@ -13,8 +13,7 @@ void deobfuscate_str(char *str, uint64_t val)
int pos = i / 2; int pos = i / 2;
bool bottom = (i % 2) == 0; bool bottom = (i % 2) == 0;
uint8_t *ch = (uint8_t *)str; uint8_t *ch = (uint8_t *)str;
uint8_t xor = bottom ? LOWER_HALFBYTE(dec_val[pos]) uint8_t xor = bottom ? LOWER_HALFBYTE(dec_val[pos]) : UPPER_HALFBYTE(dec_val[pos]);
: UPPER_HALFBYTE(dec_val[pos]);
*ch ^= xor; *ch ^= xor;

View File

@ -81,13 +81,11 @@ static optional<OBSTheme> ParseThemeMeta(const QString &path)
if (!cf_next_token(cfp)) if (!cf_next_token(cfp))
return nullopt; return nullopt;
ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", nullptr);
nullptr);
if (ret != PARSE_SUCCESS) if (ret != PARSE_SUCCESS)
break; break;
string name(cfp->cur_token->str.array, string name(cfp->cur_token->str.array, cfp->cur_token->str.len);
cfp->cur_token->str.len);
ret = cf_next_token_should_be(cfp, ":", ";", nullptr); ret = cf_next_token_should_be(cfp, ":", ";", nullptr);
if (ret != PARSE_SUCCESS) if (ret != PARSE_SUCCESS)
@ -96,14 +94,12 @@ static optional<OBSTheme> ParseThemeMeta(const QString &path)
if (!cf_next_token(cfp)) if (!cf_next_token(cfp))
return nullopt; return nullopt;
ret = cf_token_is_type(cfp, CFTOKEN_STRING, "value", ret = cf_token_is_type(cfp, CFTOKEN_STRING, "value", ";");
";");
if (ret != PARSE_SUCCESS) if (ret != PARSE_SUCCESS)
continue; continue;
BPtr str = cf_literal_to_str(cfp->cur_token->str.array, BPtr str = cf_literal_to_str(cfp->cur_token->str.array, cfp->cur_token->str.len);
cfp->cur_token->str.len);
if (str) { if (str) {
if (name == "dark") if (name == "dark")
@ -127,8 +123,7 @@ static optional<OBSTheme> ParseThemeMeta(const QString &path)
meta.isBaseTheme = filepath.extension() == ".obt"; meta.isBaseTheme = filepath.extension() == ".obt";
meta.filename = filepath.stem(); meta.filename = filepath.stem();
if (meta.id.isEmpty() || meta.name.isEmpty() || if (meta.id.isEmpty() || meta.name.isEmpty() || (!meta.isBaseTheme && meta.extends.isEmpty())) {
(!meta.isBaseTheme && meta.extends.isEmpty())) {
/* Theme is invalid */ /* Theme is invalid */
return nullopt; return nullopt;
} else { } else {
@ -156,8 +151,7 @@ static bool ParseVarName(CFParser &cfp, QString &value)
if (!cf_next_token(cfp)) if (!cf_next_token(cfp))
return false; return false;
value = QString::fromUtf8(cfp->cur_token->str.array, value = QString::fromUtf8(cfp->cur_token->str.array, cfp->cur_token->str.len);
cfp->cur_token->str.len);
ret = cf_next_token_should_be(cfp, ")", ";", nullptr); ret = cf_next_token_should_be(cfp, ")", ";", nullptr);
if (ret != PARSE_SUCCESS) if (ret != PARSE_SUCCESS)
@ -212,8 +206,7 @@ static QColor ParseColor(CFParser &cfp)
return res; return res;
} }
static bool ParseCalc(CFParser &cfp, QStringList &calc, static bool ParseCalc(CFParser &cfp, QStringList &calc, vector<OBSThemeVariable> &vars)
vector<OBSThemeVariable> &vars)
{ {
int ret = cf_next_token_should_be(cfp, "(", ";", nullptr); int ret = cf_next_token_should_be(cfp, "(", ";", nullptr);
if (ret != PARSE_SUCCESS) if (ret != PARSE_SUCCESS)
@ -231,9 +224,7 @@ static bool ParseCalc(CFParser &cfp, QStringList &calc,
OBSThemeVariable var; OBSThemeVariable var;
QStringList subcalc; QStringList subcalc;
var.name = QString("__unnamed_%1") var.name = QString("__unnamed_%1").arg(QRandomGenerator::global()->generate64());
.arg(QRandomGenerator::global()
->generate64());
if (!ParseCalc(cfp, subcalc, vars)) if (!ParseCalc(cfp, subcalc, vars))
return false; return false;
@ -249,8 +240,7 @@ static bool ParseCalc(CFParser &cfp, QStringList &calc,
calc << value; calc << value;
} else { } else {
calc << QString::fromUtf8(cfp->cur_token->str.array, calc << QString::fromUtf8(cfp->cur_token->str.array, cfp->cur_token->str.len);
cfp->cur_token->str.len);
} }
if (!cf_next_token(cfp)) if (!cf_next_token(cfp))
@ -305,8 +295,7 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
if (ret != PARSE_SUCCESS) if (ret != PARSE_SUCCESS)
break; break;
QString key = QString::fromUtf8(cfp->cur_token->str.array, QString key = QString::fromUtf8(cfp->cur_token->str.array, cfp->cur_token->str.len);
cfp->cur_token->str.len);
OBSThemeVariable var; OBSThemeVariable var;
var.name = key; var.name = key;
@ -318,8 +307,7 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
const QString osPrefix = "os_lin_"; const QString osPrefix = "os_lin_";
#endif #endif
if (key.startsWith(osPrefix) && if (key.startsWith(osPrefix) && key.length() > osPrefix.length()) {
key.length() > osPrefix.length()) {
var.name = key.sliced(osPrefix.length()); var.name = key.sliced(osPrefix.length());
} }
@ -340,17 +328,14 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
/* Look for a suffix and mark variable as size if it exists */ /* Look for a suffix and mark variable as size if it exists */
while (ch < end) { while (ch < end) {
if (!isdigit(*ch) && !isspace(*ch) && if (!isdigit(*ch) && !isspace(*ch) && *ch != '.') {
*ch != '.') { var.suffix = QString::fromUtf8(ch, end - ch);
var.suffix =
QString::fromUtf8(ch, end - ch);
var.type = OBSThemeVariable::Size; var.type = OBSThemeVariable::Size;
break; break;
} }
ch++; ch++;
} }
} else if (cf_token_is(cfp, "rgb") || cf_token_is(cfp, "#") || } else if (cf_token_is(cfp, "rgb") || cf_token_is(cfp, "#") || cf_token_is(cfp, "bikeshed")) {
cf_token_is(cfp, "bikeshed")) {
QColor color = ParseColor(cfp); QColor color = ParseColor(cfp);
if (!color.isValid()) if (!color.isValid())
continue; continue;
@ -375,9 +360,7 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
var.value = calc; var.value = calc;
} else { } else {
var.type = OBSThemeVariable::String; var.type = OBSThemeVariable::String;
BPtr strVal = BPtr strVal = cf_literal_to_str(cfp->cur_token->str.array, cfp->cur_token->str.len);
cf_literal_to_str(cfp->cur_token->str.array,
cfp->cur_token->str.len);
var.value = QString::fromUtf8(strVal.Get()); var.value = QString::fromUtf8(strVal.Get());
} }
@ -385,12 +368,9 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
return vars; return vars;
if (cf_token_is(cfp, "!") && if (cf_token_is(cfp, "!") &&
cf_next_token_should_be(cfp, "editable", nullptr, cf_next_token_should_be(cfp, "editable", nullptr, nullptr) == PARSE_SUCCESS) {
nullptr) == PARSE_SUCCESS) { if (var.type == OBSThemeVariable::Calc || var.type == OBSThemeVariable::Alias) {
if (var.type == OBSThemeVariable::Calc || blog(LOG_WARNING, "Variable of calc/alias type cannot be editable: %s",
var.type == OBSThemeVariable::Alias) {
blog(LOG_WARNING,
"Variable of calc/alias type cannot be editable: %s",
QT_TO_UTF8(var.name)); QT_TO_UTF8(var.name));
} else { } else {
var.editable = true; var.editable = true;
@ -399,8 +379,7 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
vars.push_back(std::move(var)); vars.push_back(std::move(var));
if (!cf_token_is(cfp, ";") && if (!cf_token_is(cfp, ";") && !cf_go_to_token(cfp, ";", nullptr))
!cf_go_to_token(cfp, ";", nullptr))
return vars; return vars;
} }
@ -419,8 +398,7 @@ void OBSApp::FindThemes()
{ {
string themeDir; string themeDir;
GetDataFilePath("themes/", themeDir); GetDataFilePath("themes/", themeDir);
QDirIterator it(QString::fromStdString(themeDir), filters, QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);
QDir::Files);
while (it.hasNext()) { while (it.hasNext()) {
auto theme = ParseThemeMeta(it.next()); auto theme = ParseThemeMeta(it.next());
if (theme && !themes.contains(theme->id)) if (theme && !themes.contains(theme->id))
@ -429,12 +407,9 @@ void OBSApp::FindThemes()
} }
{ {
const std::string themeDir = const std::string themeDir = App()->userConfigLocation.u8string() + "/obs-studio/themes";
App()->userConfigLocation.u8string() +
"/obs-studio/themes";
QDirIterator it(QString::fromStdString(themeDir), filters, QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);
QDir::Files);
while (it.hasNext()) { while (it.hasNext()) {
auto theme = ParseThemeMeta(it.next()); auto theme = ParseThemeMeta(it.next());
@ -449,8 +424,7 @@ void OBSApp::FindThemes()
for (OBSTheme &theme : themes) { for (OBSTheme &theme : themes) {
if (theme.extends.isEmpty()) { if (theme.extends.isEmpty()) {
if (!theme.isBaseTheme) { if (!theme.isBaseTheme) {
blog(LOG_ERROR, blog(LOG_ERROR, R"(Theme "%s" is not base, but does not specify parent!)",
R"(Theme "%s" is not base, but does not specify parent!)",
QT_TO_UTF8(theme.id)); QT_TO_UTF8(theme.id));
invalid.insert(theme.id); invalid.insert(theme.id);
} }
@ -462,46 +436,36 @@ void OBSApp::FindThemes()
while (!parentId.isEmpty()) { while (!parentId.isEmpty()) {
OBSTheme *parent = GetTheme(parentId); OBSTheme *parent = GetTheme(parentId);
if (!parent) { if (!parent) {
blog(LOG_ERROR, blog(LOG_ERROR, R"(Theme "%s" is missing ancestor "%s"!)", QT_TO_UTF8(theme.id),
R"(Theme "%s" is missing ancestor "%s"!)",
QT_TO_UTF8(theme.id),
QT_TO_UTF8(parentId)); QT_TO_UTF8(parentId));
invalid.insert(theme.id); invalid.insert(theme.id);
break; break;
} }
if (theme.isBaseTheme && !parent->isBaseTheme) { if (theme.isBaseTheme && !parent->isBaseTheme) {
blog(LOG_ERROR, blog(LOG_ERROR, R"(Ancestor "%s" of base theme "%s" is not a base theme!)",
R"(Ancestor "%s" of base theme "%s" is not a base theme!)", QT_TO_UTF8(parent->id), QT_TO_UTF8(theme.id));
QT_TO_UTF8(parent->id),
QT_TO_UTF8(theme.id));
invalid.insert(theme.id); invalid.insert(theme.id);
break; break;
} }
if (parent->id == theme.id || if (parent->id == theme.id || theme.dependencies.contains(parent->id)) {
theme.dependencies.contains(parent->id)) { blog(LOG_ERROR, R"(Dependency chain of "%s" ("%s") contains recursion!)",
blog(LOG_ERROR, QT_TO_UTF8(theme.id), QT_TO_UTF8(parent->id));
R"(Dependency chain of "%s" ("%s") contains recursion!)",
QT_TO_UTF8(theme.id),
QT_TO_UTF8(parent->id));
invalid.insert(theme.id); invalid.insert(theme.id);
break; break;
} }
/* Mark this theme as a variant of first parent that is a base theme. */ /* Mark this theme as a variant of first parent that is a base theme. */
if (!theme.isBaseTheme && parent->isBaseTheme && if (!theme.isBaseTheme && parent->isBaseTheme && theme.parent.isEmpty())
theme.parent.isEmpty())
theme.parent = parent->id; theme.parent = parent->id;
theme.dependencies.push_front(parent->id); theme.dependencies.push_front(parent->id);
parentId = parent->extends; parentId = parent->extends;
if (parentId.isEmpty() && !parent->isBaseTheme) { if (parentId.isEmpty() && !parent->isBaseTheme) {
blog(LOG_ERROR, blog(LOG_ERROR, R"(Final ancestor of "%s" ("%s") is not a base theme!)",
R"(Final ancestor of "%s" ("%s") is not a base theme!)", QT_TO_UTF8(theme.id), QT_TO_UTF8(parent->id));
QT_TO_UTF8(theme.id),
QT_TO_UTF8(parent->id));
invalid.insert(theme.id); invalid.insert(theme.id);
break; break;
} }
@ -513,8 +477,7 @@ void OBSApp::FindThemes()
} }
} }
static bool ResolveVariable(const QHash<QString, OBSThemeVariable> &vars, static bool ResolveVariable(const QHash<QString, OBSThemeVariable> &vars, OBSThemeVariable &var)
OBSThemeVariable &var)
{ {
if (var.type != OBSThemeVariable::Alias) if (var.type != OBSThemeVariable::Alias)
return true; return true;
@ -524,9 +487,8 @@ static bool ResolveVariable(const QHash<QString, OBSThemeVariable> &vars,
key = vars[key].value.toString(); key = vars[key].value.toString();
if (!vars.contains(key)) { if (!vars.contains(key)) {
blog(LOG_ERROR, blog(LOG_ERROR, R"(Variable "%s" (aliased by "%s") does not exist!)", QT_TO_UTF8(key),
R"(Variable "%s" (aliased by "%s") does not exist!)", QT_TO_UTF8(var.name));
QT_TO_UTF8(key), QT_TO_UTF8(var.name));
return false; return false;
} }
} }
@ -536,12 +498,11 @@ static bool ResolveVariable(const QHash<QString, OBSThemeVariable> &vars,
return true; return true;
} }
static QString EvalCalc(const QHash<QString, OBSThemeVariable> &vars, static QString EvalCalc(const QHash<QString, OBSThemeVariable> &vars, const OBSThemeVariable &var,
const OBSThemeVariable &var, const int recursion = 0); const int recursion = 0);
static OBSThemeVariable static OBSThemeVariable ParseCalcVariable(const QHash<QString, OBSThemeVariable> &vars, const QString &value,
ParseCalcVariable(const QHash<QString, OBSThemeVariable> &vars, const int recursion = 0)
const QString &value, const int recursion = 0)
{ {
OBSThemeVariable var; OBSThemeVariable var;
const QByteArray utf8 = value.toUtf8(); const QByteArray utf8 = value.toUtf8();
@ -555,8 +516,7 @@ ParseCalcVariable(const QHash<QString, OBSThemeVariable> &vars,
const char *dataEnd = data + utf8.size(); const char *dataEnd = data + utf8.size();
while (data < dataEnd) { while (data < dataEnd) {
if (*data && !isdigit(*data) && *data != '.') { if (*data && !isdigit(*data) && *data != '.') {
var.suffix = var.suffix = QString::fromUtf8(data, dataEnd - data);
QString::fromUtf8(data, dataEnd - data);
var.type = OBSThemeVariable::Size; var.type = OBSThemeVariable::Size;
break; break;
} }
@ -576,11 +536,8 @@ ParseCalcVariable(const QHash<QString, OBSThemeVariable> &vars,
} }
/* Only number or size would be valid here */ /* Only number or size would be valid here */
if (var.type != OBSThemeVariable::Number && if (var.type != OBSThemeVariable::Number && var.type != OBSThemeVariable::Size) {
var.type != OBSThemeVariable::Size) { blog(LOG_ERROR, "calc() operand is not a size or number: %s", QT_TO_UTF8(var.value.toString()));
blog(LOG_ERROR,
"calc() operand is not a size or number: %s",
QT_TO_UTF8(var.value.toString()));
throw invalid_argument("Operand not of numeric type"); throw invalid_argument("Operand not of numeric type");
} }
} }
@ -588,8 +545,7 @@ ParseCalcVariable(const QHash<QString, OBSThemeVariable> &vars,
return var; return var;
} }
static QString EvalCalc(const QHash<QString, OBSThemeVariable> &vars, static QString EvalCalc(const QHash<QString, OBSThemeVariable> &vars, const OBSThemeVariable &var, const int recursion)
const OBSThemeVariable &var, const int recursion)
{ {
if (recursion >= 10) { if (recursion >= 10) {
/* Abort after 10 levels of recursion */ /* Abort after 10 levels of recursion */
@ -599,16 +555,14 @@ static QString EvalCalc(const QHash<QString, OBSThemeVariable> &vars,
QStringList args = var.value.toStringList(); QStringList args = var.value.toStringList();
if (args.length() != 3) { if (args.length() != 3) {
blog(LOG_ERROR, blog(LOG_ERROR, "calc() had invalid number of arguments: %lld (%s)", args.length(),
"calc() had invalid number of arguments: %lld (%s)", QT_TO_UTF8(args.join(", ")));
args.length(), QT_TO_UTF8(args.join(", ")));
return "'Invalid expression'"; return "'Invalid expression'";
} }
QString &opt = args[1]; QString &opt = args[1];
if (opt != '*' && opt != '+' && opt != '-' && opt != '/') { if (opt != '*' && opt != '+' && opt != '-' && opt != '/') {
blog(LOG_ERROR, "Unknown/invalid calc() operator: %s", blog(LOG_ERROR, "Unknown/invalid calc() operator: %s", QT_TO_UTF8(opt));
QT_TO_UTF8(opt));
return "'Invalid expression'"; return "'Invalid expression'";
} }
@ -621,19 +575,15 @@ static QString EvalCalc(const QHash<QString, OBSThemeVariable> &vars,
} }
/* Ensure that suffixes match (if any) */ /* Ensure that suffixes match (if any) */
if (!val1.suffix.isEmpty() && !val2.suffix.isEmpty() && if (!val1.suffix.isEmpty() && !val2.suffix.isEmpty() && val1.suffix != val2.suffix) {
val1.suffix != val2.suffix) { blog(LOG_ERROR, "calc() requires suffixes to match or only one to be present! %s != %s",
blog(LOG_ERROR,
"calc() requires suffixes to match or only one to be present! %s != %s",
QT_TO_UTF8(val1.suffix), QT_TO_UTF8(val2.suffix)); QT_TO_UTF8(val1.suffix), QT_TO_UTF8(val2.suffix));
return "'Invalid expression'"; return "'Invalid expression'";
} }
double val = numeric_limits<double>::quiet_NaN(); double val = numeric_limits<double>::quiet_NaN();
double d1 = val1.userValue.isValid() ? val1.userValue.toDouble() double d1 = val1.userValue.isValid() ? val1.userValue.toDouble() : val1.value.toDouble();
: val1.value.toDouble(); double d2 = val2.userValue.isValid() ? val2.userValue.toDouble() : val2.value.toDouble();
double d2 = val2.userValue.isValid() ? val2.userValue.toDouble()
: val2.value.toDouble();
if (!isfinite(d1) || !isfinite(d2)) { if (!isfinite(d1) || !isfinite(d2)) {
blog(LOG_ERROR, blog(LOG_ERROR,
@ -687,8 +637,7 @@ static qsizetype FindEndOfOBSMetadata(const QString &content)
return end; return end;
} }
static QString PrepareQSS(const QHash<QString, OBSThemeVariable> &vars, static QString PrepareQSS(const QHash<QString, OBSThemeVariable> &vars, const QStringList &contents)
const QStringList &contents)
{ {
QString stylesheet; QString stylesheet;
QString needleTemplate("var(--%1)"); QString needleTemplate("var(--%1)");
@ -710,15 +659,13 @@ static QString PrepareQSS(const QHash<QString, OBSThemeVariable> &vars,
QString needle = needleTemplate.arg(var_.name); QString needle = needleTemplate.arg(var_.name);
QString replace; QString replace;
QVariant value = var.userValue.isValid() ? var.userValue QVariant value = var.userValue.isValid() ? var.userValue : var.value;
: var.value;
if (var.type == OBSThemeVariable::Color) { if (var.type == OBSThemeVariable::Color) {
replace = value.value<QColor>().name(QColor::HexRgb); replace = value.value<QColor>().name(QColor::HexRgb);
} else if (var.type == OBSThemeVariable::Calc) { } else if (var.type == OBSThemeVariable::Calc) {
replace = EvalCalc(vars, var); replace = EvalCalc(vars, var);
} else if (var.type == OBSThemeVariable::Size || } else if (var.type == OBSThemeVariable::Size || var.type == OBSThemeVariable::Number) {
var.type == OBSThemeVariable::Number) {
double val = value.toDouble(); double val = value.toDouble();
bool isInteger = ceill(val) == val; bool isInteger = ceill(val) == val;
replace = QString::number(val, 'f', isInteger ? 0 : -1); replace = QString::number(val, 'f', isInteger ? 0 : -1);
@ -747,8 +694,7 @@ template<typename T> static void FillEnumMap(QHash<QString, T> &map)
} }
} }
static QPalette PreparePalette(const QHash<QString, OBSThemeVariable> &vars, static QPalette PreparePalette(const QHash<QString, OBSThemeVariable> &vars, const QPalette &defaultPalette)
const QPalette &defaultPalette)
{ {
static QHash<QString, QPalette::ColorRole> roleMap; static QHash<QString, QPalette::ColorRole> roleMap;
static QHash<QString, QPalette::ColorGroup> groupMap; static QHash<QString, QPalette::ColorGroup> groupMap;
@ -767,8 +713,7 @@ static QPalette PreparePalette(const QHash<QString, OBSThemeVariable> &vars,
continue; continue;
OBSThemeVariable var(var_); OBSThemeVariable var(var_);
if (!ResolveVariable(vars, var) || if (!ResolveVariable(vars, var) || var.type != OBSThemeVariable::Color)
var.type != OBSThemeVariable::Color)
continue; continue;
/* Determine role and optionally group based on name. /* Determine role and optionally group based on name.
@ -780,9 +725,7 @@ static QPalette PreparePalette(const QHash<QString, OBSThemeVariable> &vars,
if (parts.length() >= 2) { if (parts.length() >= 2) {
QString key = parts[1].toLower(); QString key = parts[1].toLower();
if (!roleMap.contains(key)) { if (!roleMap.contains(key)) {
blog(LOG_WARNING, blog(LOG_WARNING, "Palette role \"%s\" is not valid!", QT_TO_UTF8(parts[1]));
"Palette role \"%s\" is not valid!",
QT_TO_UTF8(parts[1]));
continue; continue;
} }
role = roleMap[key]; role = roleMap[key];
@ -791,16 +734,13 @@ static QPalette PreparePalette(const QHash<QString, OBSThemeVariable> &vars,
if (parts.length() == 3) { if (parts.length() == 3) {
QString key = parts[2].toLower(); QString key = parts[2].toLower();
if (!groupMap.contains(key)) { if (!groupMap.contains(key)) {
blog(LOG_WARNING, blog(LOG_WARNING, "Palette group \"%s\" is not valid!", QT_TO_UTF8(parts[2]));
"Palette group \"%s\" is not valid!",
QT_TO_UTF8(parts[2]));
continue; continue;
} }
group = groupMap[key]; group = groupMap[key];
} }
QVariant value = var.userValue.isValid() ? var.userValue QVariant value = var.userValue.isValid() ? var.userValue : var.value;
: var.value;
QColor color = value.value<QColor>().name(QColor::HexRgb); QColor color = value.value<QColor>().name(QColor::HexRgb);
pal.setColor(group, role, color); pal.setColor(group, role, color);
@ -860,8 +800,7 @@ bool OBSApp::SetTheme(const QString &name)
return false; return false;
const QByteArray content = file.readAll(); const QByteArray content = file.readAll();
for (OBSThemeVariable &var : for (OBSThemeVariable &var : ParseThemeVariables(content.constData())) {
ParseThemeVariables(content.constData())) {
vars[var.name] = std::move(var); vars[var.name] = std::move(var);
} }
@ -881,8 +820,7 @@ bool OBSApp::SetTheme(const QString &name)
filesystem::path debugOut; filesystem::path debugOut;
char configPath[512]; char configPath[512];
if (GetAppConfigPath(configPath, sizeof(configPath), if (GetAppConfigPath(configPath, sizeof(configPath), filename.c_str())) {
filename.c_str())) {
debugOut = absolute(filesystem::u8path(configPath)); debugOut = absolute(filesystem::u8path(configPath));
filesystem::create_directories(debugOut.parent_path()); filesystem::create_directories(debugOut.parent_path());
} }
@ -904,8 +842,7 @@ bool OBSApp::SetTheme(const QString &name)
themeWatcher->addPaths(filenames); themeWatcher->addPaths(filenames);
/* Give it 250 ms before re-enabling the watcher to prevent too /* Give it 250 ms before re-enabling the watcher to prevent too
* many reloads when edited with an auto-saving IDE. */ * many reloads when edited with an auto-saving IDE. */
QTimer::singleShot(250, this, QTimer::singleShot(250, this, [&] { themeWatcher->blockSignals(false); });
[&] { themeWatcher->blockSignals(false); });
} }
return true; return true;
@ -914,8 +851,7 @@ bool OBSApp::SetTheme(const QString &name)
void OBSApp::themeFileChanged(const QString &path) void OBSApp::themeFileChanged(const QString &path)
{ {
themeWatcher->blockSignals(true); themeWatcher->blockSignals(true);
blog(LOG_INFO, "Theme file \"%s\" changed, reloading...", blog(LOG_INFO, "Theme file \"%s\" changed, reloading...", QT_TO_UTF8(path));
QT_TO_UTF8(path));
SetTheme(currentTheme->id); SetTheme(currentTheme->id);
} }
@ -957,24 +893,20 @@ bool OBSApp::InitTheme()
if (config_get_bool(userConfig, "Appearance", "AutoReload")) { if (config_get_bool(userConfig, "Appearance", "AutoReload")) {
/* Set up Qt file watcher to automatically reload themes */ /* Set up Qt file watcher to automatically reload themes */
themeWatcher = new QFileSystemWatcher(this); themeWatcher = new QFileSystemWatcher(this);
connect(themeWatcher.get(), &QFileSystemWatcher::fileChanged, connect(themeWatcher.get(), &QFileSystemWatcher::fileChanged, this, &OBSApp::themeFileChanged);
this, &OBSApp::themeFileChanged);
} }
/* Migrate old theme config key */ /* Migrate old theme config key */
if (config_has_user_value(userConfig, "General", "CurrentTheme3") && if (config_has_user_value(userConfig, "General", "CurrentTheme3") &&
!config_has_user_value(userConfig, "Appearance", "Theme")) { !config_has_user_value(userConfig, "Appearance", "Theme")) {
const char *old = config_get_string(userConfig, "General", const char *old = config_get_string(userConfig, "General", "CurrentTheme3");
"CurrentTheme3");
if (themeMigrations.count(old)) { if (themeMigrations.count(old)) {
config_set_string(userConfig, "Appearance", "Theme", config_set_string(userConfig, "Appearance", "Theme", themeMigrations[old].c_str());
themeMigrations[old].c_str());
} }
} }
QString themeName = QString themeName = config_get_string(userConfig, "Appearance", "Theme");
config_get_string(userConfig, "Appearance", "Theme");
if (themeName.isEmpty() || !GetTheme(themeName)) { if (themeName.isEmpty() || !GetTheme(themeName)) {
if (!themeName.isEmpty()) { if (!themeName.isEmpty()) {
@ -984,8 +916,7 @@ bool OBSApp::InitTheme()
QT_TO_UTF8(themeName), DEFAULT_THEME); QT_TO_UTF8(themeName), DEFAULT_THEME);
} }
#ifdef _WIN32 #ifdef _WIN32
themeName = HighContrastEnabled() ? "com.obsproject.System" themeName = HighContrastEnabled() ? "com.obsproject.System" : DEFAULT_THEME;
: DEFAULT_THEME;
#else #else
themeName = DEFAULT_THEME; themeName = DEFAULT_THEME;
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -45,15 +45,12 @@
std::string CurrentTimeString(); std::string CurrentTimeString();
std::string CurrentDateTimeString(); std::string CurrentDateTimeString();
std::string GenerateTimeDateFilename(const char *extension, std::string GenerateTimeDateFilename(const char *extension, bool noSpace = false);
bool noSpace = false); std::string GenerateSpecifiedFilename(const char *extension, bool noSpace, const char *format);
std::string GenerateSpecifiedFilename(const char *extension, bool noSpace, std::string GetFormatString(const char *format, const char *prefix, const char *suffix);
const char *format);
std::string GetFormatString(const char *format, const char *prefix,
const char *suffix);
std::string GetFormatExt(const char *container); std::string GetFormatExt(const char *container);
std::string GetOutputFilename(const char *path, const char *container, std::string GetOutputFilename(const char *path, const char *container, bool noSpace, bool overwrite,
bool noSpace, bool overwrite, const char *format); const char *format);
QObject *CreateShortcutFilter(); QObject *CreateShortcutFilter();
struct BaseLexer { struct BaseLexer {
@ -71,8 +68,7 @@ class OBSTranslator : public QTranslator {
public: public:
virtual bool isEmpty() const override { return false; } virtual bool isEmpty() const override { return false; }
virtual QString translate(const char *context, const char *sourceText, virtual QString translate(const char *context, const char *sourceText, const char *disambiguation,
const char *disambiguation,
int n) const override; int n) const override;
}; };
@ -119,8 +115,7 @@ private:
bool MigrateGlobalSettings(); bool MigrateGlobalSettings();
bool MigrateLegacySettings(uint32_t lastVersion); bool MigrateLegacySettings(uint32_t lastVersion);
bool InitUserConfig(std::filesystem::path &userConfigLocation, bool InitUserConfig(std::filesystem::path &userConfigLocation, uint32_t lastVersion);
uint32_t lastVersion);
void InitUserConfigDefaults(); void InitUserConfigDefaults();
bool InitLocale(); bool InitLocale();
@ -158,10 +153,7 @@ public:
void UpdateHotkeyFocusSetting(bool reset = true); void UpdateHotkeyFocusSetting(bool reset = true);
void DisableHotkeys(); void DisableHotkeys();
inline bool HotkeysEnabledInFocus() const inline bool HotkeysEnabledInFocus() const { return enableHotkeysInFocus; }
{
return enableHotkeysInFocus;
}
inline QMainWindow *GetMainWindow() const { return mainWindow.data(); } inline QMainWindow *GetMainWindow() const { return mainWindow.data(); }
@ -177,27 +169,18 @@ public:
QList<OBSTheme> GetThemes() const { return themes.values(); } QList<OBSTheme> GetThemes() const { return themes.values(); }
OBSTheme *GetTheme(const QString &name); OBSTheme *GetTheme(const QString &name);
bool SetTheme(const QString &name); bool SetTheme(const QString &name);
bool IsThemeDark() const bool IsThemeDark() const { return currentTheme ? currentTheme->isDark : false; }
{
return currentTheme ? currentTheme->isDark : false;
}
void SetBranchData(const std::string &data); void SetBranchData(const std::string &data);
std::vector<UpdateBranch> GetBranches(); std::vector<UpdateBranch> GetBranches();
inline lookup_t *GetTextLookup() const { return textLookup; } inline lookup_t *GetTextLookup() const { return textLookup; }
inline const char *GetString(const char *lookupVal) const inline const char *GetString(const char *lookupVal) const { return textLookup.GetString(lookupVal); }
{
return textLookup.GetString(lookupVal);
}
bool TranslateString(const char *lookupVal, const char **out) const; bool TranslateString(const char *lookupVal, const char **out) const;
profiler_name_store_t *GetProfilerNameStore() const profiler_name_store_t *GetProfilerNameStore() const { return profilerNameStore; }
{
return profilerNameStore;
}
const char *GetLastLog() const; const char *GetLastLog() const;
const char *GetCurrentLog() const; const char *GetCurrentLog() const;
@ -232,10 +215,7 @@ public:
os_inhibit_sleep_set_active(sleepInhibitor, false); os_inhibit_sleep_set_active(sleepInhibitor, false);
} }
inline void PushUITranslation(obs_frontend_translate_ui_cb cb) inline void PushUITranslation(obs_frontend_translate_ui_cb cb) { translatorHooks.emplace_front(cb); }
{
translatorHooks.emplace_front(cb);
}
inline void PopUITranslation() { translatorHooks.pop_front(); } inline void PopUITranslation() { translatorHooks.pop_front(); }
#ifndef _WIN32 #ifndef _WIN32

View File

@ -13,8 +13,7 @@ void obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks)
static inline bool callbacks_valid_(const char *func_name) static inline bool callbacks_valid_(const char *func_name)
{ {
if (!c) { if (!c) {
blog(LOG_ERROR, "Tried to call %s with no callbacks!", blog(LOG_ERROR, "Tried to call %s with no callbacks!", func_name);
func_name);
return false; return false;
} }
@ -59,20 +58,17 @@ static char **convert_string_list(vector<string> &strings)
void *obs_frontend_get_main_window(void) void *obs_frontend_get_main_window(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_main_window() return !!callbacks_valid() ? c->obs_frontend_get_main_window() : nullptr;
: nullptr;
} }
void *obs_frontend_get_main_window_handle(void) void *obs_frontend_get_main_window_handle(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_main_window_handle() return !!callbacks_valid() ? c->obs_frontend_get_main_window_handle() : nullptr;
: nullptr;
} }
void *obs_frontend_get_system_tray(void) void *obs_frontend_get_system_tray(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_system_tray() return !!callbacks_valid() ? c->obs_frontend_get_system_tray() : nullptr;
: nullptr;
} }
char **obs_frontend_get_scene_names(void) char **obs_frontend_get_scene_names(void)
@ -102,8 +98,7 @@ void obs_frontend_get_scenes(struct obs_frontend_source_list *sources)
obs_source_t *obs_frontend_get_current_scene(void) obs_source_t *obs_frontend_get_current_scene(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_current_scene() return !!callbacks_valid() ? c->obs_frontend_get_current_scene() : nullptr;
: nullptr;
} }
void obs_frontend_set_current_scene(obs_source_t *scene) void obs_frontend_set_current_scene(obs_source_t *scene)
@ -120,8 +115,7 @@ void obs_frontend_get_transitions(struct obs_frontend_source_list *sources)
obs_source_t *obs_frontend_get_current_transition(void) obs_source_t *obs_frontend_get_current_transition(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_current_transition() return !!callbacks_valid() ? c->obs_frontend_get_current_transition() : nullptr;
: nullptr;
} }
void obs_frontend_set_current_transition(obs_source_t *transition) void obs_frontend_set_current_transition(obs_source_t *transition)
@ -132,8 +126,7 @@ void obs_frontend_set_current_transition(obs_source_t *transition)
int obs_frontend_get_transition_duration(void) int obs_frontend_get_transition_duration(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_transition_duration() return !!callbacks_valid() ? c->obs_frontend_get_transition_duration() : 0;
: 0;
} }
void obs_frontend_set_transition_duration(int duration) void obs_frontend_set_transition_duration(int duration)
@ -171,9 +164,7 @@ char **obs_frontend_get_scene_collections(void)
char *obs_frontend_get_current_scene_collection(void) char *obs_frontend_get_current_scene_collection(void)
{ {
return !!callbacks_valid() return !!callbacks_valid() ? c->obs_frontend_get_current_scene_collection() : nullptr;
? c->obs_frontend_get_current_scene_collection()
: nullptr;
} }
void obs_frontend_set_current_scene_collection(const char *collection) void obs_frontend_set_current_scene_collection(const char *collection)
@ -184,8 +175,7 @@ void obs_frontend_set_current_scene_collection(const char *collection)
bool obs_frontend_add_scene_collection(const char *name) bool obs_frontend_add_scene_collection(const char *name)
{ {
return callbacks_valid() ? c->obs_frontend_add_scene_collection(name) return callbacks_valid() ? c->obs_frontend_add_scene_collection(name) : false;
: false;
} }
char **obs_frontend_get_profiles(void) char **obs_frontend_get_profiles(void)
@ -200,14 +190,12 @@ char **obs_frontend_get_profiles(void)
char *obs_frontend_get_current_profile(void) char *obs_frontend_get_current_profile(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_current_profile() return !!callbacks_valid() ? c->obs_frontend_get_current_profile() : nullptr;
: nullptr;
} }
char *obs_frontend_get_current_profile_path(void) char *obs_frontend_get_current_profile_path(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_current_profile_path() return !!callbacks_valid() ? c->obs_frontend_get_current_profile_path() : nullptr;
: nullptr;
} }
void obs_frontend_set_current_profile(const char *profile) void obs_frontend_set_current_profile(const char *profile)
@ -281,14 +269,12 @@ bool obs_frontend_recording_paused(void)
bool obs_frontend_recording_split_file(void) bool obs_frontend_recording_split_file(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_recording_split_file() return !!callbacks_valid() ? c->obs_frontend_recording_split_file() : false;
: false;
} }
bool obs_frontend_recording_add_chapter(const char *name) bool obs_frontend_recording_add_chapter(const char *name)
{ {
return !!callbacks_valid() ? c->obs_frontend_recording_add_chapter(name) return !!callbacks_valid() ? c->obs_frontend_recording_add_chapter(name) : false;
: false;
} }
void obs_frontend_replay_buffer_start(void) void obs_frontend_replay_buffer_start(void)
@ -311,24 +297,18 @@ void obs_frontend_replay_buffer_stop(void)
bool obs_frontend_replay_buffer_active(void) bool obs_frontend_replay_buffer_active(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_replay_buffer_active() return !!callbacks_valid() ? c->obs_frontend_replay_buffer_active() : false;
: false;
} }
void *obs_frontend_add_tools_menu_qaction(const char *name) void *obs_frontend_add_tools_menu_qaction(const char *name)
{ {
return !!callbacks_valid() return !!callbacks_valid() ? c->obs_frontend_add_tools_menu_qaction(name) : nullptr;
? c->obs_frontend_add_tools_menu_qaction(name)
: nullptr;
} }
void obs_frontend_add_tools_menu_item(const char *name, void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data)
obs_frontend_cb callback,
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_add_tools_menu_item(name, callback, c->obs_frontend_add_tools_menu_item(name, callback, private_data);
private_data);
} }
void *obs_frontend_add_dock(void *dock) void *obs_frontend_add_dock(void *dock)
@ -336,12 +316,9 @@ void *obs_frontend_add_dock(void *dock)
return !!callbacks_valid() ? c->obs_frontend_add_dock(dock) : nullptr; return !!callbacks_valid() ? c->obs_frontend_add_dock(dock) : nullptr;
} }
bool obs_frontend_add_dock_by_id(const char *id, const char *title, bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget)
void *widget)
{ {
return !!callbacks_valid() return !!callbacks_valid() ? c->obs_frontend_add_dock_by_id(id, title, widget) : false;
? c->obs_frontend_add_dock_by_id(id, title, widget)
: false;
} }
void obs_frontend_remove_dock(const char *id) void obs_frontend_remove_dock(const char *id)
@ -352,19 +329,16 @@ void obs_frontend_remove_dock(const char *id)
bool obs_frontend_add_custom_qdock(const char *id, void *dock) bool obs_frontend_add_custom_qdock(const char *id, void *dock)
{ {
return !!callbacks_valid() ? c->obs_frontend_add_custom_qdock(id, dock) return !!callbacks_valid() ? c->obs_frontend_add_custom_qdock(id, dock) : false;
: false;
} }
void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data)
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_add_event_callback(callback, private_data); c->obs_frontend_add_event_callback(callback, private_data);
} }
void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data)
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_remove_event_callback(callback, private_data); c->obs_frontend_remove_event_callback(callback, private_data);
@ -372,26 +346,22 @@ void obs_frontend_remove_event_callback(obs_frontend_event_cb callback,
obs_output_t *obs_frontend_get_streaming_output(void) obs_output_t *obs_frontend_get_streaming_output(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_streaming_output() return !!callbacks_valid() ? c->obs_frontend_get_streaming_output() : nullptr;
: nullptr;
} }
obs_output_t *obs_frontend_get_recording_output(void) obs_output_t *obs_frontend_get_recording_output(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_recording_output() return !!callbacks_valid() ? c->obs_frontend_get_recording_output() : nullptr;
: nullptr;
} }
obs_output_t *obs_frontend_get_replay_buffer_output(void) obs_output_t *obs_frontend_get_replay_buffer_output(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_replay_buffer_output() return !!callbacks_valid() ? c->obs_frontend_get_replay_buffer_output() : nullptr;
: nullptr;
} }
config_t *obs_frontend_get_profile_config(void) config_t *obs_frontend_get_profile_config(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_profile_config() return !!callbacks_valid() ? c->obs_frontend_get_profile_config() : nullptr;
: nullptr;
} }
config_t *obs_frontend_get_app_config(void) config_t *obs_frontend_get_app_config(void)
@ -401,8 +371,7 @@ config_t *obs_frontend_get_app_config(void)
config_t *obs_frontend_get_user_config(void) config_t *obs_frontend_get_user_config(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_user_config() return !!callbacks_valid() ? c->obs_frontend_get_user_config() : nullptr;
: nullptr;
} }
config_t *obs_frontend_get_global_config(void) config_t *obs_frontend_get_global_config(void)
@ -412,8 +381,7 @@ config_t *obs_frontend_get_global_config(void)
return !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr; return !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr;
} }
void obs_frontend_open_projector(const char *type, int monitor, void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name)
const char *geometry, const char *name)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_open_projector(type, monitor, geometry, name); c->obs_frontend_open_projector(type, monitor, geometry, name);
@ -437,29 +405,25 @@ void obs_frontend_defer_save_end(void)
c->obs_frontend_defer_save_end(); c->obs_frontend_defer_save_end();
} }
void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data)
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_add_save_callback(callback, private_data); c->obs_frontend_add_save_callback(callback, private_data);
} }
void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data)
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_remove_save_callback(callback, private_data); c->obs_frontend_remove_save_callback(callback, private_data);
} }
void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data)
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_add_preload_callback(callback, private_data); c->obs_frontend_add_preload_callback(callback, private_data);
} }
void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data)
void *private_data)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_remove_preload_callback(callback, private_data); c->obs_frontend_remove_preload_callback(callback, private_data);
@ -479,8 +443,7 @@ void obs_frontend_pop_ui_translation(void)
obs_service_t *obs_frontend_get_streaming_service(void) obs_service_t *obs_frontend_get_streaming_service(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_streaming_service() return !!callbacks_valid() ? c->obs_frontend_get_streaming_service() : nullptr;
: nullptr;
} }
void obs_frontend_set_streaming_service(obs_service_t *service) void obs_frontend_set_streaming_service(obs_service_t *service)
@ -497,9 +460,7 @@ void obs_frontend_save_streaming_service(void)
bool obs_frontend_preview_program_mode_active(void) bool obs_frontend_preview_program_mode_active(void)
{ {
return !!callbacks_valid() return !!callbacks_valid() ? c->obs_frontend_preview_program_mode_active() : false;
? c->obs_frontend_preview_program_mode_active()
: false;
} }
void obs_frontend_set_preview_program_mode(bool enable) void obs_frontend_set_preview_program_mode(bool enable)
@ -527,8 +488,7 @@ void obs_frontend_set_preview_enabled(bool enable)
obs_source_t *obs_frontend_get_current_preview_scene(void) obs_source_t *obs_frontend_get_current_preview_scene(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_current_preview_scene() return !!callbacks_valid() ? c->obs_frontend_get_current_preview_scene() : nullptr;
: nullptr;
} }
void obs_frontend_set_current_preview_scene(obs_source_t *scene) void obs_frontend_set_current_preview_scene(obs_source_t *scene)
@ -551,8 +511,7 @@ void obs_frontend_take_source_screenshot(obs_source_t *source)
obs_output_t *obs_frontend_get_virtualcam_output(void) obs_output_t *obs_frontend_get_virtualcam_output(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_virtualcam_output() return !!callbacks_valid() ? c->obs_frontend_get_virtualcam_output() : nullptr;
: nullptr;
} }
void obs_frontend_start_virtualcam(void) void obs_frontend_start_virtualcam(void)
@ -569,8 +528,7 @@ void obs_frontend_stop_virtualcam(void)
bool obs_frontend_virtualcam_active(void) bool obs_frontend_virtualcam_active(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_virtualcam_active() return !!callbacks_valid() ? c->obs_frontend_virtualcam_active() : false;
: false;
} }
void obs_frontend_reset_video(void) void obs_frontend_reset_video(void)
@ -605,15 +563,12 @@ void obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item)
char *obs_frontend_get_current_record_output_path(void) char *obs_frontend_get_current_record_output_path(void)
{ {
return !!callbacks_valid() return !!callbacks_valid() ? c->obs_frontend_get_current_record_output_path() : nullptr;
? c->obs_frontend_get_current_record_output_path()
: nullptr;
} }
const char *obs_frontend_get_locale_string(const char *string) const char *obs_frontend_get_locale_string(const char *string)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_locale_string(string) return !!callbacks_valid() ? c->obs_frontend_get_locale_string(string) : nullptr;
: nullptr;
} }
bool obs_frontend_is_theme_dark(void) bool obs_frontend_is_theme_dark(void)
@ -623,29 +578,22 @@ bool obs_frontend_is_theme_dark(void)
char *obs_frontend_get_last_recording(void) char *obs_frontend_get_last_recording(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_last_recording() return !!callbacks_valid() ? c->obs_frontend_get_last_recording() : nullptr;
: nullptr;
} }
char *obs_frontend_get_last_screenshot(void) char *obs_frontend_get_last_screenshot(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_last_screenshot() return !!callbacks_valid() ? c->obs_frontend_get_last_screenshot() : nullptr;
: nullptr;
} }
char *obs_frontend_get_last_replay(void) char *obs_frontend_get_last_replay(void)
{ {
return !!callbacks_valid() ? c->obs_frontend_get_last_replay() return !!callbacks_valid() ? c->obs_frontend_get_last_replay() : nullptr;
: nullptr;
} }
void obs_frontend_add_undo_redo_action(const char *name, void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,
const undo_redo_cb undo, const char *undo_data, const char *redo_data, bool repeatable)
const undo_redo_cb redo,
const char *undo_data,
const char *redo_data, bool repeatable)
{ {
if (callbacks_valid()) if (callbacks_valid())
c->obs_frontend_add_undo_redo_action( c->obs_frontend_add_undo_redo_action(name, undo, redo, undo_data, redo_data, repeatable);
name, undo, redo, undo_data, redo_data, repeatable);
} }

View File

@ -72,8 +72,7 @@ struct obs_frontend_source_list {
DARRAY(obs_source_t *) sources; DARRAY(obs_source_t *) sources;
}; };
static inline void static inline void obs_frontend_source_list_free(struct obs_frontend_source_list *source_list)
obs_frontend_source_list_free(struct obs_frontend_source_list *source_list)
{ {
size_t num = source_list->sources.num; size_t num = source_list->sources.num;
for (size_t i = 0; i < num; i++) for (size_t i = 0; i < num; i++)
@ -107,8 +106,7 @@ EXPORT void obs_frontend_get_scenes(struct obs_frontend_source_list *sources);
EXPORT obs_source_t *obs_frontend_get_current_scene(void); EXPORT obs_source_t *obs_frontend_get_current_scene(void);
EXPORT void obs_frontend_set_current_scene(obs_source_t *scene); EXPORT void obs_frontend_set_current_scene(obs_source_t *scene);
EXPORT void EXPORT void obs_frontend_get_transitions(struct obs_frontend_source_list *sources);
obs_frontend_get_transitions(struct obs_frontend_source_list *sources);
EXPORT obs_source_t *obs_frontend_get_current_transition(void); EXPORT obs_source_t *obs_frontend_get_current_transition(void);
EXPORT void obs_frontend_set_current_transition(obs_source_t *transition); EXPORT void obs_frontend_set_current_transition(obs_source_t *transition);
EXPORT int obs_frontend_get_transition_duration(void); EXPORT int obs_frontend_get_transition_duration(void);
@ -133,49 +131,36 @@ EXPORT void obs_frontend_delete_profile(const char *profile);
typedef void (*obs_frontend_cb)(void *private_data); typedef void (*obs_frontend_cb)(void *private_data);
EXPORT void *obs_frontend_add_tools_menu_qaction(const char *name); EXPORT void *obs_frontend_add_tools_menu_qaction(const char *name);
EXPORT void obs_frontend_add_tools_menu_item(const char *name, EXPORT void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data);
obs_frontend_cb callback,
void *private_data);
/* takes QDockWidget and returns QAction */ /* takes QDockWidget and returns QAction */
OBS_DEPRECATED OBS_DEPRECATED
EXPORT void *obs_frontend_add_dock(void *dock); EXPORT void *obs_frontend_add_dock(void *dock);
/* takes QWidget for widget */ /* takes QWidget for widget */
EXPORT bool obs_frontend_add_dock_by_id(const char *id, const char *title, EXPORT bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget);
void *widget);
EXPORT void obs_frontend_remove_dock(const char *id); EXPORT void obs_frontend_remove_dock(const char *id);
/* takes QDockWidget for dock */ /* takes QDockWidget for dock */
EXPORT bool obs_frontend_add_custom_qdock(const char *id, void *dock); EXPORT bool obs_frontend_add_custom_qdock(const char *id, void *dock);
typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data);
void *private_data);
EXPORT void obs_frontend_add_event_callback(obs_frontend_event_cb callback, EXPORT void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data);
void *private_data); EXPORT void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data);
EXPORT void obs_frontend_remove_event_callback(obs_frontend_event_cb callback,
void *private_data);
typedef void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving, typedef void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving, void *private_data);
void *private_data);
EXPORT void obs_frontend_add_save_callback(obs_frontend_save_cb callback, EXPORT void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data);
void *private_data); EXPORT void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data);
EXPORT void obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
void *private_data);
EXPORT void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, EXPORT void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data);
void *private_data); EXPORT void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data);
EXPORT void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback,
void *private_data);
typedef bool (*obs_frontend_translate_ui_cb)(const char *text, typedef bool (*obs_frontend_translate_ui_cb)(const char *text, const char **out);
const char **out);
EXPORT void EXPORT void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate);
obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate);
EXPORT void obs_frontend_pop_ui_translation(void); EXPORT void obs_frontend_pop_ui_translation(void);
#endif //!SWIG #endif //!SWIG
@ -197,8 +182,7 @@ EXPORT void obs_frontend_replay_buffer_save(void);
EXPORT void obs_frontend_replay_buffer_stop(void); EXPORT void obs_frontend_replay_buffer_stop(void);
EXPORT bool obs_frontend_replay_buffer_active(void); EXPORT bool obs_frontend_replay_buffer_active(void);
EXPORT void obs_frontend_open_projector(const char *type, int monitor, EXPORT void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name);
const char *geometry, const char *name);
EXPORT void obs_frontend_save(void); EXPORT void obs_frontend_save(void);
EXPORT void obs_frontend_defer_save_begin(void); EXPORT void obs_frontend_defer_save_begin(void);
EXPORT void obs_frontend_defer_save_end(void); EXPORT void obs_frontend_defer_save_end(void);
@ -251,9 +235,8 @@ EXPORT char *obs_frontend_get_last_screenshot(void);
EXPORT char *obs_frontend_get_last_replay(void); EXPORT char *obs_frontend_get_last_replay(void);
typedef void (*undo_redo_cb)(const char *data); typedef void (*undo_redo_cb)(const char *data);
EXPORT void obs_frontend_add_undo_redo_action( EXPORT void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,
const char *name, const undo_redo_cb undo, const undo_redo_cb redo, const char *undo_data, const char *redo_data, bool repeatable);
const char *undo_data, const char *redo_data, bool repeatable);
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */

View File

@ -11,31 +11,25 @@ struct obs_frontend_callbacks {
virtual void *obs_frontend_get_main_window_handle(void) = 0; virtual void *obs_frontend_get_main_window_handle(void) = 0;
virtual void *obs_frontend_get_system_tray(void) = 0; virtual void *obs_frontend_get_system_tray(void) = 0;
virtual void virtual void obs_frontend_get_scenes(struct obs_frontend_source_list *sources) = 0;
obs_frontend_get_scenes(struct obs_frontend_source_list *sources) = 0;
virtual obs_source_t *obs_frontend_get_current_scene(void) = 0; virtual obs_source_t *obs_frontend_get_current_scene(void) = 0;
virtual void obs_frontend_set_current_scene(obs_source_t *scene) = 0; virtual void obs_frontend_set_current_scene(obs_source_t *scene) = 0;
virtual void obs_frontend_get_transitions( virtual void obs_frontend_get_transitions(struct obs_frontend_source_list *sources) = 0;
struct obs_frontend_source_list *sources) = 0;
virtual obs_source_t *obs_frontend_get_current_transition(void) = 0; virtual obs_source_t *obs_frontend_get_current_transition(void) = 0;
virtual void virtual void obs_frontend_set_current_transition(obs_source_t *transition) = 0;
obs_frontend_set_current_transition(obs_source_t *transition) = 0;
virtual int obs_frontend_get_transition_duration(void) = 0; virtual int obs_frontend_get_transition_duration(void) = 0;
virtual void obs_frontend_set_transition_duration(int duration) = 0; virtual void obs_frontend_set_transition_duration(int duration) = 0;
virtual void obs_frontend_release_tbar(void) = 0; virtual void obs_frontend_release_tbar(void) = 0;
virtual int obs_frontend_get_tbar_position(void) = 0; virtual int obs_frontend_get_tbar_position(void) = 0;
virtual void obs_frontend_set_tbar_position(int position) = 0; virtual void obs_frontend_set_tbar_position(int position) = 0;
virtual void obs_frontend_get_scene_collections( virtual void obs_frontend_get_scene_collections(std::vector<std::string> &strings) = 0;
std::vector<std::string> &strings) = 0;
virtual char *obs_frontend_get_current_scene_collection(void) = 0; virtual char *obs_frontend_get_current_scene_collection(void) = 0;
virtual void virtual void obs_frontend_set_current_scene_collection(const char *collection) = 0;
obs_frontend_set_current_scene_collection(const char *collection) = 0;
virtual bool obs_frontend_add_scene_collection(const char *name) = 0; virtual bool obs_frontend_add_scene_collection(const char *name) = 0;
virtual void virtual void obs_frontend_get_profiles(std::vector<std::string> &strings) = 0;
obs_frontend_get_profiles(std::vector<std::string> &strings) = 0;
virtual char *obs_frontend_get_current_profile(void) = 0; virtual char *obs_frontend_get_current_profile(void) = 0;
virtual char *obs_frontend_get_current_profile_path(void) = 0; virtual char *obs_frontend_get_current_profile_path(void) = 0;
virtual void obs_frontend_set_current_profile(const char *profile) = 0; virtual void obs_frontend_set_current_profile(const char *profile) = 0;
@ -61,64 +55,44 @@ struct obs_frontend_callbacks {
virtual bool obs_frontend_replay_buffer_active(void) = 0; virtual bool obs_frontend_replay_buffer_active(void) = 0;
virtual void *obs_frontend_add_tools_menu_qaction(const char *name) = 0; virtual void *obs_frontend_add_tools_menu_qaction(const char *name) = 0;
virtual void obs_frontend_add_tools_menu_item(const char *name, virtual void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback,
obs_frontend_cb callback,
void *private_data) = 0; void *private_data) = 0;
virtual void *obs_frontend_add_dock(void *dock) = 0; virtual void *obs_frontend_add_dock(void *dock) = 0;
virtual bool obs_frontend_add_dock_by_id(const char *id, virtual bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget) = 0;
const char *title,
void *widget) = 0;
virtual void obs_frontend_remove_dock(const char *id) = 0; virtual void obs_frontend_remove_dock(const char *id) = 0;
virtual bool obs_frontend_add_custom_qdock(const char *id, virtual bool obs_frontend_add_custom_qdock(const char *id, void *dock) = 0;
void *dock) = 0;
virtual void virtual void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) = 0;
obs_frontend_add_event_callback(obs_frontend_event_cb callback, virtual void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data) = 0;
void *private_data) = 0;
virtual void
obs_frontend_remove_event_callback(obs_frontend_event_cb callback,
void *private_data) = 0;
virtual obs_output_t *obs_frontend_get_streaming_output(void) = 0; virtual obs_output_t *obs_frontend_get_streaming_output(void) = 0;
virtual obs_output_t *obs_frontend_get_recording_output(void) = 0; virtual obs_output_t *obs_frontend_get_recording_output(void) = 0;
virtual obs_output_t *obs_frontend_get_replay_buffer_output(void) = 0; virtual obs_output_t *obs_frontend_get_replay_buffer_output(void) = 0;
virtual config_t *obs_frontend_get_profile_config(void) = 0; virtual config_t *obs_frontend_get_profile_config(void) = 0;
OBS_DEPRECATED virtual config_t * OBS_DEPRECATED virtual config_t *obs_frontend_get_global_config(void) = 0;
obs_frontend_get_global_config(void) = 0;
virtual config_t *obs_frontend_get_app_config(void) = 0; virtual config_t *obs_frontend_get_app_config(void) = 0;
virtual config_t *obs_frontend_get_user_config(void) = 0; virtual config_t *obs_frontend_get_user_config(void) = 0;
virtual void obs_frontend_open_projector(const char *type, int monitor, virtual void obs_frontend_open_projector(const char *type, int monitor, const char *geometry,
const char *geometry,
const char *name) = 0; const char *name) = 0;
virtual void obs_frontend_save(void) = 0; virtual void obs_frontend_save(void) = 0;
virtual void obs_frontend_defer_save_begin(void) = 0; virtual void obs_frontend_defer_save_begin(void) = 0;
virtual void obs_frontend_defer_save_end(void) = 0; virtual void obs_frontend_defer_save_end(void) = 0;
virtual void virtual void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data) = 0;
obs_frontend_add_save_callback(obs_frontend_save_cb callback, virtual void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data) = 0;
void *private_data) = 0;
virtual void
obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
void *private_data) = 0;
virtual void virtual void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data) = 0;
obs_frontend_add_preload_callback(obs_frontend_save_cb callback, virtual void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data) = 0;
void *private_data) = 0;
virtual void
obs_frontend_remove_preload_callback(obs_frontend_save_cb callback,
void *private_data) = 0;
virtual void obs_frontend_push_ui_translation( virtual void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) = 0;
obs_frontend_translate_ui_cb translate) = 0;
virtual void obs_frontend_pop_ui_translation(void) = 0; virtual void obs_frontend_pop_ui_translation(void) = 0;
virtual obs_service_t *obs_frontend_get_streaming_service(void) = 0; virtual obs_service_t *obs_frontend_get_streaming_service(void) = 0;
virtual void virtual void obs_frontend_set_streaming_service(obs_service_t *service) = 0;
obs_frontend_set_streaming_service(obs_service_t *service) = 0;
virtual void obs_frontend_save_streaming_service() = 0; virtual void obs_frontend_save_streaming_service() = 0;
virtual bool obs_frontend_preview_program_mode_active(void) = 0; virtual bool obs_frontend_preview_program_mode_active(void) = 0;
@ -129,8 +103,7 @@ struct obs_frontend_callbacks {
virtual void obs_frontend_set_preview_enabled(bool enable) = 0; virtual void obs_frontend_set_preview_enabled(bool enable) = 0;
virtual obs_source_t *obs_frontend_get_current_preview_scene(void) = 0; virtual obs_source_t *obs_frontend_get_current_preview_scene(void) = 0;
virtual void virtual void obs_frontend_set_current_preview_scene(obs_source_t *scene) = 0;
obs_frontend_set_current_preview_scene(obs_source_t *scene) = 0;
virtual void on_load(obs_data_t *settings) = 0; virtual void on_load(obs_data_t *settings) = 0;
virtual void on_preload(obs_data_t *settings) = 0; virtual void on_preload(obs_data_t *settings) = 0;
@ -138,8 +111,7 @@ struct obs_frontend_callbacks {
virtual void on_event(enum obs_frontend_event event) = 0; virtual void on_event(enum obs_frontend_event event) = 0;
virtual void obs_frontend_take_screenshot() = 0; virtual void obs_frontend_take_screenshot() = 0;
virtual void virtual void obs_frontend_take_source_screenshot(obs_source_t *source) = 0;
obs_frontend_take_source_screenshot(obs_source_t *source) = 0;
virtual obs_output_t *obs_frontend_get_virtualcam_output(void) = 0; virtual obs_output_t *obs_frontend_get_virtualcam_output(void) = 0;
virtual void obs_frontend_start_virtualcam(void) = 0; virtual void obs_frontend_start_virtualcam(void) = 0;
@ -148,17 +120,13 @@ struct obs_frontend_callbacks {
virtual void obs_frontend_reset_video(void) = 0; virtual void obs_frontend_reset_video(void) = 0;
virtual void virtual void obs_frontend_open_source_properties(obs_source_t *source) = 0;
obs_frontend_open_source_properties(obs_source_t *source) = 0;
virtual void obs_frontend_open_source_filters(obs_source_t *source) = 0; virtual void obs_frontend_open_source_filters(obs_source_t *source) = 0;
virtual void virtual void obs_frontend_open_source_interaction(obs_source_t *source) = 0;
obs_frontend_open_source_interaction(obs_source_t *source) = 0; virtual void obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item) = 0;
virtual void
obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item) = 0;
virtual char *obs_frontend_get_current_record_output_path(void) = 0; virtual char *obs_frontend_get_current_record_output_path(void) = 0;
virtual const char * virtual const char *obs_frontend_get_locale_string(const char *string) = 0;
obs_frontend_get_locale_string(const char *string) = 0;
virtual bool obs_frontend_is_theme_dark(void) = 0; virtual bool obs_frontend_is_theme_dark(void) = 0;
@ -166,13 +134,9 @@ struct obs_frontend_callbacks {
virtual char *obs_frontend_get_last_screenshot(void) = 0; virtual char *obs_frontend_get_last_screenshot(void) = 0;
virtual char *obs_frontend_get_last_replay(void) = 0; virtual char *obs_frontend_get_last_replay(void) = 0;
virtual void obs_frontend_add_undo_redo_action(const char *name, virtual void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo,
const undo_redo_cb undo, const undo_redo_cb redo, const char *undo_data,
const undo_redo_cb redo, const char *redo_data, bool repeatable) = 0;
const char *undo_data,
const char *redo_data,
bool repeatable) = 0;
}; };
EXPORT void EXPORT void obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks);
obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks);

View File

@ -12,20 +12,16 @@ static inline uint qt_intensity(uint r, uint g, uint b)
* *
* https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp.html#6429 * https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp.html#6429
*/ */
QPixmap QPixmap OBSContextBarProxyStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
OBSContextBarProxyStyle::generatedIconPixmap(QIcon::Mode iconMode, const QStyleOption *option) const
const QPixmap &pixmap,
const QStyleOption *option) const
{ {
if (iconMode == QIcon::Disabled) { if (iconMode == QIcon::Disabled) {
QImage im = QImage im = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
/* Create a colortable based on the background /* Create a colortable based on the background
* (black -> bg -> white) */ * (black -> bg -> white) */
QColor bg = option->palette.color(QPalette::Disabled, QColor bg = option->palette.color(QPalette::Disabled, QPalette::Window);
QPalette::Window);
int red = bg.red(); int red = bg.red();
int green = bg.green(); int green = bg.green();
int blue = bg.blue(); int blue = bg.blue();
@ -49,8 +45,7 @@ OBSContextBarProxyStyle::generatedIconPixmap(QIcon::Mode iconMode,
int intensity = qt_intensity(red, green, blue); int intensity = qt_intensity(red, green, blue);
const int factor = 191; const int factor = 191;
if ((red - factor > green && red - factor > blue) || if ((red - factor > green && red - factor > blue) || (green - factor > red && green - factor > blue) ||
(green - factor > red && green - factor > blue) ||
(blue - factor > red && blue - factor > green)) (blue - factor > red && blue - factor > green))
qMin(255, intensity + 20); qMin(255, intensity + 20);
else if (intensity <= 128) else if (intensity <= 128)
@ -63,10 +58,8 @@ OBSContextBarProxyStyle::generatedIconPixmap(QIcon::Mode iconMode,
/* Calculate color table index, taking /* Calculate color table index, taking
* intensity adjustment and a magic offset into * intensity adjustment and a magic offset into
* account. */ * account. */
uint ci = uint(qGray(pixel) / 3 + uint ci = uint(qGray(pixel) / 3 + (130 - intensity / 3));
(130 - intensity / 3)); *scanLine = qRgba(reds[ci], greens[ci], blues[ci], qAlpha(pixel));
*scanLine = qRgba(reds[ci], greens[ci],
blues[ci], qAlpha(pixel));
++scanLine; ++scanLine;
} }
} }
@ -77,8 +70,7 @@ OBSContextBarProxyStyle::generatedIconPixmap(QIcon::Mode iconMode,
return QProxyStyle::generatedIconPixmap(iconMode, pixmap, option); return QProxyStyle::generatedIconPixmap(iconMode, pixmap, option);
} }
int OBSProxyStyle::styleHint(StyleHint hint, const QStyleOption *option, int OBSProxyStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
const QWidget *widget,
QStyleHintReturn *returnData) const QStyleHintReturn *returnData) const
{ {
if (hint == SH_ComboBox_AllowWheelScrolling) if (hint == SH_ComboBox_AllowWheelScrolling)

View File

@ -8,8 +8,7 @@ public:
OBSProxyStyle(const QString &key) : QProxyStyle(key) {} OBSProxyStyle(const QString &key) : QProxyStyle(key) {}
int styleHint(StyleHint hint, const QStyleOption *option, int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
const QWidget *widget,
QStyleHintReturn *returnData) const override; QStyleHintReturn *returnData) const override;
}; };

View File

@ -39,8 +39,7 @@
using namespace std; using namespace std;
static inline bool check_path(const char *data, const char *path, static inline bool check_path(const char *data, const char *path, string &output)
string &output)
{ {
ostringstream str; ostringstream str;
str << path << data; str << path << data;
@ -64,8 +63,7 @@ string GetDefaultVideoSavePath()
wchar_t path_utf16[MAX_PATH]; wchar_t path_utf16[MAX_PATH];
char path_utf8[MAX_PATH] = {}; char path_utf8[MAX_PATH] = {};
SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, path_utf16);
path_utf16);
os_wcs_to_utf8(path_utf16, wcslen(path_utf16), path_utf8, MAX_PATH); os_wcs_to_utf8(path_utf16, wcslen(path_utf16), path_utf8, MAX_PATH);
return string(path_utf8); return string(path_utf8);
@ -76,13 +74,11 @@ static vector<string> GetUserPreferredLocales()
vector<string> result; vector<string> result;
ULONG num, length = 0; ULONG num, length = 0;
if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, nullptr, if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, nullptr, &length))
&length))
return result; return result;
vector<wchar_t> buffer(length); vector<wchar_t> buffer(length);
if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, &buffer.front(), &length))
&buffer.front(), &length))
return result; return result;
result.reserve(num); result.reserve(num);
@ -192,13 +188,11 @@ void SetProcessPriority(const char *priority)
if (strcmp(priority, "High") == 0) if (strcmp(priority, "High") == 0)
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
else if (strcmp(priority, "AboveNormal") == 0) else if (strcmp(priority, "AboveNormal") == 0)
SetPriorityClass(GetCurrentProcess(), SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
ABOVE_NORMAL_PRIORITY_CLASS);
else if (strcmp(priority, "Normal") == 0) else if (strcmp(priority, "Normal") == 0)
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
else if (strcmp(priority, "BelowNormal") == 0) else if (strcmp(priority, "BelowNormal") == 0)
SetPriorityClass(GetCurrentProcess(), SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
BELOW_NORMAL_PRIORITY_CLASS);
else if (strcmp(priority, "Idle") == 0) else if (strcmp(priority, "Idle") == 0)
SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
} }
@ -220,8 +214,7 @@ bool SetDisplayAffinitySupported(void)
older Windows builds behaves like WDA_MONITOR (black box) */ older Windows builds behaves like WDA_MONITOR (black box) */
if (!checked) { if (!checked) {
if (GetWindowsVersion() > 0x0A00 || if (GetWindowsVersion() > 0x0A00 || GetWindowsVersion() == 0x0A00 && GetWindowsBuild() >= 19041)
GetWindowsVersion() == 0x0A00 && GetWindowsBuild() >= 19041)
supported = true; supported = true;
else else
supported = false; supported = false;
@ -240,10 +233,8 @@ bool DisableAudioDucking(bool disable)
ComPtr<IAudioSessionControl> sessionControl; ComPtr<IAudioSessionControl> sessionControl;
ComPtr<IAudioSessionControl2> sessionControl2; ComPtr<IAudioSessionControl2> sessionControl2;
HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void **)&devEmum);
__uuidof(IMMDeviceEnumerator),
(void **)&devEmum);
if (FAILED(result)) if (FAILED(result))
return false; return false;
@ -251,14 +242,12 @@ bool DisableAudioDucking(bool disable)
if (FAILED(result)) if (FAILED(result))
return false; return false;
result = device->Activate(__uuidof(IAudioSessionManager2), result = device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_INPROC_SERVER, nullptr,
CLSCTX_INPROC_SERVER, nullptr,
(void **)&sessionManager2); (void **)&sessionManager2);
if (FAILED(result)) if (FAILED(result))
return false; return false;
result = sessionManager2->GetAudioSessionControl(nullptr, 0, result = sessionManager2->GetAudioSessionControl(nullptr, 0, &sessionControl);
&sessionControl);
if (FAILED(result)) if (FAILED(result))
return false; return false;
@ -341,8 +330,7 @@ struct MonitorData {
bool found; bool found;
}; };
static BOOL CALLBACK GetMonitorCallback(HMONITOR monitor, HDC, LPRECT, static BOOL CALLBACK GetMonitorCallback(HMONITOR monitor, HDC, LPRECT, LPARAM param)
LPARAM param)
{ {
MonitorData *data = (MonitorData *)param; MonitorData *data = (MonitorData *)param;
@ -385,9 +373,7 @@ void TaskbarOverlayInit()
void TaskbarOverlaySetStatus(TaskbarOverlayStatus status) void TaskbarOverlaySetStatus(TaskbarOverlayStatus status)
{ {
ITaskbarList4 *taskbarIcon; ITaskbarList4 *taskbarIcon;
auto hr = CoCreateInstance(CLSID_TaskbarList, NULL, auto hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarIcon));
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&taskbarIcon));
if (FAILED(hr)) { if (FAILED(hr)) {
taskbarIcon->Release(); taskbarIcon->Release();
@ -404,12 +390,10 @@ void TaskbarOverlaySetStatus(TaskbarOverlayStatus status)
QIcon qicon; QIcon qicon;
switch (status) { switch (status) {
case TaskbarOverlayStatusActive: case TaskbarOverlayStatusActive:
qicon = QIcon::fromTheme("obs-active", qicon = QIcon::fromTheme("obs-active", QIcon(":/res/images/active.png"));
QIcon(":/res/images/active.png"));
break; break;
case TaskbarOverlayStatusPaused: case TaskbarOverlayStatusPaused:
qicon = QIcon::fromTheme("obs-paused", qicon = QIcon::fromTheme("obs-paused", QIcon(":/res/images/paused.png"));
QIcon(":/res/images/paused.png"));
break; break;
case TaskbarOverlayStatusInactive: case TaskbarOverlayStatusInactive:
taskbarIcon->SetOverlayIcon(hwnd, nullptr, nullptr); taskbarIcon->SetOverlayIcon(hwnd, nullptr, nullptr);
@ -420,8 +404,7 @@ void TaskbarOverlaySetStatus(TaskbarOverlayStatus status)
HICON hicon = nullptr; HICON hicon = nullptr;
if (!qicon.isNull()) { if (!qicon.isNull()) {
Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p); Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p);
hicon = qt_pixmapToWinHICON( hicon = qt_pixmapToWinHICON(qicon.pixmap(GetSystemMetrics(SM_CXSMICON)));
qicon.pixmap(GetSystemMetrics(SM_CXSMICON)));
if (!hicon) if (!hicon)
return; return;
} }

View File

@ -61,8 +61,7 @@ void CheckIfAlreadyRunning(bool &already_running)
int uniq = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); int uniq = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (uniq == -1) { if (uniq == -1) {
blog(LOG_ERROR, blog(LOG_ERROR, "Failed to check for running instance, socket: %d", errno);
"Failed to check for running instance, socket: %d", errno);
already_running = 0; already_running = 0;
return; return;
} }
@ -70,13 +69,10 @@ void CheckIfAlreadyRunning(bool &already_running)
struct sockaddr_un bindInfo; struct sockaddr_un bindInfo;
memset(&bindInfo, 0, sizeof(sockaddr_un)); memset(&bindInfo, 0, sizeof(sockaddr_un));
bindInfo.sun_family = AF_LOCAL; bindInfo.sun_family = AF_LOCAL;
auto bindInfoStrlen = snprintf(bindInfo.sun_path + 1, auto bindInfoStrlen = snprintf(bindInfo.sun_path + 1, sizeof(bindInfo.sun_path) - 1, "%s %d %s",
sizeof(bindInfo.sun_path) - 1, "/com/obsproject", getpid(), App()->GetVersionString().c_str());
"%s %d %s", "/com/obsproject", getpid(),
App()->GetVersionString().c_str());
int bindErr = bind(uniq, (struct sockaddr *)&bindInfo, int bindErr = bind(uniq, (struct sockaddr *)&bindInfo, sizeof(sa_family_t) + 1 + bindInfoStrlen);
sizeof(sa_family_t) + 1 + bindInfoStrlen);
already_running = bindErr == 0 ? 0 : 1; already_running = bindErr == 0 ? 0 : 1;
if (already_running) { if (already_running) {
@ -139,8 +135,7 @@ const char *RunOnce::thr_name = "OBS runonce";
void CheckIfAlreadyRunning(bool &already_running) void CheckIfAlreadyRunning(bool &already_running)
{ {
std::string tmpfile_name = std::string tmpfile_name = "/tmp/obs-studio.lock." + std::to_string(geteuid());
"/tmp/obs-studio.lock." + std::to_string(geteuid());
int fd = open(tmpfile_name.c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0600); int fd = open(tmpfile_name.c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0600);
if (fd == -1) { if (fd == -1) {
already_running = true; already_running = true;
@ -151,11 +146,9 @@ void CheckIfAlreadyRunning(bool &already_running)
procstat *ps = procstat_open_sysctl(); procstat *ps = procstat_open_sysctl();
unsigned int count; unsigned int count;
auto procs = procstat_getprocs(ps, KERN_PROC_UID | KERN_PROC_INC_THREAD, auto procs = procstat_getprocs(ps, KERN_PROC_UID | KERN_PROC_INC_THREAD, geteuid(), &count);
geteuid(), &count);
for (unsigned int i = 0; i < count; i++) { for (unsigned int i = 0; i < count; i++) {
if (!strncmp(procs[i].ki_tdname, RunOnce::thr_name, if (!strncmp(procs[i].ki_tdname, RunOnce::thr_name, sizeof(procs[i].ki_tdname))) {
sizeof(procs[i].ki_tdname))) {
already_running = true; already_running = true;
break; break;
} }
@ -175,8 +168,7 @@ void CheckIfAlreadyRunning(bool &already_running)
} }
#endif #endif
static inline bool check_path(const char *data, const char *path, static inline bool check_path(const char *data, const char *path, string &output)
string &output)
{ {
ostringstream str; ostringstream str;
str << path << data; str << path << data;
@ -197,8 +189,7 @@ bool GetDataFilePath(const char *data, string &output)
return true; return true;
} }
char *relative_data_path = char *relative_data_path = os_get_executable_path_ptr("../" OBS_DATA_PATH "/obs-studio/");
os_get_executable_path_ptr("../" OBS_DATA_PATH "/obs-studio/");
if (relative_data_path) { if (relative_data_path) {
bool result = check_path(data, relative_data_path, output); bool result = check_path(data, relative_data_path, output);
@ -276,18 +267,16 @@ void TaskbarOverlaySetStatus(TaskbarOverlayStatus) {}
bool HighContrastEnabled() bool HighContrastEnabled()
{ {
QDBusReply<QVariant> reply; QDBusReply<QVariant> reply;
QDBusMessage msgXdpSettingsVersion = QDBusMessage::createMethodCall( QDBusMessage msgXdpSettingsVersion = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop",
"org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop",
"/org/freedesktop/portal/desktop", "org.freedesktop.DBus.Properties", "Get");
"org.freedesktop.DBus.Properties", "Get");
msgXdpSettingsVersion << "org.freedesktop.portal.Settings" msgXdpSettingsVersion << "org.freedesktop.portal.Settings"
<< "version"; << "version";
reply = QDBusConnection::sessionBus().call(msgXdpSettingsVersion); reply = QDBusConnection::sessionBus().call(msgXdpSettingsVersion);
if (!reply.isValid()) { if (!reply.isValid()) {
blog(LOG_WARNING, blog(LOG_WARNING, "Get on org.freedesktop.portal.Settings returned an invalid reply");
"Get on org.freedesktop.portal.Settings returned an invalid reply");
return false; return false;
} }
@ -295,17 +284,14 @@ bool HighContrastEnabled()
* the ReadOne method. So assumes that if ReadOne is not available, contrast * the ReadOne method. So assumes that if ReadOne is not available, contrast
* isn't available either. */ * isn't available either. */
if (uint32_t version = reply.value().toUInt() < 2) { if (uint32_t version = reply.value().toUInt() < 2) {
blog(LOG_WARNING, blog(LOG_WARNING, "org.freedesktop.portal.Settings version %u does not support ReadOne", version);
"org.freedesktop.portal.Settings version %u does not support ReadOne",
version);
return false; return false;
} }
/* NOTE: If contrast is not available if will return 0 (false). */ /* NOTE: If contrast is not available if will return 0 (false). */
QDBusMessage msgXdpSettingsContrast = QDBusMessage::createMethodCall( QDBusMessage msgXdpSettingsContrast =
"org.freedesktop.portal.Desktop", QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop",
"/org/freedesktop/portal/desktop", "org.freedesktop.portal.Settings", "ReadOne");
"org.freedesktop.portal.Settings", "ReadOne");
msgXdpSettingsContrast << "org.freedesktop.appearance" msgXdpSettingsContrast << "org.freedesktop.appearance"
<< "contrast"; << "contrast";
@ -313,8 +299,7 @@ bool HighContrastEnabled()
reply = QDBusConnection::sessionBus().call(msgXdpSettingsContrast); reply = QDBusConnection::sessionBus().call(msgXdpSettingsContrast);
if (!reply.isValid()) { if (!reply.isValid()) {
blog(LOG_WARNING, blog(LOG_WARNING, "ReadOne on org.freedesktop.portal.Settings returned an invalid reply");
"ReadOne on org.freedesktop.portal.Settings returned an invalid reply");
return false; return false;
} }

View File

@ -102,8 +102,7 @@ void InstallNSThreadLocks();
void disableColorSpaceConversion(QWidget *window); void disableColorSpaceConversion(QWidget *window);
void SetMacOSDarkMode(bool dark); void SetMacOSDarkMode(bool dark);
MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, bool prompt_for_permission);
bool prompt_for_permission);
#define CheckPermission(x) CheckPermissionWithPrompt(x, false) #define CheckPermission(x) CheckPermissionWithPrompt(x, false)
#define RequestPermission(x) CheckPermissionWithPrompt(x, true) #define RequestPermission(x) CheckPermissionWithPrompt(x, true)
void OpenMacOSPrivacyPreferences(const char *tab); void OpenMacOSPrivacyPreferences(const char *tab);

View File

@ -51,8 +51,7 @@ void OBSPreviewScalingComboBox::OutputResized(uint32_t width, uint32_t height)
{ {
SetOutputSize(width, height); SetOutputSize(width, height);
bool canvasMatchesOutput = output_width == canvas_width && bool canvasMatchesOutput = output_width == canvas_width && output_height == canvas_height;
output_height == canvas_height;
SetScaleOutputEnabled(!canvasMatchesOutput); SetScaleOutputEnabled(!canvasMatchesOutput);
UpdateOutputText(); UpdateOutputText();
@ -94,8 +93,7 @@ void OBSPreviewScalingComboBox::UpdateAllText()
void OBSPreviewScalingComboBox::UpdateCanvasText() void OBSPreviewScalingComboBox::UpdateCanvasText()
{ {
QString text = QTStr("Basic.MainMenu.Edit.Scale.Canvas"); QString text = QTStr("Basic.MainMenu.Edit.Scale.Canvas");
text = text.arg(QString::number(canvas_width), text = text.arg(QString::number(canvas_width), QString::number(canvas_height));
QString::number(canvas_height));
setItemText(1, text); setItemText(1, text);
} }
@ -103,8 +101,7 @@ void OBSPreviewScalingComboBox::UpdateOutputText()
{ {
if (scaleOutputEnabled) { if (scaleOutputEnabled) {
QString text = QTStr("Basic.MainMenu.Edit.Scale.Output"); QString text = QTStr("Basic.MainMenu.Edit.Scale.Output");
text = text.arg(QString::number(output_width), text = text.arg(QString::number(output_width), QString::number(output_height));
QString::number(output_height));
setItemText(2, text); setItemText(2, text);
} }
} }
@ -127,8 +124,7 @@ void OBSPreviewScalingComboBox::UpdateSelection()
} else { } else {
if (previewScale == 1.0f) { if (previewScale == 1.0f) {
setCurrentIndex(1); setCurrentIndex(1);
} else if (scaleOutputEnabled && } else if (scaleOutputEnabled && (previewScale == outputScale)) {
(previewScale == outputScale)) {
setCurrentIndex(2); setCurrentIndex(2);
} else { } else {
setCurrentIndex(-1); setCurrentIndex(-1);

View File

@ -38,9 +38,7 @@ class OBSPreviewScalingComboBox : public QComboBox {
Q_OBJECT Q_OBJECT
public: public:
OBSPreviewScalingComboBox(QWidget *parent = nullptr) : QComboBox(parent) OBSPreviewScalingComboBox(QWidget *parent = nullptr) : QComboBox(parent) {}
{
}
inline void SetCanvasSize(uint32_t width, uint32_t height) inline void SetCanvasSize(uint32_t width, uint32_t height)
{ {

View File

@ -35,8 +35,7 @@ protected:
switch (event->type()) { switch (event->type()) {
case QEvent::PlatformSurface: case QEvent::PlatformSurface:
surfaceEvent = surfaceEvent = static_cast<QPlatformSurfaceEvent *>(event);
static_cast<QPlatformSurfaceEvent *>(event);
switch (surfaceEvent->surfaceEventType()) { switch (surfaceEvent->surfaceEventType()) {
case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
@ -60,14 +59,12 @@ static inline long long color_to_int(const QColor &color)
return ((val & 0xff) << shift); return ((val & 0xff) << shift);
}; };
return shift(color.red(), 0) | shift(color.green(), 8) | return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
shift(color.blue(), 16) | shift(color.alpha(), 24);
} }
static inline QColor rgba_to_color(uint32_t rgba) static inline QColor rgba_to_color(uint32_t rgba)
{ {
return QColor::fromRgb(rgba & 0xFF, (rgba >> 8) & 0xFF, return QColor::fromRgb(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);
(rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);
} }
static bool QTToGSWindow(QWindow *window, gs_window &gswindow) static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
@ -86,10 +83,8 @@ static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
break; break;
#ifdef ENABLE_WAYLAND #ifdef ENABLE_WAYLAND
case OBS_NIX_PLATFORM_WAYLAND: { case OBS_NIX_PLATFORM_WAYLAND: {
QPlatformNativeInterface *native = QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
QGuiApplication::platformNativeInterface(); gswindow.display = native->nativeResourceForWindow("surface", window);
gswindow.display =
native->nativeResourceForWindow("surface", window);
success = gswindow.display != nullptr; success = gswindow.display != nullptr;
break; break;
} }
@ -102,8 +97,7 @@ static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
return success; return success;
} }
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)
: QWidget(parent, flags)
{ {
setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_StaticContents);
@ -124,8 +118,7 @@ OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags)
CreateDisplay(); CreateDisplay();
} else { } else {
QSize size = GetPixelSize(this); QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), obs_display_resize(display, size.width(), size.height());
size.height());
} }
}; };

View File

@ -7,9 +7,8 @@
class OBSQTDisplay : public QWidget { class OBSQTDisplay : public QWidget {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QColor displayBackgroundColor MEMBER backgroundColor READ Q_PROPERTY(QColor displayBackgroundColor MEMBER backgroundColor READ GetDisplayBackgroundColor WRITE
GetDisplayBackgroundColor WRITE SetDisplayBackgroundColor)
SetDisplayBackgroundColor)
OBSDisplay display; OBSDisplay display;
bool destroying = false; bool destroying = false;
@ -17,16 +16,14 @@ class OBSQTDisplay : public QWidget {
virtual void paintEvent(QPaintEvent *event) override; virtual void paintEvent(QPaintEvent *event) override;
virtual void moveEvent(QMoveEvent *event) override; virtual void moveEvent(QMoveEvent *event) override;
virtual void resizeEvent(QResizeEvent *event) override; virtual void resizeEvent(QResizeEvent *event) override;
virtual bool nativeEvent(const QByteArray &eventType, void *message, virtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
qintptr *result) override;
signals: signals:
void DisplayCreated(OBSQTDisplay *window); void DisplayCreated(OBSQTDisplay *window);
void DisplayResized(); void DisplayResized();
public: public:
OBSQTDisplay(QWidget *parent = nullptr, OBSQTDisplay(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
Qt::WindowFlags flags = Qt::WindowFlags());
~OBSQTDisplay() { display = nullptr; } ~OBSQTDisplay() { display = nullptr; }
virtual QPaintEngine *paintEngine() const override; virtual QPaintEngine *paintEngine() const override;

View File

@ -58,8 +58,7 @@ void RemoteTextThread::run()
header = curl_slist_append(header, versionString.c_str()); header = curl_slist_append(header, versionString.c_str());
if (!contentTypeString.empty()) { if (!contentTypeString.empty()) {
header = curl_slist_append(header, header = curl_slist_append(header, contentTypeString.c_str());
contentTypeString.c_str());
} }
for (std::string &h : extraHeaders) for (std::string &h : extraHeaders)
@ -70,24 +69,20 @@ void RemoteTextThread::run()
curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header); curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error); curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error);
curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L); curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, string_write);
string_write);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str); curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);
curl_obs_set_revoke_setting(curl.get()); curl_obs_set_revoke_setting(curl.get());
if (timeoutSec) if (timeoutSec)
curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, timeoutSec);
timeoutSec);
if (!postData.empty()) { if (!postData.empty()) {
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, postData.c_str());
postData.c_str());
} }
code = curl_easy_perform(curl.get()); code = curl_easy_perform(curl.get());
if (code != CURLE_OK) { if (code != CURLE_OK) {
blog(LOG_WARNING, blog(LOG_WARNING, "RemoteTextThread: HTTP request failed. %s",
"RemoteTextThread: HTTP request failed. %s",
strlen(error) ? error : curl_easy_strerror(code)); strlen(error) ? error : curl_easy_strerror(code));
emit Result(QString(), QT_UTF8(error)); emit Result(QString(), QT_UTF8(error));
} else { } else {
@ -98,8 +93,7 @@ void RemoteTextThread::run()
} }
} }
static size_t header_write(char *ptr, size_t size, size_t nmemb, static size_t header_write(char *ptr, size_t size, size_t nmemb, vector<string> &list)
vector<string> &list)
{ {
string str; string str;
@ -116,12 +110,9 @@ static size_t header_write(char *ptr, size_t size, size_t nmemb,
return total; return total;
} }
bool GetRemoteFile(const char *url, std::string &str, std::string &error, bool GetRemoteFile(const char *url, std::string &str, std::string &error, long *responseCode, const char *contentType,
long *responseCode, const char *contentType, std::string request_type, const char *postData, std::vector<std::string> extraHeaders,
std::string request_type, const char *postData, std::string *signature, int timeoutSec, bool fail_on_error, int postDataSize)
std::vector<std::string> extraHeaders,
std::string *signature, int timeoutSec, bool fail_on_error,
int postDataSize)
{ {
vector<string> header_in_list; vector<string> header_in_list;
char error_in[CURL_ERROR_SIZE]; char error_in[CURL_ERROR_SIZE];
@ -145,8 +136,7 @@ bool GetRemoteFile(const char *url, std::string &str, std::string &error,
header = curl_slist_append(header, versionString.c_str()); header = curl_slist_append(header, versionString.c_str());
if (!contentTypeString.empty()) { if (!contentTypeString.empty()) {
header = curl_slist_append(header, header = curl_slist_append(header, contentTypeString.c_str());
contentTypeString.c_str());
} }
for (std::string &h : extraHeaders) for (std::string &h : extraHeaders)
@ -158,61 +148,47 @@ bool GetRemoteFile(const char *url, std::string &str, std::string &error,
curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error_in); curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error_in);
if (fail_on_error) if (fail_on_error)
curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L); curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, string_write);
string_write);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str); curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);
curl_obs_set_revoke_setting(curl.get()); curl_obs_set_revoke_setting(curl.get());
if (signature) { if (signature) {
curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, header_write);
header_write); curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &header_in_list);
curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA,
&header_in_list);
} }
if (timeoutSec) if (timeoutSec)
curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, timeoutSec);
timeoutSec);
if (!request_type.empty()) { if (!request_type.empty()) {
if (request_type != "GET") if (request_type != "GET")
curl_easy_setopt(curl.get(), curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, request_type.c_str());
CURLOPT_CUSTOMREQUEST,
request_type.c_str());
// Special case of "POST" // Special case of "POST"
if (request_type == "POST") { if (request_type == "POST") {
curl_easy_setopt(curl.get(), CURLOPT_POST, 1); curl_easy_setopt(curl.get(), CURLOPT_POST, 1);
if (!postData) if (!postData)
curl_easy_setopt(curl.get(), curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, "{}");
CURLOPT_POSTFIELDS,
"{}");
} }
} }
if (postData) { if (postData) {
if (postDataSize > 0) { if (postDataSize > 0) {
curl_easy_setopt(curl.get(), curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, (long)postDataSize);
CURLOPT_POSTFIELDSIZE,
(long)postDataSize);
} }
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, postData);
postData);
} }
code = curl_easy_perform(curl.get()); code = curl_easy_perform(curl.get());
if (responseCode) if (responseCode)
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, responseCode);
responseCode);
if (code != CURLE_OK) { if (code != CURLE_OK) {
error = strlen(error_in) ? error_in error = strlen(error_in) ? error_in : curl_easy_strerror(code);
: curl_easy_strerror(code);
} else if (signature) { } else if (signature) {
for (string &h : header_in_list) { for (string &h : header_in_list) {
string name = h.substr(0, 13); string name = h.substr(0, 13);
// HTTP headers are technically case-insensitive // HTTP headers are technically case-insensitive
if (name == "X-Signature: " || if (name == "X-Signature: " || name == "x-signature: ") {
name == "x-signature: ") {
*signature = h.substr(13); *signature = h.substr(13);
break; break;
} }

View File

@ -38,10 +38,8 @@ signals:
void Result(const QString &text, const QString &error); void Result(const QString &text, const QString &error);
public: public:
inline RemoteTextThread(std::string url_, inline RemoteTextThread(std::string url_, std::string contentType_ = std::string(),
std::string contentType_ = std::string(), std::string postData_ = std::string(), int timeoutSec_ = 0)
std::string postData_ = std::string(),
int timeoutSec_ = 0)
: url(url_), : url(url_),
contentType(contentType_), contentType(contentType_),
postData(postData_), postData(postData_),
@ -49,10 +47,8 @@ public:
{ {
} }
inline RemoteTextThread(std::string url_, inline RemoteTextThread(std::string url_, std::vector<std::string> &&extraHeaders_,
std::vector<std::string> &&extraHeaders_, std::string contentType_ = std::string(), std::string postData_ = std::string(),
std::string contentType_ = std::string(),
std::string postData_ = std::string(),
int timeoutSec_ = 0) int timeoutSec_ = 0)
: url(url_), : url(url_),
contentType(contentType_), contentType(contentType_),
@ -63,10 +59,7 @@ public:
} }
}; };
bool GetRemoteFile( bool GetRemoteFile(const char *url, std::string &str, std::string &error, long *responseCode = nullptr,
const char *url, std::string &str, std::string &error, const char *contentType = nullptr, std::string request_type = "", const char *postData = nullptr,
long *responseCode = nullptr, const char *contentType = nullptr, std::vector<std::string> extraHeaders = std::vector<std::string>(), std::string *signature = nullptr,
std::string request_type = "", const char *postData = nullptr, int timeoutSec = 0, bool fail_on_error = true, int postDataSize = 0);
std::vector<std::string> extraHeaders = std::vector<std::string>(),
std::string *signature = nullptr, int timeoutSec = 0,
bool fail_on_error = true, int postDataSize = 0);

View File

@ -182,9 +182,7 @@ void SceneTree::RepositionGrid(QDragMoveEvent *event)
QModelIndex index = indexFromItem(wItem); QModelIndex index = indexFromItem(wItem);
int off = (i >= r ? 1 : 0) - int off = (i >= r ? 1 : 0) - (i > orig && i > r ? 1 : 0) - (i > orig && i == r ? 2 : 0);
(i > orig && i > r ? 1 : 0) -
(i > orig && i == r ? 2 : 0);
int xPos = (i + off) % (int)std::ceil(wid / maxWidth); int xPos = (i + off) % (int)std::ceil(wid / maxWidth);
int yPos = (i + off) / (int)std::ceil(wid / maxWidth); int yPos = (i + off) / (int)std::ceil(wid / maxWidth);
@ -240,11 +238,9 @@ void SceneTree::rowsInserted(const QModelIndex &parent, int start, int end)
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 3) #if QT_VERSION < QT_VERSION_CHECK(6, 4, 3)
// Workaround for QTBUG-105870. Remove once that is solved upstream. // Workaround for QTBUG-105870. Remove once that is solved upstream.
void SceneTree::selectionChanged(const QItemSelection &selected, void SceneTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
const QItemSelection &deselected)
{ {
if (selected.count() == 0 && deselected.count() > 0 && if (selected.count() == 0 && deselected.count() > 0 && !property("clearing").toBool())
!property("clearing").toBool())
setCurrentRow(deselected.indexes().front().row()); setCurrentRow(deselected.indexes().front().row());
} }
#endif #endif

View File

@ -6,10 +6,8 @@
class SceneTree : public QListWidget { class SceneTree : public QListWidget {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int gridItemWidth READ GetGridItemWidth WRITE Q_PROPERTY(int gridItemWidth READ GetGridItemWidth WRITE SetGridItemWidth DESIGNABLE true)
SetGridItemWidth DESIGNABLE true) Q_PROPERTY(int gridItemHeight READ GetGridItemHeight WRITE SetGridItemHeight DESIGNABLE true)
Q_PROPERTY(int gridItemHeight READ GetGridItemHeight WRITE
SetGridItemHeight DESIGNABLE true)
bool gridMode = false; bool gridMode = false;
int maxWidth = 150; int maxWidth = 150;
@ -36,12 +34,9 @@ protected:
virtual void dropEvent(QDropEvent *event) override; virtual void dropEvent(QDropEvent *event) override;
virtual void dragMoveEvent(QDragMoveEvent *event) override; virtual void dragMoveEvent(QDragMoveEvent *event) override;
virtual void dragLeaveEvent(QDragLeaveEvent *event) override; virtual void dragLeaveEvent(QDragLeaveEvent *event) override;
virtual void rowsInserted(const QModelIndex &parent, int start, virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
int end) override;
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 3) #if QT_VERSION < QT_VERSION_CHECK(6, 4, 3)
virtual void virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected) override;
#endif #endif
signals: signals:

View File

@ -28,15 +28,11 @@ public:
OBSSignal removedSignal; OBSSignal removedSignal;
OBSSignal destroyedSignal; OBSSignal destroyedSignal;
OBSSourceLabel(const obs_source_t *source, QWidget *parent = nullptr, OBSSourceLabel(const obs_source_t *source, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
Qt::WindowFlags f = Qt::WindowFlags())
: QLabel(obs_source_get_name(source), parent, f), : QLabel(obs_source_get_name(source), parent, f),
renamedSignal(obs_source_get_signal_handler(source), "rename", renamedSignal(obs_source_get_signal_handler(source), "rename", &OBSSourceLabel::SourceRenamed, this),
&OBSSourceLabel::SourceRenamed, this), removedSignal(obs_source_get_signal_handler(source), "remove", &OBSSourceLabel::SourceRemoved, this),
removedSignal(obs_source_get_signal_handler(source), "remove", destroyedSignal(obs_source_get_signal_handler(source), "destroy", &OBSSourceLabel::SourceDestroyed,
&OBSSourceLabel::SourceRemoved, this),
destroyedSignal(obs_source_get_signal_handler(source),
"destroy", &OBSSourceLabel::SourceDestroyed,
this) this)
{ {
} }

View File

@ -30,9 +30,7 @@ static inline OBSScene GetCurrentScene()
/* ========================================================================= */ /* ========================================================================= */
SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) : tree(tree_), sceneitem(sceneitem_)
: tree(tree_),
sceneitem(sceneitem_)
{ {
setAttribute(Qt::WA_TranslucentBackground); setAttribute(Qt::WA_TranslucentBackground);
setMouseTracking(true); setMouseTracking(true);
@ -40,8 +38,7 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_)
obs_source_t *source = obs_sceneitem_get_source(sceneitem); obs_source_t *source = obs_sceneitem_get_source(sceneitem);
const char *name = obs_source_get_name(source); const char *name = obs_source_get_name(source);
OBSDataAutoRelease privData = OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneitem);
obs_sceneitem_get_private_settings(sceneitem);
int preset = obs_data_get_int(privData, "color-preset"); int preset = obs_data_get_int(privData, "color-preset");
if (preset == 1) { if (preset == 1) {
@ -84,15 +81,13 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_)
vis->setProperty("class", "checkbox-icon indicator-visibility"); vis->setProperty("class", "checkbox-icon indicator-visibility");
vis->setChecked(sourceVisible); vis->setChecked(sourceVisible);
vis->setAccessibleName(QTStr("Basic.Main.Sources.Visibility")); vis->setAccessibleName(QTStr("Basic.Main.Sources.Visibility"));
vis->setAccessibleDescription( vis->setAccessibleDescription(QTStr("Basic.Main.Sources.VisibilityDescription").arg(name));
QTStr("Basic.Main.Sources.VisibilityDescription").arg(name));
lock = new QCheckBox(); lock = new QCheckBox();
lock->setProperty("class", "checkbox-icon indicator-lock"); lock->setProperty("class", "checkbox-icon indicator-lock");
lock->setChecked(obs_sceneitem_locked(sceneitem)); lock->setChecked(obs_sceneitem_locked(sceneitem));
lock->setAccessibleName(QTStr("Basic.Main.Sources.Lock")); lock->setAccessibleName(QTStr("Basic.Main.Sources.Lock"));
lock->setAccessibleDescription( lock->setAccessibleDescription(QTStr("Basic.Main.Sources.LockDescription").arg(name));
QTStr("Basic.Main.Sources.LockDescription").arg(name));
label = new OBSSourceLabel(source); label = new OBSSourceLabel(source);
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
@ -135,26 +130,20 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_)
const char *uuid = obs_source_get_uuid(scenesource); const char *uuid = obs_source_get_uuid(scenesource);
obs_source_t *source = obs_sceneitem_get_source(sceneitem); obs_source_t *source = obs_sceneitem_get_source(sceneitem);
auto undo_redo = [](const std::string &uuid, int64_t id, auto undo_redo = [](const std::string &uuid, int64_t id, bool val) {
bool val) { OBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());
OBSSourceAutoRelease s =
obs_get_source_by_uuid(uuid.c_str());
obs_scene_t *sc = obs_group_or_scene_from_source(s); obs_scene_t *sc = obs_group_or_scene_from_source(s);
obs_sceneitem_t *si = obs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);
obs_scene_find_sceneitem_by_id(sc, id);
if (si) if (si)
obs_sceneitem_set_visible(si, val); obs_sceneitem_set_visible(si, val);
}; };
QString str = QTStr(val ? "Undo.ShowSceneItem" QString str = QTStr(val ? "Undo.ShowSceneItem" : "Undo.HideSceneItem");
: "Undo.HideSceneItem");
OBSBasic *main = OBSBasic::Get(); OBSBasic *main = OBSBasic::Get();
main->undo_s.add_action( main->undo_s.add_action(str.arg(obs_source_get_name(source), name),
str.arg(obs_source_get_name(source), name), std::bind(undo_redo, std::placeholders::_1, id, !val),
std::bind(undo_redo, std::placeholders::_1, id, !val), std::bind(undo_redo, std::placeholders::_1, id, val), uuid, uuid);
std::bind(undo_redo, std::placeholders::_1, id, val),
uuid, uuid);
QSignalBlocker sourcesSignalBlocker(this); QSignalBlocker sourcesSignalBlocker(this);
obs_sceneitem_set_visible(sceneitem, val); obs_sceneitem_set_visible(sceneitem, val);
@ -200,16 +189,12 @@ void SourceTreeItem::ReconnectSignals()
/* --------------------------------------------------------- */ /* --------------------------------------------------------- */
auto removeItem = [](void *data, calldata_t *cd) { auto removeItem = [](void *data, calldata_t *cd) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data); obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
obs_sceneitem_t *curItem = obs_scene_t *curScene = (obs_scene_t *)calldata_ptr(cd, "scene");
(obs_sceneitem_t *)calldata_ptr(cd, "item");
obs_scene_t *curScene =
(obs_scene_t *)calldata_ptr(cd, "scene");
if (curItem == this_->sceneitem) { if (curItem == this_->sceneitem) {
QMetaObject::invokeMethod(this_->tree, "Remove", QMetaObject::invokeMethod(this_->tree, "Remove", Q_ARG(OBSSceneItem, curItem),
Q_ARG(OBSSceneItem, curItem),
Q_ARG(OBSScene, curScene)); Q_ARG(OBSScene, curScene));
curItem = nullptr; curItem = nullptr;
} }
@ -218,52 +203,41 @@ void SourceTreeItem::ReconnectSignals()
}; };
auto itemVisible = [](void *data, calldata_t *cd) { auto itemVisible = [](void *data, calldata_t *cd) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data); obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
obs_sceneitem_t *curItem =
(obs_sceneitem_t *)calldata_ptr(cd, "item");
bool visible = calldata_bool(cd, "visible"); bool visible = calldata_bool(cd, "visible");
if (curItem == this_->sceneitem) if (curItem == this_->sceneitem)
QMetaObject::invokeMethod(this_, "VisibilityChanged", QMetaObject::invokeMethod(this_, "VisibilityChanged", Q_ARG(bool, visible));
Q_ARG(bool, visible));
}; };
auto itemLocked = [](void *data, calldata_t *cd) { auto itemLocked = [](void *data, calldata_t *cd) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data); obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
obs_sceneitem_t *curItem =
(obs_sceneitem_t *)calldata_ptr(cd, "item");
bool locked = calldata_bool(cd, "locked"); bool locked = calldata_bool(cd, "locked");
if (curItem == this_->sceneitem) if (curItem == this_->sceneitem)
QMetaObject::invokeMethod(this_, "LockedChanged", QMetaObject::invokeMethod(this_, "LockedChanged", Q_ARG(bool, locked));
Q_ARG(bool, locked));
}; };
auto itemSelect = [](void *data, calldata_t *cd) { auto itemSelect = [](void *data, calldata_t *cd) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data); obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
obs_sceneitem_t *curItem =
(obs_sceneitem_t *)calldata_ptr(cd, "item");
if (curItem == this_->sceneitem) if (curItem == this_->sceneitem)
QMetaObject::invokeMethod(this_, "Select"); QMetaObject::invokeMethod(this_, "Select");
}; };
auto itemDeselect = [](void *data, calldata_t *cd) { auto itemDeselect = [](void *data, calldata_t *cd) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data); obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
obs_sceneitem_t *curItem =
(obs_sceneitem_t *)calldata_ptr(cd, "item");
if (curItem == this_->sceneitem) if (curItem == this_->sceneitem)
QMetaObject::invokeMethod(this_, "Deselect"); QMetaObject::invokeMethod(this_, "Deselect");
}; };
auto reorderGroup = [](void *data, calldata_t *) { auto reorderGroup = [](void *data, calldata_t *) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data);
QMetaObject::invokeMethod(this_->tree, "ReorderItems"); QMetaObject::invokeMethod(this_->tree, "ReorderItems");
}; };
@ -288,8 +262,7 @@ void SourceTreeItem::ReconnectSignals()
/* --------------------------------------------------------- */ /* --------------------------------------------------------- */
auto removeSource = [](void *data, calldata_t *) { auto removeSource = [](void *data, calldata_t *) {
SourceTreeItem *this_ = SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
reinterpret_cast<SourceTreeItem *>(data);
this_->DisconnectSignals(); this_->DisconnectSignals();
this_->sceneitem = nullptr; this_->sceneitem = nullptr;
QMetaObject::invokeMethod(this_->tree, "RefreshItems"); QMetaObject::invokeMethod(this_->tree, "RefreshItems");
@ -308,8 +281,7 @@ void SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event)
expand->setChecked(!expand->isChecked()); expand->setChecked(!expand->isChecked());
} else { } else {
obs_source_t *source = obs_sceneitem_get_source(sceneitem); obs_source_t *source = obs_sceneitem_get_source(sceneitem);
OBSBasic *main = OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
if (obs_source_configurable(source)) { if (obs_source_configurable(source)) {
main->CreatePropertiesWindow(source); main->CreatePropertiesWindow(source);
} }
@ -366,8 +338,7 @@ void SourceTreeItem::ExitEditMode(bool save)
OBSData redoSceneData = main->BackupScene(GetCurrentScene()); OBSData redoSceneData = main->BackupScene(GetCurrentScene());
QString text = QTStr("Undo.GroupItems").arg(newName.c_str()); QString text = QTStr("Undo.GroupItems").arg(newName.c_str());
main->CreateSceneUndoRedoAction(text, tree->undoSceneData, main->CreateSceneUndoRedoAction(text, tree->undoSceneData, redoSceneData);
redoSceneData);
tree->undoSceneData = nullptr; tree->undoSceneData = nullptr;
} }
@ -400,8 +371,7 @@ void SourceTreeItem::ExitEditModeInternal(bool save)
return; return;
if (newName.empty()) { if (newName.empty()) {
OBSMessageBox::information(main, QTStr("NoNameEntered.Title"), OBSMessageBox::information(main, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
QTStr("NoNameEntered.Text"));
return; return;
} }
@ -415,13 +385,11 @@ void SourceTreeItem::ExitEditModeInternal(bool save)
/* ----------------------------------------- */ /* ----------------------------------------- */
/* check for existing source */ /* check for existing source */
OBSSourceAutoRelease existingSource = OBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.c_str());
obs_get_source_by_name(newName.c_str());
bool exists = !!existingSource; bool exists = !!existingSource;
if (exists) { if (exists) {
OBSMessageBox::information(main, QTStr("NameExists.Title"), OBSMessageBox::information(main, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
QTStr("NameExists.Text"));
return; return;
} }
@ -430,33 +398,27 @@ void SourceTreeItem::ExitEditModeInternal(bool save)
QSignalBlocker sourcesSignalBlocker(this); QSignalBlocker sourcesSignalBlocker(this);
std::string prevName(obs_source_get_name(source)); std::string prevName(obs_source_get_name(source));
std::string scene_uuid = std::string scene_uuid = obs_source_get_uuid(main->GetCurrentSceneSource());
obs_source_get_uuid(main->GetCurrentSceneSource());
auto undo = [scene_uuid, prevName, main](const std::string &data) { auto undo = [scene_uuid, prevName, main](const std::string &data) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
obs_get_source_by_uuid(data.c_str());
obs_source_set_name(source, prevName.c_str()); obs_source_set_name(source, prevName.c_str());
OBSSourceAutoRelease scene_source = OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
obs_get_source_by_uuid(scene_uuid.c_str());
main->SetCurrentScene(scene_source.Get(), true); main->SetCurrentScene(scene_source.Get(), true);
}; };
std::string editedName = newName; std::string editedName = newName;
auto redo = [scene_uuid, main, editedName](const std::string &data) { auto redo = [scene_uuid, main, editedName](const std::string &data) {
OBSSourceAutoRelease source = OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
obs_get_source_by_uuid(data.c_str());
obs_source_set_name(source, editedName.c_str()); obs_source_set_name(source, editedName.c_str());
OBSSourceAutoRelease scene_source = OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
obs_get_source_by_uuid(scene_uuid.c_str());
main->SetCurrentScene(scene_source.Get(), true); main->SetCurrentScene(scene_source.Get(), true);
}; };
const char *uuid = obs_source_get_uuid(source); const char *uuid = obs_source_get_uuid(source);
main->undo_s.add_action(QTStr("Undo.Rename").arg(newName.c_str()), undo, main->undo_s.add_action(QTStr("Undo.Rename").arg(newName.c_str()), undo, redo, uuid, uuid);
redo, uuid, uuid);
obs_source_set_name(source, newName.c_str()); obs_source_set_name(source, newName.c_str());
} }
@ -467,15 +429,11 @@ bool SourceTreeItem::eventFilter(QObject *object, QEvent *event)
return false; return false;
if (LineEditCanceled(event)) { if (LineEditCanceled(event)) {
QMetaObject::invokeMethod(this, "ExitEditMode", QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, false));
Qt::QueuedConnection,
Q_ARG(bool, false));
return true; return true;
} }
if (LineEditChanged(event)) { if (LineEditChanged(event)) {
QMetaObject::invokeMethod(this, "ExitEditMode", QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, true));
Qt::QueuedConnection,
Q_ARG(bool, true));
return true; return true;
} }
@ -559,14 +517,12 @@ void SourceTreeItem::Update(bool force)
#endif #endif
boxLayout->insertWidget(0, expand); boxLayout->insertWidget(0, expand);
OBSDataAutoRelease data = OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
obs_sceneitem_get_private_settings(sceneitem);
expand->blockSignals(true); expand->blockSignals(true);
expand->setChecked(obs_data_get_bool(data, "collapsed")); expand->setChecked(obs_data_get_bool(data, "collapsed"));
expand->blockSignals(false); expand->blockSignals(false);
connect(expand, &QPushButton::toggled, this, connect(expand, &QPushButton::toggled, this, &SourceTreeItem::ExpandClicked);
&SourceTreeItem::ExpandClicked);
} else { } else {
spacer = new QSpacerItem(3, 1); spacer = new QSpacerItem(3, 1);
@ -633,8 +589,7 @@ void SourceTreeModel::Clear()
static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr) static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{ {
QVector<OBSSceneItem> &items = QVector<OBSSceneItem> &items = *reinterpret_cast<QVector<OBSSceneItem> *>(ptr);
*reinterpret_cast<QVector<OBSSceneItem> *>(ptr);
obs_source_t *src = obs_sceneitem_get_source(item); obs_source_t *src = obs_sceneitem_get_source(item);
if (obs_source_removed(src)) { if (obs_source_removed(src)) {
@ -642,13 +597,11 @@ static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
} }
if (obs_sceneitem_is_group(item)) { if (obs_sceneitem_is_group(item)) {
OBSDataAutoRelease data = OBSDataAutoRelease data = obs_sceneitem_get_private_settings(item);
obs_sceneitem_get_private_settings(item);
bool collapse = obs_data_get_bool(data, "collapsed"); bool collapse = obs_data_get_bool(data, "collapsed");
if (!collapse) { if (!collapse) {
obs_scene_t *scene = obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, enumItem, &items); obs_scene_enum_items(scene, enumItem, &items);
} }
@ -674,15 +627,13 @@ void SourceTreeModel::SceneChanged()
bool select = obs_sceneitem_selected(items[i]); bool select = obs_sceneitem_selected(items[i]);
QModelIndex index = createIndex(i, 0); QModelIndex index = createIndex(i, 0);
st->selectionModel()->select( st->selectionModel()->select(index,
index, select ? QItemSelectionModel::Select select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
: QItemSelectionModel::Deselect);
} }
} }
/* moves a scene item index (blame linux distros for using older Qt builds) */ /* moves a scene item index (blame linux distros for using older Qt builds) */
static inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, static inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, int newIdx)
int newIdx)
{ {
OBSSceneItem item = items[oldIdx]; OBSSceneItem item = items[oldIdx];
items.remove(oldIdx); items.remove(oldIdx);
@ -755,8 +706,7 @@ void SourceTreeModel::ReorderItems()
} }
/* move items */ /* move items */
beginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1, beginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1, QModelIndex(), idx1New + count);
QModelIndex(), idx1New + count);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
int to = idx1New + count; int to = idx1New + count;
if (to > idx1Old) if (to > idx1Old)
@ -802,8 +752,7 @@ void SourceTreeModel::Remove(obs_sceneitem_t *item)
for (int i = endIdx + 1; i < items.count(); i++) { for (int i = endIdx + 1; i < items.count(); i++) {
obs_sceneitem_t *subitem = items[i]; obs_sceneitem_t *subitem = items[i];
obs_scene_t *subscene = obs_scene_t *subscene = obs_sceneitem_get_scene(subitem);
obs_sceneitem_get_scene(subitem);
if (subscene == scene) if (subscene == scene)
endIdx = i; endIdx = i;
@ -829,9 +778,7 @@ OBSSceneItem SourceTreeModel::Get(int idx)
return items[idx]; return items[idx];
} }
SourceTreeModel::SourceTreeModel(SourceTree *st_) SourceTreeModel::SourceTreeModel(SourceTree *st_) : QAbstractListModel(st_), st(st_)
: QAbstractListModel(st_),
st(st_)
{ {
obs_frontend_add_event_callback(OBSFrontendEvent, this); obs_frontend_add_event_callback(OBSFrontendEvent, this);
} }
@ -860,8 +807,7 @@ Qt::ItemFlags SourceTreeModel::flags(const QModelIndex &index) const
obs_sceneitem_t *item = items[index.row()]; obs_sceneitem_t *item = items[index.row()];
bool is_group = obs_sceneitem_is_group(item); bool is_group = obs_sceneitem_is_group(item);
return QAbstractListModel::flags(index) | Qt::ItemIsEditable | return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
Qt::ItemIsDragEnabled |
(is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags); (is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags);
} }
@ -877,8 +823,7 @@ QString SourceTreeModel::GetNewGroupName()
int i = 2; int i = 2;
for (;;) { for (;;) {
OBSSourceAutoRelease group = OBSSourceAutoRelease group = obs_get_source_by_name(QT_TO_UTF8(name));
obs_get_source_by_name(QT_TO_UTF8(name));
if (!group) if (!group)
break; break;
name = QTStr("Basic.Main.Group").arg(QString::number(i++)); name = QTStr("Basic.Main.Group").arg(QString::number(i++));
@ -890,8 +835,7 @@ QString SourceTreeModel::GetNewGroupName()
void SourceTreeModel::AddGroup() void SourceTreeModel::AddGroup()
{ {
QString name = GetNewGroupName(); QString name = GetNewGroupName();
obs_sceneitem_t *group = obs_sceneitem_t *group = obs_scene_add_group(GetCurrentScene(), QT_TO_UTF8(name));
obs_scene_add_group(GetCurrentScene(), QT_TO_UTF8(name));
if (!group) if (!group)
return; return;
@ -902,8 +846,7 @@ void SourceTreeModel::AddGroup()
st->UpdateWidget(createIndex(0, 0, nullptr), group); st->UpdateWidget(createIndex(0, 0, nullptr), group);
UpdateGroupState(true); UpdateGroupState(true);
QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection, QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection, Q_ARG(int, 0));
Q_ARG(int, 0));
} }
void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices) void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
@ -924,8 +867,7 @@ void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
st->undoSceneData = main->BackupScene(scene); st->undoSceneData = main->BackupScene(scene);
obs_sceneitem_t *item = obs_scene_insert_group( obs_sceneitem_t *item = obs_scene_insert_group(scene, QT_TO_UTF8(name), item_order.data(), item_order.size());
scene, QT_TO_UTF8(name), item_order.data(), item_order.size());
if (!item) { if (!item) {
st->undoSceneData = nullptr; st->undoSceneData = nullptr;
return; return;
@ -947,8 +889,7 @@ void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
/* that's created automatically. */ /* that's created automatically. */
int newIdx = indices[0].row(); int newIdx = indices[0].row();
QMetaObject::invokeMethod(st, "NewGroupEdit", Qt::QueuedConnection, QMetaObject::invokeMethod(st, "NewGroupEdit", Qt::QueuedConnection, Q_ARG(int, newIdx));
Q_ARG(int, newIdx));
} }
void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices) void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
@ -968,8 +909,7 @@ void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
SceneChanged(); SceneChanged();
OBSData redoData = main->BackupScene(scene); OBSData redoData = main->BackupScene(scene);
main->CreateSceneUndoRedoAction(QTStr("Basic.Main.Ungroup"), undoData, main->CreateSceneUndoRedoAction(QTStr("Basic.Main.Ungroup"), undoData, redoData);
redoData);
} }
void SourceTreeModel::ExpandGroup(obs_sceneitem_t *item) void SourceTreeModel::ExpandGroup(obs_sceneitem_t *item)
@ -1045,19 +985,17 @@ SourceTree::SourceTree(QWidget *parent_) : QListView(parent_)
{ {
SourceTreeModel *stm_ = new SourceTreeModel(this); SourceTreeModel *stm_ = new SourceTreeModel(this);
setModel(stm_); setModel(stm_);
setStyleSheet(QString( setStyleSheet(QString("*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}"
"*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}" "*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}"
"*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}" "*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}"
"*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}" "*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}"
"*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}" "*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}"
"*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}" "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}"
"*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}" "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}"
"*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}" "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
"*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
UpdateNoSourcesMessage(); UpdateNoSourcesMessage();
connect(App(), &OBSApp::StyleChanged, this, connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateNoSourcesMessage);
&SourceTree::UpdateNoSourcesMessage);
connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateIcons); connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateIcons);
setItemDelegate(new SourceTreeDelegate(this)); setItemDelegate(new SourceTreeDelegate(this));
@ -1126,9 +1064,7 @@ void SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)
QModelIndex index = stm->createIndex(i, 0); QModelIndex index = stm->createIndex(i, 0);
if (index.isValid() && select != selectionModel()->isSelected(index)) if (index.isValid() && select != selectionModel()->isSelected(index))
selectionModel()->select( selectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
index, select ? QItemSelectionModel::Select
: QItemSelectionModel::Deselect);
} }
Q_DECLARE_METATYPE(OBSSceneItem); Q_DECLARE_METATYPE(OBSSceneItem);
@ -1175,9 +1111,7 @@ void SourceTree::dropEvent(QDropEvent *event)
obs_sceneitem_t *dropItem = items[row]; /* item being dropped on */ obs_sceneitem_t *dropItem = items[row]; /* item being dropped on */
bool itemIsGroup = obs_sceneitem_is_group(dropItem); bool itemIsGroup = obs_sceneitem_is_group(dropItem);
obs_sceneitem_t *dropGroup = obs_sceneitem_t *dropGroup = itemIsGroup ? dropItem : obs_sceneitem_get_group(scene, dropItem);
itemIsGroup ? dropItem
: obs_sceneitem_get_group(scene, dropItem);
/* not a group if moving above the group */ /* not a group if moving above the group */
if (indicator == QAbstractItemView::AboveItem && itemIsGroup) if (indicator == QAbstractItemView::AboveItem && itemIsGroup)
@ -1191,14 +1125,12 @@ void SourceTree::dropEvent(QDropEvent *event)
bool dropOnCollapsed = false; bool dropOnCollapsed = false;
if (dropGroup) { if (dropGroup) {
obs_data_t *data = obs_data_t *data = obs_sceneitem_get_private_settings(dropGroup);
obs_sceneitem_get_private_settings(dropGroup);
dropOnCollapsed = obs_data_get_bool(data, "collapsed"); dropOnCollapsed = obs_data_get_bool(data, "collapsed");
obs_data_release(data); obs_data_release(data);
} }
if (indicator == QAbstractItemView::BelowItem || if (indicator == QAbstractItemView::BelowItem || indicator == QAbstractItemView::OnItem ||
indicator == QAbstractItemView::OnItem ||
indicator == QAbstractItemView::OnViewport) indicator == QAbstractItemView::OnViewport)
row++; row++;
@ -1230,8 +1162,7 @@ void SourceTree::dropEvent(QDropEvent *event)
itemBelow = stm->items[row]; itemBelow = stm->items[row];
if (hasGroups) { if (hasGroups) {
if (!itemBelow || if (!itemBelow || obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {
obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {
dropGroup = nullptr; dropGroup = nullptr;
dropOnCollapsed = false; dropOnCollapsed = false;
} }
@ -1252,8 +1183,7 @@ void SourceTree::dropEvent(QDropEvent *event)
for (int i = 0; i < indices.size(); i++) { for (int i = 0; i < indices.size(); i++) {
obs_sceneitem_t *item = items[indices[i].row()]; obs_sceneitem_t *item = items[indices[i].row()];
if (obs_sceneitem_get_scene(item) != scene) if (obs_sceneitem_get_scene(item) != scene)
sources.push_back(obs_scene_get_source( sources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));
obs_sceneitem_get_scene(item)));
} }
if (dropGroup) if (dropGroup)
sources.push_back(obs_sceneitem_get_source(dropGroup)); sources.push_back(obs_sceneitem_get_source(dropGroup));
@ -1282,13 +1212,10 @@ void SourceTree::dropEvent(QDropEvent *event)
if (obs_sceneitem_is_group(item)) { if (obs_sceneitem_is_group(item)) {
for (int j = items.size() - 1; j >= 0; j--) { for (int j = items.size() - 1; j >= 0; j--) {
obs_sceneitem_t *subitem = items[j]; obs_sceneitem_t *subitem = items[j];
obs_sceneitem_t *subitemGroup = obs_sceneitem_t *subitemGroup = obs_sceneitem_get_group(scene, subitem);
obs_sceneitem_get_group(
scene, subitem);
if (subitemGroup == item) { if (subitemGroup == item) {
QModelIndex idx = QModelIndex idx = stm->createIndex(j, 0);
stm->createIndex(j, 0);
indices.insert(i + 1, idx); indices.insert(i + 1, idx);
} }
} }
@ -1318,8 +1245,7 @@ void SourceTree::dropEvent(QDropEvent *event)
itemTo--; itemTo--;
if (itemTo != from) { if (itemTo != from) {
stm->beginMoveRows(QModelIndex(), from, from, stm->beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
QModelIndex(), to);
MoveItem(items, from, itemTo); MoveItem(items, from, itemTo);
stm->endMoveRows(); stm->endMoveRows();
} }
@ -1348,22 +1274,18 @@ void SourceTree::dropEvent(QDropEvent *event)
using insertCollapsed_t = decltype(insertCollapsed); using insertCollapsed_t = decltype(insertCollapsed);
auto preInsertCollapsed = [](obs_scene_t *, obs_sceneitem_t *item, auto preInsertCollapsed = [](obs_scene_t *, obs_sceneitem_t *item, void *param) {
void *param) {
(*reinterpret_cast<insertCollapsed_t *>(param))(item); (*reinterpret_cast<insertCollapsed_t *>(param))(item);
return true; return true;
}; };
auto insertLastGroup = [&]() { auto insertLastGroup = [&]() {
OBSDataAutoRelease data = OBSDataAutoRelease data = obs_sceneitem_get_private_settings(lastGroup);
obs_sceneitem_get_private_settings(lastGroup);
bool collapsed = obs_data_get_bool(data, "collapsed"); bool collapsed = obs_data_get_bool(data, "collapsed");
if (collapsed) { if (collapsed) {
insertCollapsedIdx = 0; insertCollapsedIdx = 0;
obs_sceneitem_group_enum_items(lastGroup, obs_sceneitem_group_enum_items(lastGroup, preInsertCollapsed, &insertCollapsed);
preInsertCollapsed,
&insertCollapsed);
} }
struct obs_sceneitem_order_info info; struct obs_sceneitem_order_info info;
@ -1407,8 +1329,7 @@ void SourceTree::dropEvent(QDropEvent *event)
insertLastGroup(); insertLastGroup();
} }
obs_scene_reorder_items2(scene, orderList.data(), obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
orderList.size());
}; };
using updateScene_t = decltype(updateScene); using updateScene_t = decltype(updateScene);
@ -1454,8 +1375,7 @@ void SourceTree::dropEvent(QDropEvent *event)
QListView::dropEvent(event); QListView::dropEvent(event);
} }
void SourceTree::selectionChanged(const QItemSelection &selected, void SourceTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
const QItemSelection &deselected)
{ {
{ {
QSignalBlocker sourcesSignalBlocker(this); QSignalBlocker sourcesSignalBlocker(this);
@ -1504,8 +1424,7 @@ void SourceTree::NewGroupEdit(int row)
OBSData redoSceneData = main->BackupScene(GetCurrentScene()); OBSData redoSceneData = main->BackupScene(GetCurrentScene());
QString text = QTStr("Undo.GroupItems").arg("Unknown"); QString text = QTStr("Undo.GroupItems").arg("Unknown");
main->CreateSceneUndoRedoAction(text, undoSceneData, main->CreateSceneUndoRedoAction(text, undoSceneData, redoSceneData);
redoSceneData);
undoSceneData = nullptr; undoSceneData = nullptr;
} }
@ -1610,10 +1529,8 @@ void SourceTree::Remove(OBSSceneItem item, OBSScene scene)
if (!main->SavingDisabled()) { if (!main->SavingDisabled()) {
obs_source_t *sceneSource = obs_scene_get_source(scene); obs_source_t *sceneSource = obs_scene_get_source(scene);
obs_source_t *itemSource = obs_sceneitem_get_source(item); obs_source_t *itemSource = obs_sceneitem_get_source(item);
blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'", blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'", obs_source_get_name(itemSource),
obs_source_get_name(itemSource), obs_source_get_id(itemSource), obs_source_get_name(sceneSource));
obs_source_get_id(itemSource),
obs_source_get_name(sceneSource));
} }
} }
@ -1637,8 +1554,7 @@ void SourceTree::AddGroup()
void SourceTree::UpdateNoSourcesMessage() void SourceTree::UpdateNoSourcesMessage()
{ {
QString file = !App()->IsThemeDark() ? ":res/images/no_sources.svg" QString file = !App()->IsThemeDark() ? ":res/images/no_sources.svg" : "theme:Dark/no_sources.svg";
: "theme:Dark/no_sources.svg";
iconNoSources.load(file); iconNoSources.load(file);
QTextOption opt(Qt::AlignHCenter); QTextOption opt(Qt::AlignHCenter);
@ -1668,8 +1584,7 @@ void SourceTree::paintEvent(QPaintEvent *event)
QSizeF thisSize = size(); QSizeF thisSize = size();
const qreal spacing = 16.0; const qreal spacing = 16.0;
qreal totalHeight = qreal totalHeight = iconSize.height() + spacing + textSize.height();
iconSize.height() + spacing + textSize.height();
qreal x = thisSize.width() / 2.0 - iconSize.width() / 2.0; qreal x = thisSize.width() / 2.0 - iconSize.width() / 2.0;
qreal y = thisSize.height() / 2.0 - totalHeight / 2.0; qreal y = thisSize.height() / 2.0 - totalHeight / 2.0;
@ -1684,13 +1599,9 @@ void SourceTree::paintEvent(QPaintEvent *event)
} }
} }
SourceTreeDelegate::SourceTreeDelegate(QObject *parent) SourceTreeDelegate::SourceTreeDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
: QStyledItemDelegate(parent)
{
}
QSize SourceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, QSize SourceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
const QModelIndex &index) const
{ {
SourceTree *tree = qobject_cast<SourceTree *>(parent()); SourceTree *tree = qobject_cast<SourceTree *>(parent());
QWidget *item = tree->indexWidget(index); QWidget *item = tree->indexWidget(index);

View File

@ -120,8 +120,7 @@ public:
explicit SourceTreeModel(SourceTree *st); explicit SourceTreeModel(SourceTree *st);
virtual int rowCount(const QModelIndex &parent) const override; virtual int rowCount(const QModelIndex &parent) const override;
virtual QVariant data(const QModelIndex &index, virtual QVariant data(const QModelIndex &index, int role) const override;
int role) const override;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
virtual Qt::DropActions supportedDropActions() const override; virtual Qt::DropActions supportedDropActions() const override;
@ -149,10 +148,7 @@ class SourceTree : public QListView {
void UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item); void UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item);
void UpdateWidgets(bool force = false); void UpdateWidgets(bool force = false);
inline SourceTreeModel *GetStm() const inline SourceTreeModel *GetStm() const { return reinterpret_cast<SourceTreeModel *>(model()); }
{
return reinterpret_cast<SourceTreeModel *>(model());
}
public: public:
inline SourceTreeItem *GetItemWidget(int idx) inline SourceTreeItem *GetItemWidget(int idx)
@ -194,9 +190,7 @@ protected:
virtual void dropEvent(QDropEvent *event) override; virtual void dropEvent(QDropEvent *event) override;
virtual void paintEvent(QPaintEvent *event) override; virtual void paintEvent(QPaintEvent *event) override;
virtual void virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected) override;
}; };
class SourceTreeDelegate : public QStyledItemDelegate { class SourceTreeDelegate : public QStyledItemDelegate {
@ -204,6 +198,5 @@ class SourceTreeDelegate : public QStyledItemDelegate {
public: public:
SourceTreeDelegate(QObject *parent); SourceTreeDelegate(QObject *parent);
virtual QSize sizeHint(const QStyleOptionViewItem &option, virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
const QModelIndex &index) const override;
}; };

View File

@ -49,16 +49,14 @@ static std::optional<std::vector<GoLiveApi::Gpu>> system_gpu_data()
/* driver version */ /* driver version */
LARGE_INTEGER umd; LARGE_INTEGER umd;
hr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), hr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &umd);
&umd);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
const uint64_t version = umd.QuadPart; const uint64_t version = umd.QuadPart;
const uint16_t aa = (version >> 48) & 0xffff; const uint16_t aa = (version >> 48) & 0xffff;
const uint16_t bb = (version >> 32) & 0xffff; const uint16_t bb = (version >> 32) & 0xffff;
const uint16_t ccccc = (version >> 16) & 0xffff; const uint16_t ccccc = (version >> 16) & 0xffff;
const uint16_t ddddd = version & 0xffff; const uint16_t ddddd = version & 0xffff;
snprintf(driver_version, sizeof(driver_version), snprintf(driver_version, sizeof(driver_version), "%" PRIu16 ".%" PRIu16 ".%" PRIu16 ".%" PRIu16,
"%" PRIu16 ".%" PRIu16 ".%" PRIu16 ".%" PRIu16,
aa, bb, ccccc, ddddd); aa, bb, ccccc, ddddd);
data.driver_version = driver_version; data.driver_version = driver_version;
} }
@ -78,15 +76,12 @@ static void get_processor_info(char **name, DWORD *speed)
memset(data, 0, sizeof(data)); memset(data, 0, sizeof(data));
status = RegOpenKeyW( status = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &key);
HKEY_LOCAL_MACHINE,
L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &key);
if (status != ERROR_SUCCESS) if (status != ERROR_SUCCESS)
return; return;
size = sizeof(data); size = sizeof(data);
status = RegQueryValueExW(key, L"ProcessorNameString", NULL, NULL, status = RegQueryValueExW(key, L"ProcessorNameString", NULL, NULL, (LPBYTE)data, &size);
(LPBYTE)data, &size);
if (status == ERROR_SUCCESS) { if (status == ERROR_SUCCESS) {
os_wcs_to_utf8_ptr(data, 0, name); os_wcs_to_utf8_ptr(data, 0, name);
} else { } else {
@ -94,25 +89,20 @@ static void get_processor_info(char **name, DWORD *speed)
} }
size = sizeof(*speed); size = sizeof(*speed);
status = RegQueryValueExW(key, L"~MHz", NULL, NULL, (LPBYTE)speed, status = RegQueryValueExW(key, L"~MHz", NULL, NULL, (LPBYTE)speed, &size);
&size);
if (status != ERROR_SUCCESS) if (status != ERROR_SUCCESS)
*speed = 0; *speed = 0;
RegCloseKey(key); RegCloseKey(key);
} }
#define WIN10_GAME_BAR_REG_KEY \ #define WIN10_GAME_BAR_REG_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\GameDVR"
L"Software\\Microsoft\\Windows\\CurrentVersion\\GameDVR" #define WIN10_GAME_DVR_POLICY_REG_KEY L"SOFTWARE\\Policies\\Microsoft\\Windows\\GameDVR"
#define WIN10_GAME_DVR_POLICY_REG_KEY \
L"SOFTWARE\\Policies\\Microsoft\\Windows\\GameDVR"
#define WIN10_GAME_DVR_REG_KEY L"System\\GameConfigStore" #define WIN10_GAME_DVR_REG_KEY L"System\\GameConfigStore"
#define WIN10_GAME_MODE_REG_KEY L"Software\\Microsoft\\GameBar" #define WIN10_GAME_MODE_REG_KEY L"Software\\Microsoft\\GameBar"
#define WIN10_HAGS_REG_KEY \ #define WIN10_HAGS_REG_KEY L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers"
L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers"
static std::optional<GoLiveApi::GamingFeatures> static std::optional<GoLiveApi::GamingFeatures> get_gaming_features_data(const win_version_info &ver)
get_gaming_features_data(const win_version_info &ver)
{ {
uint32_t win_ver = (ver.major << 8) | ver.minor; uint32_t win_ver = (ver.major << 8) | ver.minor;
if (win_ver < 0xA00) if (win_ver < 0xA00)
@ -130,36 +120,29 @@ get_gaming_features_data(const win_version_info &ver)
DWORD disabled_value; DWORD disabled_value;
}; };
struct feature_mapping_s features[] = { struct feature_mapping_s features[] = {
{&gaming_features.game_bar_enabled, HKEY_CURRENT_USER, {&gaming_features.game_bar_enabled, HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY, L"AppCaptureEnabled", 0,
WIN10_GAME_BAR_REG_KEY, L"AppCaptureEnabled", 0, false, 0}, false, 0},
{&gaming_features.game_dvr_allowed, HKEY_CURRENT_USER, {&gaming_features.game_dvr_allowed, HKEY_CURRENT_USER, WIN10_GAME_DVR_POLICY_REG_KEY, L"AllowGameDVR",
WIN10_GAME_DVR_POLICY_REG_KEY, L"AllowGameDVR", 0, false, 0}, 0, false, 0},
{&gaming_features.game_dvr_enabled, HKEY_CURRENT_USER, {&gaming_features.game_dvr_enabled, HKEY_CURRENT_USER, WIN10_GAME_DVR_REG_KEY, L"GameDVR_Enabled", 0,
WIN10_GAME_DVR_REG_KEY, L"GameDVR_Enabled", 0, false, 0}, false, 0},
{&gaming_features.game_dvr_bg_recording, HKEY_CURRENT_USER, {&gaming_features.game_dvr_bg_recording, HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY,
WIN10_GAME_BAR_REG_KEY, L"HistoricalCaptureEnabled", 0, false, L"HistoricalCaptureEnabled", 0, false, 0},
0}, {&gaming_features.game_mode_enabled, HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY, L"AutoGameModeEnabled",
{&gaming_features.game_mode_enabled, HKEY_CURRENT_USER,
WIN10_GAME_MODE_REG_KEY, L"AutoGameModeEnabled",
L"AllowAutoGameMode", false, 0}, L"AllowAutoGameMode", false, 0},
{&gaming_features.hags_enabled, HKEY_LOCAL_MACHINE, {&gaming_features.hags_enabled, HKEY_LOCAL_MACHINE, WIN10_HAGS_REG_KEY, L"HwSchMode", 0, true, 1}};
WIN10_HAGS_REG_KEY, L"HwSchMode", 0, true, 1}};
for (int i = 0; i < sizeof(features) / sizeof(*features); ++i) { for (int i = 0; i < sizeof(features) / sizeof(*features); ++i) {
struct reg_dword info; struct reg_dword info;
get_reg_dword(features[i].hkey, features[i].sub_key, get_reg_dword(features[i].hkey, features[i].sub_key, features[i].value_name, &info);
features[i].value_name, &info);
if (info.status != ERROR_SUCCESS && if (info.status != ERROR_SUCCESS && features[i].backup_value_name) {
features[i].backup_value_name) { get_reg_dword(features[i].hkey, features[i].sub_key, features[i].backup_value_name, &info);
get_reg_dword(features[i].hkey, features[i].sub_key,
features[i].backup_value_name, &info);
} }
if (info.status == ERROR_SUCCESS) { if (info.status == ERROR_SUCCESS) {
*features[i].field = info.return_value != *features[i].field = info.return_value != features[i].disabled_value;
features[i].disabled_value;
} else if (features[i].non_existence_is_false) { } else if (features[i].non_existence_is_false) {
*features[i].field = false; *features[i].field = false;
} }
@ -168,11 +151,9 @@ get_gaming_features_data(const win_version_info &ver)
return gaming_features; return gaming_features;
} }
static inline bool get_reg_sz(HKEY key, const wchar_t *val, wchar_t *buf, static inline bool get_reg_sz(HKEY key, const wchar_t *val, wchar_t *buf, DWORD size)
DWORD size)
{ {
const LSTATUS status = const LSTATUS status = RegGetValueW(key, NULL, val, RRF_RT_REG_SZ, NULL, buf, &size);
RegGetValueW(key, NULL, val, RRF_RT_REG_SZ, NULL, buf, &size);
return status == ERROR_SUCCESS; return status == ERROR_SUCCESS;
} }
@ -194,18 +175,15 @@ static inline void get_reg_ver(struct win_version_info *ver)
size = sizeof(dw_val); size = sizeof(dw_val);
status = RegQueryValueExW(key, L"CurrentMajorVersionNumber", NULL, NULL, status = RegQueryValueExW(key, L"CurrentMajorVersionNumber", NULL, NULL, (LPBYTE)&dw_val, &size);
(LPBYTE)&dw_val, &size);
if (status == ERROR_SUCCESS) if (status == ERROR_SUCCESS)
ver->major = (int)dw_val; ver->major = (int)dw_val;
status = RegQueryValueExW(key, L"CurrentMinorVersionNumber", NULL, NULL, status = RegQueryValueExW(key, L"CurrentMinorVersionNumber", NULL, NULL, (LPBYTE)&dw_val, &size);
(LPBYTE)&dw_val, &size);
if (status == ERROR_SUCCESS) if (status == ERROR_SUCCESS)
ver->minor = (int)dw_val; ver->minor = (int)dw_val;
status = RegQueryValueExW(key, L"UBR", NULL, NULL, (LPBYTE)&dw_val, status = RegQueryValueExW(key, L"UBR", NULL, NULL, (LPBYTE)&dw_val, &size);
&size);
if (status == ERROR_SUCCESS) if (status == ERROR_SUCCESS)
ver->revis = (int)dw_val; ver->revis = (int)dw_val;
@ -213,8 +191,7 @@ static inline void get_reg_ver(struct win_version_info *ver)
ver->build = wcstol(str, NULL, 10); ver->build = wcstol(str, NULL, 10);
} }
const wchar_t *release_key = ver->build > 19041 ? L"DisplayVersion" const wchar_t *release_key = ver->build > 19041 ? L"DisplayVersion" : L"ReleaseId";
: L"ReleaseId";
if (get_reg_sz(key, release_key, str, sizeof(str))) { if (get_reg_sz(key, release_key, str, sizeof(str))) {
os_wcs_to_utf8(str, 0, win_release_id, MAX_SZ_LEN); os_wcs_to_utf8(str, 0, win_release_id, MAX_SZ_LEN);
} }

View File

@ -44,8 +44,7 @@ bool UIValidation::NoSourcesConfirmation(QWidget *parent)
messageBox.setWindowTitle(QTStr("NoSources.Title")); messageBox.setWindowTitle(QTStr("NoSources.Title"));
messageBox.setText(msg); messageBox.setText(msg);
QAbstractButton *yesButton = QAbstractButton *yesButton = messageBox.addButton(QTStr("Yes"), QMessageBox::YesRole);
messageBox.addButton(QTStr("Yes"), QMessageBox::YesRole);
messageBox.addButton(QTStr("No"), QMessageBox::NoRole); messageBox.addButton(QTStr("No"), QMessageBox::NoRole);
messageBox.setIcon(QMessageBox::Question); messageBox.setIcon(QMessageBox::Question);
messageBox.exec(); messageBox.exec();
@ -56,8 +55,7 @@ bool UIValidation::NoSourcesConfirmation(QWidget *parent)
return true; return true;
} }
StreamSettingsAction StreamSettingsAction UIValidation::StreamSettingsConfirmation(QWidget *parent, OBSService service)
UIValidation::StreamSettingsConfirmation(QWidget *parent, OBSService service)
{ {
if (obs_service_can_try_to_connect(service)) if (obs_service_can_try_to_connect(service))
return StreamSettingsAction::ContinueStream; return StreamSettingsAction::ContinueStream;
@ -65,10 +63,8 @@ UIValidation::StreamSettingsConfirmation(QWidget *parent, OBSService service)
char const *serviceType = obs_service_get_type(service); char const *serviceType = obs_service_get_type(service);
bool isCustomService = (strcmp(serviceType, "rtmp_custom") == 0); bool isCustomService = (strcmp(serviceType, "rtmp_custom") == 0);
char const *streamUrl = obs_service_get_connect_info( char const *streamUrl = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_SERVER_URL);
service, OBS_SERVICE_CONNECT_INFO_SERVER_URL); char const *streamKey = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_STREAM_KEY);
char const *streamKey = obs_service_get_connect_info(
service, OBS_SERVICE_CONNECT_INFO_STREAM_KEY);
bool streamUrlMissing = !(streamUrl != NULL && streamUrl[0] != '\0'); bool streamUrlMissing = !(streamUrl != NULL && streamUrl[0] != '\0');
bool streamKeyMissing = !(streamKey != NULL && streamKey[0] != '\0'); bool streamKeyMissing = !(streamKey != NULL && streamKey[0] != '\0');
@ -83,8 +79,7 @@ UIValidation::StreamSettingsConfirmation(QWidget *parent, OBSService service)
} }
QMessageBox messageBox(parent); QMessageBox messageBox(parent);
messageBox.setWindowTitle( messageBox.setWindowTitle(QTStr("Basic.Settings.Stream.MissingSettingAlert"));
QTStr("Basic.Settings.Stream.MissingSettingAlert"));
messageBox.setText(msg); messageBox.setText(msg);
QPushButton *cancel; QPushButton *cancel;
@ -97,9 +92,7 @@ UIValidation::StreamSettingsConfirmation(QWidget *parent, OBSService service)
#define ACCEPT_BUTTON QMessageBox::NoRole #define ACCEPT_BUTTON QMessageBox::NoRole
#define REJECT_BUTTON QMessageBox::NoRole #define REJECT_BUTTON QMessageBox::NoRole
#endif #endif
settings = messageBox.addButton( settings = messageBox.addButton(QTStr("Basic.Settings.Stream.StreamSettingsWarning"), ACCEPT_BUTTON);
QTStr("Basic.Settings.Stream.StreamSettingsWarning"),
ACCEPT_BUTTON);
cancel = messageBox.addButton(QTStr("Cancel"), REJECT_BUTTON); cancel = messageBox.addButton(QTStr("Cancel"), REJECT_BUTTON);
messageBox.setDefaultButton(settings); messageBox.setDefaultButton(settings);

View File

@ -25,6 +25,5 @@ public:
* basics is missing in stream, explain missing fields and offer to * basics is missing in stream, explain missing fields and offer to
* open settings, cancel, or continue. Returns Continue if all * open settings, cancel, or continue. Returns Continue if all
* settings are valid. */ * settings are valid. */
static StreamSettingsAction static StreamSettingsAction StreamSettingsConfirmation(QWidget *parent, OBSService service);
StreamSettingsConfirmation(QWidget *parent, OBSService service);
}; };

View File

@ -6,8 +6,7 @@
undo_stack::undo_stack(ui_ptr ui) : ui(ui) undo_stack::undo_stack(ui_ptr ui) : ui(ui)
{ {
QObject::connect(&repeat_reset_timer, &QTimer::timeout, this, QObject::connect(&repeat_reset_timer, &QTimer::timeout, this, &undo_stack::reset_repeatable_state);
&undo_stack::reset_repeatable_state);
repeat_reset_timer.setSingleShot(true); repeat_reset_timer.setSingleShot(true);
repeat_reset_timer.setInterval(3000); repeat_reset_timer.setInterval(3000);
} }
@ -30,10 +29,8 @@ void undo_stack::clear()
ui->actionMainRedo->setDisabled(true); ui->actionMainRedo->setDisabled(true);
} }
void undo_stack::add_action(const QString &name, const undo_redo_cb &undo, void undo_stack::add_action(const QString &name, const undo_redo_cb &undo, const undo_redo_cb &redo,
const undo_redo_cb &redo, const std::string &undo_data, const std::string &redo_data, bool repeatable)
const std::string &undo_data,
const std::string &redo_data, bool repeatable)
{ {
if (!is_enabled()) if (!is_enabled())
return; return;
@ -85,8 +82,7 @@ void undo_stack::undo()
ui->actionMainUndo->setDisabled(true); ui->actionMainUndo->setDisabled(true);
ui->actionMainUndo->setText(QTStr("Undo.Undo")); ui->actionMainUndo->setText(QTStr("Undo.Undo"));
} else { } else {
ui->actionMainUndo->setText( ui->actionMainUndo->setText(QTStr("Undo.Item.Undo").arg(undo_items.front().name));
QTStr("Undo.Item.Undo").arg(undo_items.front().name));
} }
} }
@ -109,8 +105,7 @@ void undo_stack::redo()
ui->actionMainRedo->setDisabled(true); ui->actionMainRedo->setDisabled(true);
ui->actionMainRedo->setText(QTStr("Undo.Redo")); ui->actionMainRedo->setText(QTStr("Undo.Redo"));
} else { } else {
ui->actionMainRedo->setText( ui->actionMainRedo->setText(QTStr("Undo.Item.Redo").arg(redo_items.front().name));
QTStr("Undo.Item.Redo").arg(redo_items.front().name));
} }
} }

View File

@ -53,9 +53,8 @@ public:
void pop_disabled(); void pop_disabled();
void clear(); void clear();
void add_action(const QString &name, const undo_redo_cb &undo, void add_action(const QString &name, const undo_redo_cb &undo, const undo_redo_cb &redo,
const undo_redo_cb &redo, const std::string &undo_data, const std::string &undo_data, const std::string &redo_data, bool repeatable = false);
const std::string &redo_data, bool repeatable = false);
void undo(); void undo();
void redo(); void redo();
}; };

View File

@ -3,9 +3,8 @@
#include "mbedtls/md.h" #include "mbedtls/md.h"
#include "mbedtls/pk.h" #include "mbedtls/pk.h"
bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, const uint8_t *buf, const size_t len,
const uint8_t *buf, const size_t len, const uint8_t *sig, const uint8_t *sig, const size_t sigLen)
const size_t sigLen)
{ {
bool result = false; bool result = false;
int ret = 1; int ret = 1;
@ -15,18 +14,15 @@ bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen,
mbedtls_pk_init(&pk); mbedtls_pk_init(&pk);
// Parse PEM key // Parse PEM key
if ((ret = mbedtls_pk_parse_public_key(&pk, pubKey, pubKeyLen + 1)) != if ((ret = mbedtls_pk_parse_public_key(&pk, pubKey, pubKeyLen + 1)) != 0) {
0) {
goto exit; goto exit;
} }
// Hash input buffer // Hash input buffer
if ((ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), buf, if ((ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), buf, len, hash)) != 0) {
len, hash)) != 0) {
goto exit; goto exit;
} }
// Verify signautre // Verify signautre
if ((ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA512, hash, 64, sig, if ((ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA512, hash, 64, sig, sigLen)) != 0) {
sigLen)) != 0) {
goto exit; goto exit;
} }

View File

@ -3,6 +3,5 @@
#include <stdlib.h> #include <stdlib.h>
#include <cstdint> #include <cstdint>
bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, const uint8_t *buf, const size_t len,
const uint8_t *buf, const size_t len, const uint8_t *sig, const uint8_t *sig, const size_t sigLen);
const size_t sigLen);

View File

@ -10,16 +10,14 @@
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
static const char *MAC_BRANCHES_URL = static const char *MAC_BRANCHES_URL = "https://obsproject.com/update_studio/branches.json";
"https://obsproject.com/update_studio/branches.json";
static const char *MAC_DEFAULT_BRANCH = "stable"; static const char *MAC_DEFAULT_BRANCH = "stable";
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
bool GetBranch(std::string &selectedBranch) bool GetBranch(std::string &selectedBranch)
{ {
const char *config_branch = config_get_string( const char *config_branch = config_get_string(App()->GetAppConfig(), "General", "UpdateBranch");
App()->GetAppConfig(), "General", "UpdateBranch");
if (!config_branch) if (!config_branch)
return true; return true;
@ -50,8 +48,8 @@ void MacUpdateThread::infoMsg(const QString &title, const QString &text)
void MacUpdateThread::info(const QString &title, const QString &text) void MacUpdateThread::info(const QString &title, const QString &text)
{ {
QMetaObject::invokeMethod(this, "infoMsg", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(this, "infoMsg", Qt::BlockingQueuedConnection, Q_ARG(QString, title),
Q_ARG(QString, title), Q_ARG(QString, text)); Q_ARG(QString, text));
} }
void MacUpdateThread::run() void MacUpdateThread::run()
@ -62,18 +60,15 @@ try {
/* ----------------------------------- * /* ----------------------------------- *
* get branches from server */ * get branches from server */
if (FetchAndVerifyFile("branches", "obs-studio/updates/branches.json", if (FetchAndVerifyFile("branches", "obs-studio/updates/branches.json", MAC_BRANCHES_URL, &text))
MAC_BRANCHES_URL, &text))
App()->SetBranchData(text); App()->SetBranchData(text);
/* ----------------------------------- * /* ----------------------------------- *
* Validate branch selection */ * Validate branch selection */
if (!GetBranch(branch)) { if (!GetBranch(branch)) {
config_set_string(App()->GetAppConfig(), "General", config_set_string(App()->GetAppConfig(), "General", "UpdateBranch", MAC_DEFAULT_BRANCH);
"UpdateBranch", MAC_DEFAULT_BRANCH); info(QTStr("Updater.BranchNotFound.Title"), QTStr("Updater.BranchNotFound.Text"));
info(QTStr("Updater.BranchNotFound.Title"),
QTStr("Updater.BranchNotFound.Text"));
} }
emit Result(QString::fromStdString(branch), manualUpdate); emit Result(QString::fromStdString(branch), manualUpdate);

View File

@ -34,10 +34,8 @@ struct JsonBranch {
bool windows = false; bool windows = false;
bool macos = false; bool macos = false;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonBranch, name, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonBranch, name, display_name, description, enabled, visible,
display_name, description, windows, macos)
enabled, visible, windows,
macos)
}; };
using JsonBranches = std::vector<JsonBranch>; using JsonBranches = std::vector<JsonBranch>;

View File

@ -28,8 +28,7 @@
template<typename T> struct nlohmann::adl_serializer<std::optional<T>> { template<typename T> struct nlohmann::adl_serializer<std::optional<T>> {
static std::optional<T> from_json(const json &json) static std::optional<T> from_json(const json &json)
{ {
return json.is_null() ? std::nullopt return json.is_null() ? std::nullopt : std::optional{json.get<T>()};
: std::optional{json.get<T>()};
} }
static void to_json(json &json, std::optional<T> t) static void to_json(json &json, std::optional<T> t)
@ -46,8 +45,7 @@ struct WhatsNewPlatforms {
bool macos = false; bool macos = false;
bool linux = false; bool linux = false;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewPlatforms, windows, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewPlatforms, windows, macos, linux)
macos, linux)
}; };
struct WhatsNewItem { struct WhatsNewItem {
@ -63,8 +61,7 @@ struct WhatsNewItem {
/* Optional OS filter */ /* Optional OS filter */
std::optional<WhatsNewPlatforms> os = std::nullopt; std::optional<WhatsNewPlatforms> os = std::nullopt;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewItem, version, Beta, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewItem, version, Beta, RC, url, increment, os)
RC, url, increment, os)
}; };
using WhatsNewList = std::vector<WhatsNewItem>; using WhatsNewList = std::vector<WhatsNewItem>;

View File

@ -50,16 +50,13 @@ extern QCef *cef;
static bool QuickWriteFile(const char *file, const std::string &data) static bool QuickWriteFile(const char *file, const std::string &data)
try { try {
std::ofstream fileStream(std::filesystem::u8path(file), std::ofstream fileStream(std::filesystem::u8path(file), std::ios::binary);
std::ios::binary);
if (fileStream.fail()) if (fileStream.fail())
throw strprintf("Failed to open file '%s': %s", file, throw strprintf("Failed to open file '%s': %s", file, strerror(errno));
strerror(errno));
fileStream.write(data.data(), data.size()); fileStream.write(data.data(), data.size());
if (fileStream.fail()) if (fileStream.fail())
throw strprintf("Failed to write file '%s': %s", file, throw strprintf("Failed to write file '%s': %s", file, strerror(errno));
strerror(errno));
return true; return true;
@ -70,11 +67,9 @@ try {
static bool QuickReadFile(const char *file, std::string &data) static bool QuickReadFile(const char *file, std::string &data)
try { try {
std::ifstream fileStream(std::filesystem::u8path(file), std::ifstream fileStream(std::filesystem::u8path(file), std::ios::binary);
std::ios::binary);
if (!fileStream.is_open() || fileStream.fail()) if (!fileStream.is_open() || fileStream.fail())
throw strprintf("Failed to open file '%s': %s", file, throw strprintf("Failed to open file '%s': %s", file, strerror(errno));
strerror(errno));
fileStream.seekg(0, fileStream.end); fileStream.seekg(0, fileStream.end);
size_t size = fileStream.tellg(); size_t size = fileStream.tellg();
@ -84,8 +79,7 @@ try {
fileStream.read(&data[0], size); fileStream.read(&data[0], size);
if (fileStream.fail()) if (fileStream.fail())
throw strprintf("Failed to write file '%s': %s", file, throw strprintf("Failed to write file '%s': %s", file, strerror(errno));
strerror(errno));
return true; return true;
@ -147,8 +141,7 @@ std::string GetProgramGUID()
/* NOTE: this is an arbitrary random number that we use to count the /* NOTE: this is an arbitrary random number that we use to count the
* number of unique OBS installations and is not associated with any * number of unique OBS installations and is not associated with any
* kind of identifiable information */ * kind of identifiable information */
const char *pguid = config_get_string(App()->GetAppConfig(), "General", const char *pguid = config_get_string(App()->GetAppConfig(), "General", "InstallGUID");
"InstallGUID");
std::string guid; std::string guid;
if (pguid) if (pguid)
guid = pguid; guid = pguid;
@ -157,8 +150,7 @@ std::string GetProgramGUID()
GenerateGUID(guid); GenerateGUID(guid);
if (!guid.empty()) if (!guid.empty())
config_set_string(App()->GetAppConfig(), "General", config_set_string(App()->GetAppConfig(), "General", "InstallGUID", guid.c_str());
"InstallGUID", guid.c_str());
} }
return guid; return guid;
@ -176,16 +168,13 @@ static void LoadPublicKey(std::string &pubkey)
throw std::string("Could not read OBS public key file!"); throw std::string("Could not read OBS public key file!");
} }
static bool CheckDataSignature(const char *name, const std::string &data, static bool CheckDataSignature(const char *name, const std::string &data, const std::string &hexSig)
const std::string &hexSig)
try { try {
static std::mutex pubkey_mutex; static std::mutex pubkey_mutex;
static std::string obsPubKey; static std::string obsPubKey;
if (hexSig.empty() || hexSig.length() > 0xFFFF || if (hexSig.empty() || hexSig.length() > 0xFFFF || (hexSig.length() & 1) != 0)
(hexSig.length() & 1) != 0) throw strprintf("Missing or invalid signature for %s: %s", name, hexSig.c_str());
throw strprintf("Missing or invalid signature for %s: %s", name,
hexSig.c_str());
std::scoped_lock lock(pubkey_mutex); std::scoped_lock lock(pubkey_mutex);
if (obsPubKey.empty()) if (obsPubKey.empty())
@ -194,8 +183,7 @@ try {
// Convert hex string to bytes // Convert hex string to bytes
auto signature = QByteArray::fromHex(hexSig.data()); auto signature = QByteArray::fromHex(hexSig.data());
if (!VerifySignature((uint8_t *)obsPubKey.data(), obsPubKey.size(), if (!VerifySignature((uint8_t *)obsPubKey.data(), obsPubKey.size(), (uint8_t *)data.data(), data.size(),
(uint8_t *)data.data(), data.size(),
(uint8_t *)signature.data(), signature.size())) (uint8_t *)signature.data(), signature.size()))
throw strprintf("Signature check failed for %s", name); throw strprintf("Signature check failed for %s", name);
@ -208,8 +196,7 @@ try {
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
bool FetchAndVerifyFile(const char *name, const char *file, const char *url, bool FetchAndVerifyFile(const char *name, const char *file, const char *url, std::string *out,
std::string *out,
const std::vector<std::string> &extraHeaders) const std::vector<std::string> &extraHeaders)
{ {
long responseCode; long responseCode;
@ -223,16 +210,14 @@ bool FetchAndVerifyFile(const char *name, const char *file, const char *url,
BPtr<char> filePath = GetAppConfigPathPtr(file); BPtr<char> filePath = GetAppConfigPathPtr(file);
if (!extraHeaders.empty()) { if (!extraHeaders.empty()) {
headers.insert(headers.end(), extraHeaders.begin(), headers.insert(headers.end(), extraHeaders.begin(), extraHeaders.end());
extraHeaders.end());
} }
/* ----------------------------------- * /* ----------------------------------- *
* avoid downloading file again */ * avoid downloading file again */
if (CalculateFileHash(filePath, fileHash)) { if (CalculateFileHash(filePath, fileHash)) {
auto hash = QByteArray::fromRawData((const char *)fileHash, auto hash = QByteArray::fromRawData((const char *)fileHash, BLAKE2_HASH_LENGTH);
BLAKE2_HASH_LENGTH);
QString header = "If-None-Match: " + hash.toHex(); QString header = "If-None-Match: " + hash.toHex();
headers.push_back(header.toStdString()); headers.push_back(header.toStdString());
@ -251,15 +236,13 @@ bool FetchAndVerifyFile(const char *name, const char *file, const char *url,
/* ----------------------------------- * /* ----------------------------------- *
* get file from server */ * get file from server */
success = GetRemoteFile(url, data, error, &responseCode, nullptr, "", success = GetRemoteFile(url, data, error, &responseCode, nullptr, "", nullptr, headers, &signature);
nullptr, headers, &signature);
if (!success || (responseCode != 200 && responseCode != 304)) { if (!success || (responseCode != 200 && responseCode != 304)) {
if (responseCode == 404) if (responseCode == 404)
return false; return false;
throw strprintf("Failed to fetch %s file: %s", name, throw strprintf("Failed to fetch %s file: %s", name, error.c_str());
error.c_str());
} }
/* ----------------------------------- * /* ----------------------------------- *
@ -276,12 +259,10 @@ bool FetchAndVerifyFile(const char *name, const char *file, const char *url,
if (responseCode == 200) { if (responseCode == 200) {
if (!QuickWriteFile(filePath, data)) if (!QuickWriteFile(filePath, data))
throw strprintf("Could not write file '%s'", throw strprintf("Could not write file '%s'", filePath.Get());
filePath.Get());
} else if (out) { /* Only read file if caller wants data */ } else if (out) { /* Only read file if caller wants data */
if (!QuickReadFile(filePath, data)) if (!QuickReadFile(filePath, data))
throw strprintf("Could not read file '%s'", throw strprintf("Could not read file '%s'", filePath.Get());
filePath.Get());
} }
if (out) if (out)
@ -296,8 +277,7 @@ void WhatsNewInfoThread::run()
try { try {
std::string text; std::string text;
if (FetchAndVerifyFile("whatsnew", "obs-studio/updates/whatsnew.json", if (FetchAndVerifyFile("whatsnew", "obs-studio/updates/whatsnew.json", WHATSNEW_URL, &text)) {
WHATSNEW_URL, &text)) {
emit Result(QString::fromStdString(text)); emit Result(QString::fromStdString(text));
} }
} catch (std::string &text) { } catch (std::string &text) {

Some files were not shown because too many files have changed in this diff Show More