From a327f742c96b80f3f5d8caab7d377dc543f4151f Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Tue, 24 Dec 2024 11:25:13 -0800 Subject: [PATCH] fontend: Make OBSProject a parent window This window was previously a parent of OBSQTDisplay, however OBSQTDisplay is supposed to be completely rendered by OBS. On linux this causes issues like not having window decorations and weird rendering artifacts during resize. For wayland systems that negotiate explicit sync it also triggers protocol violations (crashes) due to QT and OBS thinking they should be rendering this window. Instead make the project a parent window owned by Qt that only contains one widget, the OBSQTDisplay. This makes the window behave like the main OBS window and preview. fixes #6283 Co-authored-by: cg2121 --- frontend/widgets/OBSProjector.cpp | 85 ++++++++++++++++++------------- frontend/widgets/OBSProjector.hpp | 7 ++- frontend/widgets/OBSQTDisplay.hpp | 2 + 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/frontend/widgets/OBSProjector.cpp b/frontend/widgets/OBSProjector.cpp index 7d6beb0d8024b0..e762fca6a29182 100644 --- a/frontend/widgets/OBSProjector.cpp +++ b/frontend/widgets/OBSProjector.cpp @@ -18,9 +18,26 @@ static QList multiviewProjectors; static bool updatingMultiview = false, mouseSwitching, transitionOnDoubleClick; 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; + layout->addWidget(&display); + layout->setContentsMargins(0, 0, 0, 0); + this->setLayout(layout); + + // Whether the compositor and qt are able to agree on the current state + // of a native window after transitioning between windowed and + // fullscreen. If not, then we should not allow users to modify existing + // projectors to switch between fullscreen/windowed. + qtFullscreenNativeWorks = true; +#if !defined(_WIN32) && !defined(__APPLE__) + const char *desktop = getenv("XDG_CURRENT_DESKTOP"); + if (desktop && strstr(desktop, "GNOME") != nullptr && QApplication::platformName().contains("wayland")) + qtFullscreenNativeWorks = false; +#endif + OBSSource source = GetSource(); if (source) { sigs.emplace_back(obs_source_get_signal_handler(source), "rename", OBSSourceRenamed, this); @@ -34,12 +51,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); - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) - // Prevents resizing of projector windows - setAttribute(Qt::WA_PaintOnScreen, false); -#endif + this->setProperty("isOBSProjectorWindow", true); type = type_; #ifdef __APPLE__ @@ -48,10 +60,7 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, setWindowIcon(QIcon::fromTheme("obs", QIcon(":/res/images/obs.png"))); #endif - if (monitor == -1) - resize(480, 270); - else - SetMonitor(monitor); + resize(480, 270); if (source) UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source))); @@ -72,11 +81,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) { @@ -94,6 +103,8 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, ready = true; + if (monitor != -1) + SetMonitor(monitor); show(); // We need it here to allow keyboard input in X11 to listen to Escape @@ -105,7 +116,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) @@ -125,9 +136,9 @@ void OBSProjector::SetMonitor(int monitor) { savedMonitor = monitor; screen = QGuiApplication::screens()[monitor]; - setGeometry(screen->geometry()); showFullScreen(); SetHideCursor(); + update(); } void OBSProjector::SetHideCursor() @@ -224,7 +235,7 @@ void OBSProjector::OBSSourceDestroyed(void *data, calldata_t *) void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event) { - OBSQTDisplay::mouseDoubleClickEvent(event); + QWidget::mouseDoubleClickEvent(event); if (!mouseSwitching) return; @@ -253,19 +264,20 @@ void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event) void OBSProjector::mousePressEvent(QMouseEvent *event) { - OBSQTDisplay::mousePressEvent(event); + QWidget::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()) { + if (qtFullscreenNativeWorks) { + QMenu *projectorMenu = new QMenu(QTStr("Fullscreen")); + OBSBasic::AddProjectorMenuMonitors(projectorMenu, this, &OBSProjector::OpenFullScreenProjector); + popup.addMenu(projectorMenu); + + if (GetMonitor() > -1) { + popup.addAction(QTStr("Windowed"), this, &OBSProjector::OpenWindowedProjector); + } + } + if (!this->isMaximized() && GetMonitor() == -1) { popup.addAction(QTStr("ResizeProjectorWindowToContent"), this, &OBSProjector::ResizeToContent); } @@ -415,20 +427,20 @@ void OBSProjector::OpenFullScreenProjector() void OBSProjector::OpenWindowedProjector() { - showFullScreen(); + + if (!prevGeometry.isNull()) { + display.setGeometry(prevGeometry); + } else + display.resize(480, 270); showNormal(); setCursor(Qt::ArrowCursor); - - if (!prevGeometry.isNull()) - setGeometry(prevGeometry); - else - resize(480, 270); + update(); savedMonitor = -1; + screen = nullptr; OBSSource source = GetSource(); UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source))); - screen = nullptr; } void OBSProjector::ResizeToContent() @@ -478,6 +490,11 @@ bool OBSProjector::IsAlwaysOnTopOverridden() const return isAlwaysOnTopOverridden; } +bool OBSProjector::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +{ + return display.nativeEvent(eventType, message, result); +} + void OBSProjector::SetIsAlwaysOnTop(bool isAlwaysOnTop, bool isOverridden) { this->isAlwaysOnTop = isAlwaysOnTop; diff --git a/frontend/widgets/OBSProjector.hpp b/frontend/widgets/OBSProjector.hpp index 417192ee01f2f4..e54094a4dae7a3 100644 --- a/frontend/widgets/OBSProjector.hpp +++ b/frontend/widgets/OBSProjector.hpp @@ -12,12 +12,16 @@ enum class ProjectorType { Multiview, }; -class OBSProjector : public OBSQTDisplay { +class QMouseEvent; + +class OBSProjector : public QWidget { Q_OBJECT private: + OBSQTDisplay display; OBSWeakSourceAutoRelease weakSource; std::vector sigs; + bool qtFullscreenNativeWorks; static void OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy); static void OBSRender(void *data, uint32_t cx, uint32_t cy); @@ -27,6 +31,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; diff --git a/frontend/widgets/OBSQTDisplay.hpp b/frontend/widgets/OBSQTDisplay.hpp index 4c1152ba56f2c9..c7a58cdc64a578 100644 --- a/frontend/widgets/OBSQTDisplay.hpp +++ b/frontend/widgets/OBSQTDisplay.hpp @@ -11,6 +11,8 @@ class OBSQTDisplay : public QWidget { Q_PROPERTY(QColor displayBackgroundColor MEMBER backgroundColor READ GetDisplayBackgroundColor WRITE SetDisplayBackgroundColor) + friend class OBSProjector; + OBSDisplay display; bool destroying = false;