#include "window-basic-main.hpp" #include "context-bar-controls.hpp" #include "qt-wrappers.hpp" #include "obs-app.hpp" #include #include #include #include "ui_browser-source-toolbar.h" #include "ui_device-select-toolbar.h" #include "ui_game-capture-toolbar.h" #include "ui_image-source-toolbar.h" #include "ui_color-source-toolbar.h" #include "ui_text-source-toolbar.h" #ifdef _WIN32 #define get_os_module(win, mac, linux) obs_get_module(win) #define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win) #elif __APPLE__ #define get_os_module(win, mac, linux) obs_get_module(mac) #define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac) #else #define get_os_module(win, mac, linux) obs_get_module(linux) #define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux) #endif /* ========================================================================= */ SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source) : QWidget(parent), weakSource(OBSGetWeakRef(source)), props(obs_source_properties(source), obs_properties_destroy) { } void SourceToolbar::SaveOldProperties(obs_source_t *source) { oldData = obs_data_create(); OBSDataAutoRelease oldSettings = obs_source_get_settings(source); obs_data_apply(oldData, oldSettings); obs_data_set_string(oldData, "undo_sname", obs_source_get_name(source)); } void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable) { if (!oldData) { blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__); return; } OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); OBSSource currentSceneSource = main->GetCurrentSceneSource(); if (!currentSceneSource) return; std::string scene_name = obs_source_get_name(currentSceneSource); auto undo_redo = [scene_name, main = std::move(main)](const std::string &data) { OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str()); OBSSourceAutoRelease source = obs_get_source_by_name( obs_data_get_string(settings, "undo_sname")); obs_source_reset_settings(source, settings); OBSSourceAutoRelease scene_source = obs_get_source_by_name(scene_name.c_str()); main->SetCurrentScene(scene_source.Get(), true); main->UpdateContextBar(); }; OBSDataAutoRelease new_settings = obs_data_create(); OBSDataAutoRelease curr_settings = obs_source_get_settings(source); obs_data_apply(new_settings, curr_settings); obs_data_set_string(new_settings, "undo_sname", obs_source_get_name(source)); std::string undo_data(obs_data_get_json(oldData)); std::string redo_data(obs_data_get_json(new_settings)); if (undo_data.compare(redo_data) != 0) main->undo_s.add_action( QTStr("Undo.Properties") .arg(obs_source_get_name(source)), undo_redo, undo_redo, undo_data, redo_data, repeatable); oldData = nullptr; } /* ========================================================================= */ BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source) : SourceToolbar(parent, source), ui(new Ui_BrowserSourceToolbar) { ui->setupUi(this); } BrowserToolbar::~BrowserToolbar() {} void BrowserToolbar::on_refresh_clicked() { OBSSource source = GetSource(); if (!source) { return; } obs_property_t *p = obs_properties_get(props.get(), "refreshnocache"); obs_property_button_clicked(p, source.Get()); } /* ========================================================================= */ ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source) : SourceToolbar(parent, source), ui(new Ui_DeviceSelectToolbar) { ui->setupUi(this); } ComboSelectToolbar::~ComboSelectToolbar() {} static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false) { size_t count = obs_property_list_item_count(p); int cur_idx = -1; for (size_t i = 0; i < count; i++) { const char *name = obs_property_list_item_name(p, i); std::string id; if (is_int) { id = std::to_string(obs_property_list_item_int(p, i)); } else { const char *val = obs_property_list_item_string(p, i); id = val ? val : ""; } if (cur_id == id) cur_idx = (int)i; c->addItem(name, id.c_str()); } return cur_idx; } void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props, const char *prop_name, bool is_int) { std::string cur_id; OBSDataAutoRelease settings = obs_source_get_settings(source); if (is_int) { cur_id = std::to_string(obs_data_get_int(settings, prop_name)); } else { cur_id = obs_data_get_string(settings, prop_name); } combo->blockSignals(true); obs_property_t *p = obs_properties_get(props, prop_name); int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int); if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) { if (cur_idx == -1) { combo->insertItem( 0, QTStr("Basic.Settings.Audio.UnknownAudioDevice")); cur_idx = 0; } SetComboItemEnabled(combo, cur_idx, false); } combo->setCurrentIndex(cur_idx); combo->blockSignals(false); } void ComboSelectToolbar::Init() { OBSSource source = GetSource(); if (!source) { return; } UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int); } void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int) { QString id = combo->itemData(idx).toString(); OBSDataAutoRelease settings = obs_data_create(); if (is_int) { obs_data_set_int(settings, prop_name, id.toInt()); } else { obs_data_set_string(settings, prop_name, QT_TO_UTF8(id)); } obs_source_update(source, settings); } void ComboSelectToolbar::on_device_currentIndexChanged(int idx) { OBSSource source = GetSource(); if (idx == -1 || !source) { return; } SaveOldProperties(source); UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int); SetUndoProperties(source); } AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) { } void AudioCaptureToolbar::Init() { delete ui->activateButton; ui->activateButton = nullptr; obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio"); if (!mod) return; const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device"); ui->deviceLabel->setText(device_str); prop_name = "device_id"; ComboSelectToolbar::Init(); } WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) { } void WindowCaptureToolbar::Init() { delete ui->activateButton; ui->activateButton = nullptr; obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture"); if (!mod) return; const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window"); ui->deviceLabel->setText(device_str); #if !defined(_WIN32) && !defined(__APPLE__) //linux prop_name = "capture_window"; #else prop_name = "window"; #endif #ifdef __APPLE__ is_int = true; #endif ComboSelectToolbar::Init(); } ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) { } void ApplicationAudioCaptureToolbar::Init() { delete ui->activateButton; ui->activateButton = nullptr; obs_module_t *mod = obs_get_module("win-wasapi"); const char *device_str = obs_module_get_locale_text(mod, "Window"); ui->deviceLabel->setText(device_str); prop_name = "window"; ComboSelectToolbar::Init(); } DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) { } void DisplayCaptureToolbar::Init() { delete ui->activateButton; ui->activateButton = nullptr; obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture"); if (!mod) return; const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen"); ui->deviceLabel->setText(device_str); is_int = true; #ifdef _WIN32 prop_name = "monitor"; #elif __APPLE__ prop_name = "display"; #else prop_name = "screen"; #endif ComboSelectToolbar::Init(); } /* ========================================================================= */ DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source) : QWidget(parent), weakSource(OBSGetWeakRef(source)), ui(new Ui_DeviceSelectToolbar) { ui->setupUi(this); delete ui->deviceLabel; delete ui->device; ui->deviceLabel = nullptr; ui->device = nullptr; OBSDataAutoRelease settings = obs_source_get_settings(source); active = obs_data_get_bool(settings, "active"); obs_module_t *mod = obs_get_module("win-dshow"); if (!mod) return; activateText = obs_module_get_locale_text(mod, "Activate"); deactivateText = obs_module_get_locale_text(mod, "Deactivate"); ui->activateButton->setText(active ? deactivateText : activateText); } DeviceCaptureToolbar::~DeviceCaptureToolbar() {} void DeviceCaptureToolbar::on_activateButton_clicked() { OBSSource source = OBSGetStrongRef(weakSource); if (!source) { return; } OBSDataAutoRelease settings = obs_source_get_settings(source); bool now_active = obs_data_get_bool(settings, "active"); bool desyncedSetting = now_active != active; active = !active; const char *text = active ? deactivateText : activateText; ui->activateButton->setText(text); if (desyncedSetting) { return; } calldata_t cd = {}; calldata_set_bool(&cd, "active", active); proc_handler_t *ph = obs_source_get_proc_handler(source); proc_handler_call(ph, "activate", &cd); calldata_free(&cd); } /* ========================================================================= */ GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source) : SourceToolbar(parent, source), ui(new Ui_GameCaptureToolbar) { obs_property_t *p; int cur_idx; ui->setupUi(this); obs_module_t *mod = obs_get_module("win-capture"); if (!mod) return; ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode")); ui->windowLabel->setText( obs_module_get_locale_text(mod, "WindowCapture.Window")); OBSDataAutoRelease settings = obs_source_get_settings(source); std::string cur_mode = obs_data_get_string(settings, "capture_mode"); std::string cur_window = obs_data_get_string(settings, "window"); ui->mode->blockSignals(true); p = obs_properties_get(props.get(), "capture_mode"); cur_idx = FillPropertyCombo(ui->mode, p, cur_mode); ui->mode->setCurrentIndex(cur_idx); ui->mode->blockSignals(false); ui->window->blockSignals(true); p = obs_properties_get(props.get(), "window"); cur_idx = FillPropertyCombo(ui->window, p, cur_window); ui->window->setCurrentIndex(cur_idx); ui->window->blockSignals(false); if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) { SetComboItemEnabled(ui->window, cur_idx, false); } UpdateWindowVisibility(); } GameCaptureToolbar::~GameCaptureToolbar() {} void GameCaptureToolbar::UpdateWindowVisibility() { QString mode = ui->mode->currentData().toString(); bool is_window = (mode == "window"); ui->windowLabel->setVisible(is_window); ui->window->setVisible(is_window); } void GameCaptureToolbar::on_mode_currentIndexChanged(int idx) { OBSSource source = GetSource(); if (idx == -1 || !source) { return; } QString id = ui->mode->itemData(idx).toString(); SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id)); obs_source_update(source, settings); SetUndoProperties(source); UpdateWindowVisibility(); } void GameCaptureToolbar::on_window_currentIndexChanged(int idx) { OBSSource source = GetSource(); if (idx == -1 || !source) { return; } QString id = ui->window->itemData(idx).toString(); SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_string(settings, "window", QT_TO_UTF8(id)); obs_source_update(source, settings); SetUndoProperties(source); } /* ========================================================================= */ ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source) : SourceToolbar(parent, source), ui(new Ui_ImageSourceToolbar) { ui->setupUi(this); obs_module_t *mod = obs_get_module("image-source"); ui->pathLabel->setText(obs_module_get_locale_text(mod, "File")); OBSDataAutoRelease settings = obs_source_get_settings(source); std::string file = obs_data_get_string(settings, "file"); ui->path->setText(file.c_str()); } ImageSourceToolbar::~ImageSourceToolbar() {} void ImageSourceToolbar::on_browse_clicked() { OBSSource source = GetSource(); if (!source) { return; } obs_property_t *p = obs_properties_get(props.get(), "file"); const char *desc = obs_property_description(p); const char *filter = obs_property_path_filter(p); const char *default_path = obs_property_path_default_path(p); QString path = OpenFile(this, desc, default_path, filter); if (path.isEmpty()) { return; } ui->path->setText(path); SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_string(settings, "file", QT_TO_UTF8(path)); obs_source_update(source, settings); SetUndoProperties(source); } /* ========================================================================= */ static inline QColor color_from_int(long long val) { return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff); } static inline long long color_to_int(QColor color) { auto shift = [&](unsigned val, int shift) { return ((val & 0xff) << shift); }; return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24); } ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source) : SourceToolbar(parent, source), ui(new Ui_ColorSourceToolbar) { ui->setupUi(this); OBSDataAutoRelease settings = obs_source_get_settings(source); unsigned int val = (unsigned int)obs_data_get_int(settings, "color"); color = color_from_int(val); UpdateColor(); } ColorSourceToolbar::~ColorSourceToolbar() {} void ColorSourceToolbar::UpdateColor() { QPalette palette = QPalette(color); ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel); ui->color->setText(color.name(QColor::HexRgb)); ui->color->setPalette(palette); ui->color->setStyleSheet( QString("background-color :%1; color: %2;") .arg(palette.color(QPalette::Window) .name(QColor::HexRgb)) .arg(palette.color(QPalette::WindowText) .name(QColor::HexRgb))); ui->color->setAutoFillBackground(true); ui->color->setAlignment(Qt::AlignCenter); } void ColorSourceToolbar::on_choose_clicked() { OBSSource source = GetSource(); if (!source) { return; } obs_property_t *p = obs_properties_get(props.get(), "color"); const char *desc = obs_property_description(p); QColorDialog::ColorDialogOptions options; options |= QColorDialog::ShowAlphaChannel; #ifndef _WIN32 options |= QColorDialog::DontUseNativeDialog; #endif QColor newColor = QColorDialog::getColor(color, this, desc, options); if (!newColor.isValid()) { return; } color = newColor; UpdateColor(); SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_int(settings, "color", color_to_int(color)); obs_source_update(source, settings); SetUndoProperties(source); } /* ========================================================================= */ extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false); TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source) : SourceToolbar(parent, source), ui(new Ui_TextSourceToolbar) { ui->setupUi(this); OBSDataAutoRelease settings = obs_source_get_settings(source); const char *id = obs_source_get_unversioned_id(source); bool ft2 = strcmp(id, "text_ft2_source") == 0; bool read_from_file = obs_data_get_bool( settings, ft2 ? "from_file" : "read_from_file"); OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font"); MakeQFont(font_obj, font); // Use "color1" if it's a freetype source and "color" elsewise unsigned int val = (unsigned int)obs_data_get_int( settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color"); color = color_from_int(val); const char *text = obs_data_get_string(settings, "text"); bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr)); ui->emptySpace->setVisible(!single_line); ui->text->setVisible(single_line); if (single_line) ui->text->setText(text); } TextSourceToolbar::~TextSourceToolbar() {} void TextSourceToolbar::on_selectFont_clicked() { OBSSource source = GetSource(); if (!source) { return; } QFontDialog::FontDialogOptions options; uint32_t flags; bool success; #ifndef _WIN32 options = QFontDialog::DontUseNativeDialog; #endif font = QFontDialog::getFont(&success, font, this, "Pick a Font", options); if (!success) { return; } OBSDataAutoRelease font_obj = obs_data_create(); obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family())); obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName())); obs_data_set_int(font_obj, "size", font.pointSize()); flags = font.bold() ? OBS_FONT_BOLD : 0; flags |= font.italic() ? OBS_FONT_ITALIC : 0; flags |= font.underline() ? OBS_FONT_UNDERLINE : 0; flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0; obs_data_set_int(font_obj, "flags", flags); SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_obj(settings, "font", font_obj); obs_source_update(source, settings); SetUndoProperties(source); } void TextSourceToolbar::on_selectColor_clicked() { OBSSource source = GetSource(); if (!source) { return; } bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0; obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color"); const char *desc = obs_property_description(p); QColorDialog::ColorDialogOptions options; options |= QColorDialog::ShowAlphaChannel; #ifndef _WIN32 options |= QColorDialog::DontUseNativeDialog; #endif QColor newColor = QColorDialog::getColor(color, this, desc, options); if (!newColor.isValid()) { return; } color = newColor; SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); if (freetype) { obs_data_set_int(settings, "color1", color_to_int(color)); obs_data_set_int(settings, "color2", color_to_int(color)); } else { obs_data_set_int(settings, "color", color_to_int(color)); } obs_source_update(source, settings); SetUndoProperties(source); } void TextSourceToolbar::on_text_textChanged() { OBSSource source = GetSource(); if (!source) { return; } SaveOldProperties(source); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_string(settings, "text", QT_TO_UTF8(ui->text->text())); obs_source_update(source, settings); SetUndoProperties(source, true); }