123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- #include <QAction>
- #include <QGuiApplication>
- #include <QMouseEvent>
- #include <QMenu>
- #include <QScreen>
- #include <qt-wrappers.hpp>
- #include "moc_window-projector.cpp"
- #include "obs-app.hpp"
- #include "window-basic-main.hpp"
- #include "display-helpers.hpp"
- #include "platform.hpp"
- #include "multiview.hpp"
- static QList<OBSProjector *> multiviewProjectors;
- static bool updatingMultiview = false, mouseSwitching, transitionOnDoubleClick;
- OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
- ProjectorType type_)
- : OBSQTDisplay(widget, Qt::Window),
- weakSource(OBSGetWeakRef(source_))
- {
- OBSSource source = GetSource();
- if (source) {
- sigs.emplace_back(obs_source_get_signal_handler(source),
- "rename", OBSSourceRenamed, this);
- sigs.emplace_back(obs_source_get_signal_handler(source),
- "destroy", OBSSourceDestroyed, this);
- }
- isAlwaysOnTop = config_get_bool(App()->GetUserConfig(), "BasicWindow",
- "ProjectorAlwaysOnTop");
- if (isAlwaysOnTop)
- setWindowFlags(Qt::WindowStaysOnTopHint);
- // Mark the window as a projector so SetDisplayAffinity
- // can skip it
- windowHandle()->setProperty("isOBSProjectorWindow", true);
- #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
- // Prevents resizing of projector windows
- setAttribute(Qt::WA_PaintOnScreen, false);
- #endif
- type = type_;
- #ifdef __APPLE__
- setWindowIcon(
- QIcon::fromTheme("obs", QIcon(":/res/images/obs_256x256.png")));
- #else
- setWindowIcon(QIcon::fromTheme("obs", QIcon(":/res/images/obs.png")));
- #endif
- if (monitor == -1)
- resize(480, 270);
- else
- SetMonitor(monitor);
- if (source)
- UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));
- else
- UpdateProjectorTitle(QString());
- QAction *action = new QAction(this);
- action->setShortcut(Qt::Key_Escape);
- addAction(action);
- connect(action, &QAction::triggered, this,
- &OBSProjector::EscapeTriggered);
- setAttribute(Qt::WA_DeleteOnClose, true);
- //disable application quit when last window closed
- setAttribute(Qt::WA_QuitOnClose, false);
- installEventFilter(CreateShortcutFilter());
- auto addDrawCallback = [this]() {
- bool isMultiview = type == ProjectorType::Multiview;
- obs_display_add_draw_callback(
- GetDisplay(),
- isMultiview ? OBSRenderMultiview : OBSRender, this);
- obs_display_set_background_color(GetDisplay(), 0x000000);
- };
- connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback);
- connect(App(), &QGuiApplication::screenRemoved, this,
- &OBSProjector::ScreenRemoved);
- if (type == ProjectorType::Multiview) {
- multiview = new Multiview();
- UpdateMultiview();
- multiviewProjectors.push_back(this);
- }
- App()->IncrementSleepInhibition();
- if (source)
- obs_source_inc_showing(source);
- ready = true;
- show();
- // We need it here to allow keyboard input in X11 to listen to Escape
- activateWindow();
- }
- OBSProjector::~OBSProjector()
- {
- sigs.clear();
- bool isMultiview = type == ProjectorType::Multiview;
- obs_display_remove_draw_callback(
- GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender,
- this);
- OBSSource source = GetSource();
- if (source)
- obs_source_dec_showing(source);
- if (isMultiview) {
- delete multiview;
- multiviewProjectors.removeAll(this);
- }
- App()->DecrementSleepInhibition();
- screen = nullptr;
- }
- void OBSProjector::SetMonitor(int monitor)
- {
- savedMonitor = monitor;
- screen = QGuiApplication::screens()[monitor];
- setGeometry(screen->geometry());
- showFullScreen();
- SetHideCursor();
- }
- void OBSProjector::SetHideCursor()
- {
- if (savedMonitor == -1)
- return;
- bool hideCursor = config_get_bool(App()->GetUserConfig(), "BasicWindow",
- "HideProjectorCursor");
- if (hideCursor && type != ProjectorType::Multiview)
- setCursor(Qt::BlankCursor);
- else
- setCursor(Qt::ArrowCursor);
- }
- void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
- {
- OBSProjector *window = (OBSProjector *)data;
- if (updatingMultiview || !window->ready)
- return;
- window->multiview->Render(cx, cy);
- }
- void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
- {
- OBSProjector *window = reinterpret_cast<OBSProjector *>(data);
- if (!window->ready)
- return;
- OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
- OBSSource source = window->GetSource();
- uint32_t targetCX;
- uint32_t targetCY;
- int x, y;
- int newCX, newCY;
- float scale;
- if (source) {
- targetCX = std::max(obs_source_get_width(source), 1u);
- targetCY = std::max(obs_source_get_height(source), 1u);
- } else {
- struct obs_video_info ovi;
- obs_get_video_info(&ovi);
- targetCX = ovi.base_width;
- targetCY = ovi.base_height;
- }
- GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);
- newCX = int(scale * float(targetCX));
- newCY = int(scale * float(targetCY));
- startRegion(x, y, newCX, newCY, 0.0f, float(targetCX), 0.0f,
- float(targetCY));
- if (window->type == ProjectorType::Preview &&
- main->IsPreviewProgramMode()) {
- OBSSource curSource = main->GetCurrentSceneSource();
- if (source != curSource) {
- obs_source_dec_showing(source);
- obs_source_inc_showing(curSource);
- source = curSource;
- window->weakSource = OBSGetWeakRef(source);
- }
- } else if (window->type == ProjectorType::Preview &&
- !main->IsPreviewProgramMode()) {
- window->weakSource = nullptr;
- }
- if (source)
- obs_source_video_render(source);
- else
- obs_render_main_texture();
- endRegion();
- }
- void OBSProjector::OBSSourceRenamed(void *data, calldata_t *params)
- {
- OBSProjector *window = reinterpret_cast<OBSProjector *>(data);
- QString oldName = calldata_string(params, "prev_name");
- QString newName = calldata_string(params, "new_name");
- QMetaObject::invokeMethod(window, "RenameProjector",
- Q_ARG(QString, oldName),
- Q_ARG(QString, newName));
- }
- void OBSProjector::OBSSourceDestroyed(void *data, calldata_t *)
- {
- OBSProjector *window = reinterpret_cast<OBSProjector *>(data);
- QMetaObject::invokeMethod(window, "EscapeTriggered");
- }
- void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)
- {
- OBSQTDisplay::mouseDoubleClickEvent(event);
- if (!mouseSwitching)
- return;
- if (!transitionOnDoubleClick)
- return;
- // Only MultiView projectors handle double click
- if (this->type != ProjectorType::Multiview)
- return;
- OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
- if (!main->IsPreviewProgramMode())
- return;
- if (event->button() == Qt::LeftButton) {
- QPoint pos = event->pos();
- OBSSource src =
- multiview->GetSourceByPosition(pos.x(), pos.y());
- if (!src)
- return;
- if (main->GetProgramSource() != src)
- main->TransitionToScene(src);
- }
- }
- void OBSProjector::mousePressEvent(QMouseEvent *event)
- {
- OBSQTDisplay::mousePressEvent(event);
- if (event->button() == Qt::RightButton) {
- QMenu *projectorMenu = new QMenu(QTStr("Fullscreen"));
- OBSBasic::AddProjectorMenuMonitors(
- projectorMenu, this,
- &OBSProjector::OpenFullScreenProjector);
- QMenu popup(this);
- popup.addMenu(projectorMenu);
- if (GetMonitor() > -1) {
- popup.addAction(QTStr("Windowed"), this,
- &OBSProjector::OpenWindowedProjector);
- } else if (!this->isMaximized()) {
- popup.addAction(QTStr("ResizeProjectorWindowToContent"),
- this, &OBSProjector::ResizeToContent);
- }
- QAction *alwaysOnTopButton = new QAction(
- QTStr("Basic.MainMenu.View.AlwaysOnTop"), this);
- alwaysOnTopButton->setCheckable(true);
- alwaysOnTopButton->setChecked(isAlwaysOnTop);
- connect(alwaysOnTopButton, &QAction::toggled, this,
- &OBSProjector::AlwaysOnTopToggled);
- popup.addAction(alwaysOnTopButton);
- popup.addAction(QTStr("Close"), this,
- &OBSProjector::EscapeTriggered);
- popup.exec(QCursor::pos());
- } else if (event->button() == Qt::LeftButton) {
- // Only MultiView projectors handle left click
- if (this->type != ProjectorType::Multiview)
- return;
- if (!mouseSwitching)
- return;
- QPoint pos = event->pos();
- OBSSource src =
- multiview->GetSourceByPosition(pos.x(), pos.y());
- if (!src)
- return;
- OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
- if (main->GetCurrentSceneSource() != src)
- main->SetCurrentScene(src, false);
- }
- }
- void OBSProjector::EscapeTriggered()
- {
- OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
- main->DeleteProjector(this);
- }
- void OBSProjector::UpdateMultiview()
- {
- MultiviewLayout multiviewLayout = static_cast<MultiviewLayout>(
- config_get_int(App()->GetUserConfig(), "BasicWindow",
- "MultiviewLayout"));
- bool drawLabel = config_get_bool(App()->GetUserConfig(), "BasicWindow",
- "MultiviewDrawNames");
- bool drawSafeArea = config_get_bool(
- App()->GetUserConfig(), "BasicWindow", "MultiviewDrawAreas");
- mouseSwitching = config_get_bool(App()->GetUserConfig(), "BasicWindow",
- "MultiviewMouseSwitch");
- transitionOnDoubleClick = config_get_bool(App()->GetUserConfig(),
- "BasicWindow",
- "TransitionOnDoubleClick");
- multiview->Update(multiviewLayout, drawLabel, drawSafeArea);
- }
- void OBSProjector::UpdateProjectorTitle(QString name)
- {
- bool window = (GetMonitor() == -1);
- QString title = nullptr;
- switch (type) {
- case ProjectorType::Scene:
- if (!window)
- title = QTStr("SceneProjector") + " - " + name;
- else
- title = QTStr("SceneWindow") + " - " + name;
- break;
- case ProjectorType::Source:
- if (!window)
- title = QTStr("SourceProjector") + " - " + name;
- else
- title = QTStr("SourceWindow") + " - " + name;
- break;
- case ProjectorType::Preview:
- if (!window)
- title = QTStr("PreviewProjector");
- else
- title = QTStr("PreviewWindow");
- break;
- case ProjectorType::StudioProgram:
- if (!window)
- title = QTStr("StudioProgramProjector");
- else
- title = QTStr("StudioProgramWindow");
- break;
- case ProjectorType::Multiview:
- if (!window)
- title = QTStr("MultiviewProjector");
- else
- title = QTStr("MultiviewWindowed");
- break;
- default:
- title = name;
- break;
- }
- setWindowTitle(title);
- }
- OBSSource OBSProjector::GetSource()
- {
- return OBSGetStrongRef(weakSource);
- }
- ProjectorType OBSProjector::GetProjectorType()
- {
- return type;
- }
- int OBSProjector::GetMonitor()
- {
- return savedMonitor;
- }
- void OBSProjector::UpdateMultiviewProjectors()
- {
- obs_enter_graphics();
- updatingMultiview = true;
- obs_leave_graphics();
- for (auto &projector : multiviewProjectors)
- projector->UpdateMultiview();
- obs_enter_graphics();
- updatingMultiview = false;
- obs_leave_graphics();
- }
- void OBSProjector::RenameProjector(QString oldName, QString newName)
- {
- if (oldName == newName)
- return;
- UpdateProjectorTitle(newName);
- }
- void OBSProjector::OpenFullScreenProjector()
- {
- if (!isFullScreen())
- prevGeometry = geometry();
- int monitor = sender()->property("monitor").toInt();
- SetMonitor(monitor);
- OBSSource source = GetSource();
- UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));
- }
- void OBSProjector::OpenWindowedProjector()
- {
- showFullScreen();
- showNormal();
- setCursor(Qt::ArrowCursor);
- if (!prevGeometry.isNull())
- setGeometry(prevGeometry);
- else
- resize(480, 270);
- savedMonitor = -1;
- OBSSource source = GetSource();
- UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));
- screen = nullptr;
- }
- void OBSProjector::ResizeToContent()
- {
- OBSSource source = GetSource();
- uint32_t targetCX;
- uint32_t targetCY;
- int x, y, newX, newY;
- float scale;
- if (source) {
- targetCX = std::max(obs_source_get_width(source), 1u);
- targetCY = std::max(obs_source_get_height(source), 1u);
- } else {
- struct obs_video_info ovi;
- obs_get_video_info(&ovi);
- targetCX = ovi.base_width;
- targetCY = ovi.base_height;
- }
- QSize size = this->size();
- GetScaleAndCenterPos(targetCX, targetCY, size.width(), size.height(), x,
- y, scale);
- newX = size.width() - (x * 2);
- newY = size.height() - (y * 2);
- resize(newX, newY);
- }
- void OBSProjector::AlwaysOnTopToggled(bool isAlwaysOnTop)
- {
- SetIsAlwaysOnTop(isAlwaysOnTop, true);
- }
- void OBSProjector::closeEvent(QCloseEvent *event)
- {
- EscapeTriggered();
- event->accept();
- }
- bool OBSProjector::IsAlwaysOnTop() const
- {
- return isAlwaysOnTop;
- }
- bool OBSProjector::IsAlwaysOnTopOverridden() const
- {
- return isAlwaysOnTopOverridden;
- }
- void OBSProjector::SetIsAlwaysOnTop(bool isAlwaysOnTop, bool isOverridden)
- {
- this->isAlwaysOnTop = isAlwaysOnTop;
- this->isAlwaysOnTopOverridden = isOverridden;
- SetAlwaysOnTop(this, isAlwaysOnTop);
- }
- void OBSProjector::ScreenRemoved(QScreen *screen_)
- {
- if (GetMonitor() < 0 || !screen)
- return;
- if (screen == screen_)
- EscapeTriggered();
- }
|