From 93f5c40a27e676585900a641e968f7b4ff0ed5ec Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sat, 23 Nov 2024 17:53:24 -0600 Subject: [PATCH] UI: Make projectors somewhat work on Wayland This makes the projectors work on Wayland. The ability to set a fullscreen projector windowed and vice-versa is disabled, as Wayland doesn't really support resizing windows from code. --- UI/window-projector.cpp | 89 +++++++++++++++++++++++++++++------------ UI/window-projector.hpp | 4 +- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 60f873bc93b7d1..261eb29128342a 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -11,14 +11,35 @@ #include "platform.hpp" #include "multiview.hpp" +#ifdef ENABLE_WAYLAND +#include +#endif + +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + static QList multiviewProjectors; static bool updatingMultiview = false, mouseSwitching, transitionOnDoubleClick; +static bool IsWayland() +{ + return QApplication::platformName().contains("wayland"); +} + OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, ProjectorType type_) - : OBSQTDisplay(widget, Qt::Window), + : QWidget(widget, Qt::Window), + display(this), weakSource(OBSGetWeakRef(source_)) { + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(&display); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + OBSSource source = GetSource(); if (source) { sigs.emplace_back(obs_source_get_signal_handler(source), "rename", OBSSourceRenamed, this); @@ -32,7 +53,7 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, // Mark the window as a projector so SetDisplayAffinity // can skip it - windowHandle()->setProperty("isOBSProjectorWindow", true); + display.windowHandle()->setProperty("isOBSProjectorWindow", true); #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) // Prevents resizing of projector windows @@ -70,11 +91,11 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, 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); + obs_display_add_draw_callback(display.GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, this); + obs_display_set_background_color(display.GetDisplay(), 0x000000); }; - connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback); + connect(&display, &OBSQTDisplay::DisplayCreated, addDrawCallback); connect(App(), &QGuiApplication::screenRemoved, this, &OBSProjector::ScreenRemoved); if (type == ProjectorType::Multiview) { @@ -93,9 +114,6 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, ready = true; show(); - - // We need it here to allow keyboard input in X11 to listen to Escape - activateWindow(); } OBSProjector::~OBSProjector() @@ -103,7 +121,7 @@ OBSProjector::~OBSProjector() sigs.clear(); bool isMultiview = type == ProjectorType::Multiview; - obs_display_remove_draw_callback(GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, this); + obs_display_remove_draw_callback(display.GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, this); OBSSource source = GetSource(); if (source) @@ -222,7 +240,7 @@ void OBSProjector::OBSSourceDestroyed(void *data, calldata_t *) void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event) { - OBSQTDisplay::mouseDoubleClickEvent(event); + QWidget::mouseDoubleClickEvent(event); if (!mouseSwitching) return; @@ -251,29 +269,33 @@ void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event) void OBSProjector::mousePressEvent(QMouseEvent *event) { - OBSQTDisplay::mousePressEvent(event); + QWidget::mousePressEvent(event); + + QMenu popup(this); if (event->button() == Qt::RightButton) { - QMenu *projectorMenu = new QMenu(QTStr("Fullscreen")); - OBSBasic::AddProjectorMenuMonitors(projectorMenu, this, &OBSProjector::OpenFullScreenProjector); + if (!IsWayland()) { + QMenu *projectorMenu = new QMenu(QTStr("Fullscreen")); + OBSBasic::AddProjectorMenuMonitors(projectorMenu, this, &OBSProjector::OpenFullScreenProjector); - QMenu popup(this); - popup.addMenu(projectorMenu); + popup.addMenu(projectorMenu); - if (GetMonitor() > -1) { - popup.addAction(QTStr("Windowed"), this, &OBSProjector::OpenWindowedProjector); + if (GetMonitor() > -1) + popup.addAction(QTStr("Windowed"), this, &OBSProjector::OpenWindowedProjector); + } - } else if (!this->isMaximized()) { + if (!isMaximized() && GetMonitor() == -1) popup.addAction(QTStr("ResizeProjectorWindowToContent"), this, &OBSProjector::ResizeToContent); - } - QAction *alwaysOnTopButton = new QAction(QTStr("Basic.MainMenu.View.AlwaysOnTop"), this); - alwaysOnTopButton->setCheckable(true); - alwaysOnTopButton->setChecked(isAlwaysOnTop); + if (!IsWayland()) { + QAction *alwaysOnTopButton = new QAction(QTStr("Basic.MainMenu.AlwaysOnTop"), this); + alwaysOnTopButton->setCheckable(true); + alwaysOnTopButton->setChecked(isAlwaysOnTop); - connect(alwaysOnTopButton, &QAction::toggled, this, &OBSProjector::AlwaysOnTopToggled); + connect(alwaysOnTopButton, &QAction::toggled, this, &OBSProjector::AlwaysOnTopToggled); - popup.addAction(alwaysOnTopButton); + popup.addAction(alwaysOnTopButton); + } popup.addAction(QTStr("Close"), this, &OBSProjector::EscapeTriggered); popup.exec(QCursor::pos()); @@ -413,7 +435,6 @@ void OBSProjector::OpenFullScreenProjector() void OBSProjector::OpenWindowedProjector() { - showFullScreen(); showNormal(); setCursor(Qt::ArrowCursor); @@ -466,6 +487,24 @@ void OBSProjector::closeEvent(QCloseEvent *event) event->accept(); } +bool OBSProjector::nativeEvent(const QByteArray &, void *message, qintptr *) +{ +#ifdef _WIN32 + const MSG &msg = *static_cast(message); + switch (msg.message) { + case WM_MOVE: + display.OnMove(); + break; + case WM_DISPLAYCHANGE: + display.OnDisplayChange(); + } +#else + UNUSED_PARAMETER(message); +#endif + + return false; +} + bool OBSProjector::IsAlwaysOnTop() const { return isAlwaysOnTop; diff --git a/UI/window-projector.hpp b/UI/window-projector.hpp index f95063261c73ee..8416280d14a852 100644 --- a/UI/window-projector.hpp +++ b/UI/window-projector.hpp @@ -14,12 +14,13 @@ enum class ProjectorType { class QMouseEvent; -class OBSProjector : public OBSQTDisplay { +class OBSProjector : public QWidget { Q_OBJECT private: OBSWeakSourceAutoRelease weakSource; std::vector sigs; + OBSQTDisplay display; static void OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy); static void OBSRender(void *data, uint32_t cx, uint32_t cy); @@ -29,6 +30,7 @@ class OBSProjector : public OBSQTDisplay { void mousePressEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void closeEvent(QCloseEvent *event) override; + virtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override; bool isAlwaysOnTop; bool isAlwaysOnTopOverridden = false;