From 28131eea1603d917b11dc390e969ef96f9cf19a0 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 19 Nov 2025 16:32:04 +0200 Subject: [PATCH] Allow specifying explicit desired screen If the window position is not specified, which is a reasonable thing to do when using the layer shell protocol, setWidth() and setHeight() can unintentionally change the screen() to the wrong one. This change adds a setDesiredScreen() function so it's harder to shoot yourself in the foot while using the layer shell protocol. --- src/interfaces/window.cpp | 43 ++++++++++++++++++++++++++++++---- src/interfaces/window.h | 45 +++++++++++++++++++++++++----------- src/qwaylandlayersurface.cpp | 7 +++++- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/interfaces/window.cpp b/src/interfaces/window.cpp index 0389d51..e0d279b 100644 --- a/src/interfaces/window.cpp +++ b/src/interfaces/window.cpp @@ -34,7 +34,8 @@ public: Window::Layer layer = Window::LayerTop; QMargins margins; QSize desiredSize = QSize(0, 0); - Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow; + QPointer desiredScreen; + bool desiredActiveScreen = false; bool closeOnDismissed = true; bool activateOnShow = true; }; @@ -152,14 +153,46 @@ Window::Layer Window::layer() const return d->layer; } -Window::ScreenConfiguration Window::screenConfiguration() const +void Window::setDesiredActiveScreen(bool set) { - return d->screenConfiguration; + if (d->desiredActiveScreen == set) { + return; + } + + d->desiredActiveScreen = set; + + if (d->desiredActiveScreen && d->desiredScreen) { + d->desiredScreen = nullptr; + Q_EMIT desiredScreenChanged(); + } + + Q_EMIT desiredActiveScreenChanged(); } -void Window::setScreenConfiguration(Window::ScreenConfiguration screenConfiguration) +bool Window::desiredActiveScreen() const { - d->screenConfiguration = screenConfiguration; + return d->desiredActiveScreen; +} + +void Window::setDesiredScreen(QScreen *screen) +{ + if (d->desiredScreen == screen) { + return; + } + + d->desiredScreen = screen; + + if (d->desiredScreen && d->desiredActiveScreen) { + d->desiredActiveScreen = false; + Q_EMIT desiredActiveScreenChanged(); + } + + Q_EMIT desiredScreenChanged(); +} + +QScreen *Window::desiredScreen() const +{ + return d->desiredScreen; } bool Window::closeOnDismissed() const diff --git a/src/interfaces/window.h b/src/interfaces/window.h index b9e4824..8f0f5ba 100644 --- a/src/interfaces/window.h +++ b/src/interfaces/window.h @@ -27,8 +27,9 @@ class LAYERSHELLQT_EXPORT Window : public QObject Q_PROPERTY(qint32 exclusionZone READ exclusionZone WRITE setExclusiveZone NOTIFY exclusionZoneChanged) Q_PROPERTY(Layer layer READ layer WRITE setLayer NOTIFY layerChanged) Q_PROPERTY(KeyboardInteractivity keyboardInteractivity READ keyboardInteractivity WRITE setKeyboardInteractivity NOTIFY keyboardInteractivityChanged) - Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration) Q_PROPERTY(bool activateOnShow READ activateOnShow WRITE setActivateOnShow) + Q_PROPERTY(bool desiredActiveScreen READ desiredActiveScreen WRITE setDesiredActiveScreen NOTIFY desiredActiveScreenChanged) + Q_PROPERTY(QScreen *desiredScreen READ desiredScreen WRITE setDesiredScreen NOTIFY desiredScreenChanged) public: ~Window() override; @@ -64,17 +65,6 @@ public: }; Q_ENUM(KeyboardInteractivity) - /** - * This enum type is used to specify which screen to place the surface on. - * ScreenFromQWindow (the default) reads QWindow::screen() while ScreenFromCompositor - * passes nil and lets the compositor decide. - */ - enum ScreenConfiguration { - ScreenFromQWindow = 0, - ScreenFromCompositor = 1, - }; - Q_ENUM(ScreenConfiguration) - void setAnchors(Anchors anchor); Anchors anchors() const; @@ -96,8 +86,33 @@ public: void setLayer(Layer layer); Layer layer() const; - void setScreenConfiguration(ScreenConfiguration screenConfiguration); - ScreenConfiguration screenConfiguration() const; + /** + * Indicates whether the layer shell surface should be placed on the active screen based on @a set. + * + * The active screen depends on the compositor policies. + * + * If no explicit desired screen has been specified with the setDesiredScreen() function or the layer + * surface doesn't need to be placed on the active screen, i.e. setDesiredActiveScreen() has not + * been called, the QWindow::screen() will be used to decide what screen the layer shell surface + * should be placed on. + * + * The desiredScreen() will be reset if @a set is @c true. + */ + void setDesiredActiveScreen(bool set); + bool desiredActiveScreen() const; + + /** + * Indicates that the layer shell surface should be placed on the specified @a screen. + * + * If no explicit desired screen has been specified with the setDesiredScreen() function or the layer + * surface doesn't need to be placed on the active screen, i.e. setDesiredActiveScreen() has not + * been called, the QWindow::screen() will be used to decide what screen the layer shell surface + * should be placed on. + * + * The desiredActiveScreen() will be reset to @c false after calling this function. + */ + void setDesiredScreen(QScreen *screen); + QScreen *desiredScreen() const; /** * Sets a string based identifier for this window. @@ -148,6 +163,8 @@ Q_SIGNALS: void desiredSizeChanged(); void keyboardInteractivityChanged(); void layerChanged(); + void desiredActiveScreenChanged(); + void desiredScreenChanged(); private: void initializeShell(); diff --git a/src/qwaylandlayersurface.cpp b/src/qwaylandlayersurface.cpp index fd8851c..e0fb749 100644 --- a/src/qwaylandlayersurface.cpp +++ b/src/qwaylandlayersurface.cpp @@ -26,7 +26,12 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell, , m_window(window) { wl_output *output = nullptr; - if (m_interface->screenConfiguration() == Window::ScreenFromQWindow) { + if (!m_interface->desiredActiveScreen()) { + QScreen *desiredScreen = m_interface->desiredScreen(); + if (!desiredScreen) { + window->window()->screen(); + } + auto waylandScreen = dynamic_cast(window->window()->screen()->handle()); // Qt will always assign a screen to a window, but if the compositor has no screens available a dummy QScreen object is created // this will not cast to a QWaylandScreen