From 4ca6a4c3066fb269c0328f93986d3515c0f17b1c Mon Sep 17 00:00:00 2001 From: encrypt Date: Mon, 20 Apr 2026 17:56:56 +0200 Subject: [PATCH 1/2] Add QWaylandAdwaitaDecorationPlugin to the linux build --- scripts/utils/qt6_compile.sh | 1 + src/cmake/linux.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/utils/qt6_compile.sh b/scripts/utils/qt6_compile.sh index 9b9e0d24a8..ab9a019f11 100755 --- a/scripts/utils/qt6_compile.sh +++ b/scripts/utils/qt6_compile.sh @@ -78,6 +78,7 @@ LINUX=" -bundled-xcb-xinput \ -feature-qdbus \ -feature-wayland \ + -feature-wayland-client \ -no-feature-gssapi \ -no-feature-zstd \ -xcb \ diff --git a/src/cmake/linux.cmake b/src/cmake/linux.cmake index e2db58a2e3..f41b413281 100644 --- a/src/cmake/linux.cmake +++ b/src/cmake/linux.cmake @@ -62,6 +62,7 @@ if(NOT BUILD_FLATPAK) find_package(Qt6 REQUIRED COMPONENTS WaylandClientPrivate) qt_import_plugins(mozillavpn INCLUDE Qt6::QWaylandIntegrationPlugin) + qt_import_plugins(mozillavpn INCLUDE Qt6::QWaylandAdwaitaDecorationPlugin) target_link_libraries(mozillavpn PRIVATE Qt6::WaylandClientPrivate) else() target_link_libraries(mozillavpn PRIVATE PkgConfig::LIBCAP) From 25862869a160aa52d390da9d56b08a8e8841d26c Mon Sep 17 00:00:00 2001 From: encrypt Date: Mon, 20 Apr 2026 17:59:37 +0200 Subject: [PATCH 2/2] Add QWaylandAdwaitaDecorationPlugin patch to draw shadows --- .../scripts/toolchain/compile_qt_6_linux.sh | 3 + ...waylandadwaitadecoration-add-shadows.patch | 124 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 taskcluster/scripts/toolchain/patches/VPN-7529-qwaylandadwaitadecoration-add-shadows.patch diff --git a/taskcluster/scripts/toolchain/compile_qt_6_linux.sh b/taskcluster/scripts/toolchain/compile_qt_6_linux.sh index d3bee00e0d..acf9e74755 100755 --- a/taskcluster/scripts/toolchain/compile_qt_6_linux.sh +++ b/taskcluster/scripts/toolchain/compile_qt_6_linux.sh @@ -16,6 +16,9 @@ if [[ $(echo -e "${QT_SOURCE_VERSION}\n6.10.3" | sort --version-sort | head -1) patch -d ${QT_SOURCE_DIR}/qtdeclarative -p1 < ${VCS_PATH}/taskcluster/scripts/toolchain/patches/qtbug-141830-qsortfilterproxymodel.patch fi +echo "Patching QWaylandAdwaitaDecorations to add shadows" +patch -d ${QT_SOURCE_DIR}/qtwayland -p1 < ${VCS_PATH}/taskcluster/scripts/toolchain/patches/VPN-7529-qwaylandadwaitadecoration-add-shadows.patch + echo "Installing Qt build dependencies" if [ -f /etc/redhat-release ]; then sudo yum -y install \ diff --git a/taskcluster/scripts/toolchain/patches/VPN-7529-qwaylandadwaitadecoration-add-shadows.patch b/taskcluster/scripts/toolchain/patches/VPN-7529-qwaylandadwaitadecoration-add-shadows.patch new file mode 100644 index 0000000000..2ef9af93f2 --- /dev/null +++ b/taskcluster/scripts/toolchain/patches/VPN-7529-qwaylandadwaitadecoration-add-shadows.patch @@ -0,0 +1,124 @@ +diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp +index ca1f0af0..5b6414f8 100644 +--- a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp ++++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp +@@ -42,7 +42,7 @@ namespace QtWaylandClient { + static constexpr int ceButtonSpacing = 12; + static constexpr int ceButtonWidth = 24; + static constexpr int ceCornerRadius = 12; +-static constexpr int ceShadowsWidth = 10; ++static constexpr int ceShadowsWidth = 30; + static constexpr int ceTitlebarHeight = 38; + static constexpr int ceWindowBorderWidth = 1; + +@@ -124,6 +124,96 @@ void QWaylandAdwaitaDecoration::paint(QPaintDevice *device) + QPainter p(device); + p.setRenderHint(QPainter::Antialiasing); + ++ /* ++ * Shadow ++ * ++ */ ++ ++ const bool active = waylandWindow()->windowStates() & Qt::WindowActive; ++ const QColor shadowColor(0, 0, 0, active ? 25 : 10); ++ const QColor shadowTransparent(0, 0, 0, 0); ++ ++ const QMargins sm = margins(ShadowsOnly); ++ const qreal frameLeft = sm.left(); ++ const qreal frameTop = sm.top(); ++ const qreal frameRight = surfaceRect.width() - sm.right(); ++ const qreal frameBottom = surfaceRect.height() - sm.bottom(); ++ const QRectF frameRect(frameLeft, frameTop, frameRight - frameLeft, frameBottom - frameTop); ++ ++ // The top corners are rounded unless the window is maximized or tiled. ++ const bool hasRoundedTopCorners = ++ !(waylandWindow()->windowStates() & Qt::WindowMaximized) ++ && waylandWindow()->toplevelWindowTilingStates() == QWaylandWindow::WindowNoState; ++ const qreal topCornerRadius = hasRoundedTopCorners ? static_cast(ceCornerRadius) : 0.0; ++ ++ p.save(); ++ ++ // Linear gradients for edges ++ const QList> edges = { ++ // Top edge ++ { QPointF(0.0, frameRect.top()), QPointF(0.0, 0.0), ++ QRectF(frameRect.left() + topCornerRadius, 0.0, ++ frameRect.width() - 2.0 * topCornerRadius, frameRect.top()) }, ++ // Bottom edge ++ { QPointF(0.0, frameRect.bottom()), QPointF(0.0, surfaceRect.height()), ++ QRectF(frameRect.left(), frameRect.bottom(), ++ frameRect.width(), surfaceRect.height() - frameRect.bottom()) }, ++ // Left edge ++ { QPointF(frameRect.left(), 0.0), QPointF(0.0, 0.0), ++ QRectF(0.0, frameRect.top() + topCornerRadius, ++ frameRect.left(), frameRect.height() - topCornerRadius) }, ++ // Right edge ++ { QPointF(frameRect.right(), 0.0), QPointF(surfaceRect.width(), 0.0), ++ QRectF(frameRect.right(), frameRect.top() + topCornerRadius, ++ surfaceRect.width() - frameRect.right(), frameRect.height() - topCornerRadius) } ++ }; ++ ++ for (const auto &edge : edges) { ++ QLinearGradient grad(std::get<0>(edge), std::get<1>(edge)); ++ grad.setColorAt(0.0, shadowColor); ++ grad.setColorAt(1.0, shadowTransparent); ++ p.fillRect(std::get<2>(edge), grad); ++ } ++ ++ // Radial gradients for corners ++ const qreal topLeftGradRadius = qMax(sm.left(), sm.top()) + topCornerRadius; ++ const qreal topRightGradRadius = qMax(sm.right(), sm.top()) + topCornerRadius; ++ ++ const QList> corners = { ++ // Top-left corner ++ { frameRect.topLeft() + QPointF(topCornerRadius, topCornerRadius), topLeftGradRadius, ++ topCornerRadius > 0.0, topCornerRadius / topLeftGradRadius, ++ QRectF(0.0, 0.0, frameRect.left() + topCornerRadius, frameRect.top() + topCornerRadius) }, ++ // Top-right corner ++ { frameRect.topRight() + QPointF(-topCornerRadius, topCornerRadius), topRightGradRadius, ++ topCornerRadius > 0.0, topCornerRadius / topRightGradRadius, ++ QRectF(frameRect.right() - topCornerRadius, 0.0, ++ surfaceRect.width() - frameRect.right() + topCornerRadius, frameRect.top() + topCornerRadius) }, ++ // Bottom-left corner ++ { frameRect.bottomLeft(), qMax(sm.left(), sm.bottom()), ++ false, 0.0, ++ QRectF(0.0, frameRect.bottom(), ++ frameRect.left(), surfaceRect.height() - frameRect.bottom()) }, ++ // Bottom-right corner ++ { frameRect.bottomRight(), qMax(sm.right(), sm.bottom()), ++ false, 0.0, ++ QRectF(frameRect.right(), frameRect.bottom(), ++ surfaceRect.width() - frameRect.right(), ++ surfaceRect.height() - frameRect.bottom()) } ++ }; ++ ++ for (const auto &corner : corners) { ++ QRadialGradient grad(std::get<0>(corner), std::get<1>(corner)); ++ grad.setColorAt(0.0, shadowColor); ++ if (std::get<2>(corner)) ++ grad.setColorAt(std::get<3>(corner), shadowColor); ++ grad.setColorAt(1.0, shadowTransparent); ++ p.fillRect(std::get<4>(corner), grad); ++ } ++ ++ p.restore(); ++ ++ + /* + * Titlebar and window border + */ +diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h +index 34874e08..abd5c6d6 100644 +--- a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h ++++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h +@@ -42,9 +42,6 @@ namespace QtWaylandClient { + // to Gtk3 will not work out. Possibly in future we can make a QGtk4Theme providing us + // what we need to paint the decorations without having to deal with the colors ourself. + // +-// TODO: Implement shadows +- +- + class QWaylandAdwaitaDecoration : public QWaylandAbstractDecoration + { + Q_OBJECT