diff --git a/qtwayland-client-expose-toplevel-window-state.patch b/qtwayland-client-expose-toplevel-window-state.patch new file mode 100644 index 0000000..7823473 --- /dev/null +++ b/qtwayland-client-expose-toplevel-window-state.patch @@ -0,0 +1,166 @@ +From d533901938a996367d7b6f87b0214f5a17098aed Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Tue, 23 Mar 2021 16:03:22 +0100 +Subject: [PATCH] Client: expose toplevel window state + +QWaylandWindow has only basic information about window state, like if +it's active or maximized, but it has no information about tiling, which +can be useful for client-side decorations. We also need to bump version +of xdg-shell protocol we support, because additional states are not in +the version currently supported by QtWayland. It shouldn't be a problem +to increase the version as the new version adds just these additional +window states. + +Change-Id: I4c46516d9c7296c69ea51a022b3bdb4ca06bef8d +Reviewed-by: David Edmundson +--- + src/client/qwaylandwindow.cpp | 15 +++++++++++++++ + src/client/qwaylandwindow_p.h | 16 ++++++++++++++++ + .../xdg-shell/qwaylandxdgshell.cpp | 16 +++++++++++++++- + .../xdg-shell/qwaylandxdgshell_p.h | 3 ++- + 4 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index ba881cb..a1e891d 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -1089,6 +1089,16 @@ Qt::WindowStates QWaylandWindow::windowStates() const + return mLastReportedWindowStates; + } + ++QWaylandWindow::ToplevelWindowTilingStates QWaylandWindow::toplevelWindowTilingStates() const ++{ ++ return mLastReportedToplevelWindowTilingStates; ++} ++ ++void QWaylandWindow::handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states) ++{ ++ mLastReportedToplevelWindowTilingStates = states; ++} ++ + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) + { + createDecoration(); +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index e068796..f4e5d3d 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -95,6 +95,15 @@ public: + Vulkan + }; + ++ enum ToplevelWindowTilingState { ++ WindowNoState = 0, ++ WindowTiledLeft = 1, ++ WindowTiledRight = 2, ++ WindowTiledTop = 4, ++ WindowTiledBottom = 8 ++ }; ++ Q_DECLARE_FLAGS(ToplevelWindowTilingStates, ToplevelWindowTilingState) ++ + QWaylandWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandWindow() override; + +@@ -145,6 +154,9 @@ public: + void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; + void setOrientationMask(Qt::ScreenOrientations mask); + ++ ToplevelWindowTilingStates toplevelWindowTilingStates() const; ++ void handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states); ++ + void setWindowState(Qt::WindowStates states) override; + void setWindowFlags(Qt::WindowFlags flags) override; + void handleWindowStatesChanged(Qt::WindowStates states); +@@ -257,6 +269,7 @@ protected: + QRegion mMask; + QRegion mOpaqueArea; + Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState; ++ ToplevelWindowTilingStates mLastReportedToplevelWindowTilingStates = WindowNoState; + + QWaylandShmBackingStore *mBackingStore = nullptr; + QWaylandBuffer *mQueuedBuffer = nullptr; +@@ -293,6 +306,8 @@ private: + friend class QWaylandSubSurface; + }; + ++Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandWindow::ToplevelWindowTilingStates) ++ + inline QIcon QWaylandWindow::windowIcon() const + { + return mWindowIcon; +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index d7d0ddf..2c6e84b 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -88,6 +88,7 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) + m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); + ++ m_xdgSurface->m_window->handleToplevelWindowTilingStatesChanged(m_toplevelStates); + m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states); + + if (m_pending.size.isEmpty()) { +@@ -120,6 +121,7 @@ void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t + size_t numStates = states->size / sizeof(uint32_t); + + m_pending.states = Qt::WindowNoState; ++ m_toplevelStates = QWaylandWindow::WindowNoState; + + for (size_t i = 0; i < numStates; i++) { + switch (xdgStates[i]) { +@@ -132,6 +134,18 @@ void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t + case XDG_TOPLEVEL_STATE_FULLSCREEN: + m_pending.states |= Qt::WindowFullScreen; + break; ++ case XDG_TOPLEVEL_STATE_TILED_LEFT: ++ m_toplevelStates |= QWaylandWindow::WindowTiledLeft; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_RIGHT: ++ m_toplevelStates |= QWaylandWindow::WindowTiledRight; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_TOP: ++ m_toplevelStates |= QWaylandWindow::WindowTiledTop; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_BOTTOM: ++ m_toplevelStates |= QWaylandWindow::WindowTiledBottom; ++ break; + default: + break; + } +@@ -451,7 +465,7 @@ void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) + } + + QWaylandXdgShell::QWaylandXdgShell(QWaylandDisplay *display, uint32_t id, uint32_t availableVersion) +- : QtWayland::xdg_wm_base(display->wl_registry(), id, qMin(availableVersion, 1u)) ++ : QtWayland::xdg_wm_base(display->wl_registry(), id, qMin(availableVersion, 2u)) + , m_display(display) + { + display->addRegistryListener(&QWaylandXdgShell::handleRegistryGlobal, this); +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +index 0c98be3..d791213 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +@@ -58,6 +58,7 @@ + + #include + #include ++#include + + #include + #include +@@ -69,7 +70,6 @@ class QWindow; + namespace QtWaylandClient { + + class QWaylandDisplay; +-class QWaylandWindow; + class QWaylandInputDevice; + class QWaylandXdgShell; + +@@ -123,6 +123,7 @@ private: + QSize size = {0, 0}; + Qt::WindowStates states = Qt::WindowNoState; + } m_pending, m_applied; ++ QWaylandWindow::ToplevelWindowTilingStates m_toplevelStates = QWaylandWindow::WindowNoState; + QSize m_normalSize; + + QWaylandXdgSurface *m_xdgSurface = nullptr; diff --git a/qtwayland-decoration-support-backports-from-qt6.patch b/qtwayland-decoration-support-backports-from-qt6.patch new file mode 100644 index 0000000..fa8ae9e --- /dev/null +++ b/qtwayland-decoration-support-backports-from-qt6.patch @@ -0,0 +1,234 @@ +diff --git a/src/client/qwaylandabstractdecoration_p.h b/src/client/qwaylandabstractdecoration_p.h +index 81c8e177..61cbde77 100644 +--- a/src/client/qwaylandabstractdecoration_p.h ++++ b/src/client/qwaylandabstractdecoration_p.h +@@ -82,6 +82,12 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandAbstractDecoration : public QObject + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandAbstractDecoration) + public: ++ enum MarginsType { ++ Full, ++ ShadowsExcluded, ++ ShadowsOnly ++ }; ++ + QWaylandAbstractDecoration(); + ~QWaylandAbstractDecoration() override; + +@@ -91,7 +97,8 @@ public: + void update(); + bool isDirty() const; + +- virtual QMargins margins() const = 0; ++ virtual QMargins margins(MarginsType marginsType = Full) const = 0; ++ + QWindow *window() const; + const QImage &contentImage(); + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index bd70f4af..0e819235 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -380,6 +380,16 @@ void QWaylandWindow::setGeometry(const QRect &rect) + void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) + { + QMargins margins = frameMargins(); ++ ++ // Exclude shadows from margins once they are excluded from window geometry ++ // 1) First resizeFromApplyConfigure() call will have sizeWithMargins equal to surfaceSize() ++ // which has full margins (shadows included). ++ // 2) Following resizeFromApplyConfigure() calls should have sizeWithMargins equal to ++ // windowContentGeometry() which excludes shadows, therefore in this case we have to ++ // exclude them too in order not to accidentally apply smaller size to the window. ++ if (mWindowDecoration && (sizeWithMargins != surfaceSize())) ++ margins = mWindowDecoration->margins(QWaylandAbstractDecoration::ShadowsExcluded); ++ + int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1); + int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1); + QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins)); +@@ -690,7 +700,12 @@ QSize QWaylandWindow::surfaceSize() const + */ + QRect QWaylandWindow::windowContentGeometry() const + { +- return QRect(QPoint(), surfaceSize()); ++ QMargins shadowMargins; ++ ++ if (mWindowDecoration) ++ shadowMargins = mWindowDecoration->margins(QWaylandAbstractDecoration::ShadowsOnly); ++ ++ return QRect(QPoint(shadowMargins.left(), shadowMargins.top()), surfaceSize().shrunkBy(shadowMargins)); + } + + /*! +@@ -1079,6 +1094,21 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) + return true; + } + ++QWaylandWindow::ToplevelWindowTilingStates QWaylandWindow::toplevelWindowTilingStates() const ++{ ++ return mLastReportedToplevelWindowTilingStates; ++} ++ ++void QWaylandWindow::handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states) ++{ ++ mLastReportedToplevelWindowTilingStates = states; ++} ++ ++Qt::WindowStates QWaylandWindow::windowStates() const ++{ ++ return mLastReportedWindowStates; ++} ++ + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) + { + createDecoration(); +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index 6cc1664b..93ba0623 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -95,6 +95,15 @@ public: + Vulkan + }; + ++ enum ToplevelWindowTilingState { ++ WindowNoState = 0, ++ WindowTiledLeft = 1, ++ WindowTiledRight = 2, ++ WindowTiledTop = 4, ++ WindowTiledBottom = 8 ++ }; ++ Q_DECLARE_FLAGS(ToplevelWindowTilingStates, ToplevelWindowTilingState) ++ + QWaylandWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandWindow() override; + +@@ -145,6 +154,10 @@ public: + void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; + void setOrientationMask(Qt::ScreenOrientations mask); + ++ ToplevelWindowTilingStates toplevelWindowTilingStates() const; ++ void handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states); ++ ++ Qt::WindowStates windowStates() const; + void setWindowState(Qt::WindowStates states) override; + void setWindowFlags(Qt::WindowFlags flags) override; + void handleWindowStatesChanged(Qt::WindowStates states); +@@ -256,6 +269,7 @@ protected: + QRegion mMask; + QRegion mOpaqueArea; + Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState; ++ ToplevelWindowTilingStates mLastReportedToplevelWindowTilingStates = WindowNoState; + + QWaylandShmBackingStore *mBackingStore = nullptr; + QWaylandBuffer *mQueuedBuffer = nullptr; +@@ -292,6 +306,8 @@ private: + friend class QWaylandSubSurface; + }; + ++Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandWindow::ToplevelWindowTilingStates) ++ + inline QIcon QWaylandWindow::windowIcon() const + { + return mWindowIcon; +diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp +index e75fda3c..72dda67f 100644 +--- a/src/plugins/decorations/bradient/main.cpp ++++ b/src/plugins/decorations/bradient/main.cpp +@@ -72,7 +72,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandBradientDecoration : public QWaylandAbstra + public: + QWaylandBradientDecoration(); + protected: +- QMargins margins() const override; ++ QMargins margins(MarginsType marginsType = Full) const override; + void paint(QPaintDevice *device) override; + bool handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global,Qt::MouseButtons b,Qt::KeyboardModifiers mods) override; + bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods) override; +@@ -129,8 +129,11 @@ QRectF QWaylandBradientDecoration::minimizeButtonRect() const + (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH); + } + +-QMargins QWaylandBradientDecoration::margins() const ++QMargins QWaylandBradientDecoration::margins(MarginsType marginsType) const + { ++ if (marginsType == ShadowsOnly) ++ return QMargins(); ++ + return QMargins(3, 30, 3, 3); + } + +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index 7d33dabd..cf7eb4e9 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -94,6 +94,7 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + // TODO: none of the other plugins send WindowActive either, but is it on purpose? + Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; + ++ m_xdgSurface->m_window->handleToplevelWindowTilingStatesChanged(m_toplevelStates); + m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive); + + if (m_pending.size.isEmpty()) { +@@ -126,6 +127,7 @@ void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t + size_t numStates = states->size / sizeof(uint32_t); + + m_pending.states = Qt::WindowNoState; ++ m_toplevelStates = QWaylandWindow::WindowNoState; + + for (size_t i = 0; i < numStates; i++) { + switch (xdgStates[i]) { +@@ -138,6 +140,18 @@ void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t + case XDG_TOPLEVEL_STATE_FULLSCREEN: + m_pending.states |= Qt::WindowFullScreen; + break; ++ case XDG_TOPLEVEL_STATE_TILED_LEFT: ++ m_toplevelStates |= QWaylandWindow::WindowTiledLeft; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_RIGHT: ++ m_toplevelStates |= QWaylandWindow::WindowTiledRight; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_TOP: ++ m_toplevelStates |= QWaylandWindow::WindowTiledTop; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_BOTTOM: ++ m_toplevelStates |= QWaylandWindow::WindowTiledBottom; ++ break; + default: + break; + } +@@ -457,7 +471,7 @@ void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) + } + + QWaylandXdgShell::QWaylandXdgShell(QWaylandDisplay *display, uint32_t id, uint32_t availableVersion) +- : QtWayland::xdg_wm_base(display->wl_registry(), id, qMin(availableVersion, 1u)) ++ : QtWayland::xdg_wm_base(display->wl_registry(), id, qMin(availableVersion, 2u)) + , m_display(display) + { + display->addRegistryListener(&QWaylandXdgShell::handleRegistryGlobal, this); +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +index 0c98be35..d7912132 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +@@ -58,6 +58,7 @@ + + #include + #include ++#include + + #include + #include +@@ -69,7 +70,6 @@ class QWindow; + namespace QtWaylandClient { + + class QWaylandDisplay; +-class QWaylandWindow; + class QWaylandInputDevice; + class QWaylandXdgShell; + +@@ -123,6 +123,7 @@ private: + QSize size = {0, 0}; + Qt::WindowStates states = Qt::WindowNoState; + } m_pending, m_applied; ++ QWaylandWindow::ToplevelWindowTilingStates m_toplevelStates = QWaylandWindow::WindowNoState; + QSize m_normalSize; + + QWaylandXdgSurface *m_xdgSurface = nullptr;