void OBSBasic::RefreshProfiles() { QList<QAction*> menuActions = ui->profileMenu->actions(); int count = 0; for (int i = 0; i < menuActions.count(); i++) { QVariant v = menuActions[i]->property("file_name"); if (v.typeName() != nullptr) delete menuActions[i]; } const char *curName = config_get_string(App()->GlobalConfig(), "Basic", "Profile"); auto addProfile = [&](const char *name, const char *path) { std::string file = strrchr(path, '/') + 1; QAction *action = new QAction(QT_UTF8(name), this); action->setProperty("file_name", QT_UTF8(path)); connect(action, &QAction::triggered, this, &OBSBasic::ChangeProfile); action->setCheckable(true); action->setChecked(strcmp(name, curName) == 0); ui->profileMenu->addAction(action); count++; return true; }; EnumProfiles(addProfile); ui->actionRemoveProfile->setEnabled(count > 1); }
OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) : QDialog (parent), ui (new Ui::OBSBasicSourceSelect), id (id_) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui->setupUi(this); ui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false); QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))}; QString text{placeHolderText}; int i = 2; obs_source_t *source = nullptr; while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) { obs_source_release(source); text = QString("%1 %2").arg(placeHolderText).arg(i++); } ui->sourceName->setText(text); ui->sourceName->setFocus(); //Fixes deselect of text. ui->sourceName->selectAll(); installEventFilter(CreateShortcutFilter()); if (strcmp(id_, "scene") == 0) { OBSBasic *main = reinterpret_cast<OBSBasic*>( App()->GetMainWindow()); OBSSource curSceneSource = main->GetCurrentSceneSource(); ui->selectExisting->setChecked(true); ui->createNew->setChecked(false); ui->createNew->setEnabled(false); ui->sourceName->setEnabled(false); int count = main->ui->scenes->count(); for (int i = 0; i < count; i++) { QListWidgetItem *item = main->ui->scenes->item(i); OBSScene scene = GetOBSRef<OBSScene>(item); OBSSource sceneSource = obs_scene_get_source(scene); if (curSceneSource == sceneSource) continue; const char *name = obs_source_get_name(sceneSource); ui->sourceList->addItem(QT_UTF8(name)); } } else if (strcmp(id_, "group") == 0) { obs_enum_sources(EnumGroups, this); } else { obs_enum_sources(EnumSources, this); } }
void OBSBasic::TBLiveNeedUpdate(std::string ver, std::string msg, std::string download_url) { QString str = QTStr("TBLiveUpdateAvailable.Text"); QMessageBox messageBox(this); str = str.arg(QT_UTF8(ver.c_str()), QT_UTF8(download_url.c_str())); messageBox.setWindowTitle(QTStr("TBLiveUpdateAvailable")); messageBox.setTextFormat(Qt::RichText); messageBox.setText(str); messageBox.setInformativeText(QT_UTF8(msg.c_str())); messageBox.exec(); }
void OBSBasic::ChangeSceneCollection() { QAction *action = reinterpret_cast<QAction*>(sender()); std::string fileName; if (!action) return; fileName = QT_TO_UTF8(action->property("fileName").value<QString>()); if (fileName.empty()) return; const char *oldName = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollection"); if (action->text().compare(QT_UTF8(oldName)) == 0) { action->setChecked(true); return; } SaveProjectNow(); Load(fileName.c_str()); RefreshSceneCollections(); const char *newName = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollection"); const char *newFile = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile"); blog(LOG_INFO, "Switched to scene collection '%s' (%s.json)", newName, newFile); blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); }
void OBSBasic::on_actionRemoveSceneCollection_triggered() { std::string newName; std::string newPath; std::string oldFile = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile"); std::string oldName = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollection"); auto cb = [&](const char *name, const char *filePath) { if (strcmp(oldName.c_str(), name) != 0) { newName = name; newPath = filePath; return false; } return true; }; EnumSceneCollections(cb); /* this should never be true due to menu item being grayed out */ if (newPath.empty()) return; QString text = QTStr("ConfirmRemove.Text"); text.replace("$1", QT_UTF8(oldName.c_str())); QMessageBox::StandardButton button = QMessageBox::question(this, QTStr("ConfirmRemove.Title"), text); if (button == QMessageBox::No) return; char path[512]; int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return; } oldFile.insert(0, path); oldFile += ".json"; os_unlink(oldFile.c_str()); Load(newPath.c_str()); RefreshSceneCollections(); const char *newFile = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile"); blog(LOG_INFO, "Removed scene collection '%s' (%s.json), " "switched to '%s' (%s.json)", oldName.c_str(), oldFile.c_str(), newName.c_str(), newFile); blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); }
void OBSAbout::ShowLicense() { std::string path; QString error = "Error! File could not be read.\n\n \ Go to: https://github.com/obsproject/obs-studio/blob/master/COPYING"; if (!GetDataFilePath("license/gplv2.txt", path)) { ui->textBrowser->setPlainText(error); return; } BPtr<char> text = os_quick_read_utf8_file(path.c_str()); if (!text || !*text) { ui->textBrowser->setPlainText(error); return; } ui->textBrowser->setPlainText(QT_UTF8(text)); ui->info->hide(); ui->contribute->hide(); ui->donate->hide(); ui->getInvolved->hide(); ui->textBrowser->show(); }
void OBSHotkeyEdit::RenderKey() { DStr str; obs_key_combination_to_str(key, str); setText(QT_UTF8(str)); }
bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t source) { OBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect*>(data); const char *name = obs_source_get_name(source); const char *id = obs_source_get_id(source); if (strcmp(id, window->id) == 0) window->ui->sourceList->addItem(QT_UTF8(name)); return true; }
void OBSBasic::ChangeProfile() { QAction *action = reinterpret_cast<QAction*>(sender()); ConfigFile config; std::string path; if (!action) return; path = QT_TO_UTF8(action->property("file_name").value<QString>()); if (path.empty()) return; const char *oldName = config_get_string(App()->GlobalConfig(), "Basic", "Profile"); if (action->text().compare(QT_UTF8(oldName)) == 0) { action->setChecked(true); return; } size_t path_len = path.size(); path += "/basic.ini"; if (config.Open(path.c_str(), CONFIG_OPEN_ALWAYS) != 0) { blog(LOG_ERROR, "ChangeProfile: Failed to load file '%s'", path.c_str()); return; } path.resize(path_len); const char *newName = config_get_string(config, "General", "Name"); const char *newDir = strrchr(path.c_str(), '/') + 1; config_set_string(App()->GlobalConfig(), "Basic", "Profile", newName); config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir); config.Swap(basicConfig); InitBasicConfigDefaults(); ResetProfileData(); RefreshProfiles(); config_save_safe(App()->GlobalConfig(), "tmp", nullptr); UpdateTitleBar(); blog(LOG_INFO, "Switched to profile '%s' (%s)", newName, newDir); blog(LOG_INFO, "------------------------------------------------"); if (api) api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); }
static bool GetProfileName(QWidget *parent, std::string &name, std::string &file, const char *title, const char *text, const char *oldName = nullptr) { char path[512]; int ret; for (;;) { bool success = NameDialog::AskForName(parent, title, text, name, QT_UTF8(oldName)); if (!success) { return false; } if (name.empty()) { QMessageBox::information(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; } if (ProfileExists(name.c_str())) { QMessageBox::information(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; } break; } if (!GetFileSafeName(name.c_str(), file)) { blog(LOG_WARNING, "Failed to create safe file name for '%s'", name.c_str()); return false; } ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profiles config path"); return false; } file.insert(0, path); if (!GetClosestUnusedFileName(file, nullptr)) { blog(LOG_WARNING, "Failed to get closest file name for %s", file.c_str()); return false; } file.erase(0, ret); return true; }
void OBSBasic::on_switchSceneBtn_clicked() { tbliveLog.Log(lss_info, L"on_switchSceneBtn_clicked"); OBSScene curScene = GetCurrentScene(); obs_source_t * source = obs_scene_get_source(curScene); SetCurrentScene(source, false); TransitionToScene(source, false); m_streamingScene = source; SetTabStreamingStatus(QT_UTF8(obs_source_get_name(m_streamingScene))); CurrentTabSceneUpdateControls(); }
bool OBSBasicSourceSelect::EnumGroups(void *data, obs_source_t *source) { OBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect*>(data); const char *name = obs_source_get_name(source); const char *id = obs_source_get_id(source); if (strcmp(id, window->id) == 0) { OBSBasic *main = reinterpret_cast<OBSBasic*>( App()->GetMainWindow()); OBSScene scene = main->GetCurrentScene(); obs_sceneitem_t *existing = obs_scene_get_group(scene, name); if (!existing) window->ui->sourceList->addItem(QT_UTF8(name)); } return true; }
void AutoConfigStreamPage::OnOAuthStreamKeyConnected() { #ifdef BROWSER_AVAILABLE OAuthStreamKey *a = reinterpret_cast<OAuthStreamKey*>(auth.get()); if (a) { bool validKey = !a->key().empty(); if (validKey) ui->key->setText(QT_UTF8(a->key().c_str())); ui->streamKeyWidget->setVisible(!validKey); ui->streamKeyLabel->setVisible(!validKey); ui->connectAccount2->setVisible(!validKey); ui->disconnectAccount->setVisible(validKey); } ui->stackedWidget->setCurrentIndex((int)Section::StreamKey); UpdateCompleted(); #endif }
OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) : QDialog (parent), ui (new Ui::OBSBasicSourceSelect), id (id_) { ui->setupUi(this); QString placeHolderText{QT_UTF8(obs_source_get_display_name( OBS_SOURCE_TYPE_INPUT, id))}; QString text{placeHolderText}; int i = 1; obs_source_t source = nullptr; while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) { obs_source_release(source); text = QString("%1 %2").arg(placeHolderText).arg(i++); } ui->sourceName->setText(text); ui->sourceName->setFocus(); //Fixes deselect of text. ui->sourceName->selectAll(); obs_enum_sources(EnumSources, this); }
void OBSBasic::on_actionRemoveProfile_triggered() { std::string newName; std::string newPath; ConfigFile config; std::string oldDir = config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir"); std::string oldName = config_get_string(App()->GlobalConfig(), "Basic", "Profile"); auto cb = [&](const char *name, const char *filePath) { if (strcmp(oldName.c_str(), name) != 0) { newName = name; newPath = filePath; return false; } return true; }; EnumProfiles(cb); /* this should never be true due to menu item being grayed out */ if (newPath.empty()) return; QString text = QTStr("ConfirmRemove.Text"); text.replace("$1", QT_UTF8(oldName.c_str())); QMessageBox::StandardButton button = QMessageBox::question(this, QTStr("ConfirmRemove.Title"), text); if (button == QMessageBox::No) return; size_t newPath_len = newPath.size(); newPath += "/basic.ini"; if (config.Open(newPath.c_str(), CONFIG_OPEN_ALWAYS) != 0) { blog(LOG_ERROR, "ChangeProfile: Failed to load file '%s'", newPath.c_str()); return; } newPath.resize(newPath_len); const char *newDir = strrchr(newPath.c_str(), '/') + 1; config_set_string(App()->GlobalConfig(), "Basic", "Profile", newName.c_str()); config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir); config.Swap(basicConfig); InitBasicConfigDefaults(); ResetProfileData(); DeleteProfile(oldName.c_str(), oldDir.c_str()); RefreshProfiles(); config_save_safe(App()->GlobalConfig(), "tmp", nullptr); blog(LOG_INFO, "Switched to profile '%s' (%s)", newName.c_str(), newDir); blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); }
static bool GetSceneCollectionName(QWidget *parent, std::string &name, std::string &file, const char *oldName = nullptr) { bool rename = oldName != nullptr; const char *title; const char *text; char path[512]; size_t len; int ret; if (rename) { title = Str("Basic.Main.RenameSceneCollection.Title"); text = Str("Basic.Main.AddSceneCollection.Text"); } else { title = Str("Basic.Main.AddSceneCollection.Title"); text = Str("Basic.Main.AddSceneCollection.Text"); } for (;;) { bool success = NameDialog::AskForName(parent, title, text, name, QT_UTF8(oldName)); if (!success) { return false; } if (name.empty()) { QMessageBox::information(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; } if (SceneCollectionExists(name.c_str())) { QMessageBox::information(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; } break; } if (!GetFileSafeName(name.c_str(), file)) { blog(LOG_WARNING, "Failed to create safe file name for '%s'", name.c_str()); return false; } ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return false; } len = file.size(); file.insert(0, path); if (!GetClosestUnusedFileName(file, "json")) { blog(LOG_WARNING, "Failed to get closest file name for %s", file.c_str()); return false; } file.erase(file.size() - 5, 5); file.erase(0, file.size() - len); return true; }
OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) : source(source_) { QHBoxLayout *hlayout; signal_handler_t *handler = obs_source_get_signal_handler(source); const char *sourceName = obs_source_get_name(source); float vol = obs_source_get_volume(source); uint32_t flags = obs_source_get_flags(source); uint32_t mixers = obs_source_get_audio_mixers(source); forceMonoContainer = new QWidget(); mixerContainer = new QWidget(); balanceContainer = new QWidget(); labelL = new QLabel(); labelR = new QLabel(); nameLabel = new QLabel(); volume = new QDoubleSpinBox(); forceMono = new QCheckBox(); balance = new BalanceSlider(); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType = new QComboBox(); #endif syncOffset = new QSpinBox(); mixer1 = new QCheckBox(); mixer2 = new QCheckBox(); mixer3 = new QCheckBox(); mixer4 = new QCheckBox(); mixer5 = new QCheckBox(); mixer6 = new QCheckBox(); volChangedSignal.Connect(handler, "volume", OBSSourceVolumeChanged, this); syncOffsetSignal.Connect(handler, "audio_sync", OBSSourceSyncChanged, this); flagsSignal.Connect(handler, "update_flags", OBSSourceFlagsChanged, this); mixersSignal.Connect(handler, "audio_mixers", OBSSourceMixersChanged, this); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); forceMonoContainer->setLayout(hlayout); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); mixerContainer->setLayout(hlayout); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); balanceContainer->setLayout(hlayout); balanceContainer->setMinimumWidth(100); labelL->setText("L"); labelR->setText("R"); nameLabel->setMinimumWidth(170); nameLabel->setText(QT_UTF8(sourceName)); nameLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); volume->setMinimum(MIN_DB - 0.1); volume->setMaximum(MAX_DB); volume->setSingleStep(0.1); volume->setDecimals(1); volume->setSuffix(" dB"); volume->setValue(obs_mul_to_db(vol)); if (volume->value() < MIN_DB) volume->setSpecialValueText("-inf dB"); forceMono->setChecked((flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0); forceMonoContainer->layout()->addWidget(forceMono); forceMonoContainer->layout()->setAlignment(forceMono, Qt::AlignHCenter | Qt::AlignVCenter); balance->setOrientation(Qt::Horizontal); balance->setMinimum(0); balance->setMaximum(100); balance->setTickPosition(QSlider::TicksAbove); balance->setTickInterval(50); OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow()); const char *speakers = config_get_string(main->Config(), "Audio", "ChannelSetup"); if (strcmp(speakers, "Mono") == 0) balance->setEnabled(false); else balance->setEnabled(true); float bal = obs_source_get_balance_value(source) * 100.0f; balance->setValue((int)bal); int64_t cur_sync = obs_source_get_sync_offset(source); syncOffset->setMinimum(-950); syncOffset->setMaximum(20000); syncOffset->setValue(int(cur_sync / NSEC_PER_MSEC)); int idx; #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.None"), (int)OBS_MONITORING_TYPE_NONE); monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.MonitorOnly"), (int)OBS_MONITORING_TYPE_MONITOR_ONLY); monitoringType->addItem(QTStr("Basic.AdvAudio.Monitoring.Both"), (int)OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT); int mt = (int)obs_source_get_monitoring_type(source); idx = monitoringType->findData(mt); monitoringType->setCurrentIndex(idx); #endif mixer1->setText("1"); mixer1->setChecked(mixers & (1<<0)); mixer2->setText("2"); mixer2->setChecked(mixers & (1<<1)); mixer3->setText("3"); mixer3->setChecked(mixers & (1<<2)); mixer4->setText("4"); mixer4->setChecked(mixers & (1<<3)); mixer5->setText("5"); mixer5->setChecked(mixers & (1<<4)); mixer6->setText("6"); mixer6->setChecked(mixers & (1<<5)); speaker_layout sl = obs_source_get_speaker_layout(source); if (sl == SPEAKERS_STEREO) { balanceContainer->layout()->addWidget(labelL); balanceContainer->layout()->addWidget(balance); balanceContainer->layout()->addWidget(labelR); balanceContainer->setMaximumWidth(170); } mixerContainer->layout()->addWidget(mixer1); mixerContainer->layout()->addWidget(mixer2); mixerContainer->layout()->addWidget(mixer3); mixerContainer->layout()->addWidget(mixer4); mixerContainer->layout()->addWidget(mixer5); mixerContainer->layout()->addWidget(mixer6); QWidget::connect(volume, SIGNAL(valueChanged(double)), this, SLOT(volumeChanged(double))); QWidget::connect(forceMono, SIGNAL(clicked(bool)), this, SLOT(downmixMonoChanged(bool))); QWidget::connect(balance, SIGNAL(valueChanged(int)), this, SLOT(balanceChanged(int))); QWidget::connect(balance, SIGNAL(doubleClicked()), this, SLOT(ResetBalance())); QWidget::connect(syncOffset, SIGNAL(valueChanged(int)), this, SLOT(syncOffsetChanged(int))); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO QWidget::connect(monitoringType, SIGNAL(currentIndexChanged(int)), this, SLOT(monitoringTypeChanged(int))); #endif QWidget::connect(mixer1, SIGNAL(clicked(bool)), this, SLOT(mixer1Changed(bool))); QWidget::connect(mixer2, SIGNAL(clicked(bool)), this, SLOT(mixer2Changed(bool))); QWidget::connect(mixer3, SIGNAL(clicked(bool)), this, SLOT(mixer3Changed(bool))); QWidget::connect(mixer4, SIGNAL(clicked(bool)), this, SLOT(mixer4Changed(bool))); QWidget::connect(mixer5, SIGNAL(clicked(bool)), this, SLOT(mixer5Changed(bool))); QWidget::connect(mixer6, SIGNAL(clicked(bool)), this, SLOT(mixer6Changed(bool))); setObjectName(sourceName); }