123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /******************************************************************************
- Copyright (C) 2023 by Lain Bailey <[email protected]>
- Zachary Lund <[email protected]>
- Philippe Groarke <[email protected]>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ******************************************************************************/
- #include "OBSBasic.hpp"
- #include "OBSProjector.hpp"
- #include <utility/display-helpers.hpp>
- #include <utility/QuickTransition.hpp>
- #include <qt-wrappers.hpp>
- #include <slider-ignorewheel.hpp>
- #include <QToolTip>
- void OBSBasic::CreateProgramDisplay()
- {
- program = new OBSQTDisplay();
- program->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(program.data(), &QWidget::customContextMenuRequested, this, &OBSBasic::ProgramViewContextMenuRequested);
- auto displayResize = [this]() {
- struct obs_video_info ovi;
- if (obs_get_video_info(&ovi))
- ResizeProgram(ovi.base_width, ovi.base_height);
- };
- connect(program.data(), &OBSQTDisplay::DisplayResized, displayResize);
- auto addDisplay = [this](OBSQTDisplay *window) {
- obs_display_add_draw_callback(window->GetDisplay(), OBSBasic::RenderProgram, this);
- struct obs_video_info ovi;
- if (obs_get_video_info(&ovi))
- ResizeProgram(ovi.base_width, ovi.base_height);
- };
- connect(program.data(), &OBSQTDisplay::DisplayCreated, addDisplay);
- program->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- }
- #define T_BAR_PRECISION 1024
- #define T_BAR_PRECISION_F ((float)T_BAR_PRECISION)
- #define T_BAR_CLAMP (T_BAR_PRECISION / 10)
- void OBSBasic::CreateProgramOptions()
- {
- programOptions = new QWidget();
- QVBoxLayout *layout = new QVBoxLayout();
- layout->setSpacing(4);
- QPushButton *configTransitions = new QPushButton();
- configTransitions->setProperty("class", "icon-dots-vert");
- QHBoxLayout *mainButtonLayout = new QHBoxLayout();
- mainButtonLayout->setSpacing(2);
- transitionButton = new QPushButton(QTStr("Transition"));
- transitionButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- QHBoxLayout *quickTransitionsLayout = new QHBoxLayout();
- quickTransitionsLayout->setSpacing(2);
- QPushButton *addQuickTransition = new QPushButton();
- addQuickTransition->setProperty("class", "icon-plus");
- QLabel *quickTransitionsLabel = new QLabel(QTStr("QuickTransitions"));
- quickTransitionsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- quickTransitionsLayout->addWidget(quickTransitionsLabel);
- quickTransitionsLayout->addWidget(addQuickTransition);
- mainButtonLayout->addWidget(transitionButton);
- mainButtonLayout->addWidget(configTransitions);
- tBar = new SliderIgnoreClick(Qt::Horizontal);
- tBar->setMinimum(0);
- tBar->setMaximum(T_BAR_PRECISION - 1);
- tBar->setProperty("class", "slider-tbar");
- connect(tBar, &QSlider::valueChanged, this, &OBSBasic::TBarChanged);
- connect(tBar, &QSlider::sliderReleased, this, &OBSBasic::TBarReleased);
- layout->addStretch(0);
- layout->addLayout(mainButtonLayout);
- layout->addLayout(quickTransitionsLayout);
- layout->addWidget(tBar);
- layout->addStretch(0);
- programOptions->setLayout(layout);
- auto onAdd = [this]() {
- QScopedPointer<QMenu> menu(CreateTransitionMenu(this, nullptr));
- menu->exec(QCursor::pos());
- };
- auto onConfig = [this]() {
- QMenu menu(this);
- QAction *action;
- auto toggleEditProperties = [this]() {
- editPropertiesMode = !editPropertiesMode;
- OBSSource actualScene = OBSGetStrongRef(programScene);
- if (actualScene)
- TransitionToScene(actualScene, true);
- };
- auto toggleSwapScenesMode = [this]() {
- swapScenesMode = !swapScenesMode;
- };
- auto toggleSceneDuplication = [this]() {
- sceneDuplicationMode = !sceneDuplicationMode;
- OBSSource actualScene = OBSGetStrongRef(programScene);
- if (actualScene)
- TransitionToScene(actualScene, true);
- };
- auto showToolTip = [&]() {
- QAction *act = menu.activeAction();
- QToolTip::showText(QCursor::pos(), act->toolTip(), &menu, menu.actionGeometry(act));
- };
- action = menu.addAction(QTStr("QuickTransitions.DuplicateScene"));
- action->setToolTip(QTStr("QuickTransitions.DuplicateSceneTT"));
- action->setCheckable(true);
- action->setChecked(sceneDuplicationMode);
- connect(action, &QAction::triggered, toggleSceneDuplication);
- connect(action, &QAction::hovered, showToolTip);
- action = menu.addAction(QTStr("QuickTransitions.EditProperties"));
- action->setToolTip(QTStr("QuickTransitions.EditPropertiesTT"));
- action->setCheckable(true);
- action->setChecked(editPropertiesMode);
- action->setEnabled(sceneDuplicationMode);
- connect(action, &QAction::triggered, toggleEditProperties);
- connect(action, &QAction::hovered, showToolTip);
- action = menu.addAction(QTStr("QuickTransitions.SwapScenes"));
- action->setToolTip(QTStr("QuickTransitions.SwapScenesTT"));
- action->setCheckable(true);
- action->setChecked(swapScenesMode);
- connect(action, &QAction::triggered, toggleSwapScenesMode);
- connect(action, &QAction::hovered, showToolTip);
- menu.exec(QCursor::pos());
- };
- connect(transitionButton.data(), &QAbstractButton::clicked, this, &OBSBasic::TransitionClicked);
- connect(addQuickTransition, &QAbstractButton::clicked, onAdd);
- connect(configTransitions, &QAbstractButton::clicked, onConfig);
- }
- void OBSBasic::TogglePreviewProgramMode()
- {
- SetPreviewProgramMode(!IsPreviewProgramMode());
- }
- void OBSBasic::SetPreviewProgramMode(bool enabled)
- {
- if (IsPreviewProgramMode() == enabled)
- return;
- os_atomic_set_bool(&previewProgramMode, enabled);
- emit PreviewProgramModeChanged(enabled);
- if (IsPreviewProgramMode()) {
- if (!previewEnabled)
- EnablePreviewDisplay(true);
- CreateProgramDisplay();
- CreateProgramOptions();
- OBSScene curScene = GetCurrentScene();
- OBSSceneAutoRelease dup;
- if (sceneDuplicationMode) {
- dup = obs_scene_duplicate(curScene, obs_source_get_name(obs_scene_get_source(curScene)),
- editPropertiesMode ? OBS_SCENE_DUP_PRIVATE_COPY
- : OBS_SCENE_DUP_PRIVATE_REFS);
- } else {
- dup = std::move(OBSScene(curScene));
- }
- OBSSourceAutoRelease transition = obs_get_output_source(0);
- obs_source_t *dup_source = obs_scene_get_source(dup);
- obs_transition_set(transition, dup_source);
- if (curScene) {
- obs_source_t *source = obs_scene_get_source(curScene);
- obs_source_inc_showing(source);
- lastScene = OBSGetWeakRef(source);
- programScene = OBSGetWeakRef(source);
- }
- RefreshQuickTransitions();
- programLabel = new QLabel(QTStr("StudioMode.ProgramSceneLabel"), this);
- programLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
- programLabel->setProperty("class", "label-preview-title");
- programWidget = new QWidget();
- programLayout = new QVBoxLayout();
- programLayout->setContentsMargins(0, 0, 0, 0);
- programLayout->setSpacing(0);
- programLayout->addWidget(programLabel);
- programLayout->addWidget(program);
- programWidget->setLayout(programLayout);
- ui->previewLayout->addWidget(programOptions);
- ui->previewLayout->addWidget(programWidget);
- ui->previewLayout->setAlignment(programOptions, Qt::AlignCenter);
- OnEvent(OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED);
- blog(LOG_INFO, "Switched to Preview/Program mode");
- blog(LOG_INFO, "-----------------------------"
- "-------------------");
- } else {
- OBSSource actualProgramScene = OBSGetStrongRef(programScene);
- if (!actualProgramScene)
- actualProgramScene = GetCurrentSceneSource();
- else
- SetCurrentScene(actualProgramScene, true);
- TransitionToScene(actualProgramScene, true);
- delete programOptions;
- delete program;
- delete programLabel;
- delete programWidget;
- if (lastScene) {
- OBSSource actualLastScene = OBSGetStrongRef(lastScene);
- if (actualLastScene)
- obs_source_dec_showing(actualLastScene);
- lastScene = nullptr;
- }
- programScene = nullptr;
- swapScene = nullptr;
- prevFTBSource = nullptr;
- for (QuickTransition &qt : quickTransitions)
- qt.button = nullptr;
- if (!previewEnabled)
- EnablePreviewDisplay(false);
- ui->transitions->setEnabled(true);
- tBarActive = false;
- OnEvent(OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED);
- blog(LOG_INFO, "Switched to regular Preview mode");
- blog(LOG_INFO, "-----------------------------"
- "-------------------");
- }
- ResetUI();
- UpdateTitleBar();
- }
- void OBSBasic::RenderProgram(void *data, uint32_t, uint32_t)
- {
- GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "RenderProgram");
- OBSBasic *window = static_cast<OBSBasic *>(data);
- obs_video_info ovi;
- obs_get_video_info(&ovi);
- window->programCX = int(window->programScale * float(ovi.base_width));
- window->programCY = int(window->programScale * float(ovi.base_height));
- gs_viewport_push();
- gs_projection_push();
- /* --------------------------------------- */
- gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height), -100.0f, 100.0f);
- gs_set_viewport(window->programX, window->programY, window->programCX, window->programCY);
- obs_render_main_texture_src_color_only();
- gs_load_vertexbuffer(nullptr);
- /* --------------------------------------- */
- gs_projection_pop();
- gs_viewport_pop();
- GS_DEBUG_MARKER_END();
- }
- void OBSBasic::ResizeProgram(uint32_t cx, uint32_t cy)
- {
- QSize targetSize;
- /* resize program panel to fix to the top section of the window */
- targetSize = GetPixelSize(program);
- GetScaleAndCenterPos(int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2,
- targetSize.height() - PREVIEW_EDGE_SIZE * 2, programX, programY, programScale);
- programX += float(PREVIEW_EDGE_SIZE);
- programY += float(PREVIEW_EDGE_SIZE);
- }
- void OBSBasic::UpdatePreviewProgramIndicators()
- {
- bool labels = previewProgramMode ? config_get_bool(App()->GetUserConfig(), "BasicWindow", "StudioModeLabels")
- : false;
- ui->previewLabel->setVisible(labels);
- if (programLabel)
- programLabel->setVisible(labels);
- if (!labels)
- return;
- QString preview =
- QTStr("StudioMode.PreviewSceneName").arg(QT_UTF8(obs_source_get_name(GetCurrentSceneSource())));
- QString program = QTStr("StudioMode.ProgramSceneName").arg(QT_UTF8(obs_source_get_name(GetProgramSource())));
- if (ui->previewLabel->text() != preview)
- ui->previewLabel->setText(preview);
- if (programLabel && programLabel->text() != program)
- programLabel->setText(program);
- }
- OBSSource OBSBasic::GetProgramSource()
- {
- return OBSGetStrongRef(programScene);
- }
- void OBSBasic::ProgramViewContextMenuRequested()
- {
- QMenu popup(this);
- QPointer<QMenu> studioProgramProjector;
- studioProgramProjector = new QMenu(QTStr("Projector.Open.Program"));
- AddProjectorMenuMonitors(studioProgramProjector, this, &OBSBasic::OpenStudioProgramProjector);
- studioProgramProjector->addSeparator();
- studioProgramProjector->addAction(QTStr("Projector.Window"), this, &OBSBasic::OpenStudioProgramWindow);
- popup.addMenu(studioProgramProjector);
- popup.addSeparator();
- popup.addAction(QTStr("Screenshot.StudioProgram"), this, &OBSBasic::ScreenshotProgram);
- popup.exec(QCursor::pos());
- }
- void OBSBasic::EnablePreviewProgram()
- {
- SetPreviewProgramMode(true);
- }
- void OBSBasic::DisablePreviewProgram()
- {
- SetPreviewProgramMode(false);
- }
- void OBSBasic::OpenStudioProgramProjector()
- {
- int monitor = sender()->property("monitor").toInt();
- OpenProjector(nullptr, monitor, ProjectorType::StudioProgram);
- }
- void OBSBasic::OpenStudioProgramWindow()
- {
- OpenProjector(nullptr, -1, ProjectorType::StudioProgram);
- }
|