From 5d3ac864091f88cc98fc77c7a7c31d7d3ab4f2b3 Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Fri, 6 Dec 2024 16:11:08 +0100 Subject: [PATCH] 6.8.1 Resolves: RHEL-53982 --- .gitignore | 1 + qt6-qtwayland.spec | 40 +- qtwayland-add-gnome-like-csd-plugin.patch | 1116 ----------------- ...land-adwaita-improve-border-painting.patch | 74 ++ ...nt-redo-management-of-tablet-proxies.patch | 260 ++++ ...update-wayland-xml-to-version-1.23.0.patch | 491 ++++++++ sources | 2 +- 7 files changed, 862 insertions(+), 1122 deletions(-) delete mode 100644 qtwayland-add-gnome-like-csd-plugin.patch create mode 100644 qtwayland-adwaita-improve-border-painting.patch create mode 100644 qtwayland-client-redo-management-of-tablet-proxies.patch create mode 100644 qtwayland-update-wayland-xml-to-version-1.23.0.patch diff --git a/.gitignore b/.gitignore index 1386446..5814285 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ /qtwayland-everywhere-src-6.6.1.tar.xz /qtwayland-everywhere-src-6.7.0.tar.xz /qtwayland-everywhere-src-6.7.1.tar.xz +/qtwayland-everywhere-src-6.8.1.tar.xz diff --git a/qt6-qtwayland.spec b/qt6-qtwayland.spec index bd27835..5aba33c 100644 --- a/qt6-qtwayland.spec +++ b/qt6-qtwayland.spec @@ -11,8 +11,8 @@ Summary: Qt6 - Wayland platform support and QtCompositor module Name: qt6-%{qt_module} -Version: 6.7.1 -Release: 4%{?dist} +Version: 6.8.1 +Release: 1%{?dist} License: LGPL-3.0-only OR GPL-3.0-only WITH Qt-GPL-exception-1.0 Url: http://www.qt.io @@ -26,9 +26,9 @@ Source0: https://download.qt.io/official_releases/qt/%{majmin}/%{version}/submod %endif # Upstream patches -# Backport Qt Adwaita decorations from upstream to qt6-qtwayland package -# https://issues.redhat.com/browse/DESKTOP-883 -Patch0: qtwayland-add-gnome-like-csd-plugin.patch +Patch0: qtwayland-update-wayland-xml-to-version-1.23.0.patch +Patch1: qtwayland-client-redo-management-of-tablet-proxies.patch +Patch2: qtwayland-adwaita-improve-border-painting.patch # Upstreamable patches @@ -43,6 +43,7 @@ BuildRequires: qt6-qtbase-static BuildRequires: qt6-qtbase-private-devel %{?_qt6:Requires: %{_qt6}%{?_isa} = %{_qt6_version}} BuildRequires: qt6-qtdeclarative-devel +# For Adwaita decorations BuildRequires: qt6-qtsvg-devel BuildRequires: pkgconfig(xkbcommon) @@ -130,9 +131,14 @@ popd %files %doc README %license LICENSES/* +%{_qt6_archdatadir}/sbom/%{qt_module}-%{qt_version}.spdx %{_qt6_libdir}/libQt6WaylandCompositor.so.6* %{_qt6_libdir}/libQt6WaylandClient.so.6* %{_qt6_libdir}/libQt6WaylandCompositor.so.6* +%{_qt6_libdir}/libQt6WaylandCompositorIviapplication.so.6* +%{_qt6_libdir}/libQt6WaylandCompositorPresentationTime.so.6* +%{_qt6_libdir}/libQt6WaylandCompositorWLShell.so.6* +%{_qt6_libdir}/libQt6WaylandCompositorXdgShell.so.6* %{_qt6_libdir}/libQt6WaylandClient.so.6* %{_qt6_libdir}/libQt6WaylandEglClientHwIntegration.so.6* %{_qt6_libdir}/libQt6WaylandEglCompositorHwIntegration.so.6* @@ -150,17 +156,29 @@ popd %files devel %{_qt6_libexecdir}/qtwaylandscanner %{_qt6_headerdir}/QtWaylandCompositor/ +%{_qt6_headerdir}/QtWaylandCompositorIviapplication/ +%{_qt6_headerdir}/QtWaylandCompositorPresentationTime/ +%{_qt6_headerdir}/QtWaylandCompositorWLShell/ +%{_qt6_headerdir}/QtWaylandCompositorXdgShell/ %{_qt6_headerdir}/QtWaylandClient/ %{_qt6_headerdir}/QtWaylandEglClientHwIntegration/ %{_qt6_headerdir}/QtWaylandEglCompositorHwIntegration/ %{_qt6_headerdir}/QtWlShellIntegration/ %{_qt6_headerdir}/QtWaylandGlobal/ %{_qt6_libdir}/libQt6WaylandCompositor.so +%{_qt6_libdir}/libQt6WaylandCompositorIviapplication.so +%{_qt6_libdir}/libQt6WaylandCompositorPresentationTime.so +%{_qt6_libdir}/libQt6WaylandCompositorWLShell.so +%{_qt6_libdir}/libQt6WaylandCompositorXdgShell.so %{_qt6_libdir}/libQt6WaylandClient.so %{_qt6_libdir}/libQt6WaylandEglClientHwIntegration.so %{_qt6_libdir}/libQt6WaylandEglCompositorHwIntegration.so %{_qt6_libdir}/libQt6WlShellIntegration.so %{_qt6_libdir}/libQt6WaylandCompositor.prl +%{_qt6_libdir}/libQt6WaylandCompositorIviapplication.prl +%{_qt6_libdir}/libQt6WaylandCompositorPresentationTime.prl +%{_qt6_libdir}/libQt6WaylandCompositorWLShell.prl +%{_qt6_libdir}/libQt6WaylandCompositorXdgShell.prl %{_qt6_libdir}/libQt6WaylandClient.prl %{_qt6_libdir}/libQt6WaylandEglClientHwIntegration.prl %{_qt6_libdir}/libQt6WaylandEglCompositorHwIntegration.prl @@ -173,6 +191,14 @@ popd %{_qt6_libdir}/cmake/Qt6Qml/QmlPlugins/*.cmake %dir %{_qt6_libdir}/cmake/Qt6WaylandCompositor/ %{_qt6_libdir}/cmake/Qt6WaylandCompositor/ +%dir %{_qt6_libdir}/cmake/Qt6WaylandCompositorIviapplication/ +%{_qt6_libdir}/cmake/Qt6WaylandCompositorIviapplication/ +%dir %{_qt6_libdir}/cmake/Qt6WaylandCompositorPresentationTime/ +%{_qt6_libdir}/cmake/Qt6WaylandCompositorPresentationTime/ +%dir %{_qt6_libdir}/cmake/Qt6WaylandCompositorWLShell/ +%{_qt6_libdir}/cmake/Qt6WaylandCompositorWLShell/ +%dir %{_qt6_libdir}/cmake/Qt6WaylandCompositorXdgShell/ +%{_qt6_libdir}/cmake/Qt6WaylandCompositorXdgShell/ %dir %{_qt6_libdir}/cmake/Qt6WaylandClient/ %{_qt6_libdir}/cmake/Qt6WaylandClient/ %dir %{_qt6_libdir}/cmake/Qt6WaylandScannerTools/ @@ -201,6 +227,10 @@ popd %endif %changelog +* Fri Dec 06 2024 Jan Grulich - 6.8.1-1 +- 6.8.1 + Resolves: RHEL-53982 + * Tue Oct 29 2024 Troy Dawson - 6.7.1-4 - Bump release for October 2024 mass rebuild: Resolves: RHEL-64018 diff --git a/qtwayland-add-gnome-like-csd-plugin.patch b/qtwayland-add-gnome-like-csd-plugin.patch deleted file mode 100644 index 3e47e8c..0000000 --- a/qtwayland-add-gnome-like-csd-plugin.patch +++ /dev/null @@ -1,1116 +0,0 @@ -From 40116ae353fd5f40d386c5398911d1aee483bb13 Mon Sep 17 00:00:00 2001 -From: Jan Grulich -Date: Tue, 12 Dec 2023 10:08:17 +0100 -Subject: [PATCH] Add GNOME-like client-side decoration plugin - -Adds a client-side decoration plugin implementing GNOME's Adwaita style. -This is trying to follow GTK4 Adwaita style, using xdg-desktop-portal to -get user's configuration in order to get whether a light or dark colors -should be used and to get the titlebar button layout. This plugin is now -used on GNOME by default, while defaulting to the original behavior for -non-GNOME DEs. It depends on QtSvg used to draw titlebar buttons so in -case QtSvg is not found, this plugin will not be build. - -[ChangeLog][QtWaylandClient][Added GNOME-like client-side decoration -plugin] - -Fixes: QTBUG-120070 -Change-Id: I0f1777c4e0aa3467dafbbae8004b594cc82f9aa0 -Reviewed-by: David Edmundson ---- - CMakeLists.txt | 2 + - dependencies.yaml | 3 + - src/client/qwaylandwindow.cpp | 19 + - src/configure.cmake | 8 + - src/plugins/decorations/CMakeLists.txt | 3 + - .../decorations/adwaita/CMakeLists.txt | 25 + - src/plugins/decorations/adwaita/adwaita.json | 3 + - src/plugins/decorations/adwaita/main.cpp | 36 + - .../adwaita/qwaylandadwaitadecoration.cpp | 731 ++++++++++++++++++ - .../adwaita/qwaylandadwaitadecoration_p.h | 155 ++++ - 10 files changed, 985 insertions(+) - create mode 100644 src/plugins/decorations/adwaita/CMakeLists.txt - create mode 100644 src/plugins/decorations/adwaita/adwaita.json - create mode 100644 src/plugins/decorations/adwaita/main.cpp - create mode 100644 src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp - create mode 100644 src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 6649dfccb..c498e15b3 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -28,9 +28,11 @@ find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS - ) - - find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS -+ DBus - Gui - OpenGL - Quick -+ Svg - ) - - # special case begin -diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp -index 215193a7b..c0a415725 100644 ---- a/src/client/qwaylandwindow.cpp -+++ b/src/client/qwaylandwindow.cpp -@@ -26,6 +26,7 @@ - - #include - #include -+#include - #include - - #include -@@ -36,6 +37,8 @@ - - QT_BEGIN_NAMESPACE - -+using namespace Qt::StringLiterals; -+ - namespace QtWaylandClient { - - Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore") -@@ -1092,6 +1095,22 @@ bool QWaylandWindow::createDecoration() - } - } - -+ if (targetKey.isEmpty()) { -+ auto unixServices = dynamic_cast( -+ QGuiApplicationPrivate::platformIntegration()->services()); -+ const QByteArray currentDesktop = unixServices->desktopEnvironment(); -+ if (currentDesktop == "GNOME") { -+ if (decorations.contains("adwaita"_L1)) -+ targetKey = "adwaita"_L1; -+ else if (decorations.contains("gnome"_L1)) -+ targetKey = "gnome"_L1; -+ } else { -+ // Do not use Adwaita/GNOME decorations on other DEs -+ decorations.removeAll("adwaita"_L1); -+ decorations.removeAll("gnome"_L1); -+ } -+ } -+ - if (targetKey.isEmpty()) - targetKey = decorations.first(); // first come, first served. - -diff --git a/src/configure.cmake b/src/configure.cmake -index eda1f0850..45f945333 100644 ---- a/src/configure.cmake -+++ b/src/configure.cmake -@@ -245,6 +245,11 @@ qt_feature("wayland-vulkan-server-buffer" PRIVATE - qt_feature("wayland-datadevice" PRIVATE - CONDITION QT_FEATURE_draganddrop OR QT_FEATURE_clipboard - ) -+qt_feature("wayland-decoration-adwaita" PRIVATE -+ LABEL "GNOME-like client-side decorations" -+ CONDITION NOT WIN32 AND QT_FEATURE_wayland_client AND TARGET Qt::DBus AND TARGET Qt::Svg -+) -+ - - qt_configure_add_summary_entry(ARGS "wayland-client") - qt_configure_add_summary_entry(ARGS "wayland-server") -@@ -257,6 +262,9 @@ qt_configure_add_summary_entry(ARGS "wayland-dmabuf-server-buffer") - qt_configure_add_summary_entry(ARGS "wayland-shm-emulation-server-buffer") - qt_configure_add_summary_entry(ARGS "wayland-vulkan-server-buffer") - qt_configure_end_summary_section() # end of "Qt Wayland Drivers" section -+qt_configure_add_summary_section(NAME "Qt Wayland Decoration Plugins") -+qt_configure_add_summary_entry(ARGS "wayland-decoration-adwaita") -+qt_configure_end_summary_section() # end of "Qt Wayland Decoration Plugins" section - - qt_configure_add_report_entry( - TYPE ERROR -diff --git a/src/plugins/decorations/CMakeLists.txt b/src/plugins/decorations/CMakeLists.txt -index 73c59e4a5..abe3c375b 100644 ---- a/src/plugins/decorations/CMakeLists.txt -+++ b/src/plugins/decorations/CMakeLists.txt -@@ -2,5 +2,8 @@ - # SPDX-License-Identifier: BSD-3-Clause - - # Generated from decorations.pro. -+if (QT_FEATURE_wayland_decoration_adwaita) -+ add_subdirectory(adwaita) -+endif() - - add_subdirectory(bradient) -diff --git a/src/plugins/decorations/adwaita/CMakeLists.txt b/src/plugins/decorations/adwaita/CMakeLists.txt -new file mode 100644 -index 000000000..b318c2b8b ---- /dev/null -+++ b/src/plugins/decorations/adwaita/CMakeLists.txt -@@ -0,0 +1,25 @@ -+# Copyright (C) 2023 The Qt Company Ltd. -+# SPDX-License-Identifier: BSD-3-Clause -+ -+##################################################################### -+## QWaylandAdwaitaDecorationPlugin Plugin: -+##################################################################### -+ -+qt_internal_add_plugin(QWaylandAdwaitaDecorationPlugin -+ OUTPUT_NAME adwaita -+ PLUGIN_TYPE wayland-decoration-client -+ SOURCES -+ main.cpp -+ qwaylandadwaitadecoration.cpp -+ LIBRARIES -+ Qt::Core -+ Qt::DBus -+ Qt::Gui -+ Qt::Svg -+ Qt::WaylandClientPrivate -+ Wayland::Client -+) -+ -+#### Keys ignored in scope 1:.:.:bradient.pro:: -+# OTHER_FILES = "bradient.json" -+ -diff --git a/src/plugins/decorations/adwaita/adwaita.json b/src/plugins/decorations/adwaita/adwaita.json -new file mode 100644 -index 000000000..69ec79e9b ---- /dev/null -+++ b/src/plugins/decorations/adwaita/adwaita.json -@@ -0,0 +1,3 @@ -+{ -+ "Keys": [ "adwaita", "gnome" ] -+} -diff --git a/src/plugins/decorations/adwaita/main.cpp b/src/plugins/decorations/adwaita/main.cpp -new file mode 100644 -index 000000000..e5b1be830 ---- /dev/null -+++ b/src/plugins/decorations/adwaita/main.cpp -@@ -0,0 +1,36 @@ -+// Copyright (C) 2023 Jan Grulich -+// Copyright (C) 2023 The Qt Company Ltd. -+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -+ -+#include -+ -+#include "qwaylandadwaitadecoration_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+using namespace Qt::StringLiterals; -+ -+namespace QtWaylandClient { -+ -+class QWaylandAdwaitaDecorationPlugin : public QWaylandDecorationPlugin -+{ -+ Q_OBJECT -+ Q_PLUGIN_METADATA(IID QWaylandDecorationFactoryInterface_iid FILE "adwaita.json") -+public: -+ QWaylandAbstractDecoration *create(const QString &key, const QStringList ¶ms) override; -+}; -+ -+QWaylandAbstractDecoration *QWaylandAdwaitaDecorationPlugin::create(const QString &key, const QStringList ¶ms) -+{ -+ Q_UNUSED(params); -+ if (!key.compare("adwaita"_L1, Qt::CaseInsensitive) || -+ !key.compare("gnome"_L1, Qt::CaseInsensitive)) -+ return new QWaylandAdwaitaDecoration(); -+ return nullptr; -+} -+ -+} -+ -+QT_END_NAMESPACE -+ -+#include "main.moc" -diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp -new file mode 100644 -index 000000000..2d3575bce ---- /dev/null -+++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp -@@ -0,0 +1,731 @@ -+// Copyright (C) 2023 Jan Grulich -+// Copyright (C) 2023 The Qt Company Ltd. -+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -+ -+#include "qwaylandadwaitadecoration_p.h" -+ -+// QtCore -+#include -+#include -+ -+// QtDBus -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+// QtGui -+#include -+#include -+#include -+ -+#include -+#include -+ -+// QtSvg -+#include -+ -+// QtWayland -+#include -+#include -+ -+ -+QT_BEGIN_NAMESPACE -+ -+using namespace Qt::StringLiterals; -+ -+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 ceTitlebarHeight = 38; -+static constexpr int ceWindowBorderWidth = 1; -+static constexpr qreal ceTitlebarSeperatorWidth = 0.5; -+ -+static QMap buttonMap = { -+ { QWaylandAdwaitaDecoration::CloseIcon, "window-close-symbolic"_L1 }, -+ { QWaylandAdwaitaDecoration::MinimizeIcon, "window-minimize-symbolic"_L1 }, -+ { QWaylandAdwaitaDecoration::MaximizeIcon, "window-maximize-symbolic"_L1 }, -+ { QWaylandAdwaitaDecoration::RestoreIcon, "window-restore-symbolic"_L1 } -+}; -+ -+const QDBusArgument &operator>>(const QDBusArgument &argument, QMap &map) -+{ -+ argument.beginMap(); -+ map.clear(); -+ -+ while (!argument.atEnd()) { -+ QString key; -+ QVariantMap value; -+ argument.beginMapEntry(); -+ argument >> key >> value; -+ argument.endMapEntry(); -+ map.insert(key, value); -+ } -+ -+ argument.endMap(); -+ return argument; -+} -+ -+Q_LOGGING_CATEGORY(lcQWaylandAdwaitaDecorationLog, "qt.qpa.qwaylandadwaitadecoration", QtWarningMsg) -+ -+QWaylandAdwaitaDecoration::QWaylandAdwaitaDecoration() -+ : QWaylandAbstractDecoration() -+{ -+ m_lastButtonClick = QDateTime::currentDateTime(); -+ -+ QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter); -+ option.setWrapMode(QTextOption::NoWrap); -+ m_windowTitle.setTextOption(option); -+ m_windowTitle.setTextFormat(Qt::PlainText); -+ -+ const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); -+ if (const QFont *font = theme->font(QPlatformTheme::TitleBarFont)) -+ m_font = std::make_unique(*font); -+ if (!m_font) // Fallback to GNOME's default font -+ m_font = std::make_unique("Cantarell"_L1, 10); -+ -+ QTimer::singleShot(0, this, &QWaylandAdwaitaDecoration::loadConfiguration); -+} -+ -+QMargins QWaylandAdwaitaDecoration::margins(QWaylandAbstractDecoration::MarginsType marginsType) const -+{ -+ const bool onlyShadows = marginsType == QWaylandAbstractDecoration::ShadowsOnly; -+ const bool shadowsExcluded = marginsType == ShadowsExcluded; -+ -+ if (waylandWindow()->windowStates() & Qt::WindowMaximized) { -+ // Maximized windows don't have anything around, no shadows, border, -+ // etc. Only report titlebar height in case we are not asking for shadow -+ // margins. -+ return QMargins(0, onlyShadows ? 0 : ceTitlebarHeight, 0, 0); -+ } -+ -+ const QWaylandWindow::ToplevelWindowTilingStates tilingStates = waylandWindow()->toplevelWindowTilingStates(); -+ -+ // Since all sides (left, right, bottom) are going to be same -+ const int marginsBase = shadowsExcluded ? ceWindowBorderWidth : ceShadowsWidth + ceWindowBorderWidth; -+ const int sideMargins = onlyShadows ? ceShadowsWidth : marginsBase; -+ const int topMargins = onlyShadows ? ceShadowsWidth : ceTitlebarHeight + marginsBase; -+ -+ return QMargins(tilingStates & QWaylandWindow::WindowTiledLeft ? 0 : sideMargins, -+ tilingStates & QWaylandWindow::WindowTiledTop ? onlyShadows ? 0 : ceTitlebarHeight : topMargins, -+ tilingStates & QWaylandWindow::WindowTiledRight ? 0 : sideMargins, -+ tilingStates & QWaylandWindow::WindowTiledBottom ? 0 : sideMargins); -+} -+ -+void QWaylandAdwaitaDecoration::paint(QPaintDevice *device) -+{ -+ const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly); -+ -+ QPainter p(device); -+ p.setRenderHint(QPainter::Antialiasing); -+ -+ /* -+ * Titlebar and window border -+ */ -+ const int titleBarWidth = surfaceRect.width() - margins().left() - margins().right(); -+ QPainterPath path; -+ -+ // Maximized or tiled won't have rounded corners -+ if (waylandWindow()->windowStates() & Qt::WindowMaximized -+ || waylandWindow()->toplevelWindowTilingStates() != QWaylandWindow::WindowNoState) -+ path.addRect(margins().left(), margins().bottom(), titleBarWidth, margins().top()); -+ else -+ path.addRoundedRect(margins().left(), margins().bottom(), titleBarWidth, -+ margins().top() + ceCornerRadius, ceCornerRadius, ceCornerRadius); -+ -+ p.save(); -+ p.setPen(color(Border)); -+ p.fillPath(path.simplified(), color(Background)); -+ p.drawPath(path); -+ p.drawRect(margins().left(), margins().top(), titleBarWidth, surfaceRect.height() - margins().top() - margins().bottom()); -+ p.restore(); -+ -+ -+ /* -+ * Titlebar separator -+ */ -+ p.save(); -+ p.setPen(color(Border)); -+ p.drawLine(QLineF(margins().left(), margins().top() - ceTitlebarSeperatorWidth, -+ surfaceRect.width() - margins().right(), -+ margins().top() - ceTitlebarSeperatorWidth)); -+ p.restore(); -+ -+ -+ /* -+ * Window title -+ */ -+ const QRect top = QRect(margins().left(), margins().bottom(), surfaceRect.width(), -+ margins().top() - margins().bottom()); -+ const QString windowTitleText = waylandWindow()->windowTitle(); -+ if (!windowTitleText.isEmpty()) { -+ if (m_windowTitle.text() != windowTitleText) { -+ m_windowTitle.setText(windowTitleText); -+ m_windowTitle.prepare(); -+ } -+ -+ QRect titleBar = top; -+ if (m_placement == Right) { -+ titleBar.setLeft(margins().left()); -+ titleBar.setRight(static_cast(buttonRect(Minimize).left()) - 8); -+ } else { -+ titleBar.setLeft(static_cast(buttonRect(Minimize).right()) + 8); -+ titleBar.setRight(surfaceRect.width() - margins().right()); -+ } -+ -+ p.save(); -+ p.setClipRect(titleBar); -+ p.setPen(color(Foreground)); -+ QSize size = m_windowTitle.size().toSize(); -+ int dx = (top.width() - size.width()) / 2; -+ int dy = (top.height() - size.height()) / 2; -+ p.setFont(*m_font); -+ QPoint windowTitlePoint(top.topLeft().x() + dx, top.topLeft().y() + dy); -+ p.drawStaticText(windowTitlePoint, m_windowTitle); -+ p.restore(); -+ } -+ -+ -+ /* -+ * Buttons -+ */ -+ if (m_buttons.contains(Close)) -+ drawButton(Close, &p); -+ -+ if (m_buttons.contains(Maximize)) -+ drawButton(Maximize, &p); -+ -+ if (m_buttons.contains(Minimize)) -+ drawButton(Minimize, &p); -+} -+ -+bool QWaylandAdwaitaDecoration::handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, -+ const QPointF &global, Qt::MouseButtons b, -+ Qt::KeyboardModifiers mods) -+{ -+ Q_UNUSED(global) -+ -+ if (local.y() > margins().top()) -+ updateButtonHoverState(Button::None); -+ -+ // Figure out what area mouse is in -+ QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly); -+ if (local.y() <= surfaceRect.top() + margins().top()) -+ processMouseTop(inputDevice, local, b, mods); -+ else if (local.y() > surfaceRect.bottom() - margins().bottom()) -+ processMouseBottom(inputDevice, local, b, mods); -+ else if (local.x() <= surfaceRect.left() + margins().left()) -+ processMouseLeft(inputDevice, local, b, mods); -+ else if (local.x() > surfaceRect.right() - margins().right()) -+ processMouseRight(inputDevice, local, b, mods); -+ else { -+#if QT_CONFIG(cursor) -+ waylandWindow()->restoreMouseCursor(inputDevice); -+#endif -+ } -+ -+ // Reset clicking state in case a button press is released outside -+ // the button area -+ if (isLeftReleased(b)) { -+ m_clicking = None; -+ requestRepaint(); -+ } -+ -+ setMouseButtons(b); -+ return false; -+} -+ -+bool QWaylandAdwaitaDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, -+ const QPointF &global, QEventPoint::State state, -+ Qt::KeyboardModifiers mods) -+{ -+ Q_UNUSED(inputDevice) -+ Q_UNUSED(global) -+ Q_UNUSED(mods) -+ -+ bool handled = state == QEventPoint::Pressed; -+ -+ if (handled) { -+ if (buttonRect(Close).contains(local)) -+ QWindowSystemInterface::handleCloseEvent(window()); -+ else if (m_buttons.contains(Maximize) && buttonRect(Maximize).contains(local)) -+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); -+ else if (m_buttons.contains(Minimize) && buttonRect(Minimize).contains(local)) -+ window()->setWindowState(Qt::WindowMinimized); -+ else if (local.y() <= margins().top()) -+ waylandWindow()->shellSurface()->move(inputDevice); -+ else -+ handled = false; -+ } -+ -+ return handled; -+} -+ -+QString getIconSvg(const QString &iconName) -+{ -+ const QStringList themeNames = { QIcon::themeName(), QIcon::fallbackThemeName(), "Adwaita"_L1 }; -+ -+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Searched icon themes: " << themeNames; -+ -+ for (const QString &themeName : themeNames) { -+ if (themeName.isEmpty()) -+ continue; -+ -+ for (const QString &path : QIcon::themeSearchPaths()) { -+ if (path.startsWith(QLatin1Char(':'))) -+ continue; -+ -+ const QString fullPath = QString("%1/%2").arg(path).arg(themeName); -+ QDirIterator dirIt(fullPath, {"*.svg"}, QDir::Files, QDirIterator::Subdirectories); -+ while (dirIt.hasNext()) { -+ const QString fileName = dirIt.next(); -+ const QFileInfo fileInfo(fileName); -+ -+ if (fileInfo.fileName() == iconName) { -+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Using " << iconName << " from " << themeName << " theme"; -+ QFile readFile(fileInfo.filePath()); -+ readFile.open(QFile::ReadOnly); -+ return readFile.readAll(); -+ } -+ } -+ } -+ } -+ -+ qCWarning(lcQWaylandAdwaitaDecorationLog) << "Failed to find an svg icon for " << iconName; -+ -+ return QString(); -+} -+ -+void QWaylandAdwaitaDecoration::loadConfiguration() -+{ -+ qRegisterMetaType(); -+ qDBusRegisterMetaType>(); -+ -+ QDBusConnection connection = QDBusConnection::sessionBus(); -+ -+ QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, -+ "/org/freedesktop/portal/desktop"_L1, -+ "org.freedesktop.portal.Settings"_L1, -+ "ReadAll"_L1); -+ message << QStringList{ { "org.gnome.desktop.wm.preferences"_L1 }, -+ { "org.freedesktop.appearance"_L1 } }; -+ -+ QDBusPendingCall pendingCall = connection.asyncCall(message); -+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); -+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { -+ QDBusPendingReply> reply = *watcher; -+ if (reply.isValid()) { -+ QMap settings = reply.value(); -+ if (!settings.isEmpty()) { -+ const uint colorScheme = settings.value("org.freedesktop.appearance"_L1).value("color-scheme"_L1).toUInt(); -+ updateColors(colorScheme == 1); // 1 == Prefer Dark -+ const QString buttonLayout = settings.value("org.gnome.desktop.wm.preferences"_L1).value("button-layout"_L1).toString(); -+ if (!buttonLayout.isEmpty()) -+ updateTitlebarLayout(buttonLayout); -+ // Workaround for QGtkStyle not having correct titlebar font -+ const QString titlebarFont = -+ settings.value("org.gnome.desktop.wm.preferences"_L1).value("titlebar-font"_L1).toString(); -+ if (titlebarFont.contains("bold"_L1, Qt::CaseInsensitive)) { -+ m_font->setBold(true); -+ } -+ } -+ } -+ watcher->deleteLater(); -+ }); -+ -+ QDBusConnection::sessionBus().connect(QString(), "/org/freedesktop/portal/desktop"_L1, -+ "org.freedesktop.portal.Settings"_L1, "SettingChanged"_L1, this, -+ SLOT(settingChanged(QString, QString, QDBusVariant))); -+ -+ // Load SVG icons -+ for (auto mapIt = buttonMap.constBegin(); mapIt != buttonMap.constEnd(); mapIt++) { -+ const QString fullName = mapIt.value() + QStringLiteral(".svg"); -+ m_icons[mapIt.key()] = getIconSvg(fullName); -+ } -+ -+ updateColors(false); -+} -+ -+void QWaylandAdwaitaDecoration::updateColors(bool isDark) -+{ -+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Color scheme changed to: " << (isDark ? "dark" : "light"); -+ -+ m_colors = { { Background, isDark ? QColor(0x303030) : QColor(0xffffff) }, -+ { BackgroundInactive, isDark ? QColor(0x242424) : QColor(0xfafafa) }, -+ { Foreground, isDark ? QColor(0xffffff) : QColor(0x2e2e2e) }, -+ { ForegroundInactive, isDark ? QColor(0x919191) : QColor(0x949494) }, -+ { Border, isDark ? QColor(0x3b3b3b) : QColor(0xdbdbdb) }, -+ { BorderInactive, isDark ? QColor(0x303030) : QColor(0xdbdbdb) }, -+ { ButtonBackground, isDark ? QColor(0x444444) : QColor(0xebebeb) }, -+ { ButtonBackgroundInactive, isDark ? QColor(0x2e2e2e) : QColor(0xf0f0f0) }, -+ { HoveredButtonBackground, isDark ? QColor(0x4f4f4f) : QColor(0xe0e0e0) }, -+ { PressedButtonBackground, isDark ? QColor(0x6e6e6e) : QColor(0xc2c2c2) } }; -+ requestRepaint(); -+} -+ -+void QWaylandAdwaitaDecoration::updateTitlebarLayout(const QString &layout) -+{ -+ const QStringList layouts = layout.split(QLatin1Char(':')); -+ if (layouts.count() != 2) -+ return; -+ -+ // Remove previous configuration -+ m_buttons.clear(); -+ -+ const QString &leftLayout = layouts.at(0); -+ const QString &rightLayout = layouts.at(1); -+ m_placement = leftLayout.contains("close"_L1) ? Left : Right; -+ -+ int pos = 1; -+ const QString &buttonLayout = m_placement == Right ? rightLayout : leftLayout; -+ -+ QStringList buttonList = buttonLayout.split(QLatin1Char(',')); -+ if (m_placement == Right) -+ std::reverse(buttonList.begin(), buttonList.end()); -+ -+ for (const QString &button : buttonList) { -+ if (button == "close"_L1) -+ m_buttons.insert(Close, pos); -+ else if (button == "maximize"_L1) -+ m_buttons.insert(Maximize, pos); -+ else if (button == "minimize"_L1) -+ m_buttons.insert(Minimize, pos); -+ -+ pos++; -+ } -+ -+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Button layout changed to: " << layout; -+ -+ requestRepaint(); -+} -+ -+void QWaylandAdwaitaDecoration::settingChanged(const QString &group, const QString &key, -+ const QDBusVariant &value) -+{ -+ if (group == "org.gnome.desktop.wm.preferences"_L1 && key == "button-layout"_L1) { -+ const QString layout = value.variant().toString(); -+ updateTitlebarLayout(layout); -+ } else if (group == "org.freedesktop.appearance"_L1 && key == "color-scheme"_L1) { -+ const uint colorScheme = value.variant().toUInt(); -+ updateColors(colorScheme == 1); // 1 == Prefer Dark -+ } -+} -+ -+QRectF QWaylandAdwaitaDecoration::buttonRect(Button button) const -+{ -+ int xPos; -+ int yPos; -+ const int btnPos = m_buttons.value(button); -+ -+ const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(QWaylandAbstractDecoration::ShadowsOnly); -+ if (m_placement == Right) { -+ xPos = surfaceRect.width(); -+ xPos -= ceButtonWidth * btnPos; -+ xPos -= ceButtonSpacing * btnPos; -+ xPos -= margins(ShadowsOnly).right(); -+ } else { -+ xPos = 0; -+ xPos += ceButtonWidth * btnPos; -+ xPos += ceButtonSpacing * btnPos; -+ xPos += margins(ShadowsOnly).left(); -+ // We are painting from the left to the right so the real -+ // position doesn't need to by moved by the size of the button. -+ xPos -= ceButtonWidth; -+ } -+ -+ yPos = margins().top(); -+ yPos += margins().bottom(); -+ yPos -= ceButtonWidth; -+ yPos /= 2; -+ -+ return QRectF(xPos, yPos, ceButtonWidth, ceButtonWidth); -+} -+ -+static void renderFlatRoundedButtonFrame(QPainter *painter, const QRect &rect, const QColor &color) -+{ -+ painter->save(); -+ painter->setRenderHint(QPainter::Antialiasing, true); -+ painter->setPen(Qt::NoPen); -+ painter->setBrush(color); -+ painter->drawEllipse(rect); -+ painter->restore(); -+} -+ -+static void renderButtonIcon(const QString &svgIcon, QPainter *painter, const QRect &rect, const QColor &color) -+{ -+ painter->save(); -+ painter->setRenderHints(QPainter::Antialiasing, true); -+ -+ QString icon = svgIcon; -+ QRegularExpression regexp("fill=[\"']#[0-9A-F]{6}[\"']", QRegularExpression::CaseInsensitiveOption); -+ QRegularExpression regexpAlt("fill:#[0-9A-F]{6}", QRegularExpression::CaseInsensitiveOption); -+ QRegularExpression regexpCurrentColor("fill=[\"']currentColor[\"']"); -+ icon.replace(regexp, QString("fill=\"%1\"").arg(color.name())); -+ icon.replace(regexpAlt, QString("fill:%1").arg(color.name())); -+ icon.replace(regexpCurrentColor, QString("fill=\"%1\"").arg(color.name())); -+ QSvgRenderer svgRenderer(icon.toLocal8Bit()); -+ svgRenderer.render(painter, rect); -+ -+ painter->restore(); -+} -+ -+static void renderButtonIcon(QWaylandAdwaitaDecoration::ButtonIcon buttonIcon, QPainter *painter, const QRect &rect) -+{ -+ QString iconName = buttonMap[buttonIcon]; -+ -+ painter->save(); -+ painter->setRenderHints(QPainter::Antialiasing, true); -+ painter->drawPixmap(rect, QIcon::fromTheme(iconName).pixmap(ceButtonWidth, ceButtonWidth)); -+ painter->restore(); -+} -+ -+static QWaylandAdwaitaDecoration::ButtonIcon iconFromButtonAndState(QWaylandAdwaitaDecoration::Button button, bool maximized) -+{ -+ if (button == QWaylandAdwaitaDecoration::Close) -+ return QWaylandAdwaitaDecoration::CloseIcon; -+ else if (button == QWaylandAdwaitaDecoration::Minimize) -+ return QWaylandAdwaitaDecoration::MinimizeIcon; -+ else if (button == QWaylandAdwaitaDecoration::Maximize && maximized) -+ return QWaylandAdwaitaDecoration::RestoreIcon; -+ else -+ return QWaylandAdwaitaDecoration::MaximizeIcon; -+} -+ -+void QWaylandAdwaitaDecoration::drawButton(Button button, QPainter *painter) -+{ -+ const Qt::WindowStates windowStates = waylandWindow()->windowStates(); -+ const bool maximized = windowStates & Qt::WindowMaximized; -+ -+ const QRect btnRect = buttonRect(button).toRect(); -+ renderFlatRoundedButtonFrame(painter, btnRect, color(ButtonBackground, button)); -+ -+ QRect adjustedBtnRect = btnRect; -+ adjustedBtnRect.setSize(QSize(16, 16)); -+ adjustedBtnRect.translate(4, 4); -+ const QString svgIcon = m_icons[iconFromButtonAndState(button, maximized)]; -+ if (!svgIcon.isEmpty()) -+ renderButtonIcon(svgIcon, painter, adjustedBtnRect, color(Foreground)); -+ else // Fallback to use QIcon -+ renderButtonIcon(iconFromButtonAndState(button, maximized), painter, adjustedBtnRect); -+} -+ -+QColor QWaylandAdwaitaDecoration::color(ColorType type, Button button) -+{ -+ const bool active = waylandWindow()->windowStates() & Qt::WindowActive; -+ -+ switch (type) { -+ case Background: -+ case BackgroundInactive: -+ return active ? m_colors[Background] : m_colors[BackgroundInactive]; -+ case Foreground: -+ case ForegroundInactive: -+ return active ? m_colors[Foreground] : m_colors[ForegroundInactive]; -+ case Border: -+ case BorderInactive: -+ return active ? m_colors[Border] : m_colors[BorderInactive]; -+ case ButtonBackground: -+ case ButtonBackgroundInactive: -+ case HoveredButtonBackground: { -+ if (m_clicking == button) { -+ return m_colors[PressedButtonBackground]; -+ } else if (m_hoveredButtons.testFlag(button)) { -+ return m_colors[HoveredButtonBackground]; -+ } -+ return active ? m_colors[ButtonBackground] : m_colors[ButtonBackgroundInactive]; -+ } -+ default: -+ return m_colors[Background]; -+ } -+} -+ -+bool QWaylandAdwaitaDecoration::clickButton(Qt::MouseButtons b, Button btn) -+{ -+ auto repaint = qScopeGuard([this] { requestRepaint(); }); -+ -+ if (isLeftClicked(b)) { -+ m_clicking = btn; -+ return false; -+ } else if (isLeftReleased(b)) { -+ if (m_clicking == btn) { -+ m_clicking = None; -+ return true; -+ } else { -+ m_clicking = None; -+ } -+ } -+ return false; -+} -+ -+bool QWaylandAdwaitaDecoration::doubleClickButton(Qt::MouseButtons b, const QPointF &local, -+ const QDateTime ¤tTime) -+{ -+ if (isLeftClicked(b)) { -+ const qint64 clickInterval = m_lastButtonClick.msecsTo(currentTime); -+ m_lastButtonClick = currentTime; -+ const int doubleClickDistance = 5; -+ const QPointF posDiff = m_lastButtonClickPosition - local; -+ if ((clickInterval <= 500) -+ && ((posDiff.x() <= doubleClickDistance && posDiff.x() >= -doubleClickDistance) -+ && ((posDiff.y() <= doubleClickDistance && posDiff.y() >= -doubleClickDistance)))) { -+ return true; -+ } -+ -+ m_lastButtonClickPosition = local; -+ } -+ -+ return false; -+} -+ -+void QWaylandAdwaitaDecoration::updateButtonHoverState(Button hoveredButton) -+{ -+ bool currentCloseButtonState = m_hoveredButtons.testFlag(Close); -+ bool currentMaximizeButtonState = m_hoveredButtons.testFlag(Maximize); -+ bool currentMinimizeButtonState = m_hoveredButtons.testFlag(Minimize); -+ -+ m_hoveredButtons.setFlag(Close, hoveredButton == Button::Close); -+ m_hoveredButtons.setFlag(Maximize, hoveredButton == Button::Maximize); -+ m_hoveredButtons.setFlag(Minimize, hoveredButton == Button::Minimize); -+ -+ if (m_hoveredButtons.testFlag(Close) != currentCloseButtonState -+ || m_hoveredButtons.testFlag(Maximize) != currentMaximizeButtonState -+ || m_hoveredButtons.testFlag(Minimize) != currentMinimizeButtonState) { -+ requestRepaint(); -+ } -+} -+ -+void QWaylandAdwaitaDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods) -+{ -+ Q_UNUSED(mods) -+ -+ QDateTime currentDateTime = QDateTime::currentDateTime(); -+ QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly); -+ -+ if (!buttonRect(Close).contains(local) && !buttonRect(Maximize).contains(local) -+ && !buttonRect(Minimize).contains(local)) -+ updateButtonHoverState(Button::None); -+ -+ if (local.y() <= surfaceRect.top() + margins().bottom()) { -+ if (local.x() <= margins().left()) { -+ // top left bit -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor); -+#endif -+ startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b); -+ } else if (local.x() > surfaceRect.right() - margins().left()) { -+ // top right bit -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor); -+#endif -+ startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b); -+ } else { -+ // top resize bit -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor); -+#endif -+ startResize(inputDevice, Qt::TopEdge, b); -+ } -+ } else if (local.x() <= surfaceRect.left() + margins().left()) { -+ processMouseLeft(inputDevice, local, b, mods); -+ } else if (local.x() > surfaceRect.right() - margins().right()) { -+ processMouseRight(inputDevice, local, b, mods); -+ } else if (buttonRect(Close).contains(local)) { -+ if (clickButton(b, Close)) { -+ QWindowSystemInterface::handleCloseEvent(window()); -+ m_hoveredButtons.setFlag(Close, false); -+ } -+ updateButtonHoverState(Close); -+ } else if (m_buttons.contains(Maximize) && buttonRect(Maximize).contains(local)) { -+ updateButtonHoverState(Maximize); -+ if (clickButton(b, Maximize)) { -+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); -+ m_hoveredButtons.setFlag(Maximize, false); -+ } -+ } else if (m_buttons.contains(Minimize) && buttonRect(Minimize).contains(local)) { -+ updateButtonHoverState(Minimize); -+ if (clickButton(b, Minimize)) { -+ window()->setWindowState(Qt::WindowMinimized); -+ m_hoveredButtons.setFlag(Minimize, false); -+ } -+ } else if (doubleClickButton(b, local, currentDateTime)) { -+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); -+ } else { -+ // Show window menu -+ if (b == Qt::MouseButton::RightButton) -+ waylandWindow()->shellSurface()->showWindowMenu(inputDevice); -+#if QT_CONFIG(cursor) -+ waylandWindow()->restoreMouseCursor(inputDevice); -+#endif -+ startMove(inputDevice, b); -+ } -+} -+ -+void QWaylandAdwaitaDecoration::processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods) -+{ -+ Q_UNUSED(mods) -+ if (local.x() <= margins().left()) { -+ // bottom left bit -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor); -+#endif -+ startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b); -+ } else if (local.x() > window()->width() + margins().right()) { -+ // bottom right bit -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor); -+#endif -+ startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b); -+ } else { -+ // bottom bit -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor); -+#endif -+ startResize(inputDevice, Qt::BottomEdge, b); -+ } -+} -+ -+void QWaylandAdwaitaDecoration::processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods) -+{ -+ Q_UNUSED(local) -+ Q_UNUSED(mods) -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor); -+#endif -+ startResize(inputDevice, Qt::LeftEdge, b); -+} -+ -+void QWaylandAdwaitaDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods) -+{ -+ Q_UNUSED(local) -+ Q_UNUSED(mods) -+#if QT_CONFIG(cursor) -+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor); -+#endif -+ startResize(inputDevice, Qt::RightEdge, b); -+} -+ -+void QWaylandAdwaitaDecoration::requestRepaint() const -+{ -+ // Set dirty flag -+ if (waylandWindow()->decoration()) -+ waylandWindow()->decoration()->update(); -+ -+ // Request re-paint -+ waylandWindow()->window()->requestUpdate(); -+} -+ -+} // namespace QtWaylandClient -+ -+QT_END_NAMESPACE -+ -+#include "moc_qwaylandadwaitadecoration_p.cpp" -diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h -new file mode 100644 -index 000000000..34874e088 ---- /dev/null -+++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h -@@ -0,0 +1,155 @@ -+// Copyright (C) 2023 Jan Grulich -+// Copyright (C) 2023 The Qt Company Ltd. -+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -+ -+#ifndef QWAYLANDADWAITADECORATION_P_H -+#define QWAYLANDADWAITADECORATION_P_H -+ -+#include -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QDBusVariant; -+class QPainter; -+ -+namespace QtWaylandClient { -+ -+// -+// INFO -+// ------------- -+// -+// This is a Qt decoration plugin implementing Adwaita-like (GNOME) client-side -+// window decorations. It uses xdg-desktop-portal to get the user configuration. -+// This plugin was originally part of QGnomePlatform and later made a separate -+// project named QAdwaitaDecorations. -+// -+// INFO: How SVG icons are used here? -+// We try to find an SVG icon for a particular button from the current icon theme. -+// This icon is then opened as a file, it's content saved and later loaded to be -+// painted with QSvgRenderer, but before it's painted, we try to find following -+// patterns: -+// 1) fill=[\"']#[0-9A-F]{6}[\"'] -+// 2) fill:#[0-9A-F]{6} -+// 3) fill=[\"']currentColor[\"'] -+// The color in this case doesn't match the theme and is replaced by Foreground color. -+// -+// FIXME/TODO: -+// This plugin currently have all the colors for the decorations hardcoded. -+// There might be a way to get these from GTK/libadwaita (not sure), but problem is -+// we want Gtk4 version and using Gtk4 together with QGtk3Theme from QtBase that links -+// 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 -+public: -+ enum ColorType { -+ Background, -+ BackgroundInactive, -+ Foreground, -+ ForegroundInactive, -+ Border, -+ BorderInactive, -+ ButtonBackground, -+ ButtonBackgroundInactive, -+ HoveredButtonBackground, -+ PressedButtonBackground -+ }; -+ -+ enum Placement { -+ Left = 0, -+ Right = 1 -+ }; -+ -+ enum Button { -+ None = 0x0, -+ Close = 0x1, -+ Minimize = 0x02, -+ Maximize = 0x04 -+ }; -+ Q_DECLARE_FLAGS(Buttons, Button); -+ -+ enum ButtonIcon { -+ CloseIcon, -+ MinimizeIcon, -+ MaximizeIcon, -+ RestoreIcon -+ }; -+ -+ QWaylandAdwaitaDecoration(); -+ virtual ~QWaylandAdwaitaDecoration() = default; -+ -+protected: -+ 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, -+ QEventPoint::State state, Qt::KeyboardModifiers mods) override; -+ -+private Q_SLOTS: -+ void settingChanged(const QString &group, const QString &key, const QDBusVariant &value); -+ -+private: -+ // Makes a call to xdg-desktop-portal (Settings) to load initial configuration -+ void loadConfiguration(); -+ // Updates color scheme from light to dark and vice-versa -+ void updateColors(bool isDark); -+ // Updates titlebar layout with position and button order -+ void updateTitlebarLayout(const QString &layout); -+ -+ // Returns a bounding rect for a given button type -+ QRectF buttonRect(Button button) const; -+ // Draw given button type using SVG icon (when found) or fallback to QPixmap icon -+ void drawButton(Button button, QPainter *painter); -+ -+ // Returns color for given type and button -+ QColor color(ColorType type, Button button = None); -+ -+ // Returns whether the left button was clicked i.e. pressed and released -+ bool clickButton(Qt::MouseButtons b, Button btn); -+ // Returns whether the left button was double-clicked -+ bool doubleClickButton(Qt::MouseButtons b, const QPointF &local, const QDateTime ¤tTime); -+ // Updates button hover state -+ void updateButtonHoverState(Button hoveredButton); -+ -+ void processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, -+ Qt::KeyboardModifiers mods); -+ void processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods); -+ void processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods); -+ void processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, -+ Qt::MouseButtons b, Qt::KeyboardModifiers mods); -+ // Request to repaint the decorations. This will be invoked when button hover changes or -+ // when there is a setting change (e.g. layout change). -+ void requestRepaint() const; -+ -+ // Button states -+ Button m_clicking = None; -+ Buttons m_hoveredButtons = None; -+ QDateTime m_lastButtonClick; -+ QPointF m_lastButtonClickPosition; -+ -+ // Configuration -+ QMap m_buttons; -+ QMap m_colors; -+ QMap m_icons; -+ std::unique_ptr m_font; -+ Placement m_placement = Right; -+ -+ QStaticText m_windowTitle; -+}; -+Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandAdwaitaDecoration::Buttons) -+ -+} // namespace QtWaylandClient -+ -+QT_END_NAMESPACE -+ -+#endif // QWAYLANDADWAITADECORATION_P_H diff --git a/qtwayland-adwaita-improve-border-painting.patch b/qtwayland-adwaita-improve-border-painting.patch new file mode 100644 index 0000000..26feae3 --- /dev/null +++ b/qtwayland-adwaita-improve-border-painting.patch @@ -0,0 +1,74 @@ +From 1304ba3997951a93939b04b9ad1df1e0a0c146eb Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Tue, 19 Nov 2024 19:15:08 +0100 +Subject: [PATCH] Adwaita decorations: improve border painting + +This is a backport of two changes pushed to QAdwaitaDecorations upstream +that further improve painting of border around the application window. +Using the 0.5px adjustment makes the border to look better as the line +better aligns. Also the separator between window and title doesn't need +to be painted separately. Co-authored with Lorenzo Bertini. + +Pick-to: 6.8 +Change-Id: Ib841073884dee58e599a173c259547416d2ed8ce +--- + +diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp +index 2d3575b..c32ab4a 100644 +--- a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp ++++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp +@@ -46,7 +46,6 @@ + static constexpr int ceShadowsWidth = 10; + static constexpr int ceTitlebarHeight = 38; + static constexpr int ceWindowBorderWidth = 1; +-static constexpr qreal ceTitlebarSeperatorWidth = 0.5; + + static QMap buttonMap = { + { QWaylandAdwaitaDecoration::CloseIcon, "window-close-symbolic"_L1 }, +@@ -129,36 +128,29 @@ + /* + * Titlebar and window border + */ +- const int titleBarWidth = surfaceRect.width() - margins().left() - margins().right(); + QPainterPath path; ++ const QPointF topLeft = { margins(ShadowsOnly).left() + 0.5, ++ margins(ShadowsOnly).top() - 0.5 }; ++ const int titleBarWidth = surfaceRect.width() - margins(ShadowsOnly).left() ++ - margins(ShadowsOnly).right() - 0.5; ++ const int borderRectHeight = ++ surfaceRect.height() - margins().top() - margins().bottom() + 0.5; + + // Maximized or tiled won't have rounded corners + if (waylandWindow()->windowStates() & Qt::WindowMaximized + || waylandWindow()->toplevelWindowTilingStates() != QWaylandWindow::WindowNoState) +- path.addRect(margins().left(), margins().bottom(), titleBarWidth, margins().top()); ++ path.addRect(QRectF(topLeft, QSizeF(titleBarWidth, margins().top()))); + else +- path.addRoundedRect(margins().left(), margins().bottom(), titleBarWidth, +- margins().top() + ceCornerRadius, ceCornerRadius, ceCornerRadius); ++ path.addRoundedRect(QRectF(topLeft, QSizeF(titleBarWidth, margins().top() + ceCornerRadius)), ++ ceCornerRadius, ceCornerRadius); + + p.save(); + p.setPen(color(Border)); + p.fillPath(path.simplified(), color(Background)); + p.drawPath(path); +- p.drawRect(margins().left(), margins().top(), titleBarWidth, surfaceRect.height() - margins().top() - margins().bottom()); ++ p.drawRect(QRectF(topLeft.x(), margins().top(), titleBarWidth, borderRectHeight)); + p.restore(); + +- +- /* +- * Titlebar separator +- */ +- p.save(); +- p.setPen(color(Border)); +- p.drawLine(QLineF(margins().left(), margins().top() - ceTitlebarSeperatorWidth, +- surfaceRect.width() - margins().right(), +- margins().top() - ceTitlebarSeperatorWidth)); +- p.restore(); +- +- + /* + * Window title + */ diff --git a/qtwayland-client-redo-management-of-tablet-proxies.patch b/qtwayland-client-redo-management-of-tablet-proxies.patch new file mode 100644 index 0000000..7f84097 --- /dev/null +++ b/qtwayland-client-redo-management-of-tablet-proxies.patch @@ -0,0 +1,260 @@ +From 24002ac6cbd01dbde4944b63c1f7c87ed2bd72b5 Mon Sep 17 00:00:00 2001 +From: David Redondo +Date: Fri, 22 Nov 2024 10:56:41 +0100 +Subject: [PATCH] client: Redo management of tablet object proxies + +Since 5af836aea3bb91a9f388e565415dc33b2fde4577 tools and pads can sometimes +be parented to tablets. When a tablet is unplugged sometimes the remove +event for the tablet can be sent before the remove events for the pad. +Deleting the tablet will also delete the pad but not the pad proxy, +resulting in a crash when the pad remove event is received. +This moves destruction of the wayland proxies which makes everything much +simpler as not every location that deletes those objects needs to call +destroy and fixes the described problem. + +Change-Id: I0aaeda3d3996251e411229b5e97aa1ce0bce43ac +Reviewed-by: David Edmundson +(cherry picked from commit 1f76835d1805d9b1c25c136a19c1101f19cc2259) +Reviewed-by: Qt Cherry-pick Bot +--- + src/client/qwaylandtabletv2.cpp | 24 +++++---- + src/client/qwaylandtabletv2_p.h | 4 +- + tests/auto/client/tabletv2/tst_tabletv2.cpp | 60 +++++++++++++++++---- + 3 files changed, 67 insertions(+), 21 deletions(-) + +diff --git a/src/client/qwaylandtabletv2.cpp b/src/client/qwaylandtabletv2.cpp +index ea2b225..67cee3b 100644 +--- a/src/client/qwaylandtabletv2.cpp ++++ b/src/client/qwaylandtabletv2.cpp +@@ -187,12 +187,6 @@ QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWa + + QWaylandTabletSeatV2::~QWaylandTabletSeatV2() + { +- for (auto *tablet : m_tablets) +- tablet->destroy(); +- for (auto *tool : m_tools) +- tool->destroy(); +- for (auto *pad : m_pads) +- pad->destroy(); + qDeleteAll(m_tablets); + qDeleteAll(m_tools); + qDeleteAll(m_deadTools); +@@ -254,6 +248,11 @@ QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet, const QString &seatN + d->seatName = seatName; + } + ++QWaylandTabletV2::~QWaylandTabletV2() ++{ ++ destroy(); ++} ++ + void QWaylandTabletV2::zwp_tablet_v2_name(const QString &name) + { + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); +@@ -292,7 +291,6 @@ void QWaylandTabletSeatV2::toolRemoved(QWaylandTabletToolV2 *tool) + + void QWaylandTabletV2::zwp_tablet_v2_removed() + { +- destroy(); + deleteLater(); + } + +@@ -316,7 +314,10 @@ QWaylandTabletToolV2::QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::z + #endif + } + +-QWaylandTabletToolV2::~QWaylandTabletToolV2() = default; ++QWaylandTabletToolV2::~QWaylandTabletToolV2() ++{ ++ destroy(); ++} + + void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type) + { +@@ -410,7 +411,6 @@ void QWaylandTabletToolV2::zwp_tablet_tool_v2_done() + + void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed() + { +- destroy(); + m_tabletSeat->toolRemoved(this); + } + +@@ -602,6 +602,11 @@ QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad) + { + } + ++QWaylandTabletPadV2::~QWaylandTabletPadV2() ++{ ++ destroy(); ++} ++ + void QWaylandTabletPadV2::zwp_tablet_pad_v2_path(const QString &path) + { + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); +@@ -621,7 +626,6 @@ void QWaylandTabletPadV2::zwp_tablet_pad_v2_done() + + void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed() + { +- destroy(); + delete this; + } + +diff --git a/src/client/qwaylandtabletv2_p.h b/src/client/qwaylandtabletv2_p.h +index 94b687e..f0d7cd1 100644 +--- a/src/client/qwaylandtabletv2_p.h ++++ b/src/client/qwaylandtabletv2_p.h +@@ -83,6 +83,7 @@ class Q_WAYLANDCLIENT_EXPORT QWaylandTabletV2 : public QPointingDevice, public Q + Q_OBJECT + public: + explicit QWaylandTabletV2(::zwp_tablet_v2 *tablet, const QString &seatName); ++ ~QWaylandTabletV2(); + + protected: + // callbacks which act as setters +@@ -98,7 +99,7 @@ class Q_WAYLANDCLIENT_EXPORT QWaylandTabletToolV2 : public QPointingDevice, publ + Q_OBJECT + public: + QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::zwp_tablet_tool_v2 *tool); +- ~QWaylandTabletToolV2() override; ++ ~QWaylandTabletToolV2(); + + void updateCursor(); + +@@ -181,6 +182,7 @@ class Q_WAYLANDCLIENT_EXPORT QWaylandTabletPadV2 : public QPointingDevice, publi + Q_OBJECT + public: + explicit QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad); ++ ~QWaylandTabletPadV2(); + + protected: + // void zwp_tablet_pad_v2_group(struct ::zwp_tablet_pad_group_v2 *pad_group) override; +diff --git a/tests/auto/client/tabletv2/tst_tabletv2.cpp b/tests/auto/client/tabletv2/tst_tabletv2.cpp +index 85df099..d5c2ccb 100644 +--- a/tests/auto/client/tabletv2/tst_tabletv2.cpp ++++ b/tests/auto/client/tabletv2/tst_tabletv2.cpp +@@ -186,9 +186,9 @@ public: + QList m_tablets; + QList m_tabletsWaitingForDestroy; + QList m_tools; +- QList m_toolsWaitingForDestroy; ++ QList m_toolsWaitingForDestroy; + QList m_pads; +- QList m_padsWaitingForDestroy; ++ QList m_padsWaitingForDestroy; + + protected: + void zwp_tablet_seat_v2_bind_resource(Resource *resource) override +@@ -274,11 +274,12 @@ void TabletV2::zwp_tablet_v2_destroy(QtWaylandServer::zwp_tablet_v2::Resource *r + + void TabletToolV2::sendRemoved() + { +- for (auto *resource : resourceMap()) ++ for (auto *resource : resourceMap()) { + zwp_tablet_tool_v2_send_removed(resource->handle); ++ m_tabletSeat->m_toolsWaitingForDestroy.append(resource); ++ } + bool removed = m_tabletSeat->m_tools.removeOne(this); + QVERIFY(removed); +- m_tabletSeat->m_toolsWaitingForDestroy.append(this); + } + + uint TabletToolV2::sendProximityIn(TabletV2 *tablet, Surface *surface) +@@ -333,26 +334,25 @@ uint TabletToolV2::sendFrame() + void TabletToolV2::zwp_tablet_tool_v2_destroy(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource) + { + if (m_tabletSeat) { +- bool removed = m_tabletSeat->m_toolsWaitingForDestroy.removeOne(this); +- QVERIFY(removed); ++ m_tabletSeat->m_toolsWaitingForDestroy.removeOne(resource); + } + wl_resource_destroy(resource->handle); + } + + void TabletPadV2::sendRemoved() + { +- for (auto *resource : resourceMap()) ++ for (auto *resource : resourceMap()) { + zwp_tablet_pad_v2_send_removed(resource->handle); ++ m_tabletSeat->m_padsWaitingForDestroy.append(resource); ++ } + bool removed = m_tabletSeat->m_pads.removeOne(this); + QVERIFY(removed); +- m_tabletSeat->m_padsWaitingForDestroy.append(this); + } + + void TabletPadV2::zwp_tablet_pad_v2_destroy(QtWaylandServer::zwp_tablet_pad_v2::Resource *resource) + { + if (m_tabletSeat) { +- bool removed = m_tabletSeat->m_padsWaitingForDestroy.removeOne(this); +- QVERIFY(removed); ++ m_tabletSeat->m_padsWaitingForDestroy.removeOne(resource); + } + wl_resource_destroy(resource->handle); + } +@@ -405,6 +405,8 @@ private slots: + void destroysTablet(); + void destroysTool(); + void destroysPad(); ++ void removeTabletBeforeTool(); ++ void removeTabletBeforePad(); + void proximityEvents(); + void moveEvent(); + void pointerType_data(); +@@ -502,12 +504,14 @@ void tst_tabletv2::destroysTool() + { + QCOMPOSITOR_TRY_VERIFY(tabletSeat()); + exec([&] { ++ tabletSeat()->addTablet(); + tabletSeat()->addTool(); + }); + QCOMPOSITOR_TRY_VERIFY(tabletTool()); + + exec([&] { + tabletTool()->sendRemoved(); ++ tablet()->sendRemoved(); + }); + + QCOMPOSITOR_TRY_VERIFY(!tabletTool()); +@@ -530,6 +534,42 @@ void tst_tabletv2::destroysPad() + QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_padsWaitingForDestroy.empty()); + } + ++void tst_tabletv2::removeTabletBeforeTool() ++{ ++ QCOMPOSITOR_TRY_VERIFY(tabletSeat()); ++ exec([&] { ++ tabletSeat()->addTablet(); ++ tabletSeat()->addTool(); ++ }); ++ QCOMPOSITOR_TRY_VERIFY(tablet()); ++ QCOMPOSITOR_TRY_VERIFY(tabletTool()); ++ ++ exec([&] { tablet()->sendRemoved(); }); ++ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_tabletsWaitingForDestroy.empty()); ++ ++ exec([&] { tabletTool()->sendRemoved(); }); ++ QCOMPOSITOR_TRY_VERIFY(!tabletTool()); ++ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_toolsWaitingForDestroy.empty()); ++} ++ ++void tst_tabletv2::removeTabletBeforePad() ++{ ++ QCOMPOSITOR_TRY_VERIFY(tabletSeat()); ++ exec([&] { ++ tabletSeat()->addTablet(); ++ tabletSeat()->addPad(); ++ }); ++ QCOMPOSITOR_TRY_VERIFY(tablet()); ++ QCOMPOSITOR_TRY_VERIFY(tabletPad()); ++ ++ exec([&] { tablet()->sendRemoved(); }); ++ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_tabletsWaitingForDestroy.empty()); ++ ++ exec([&] { tabletPad()->sendRemoved(); }); ++ QCOMPOSITOR_TRY_VERIFY(!tabletPad()); ++ QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_padsWaitingForDestroy.empty()); ++} ++ + void tst_tabletv2::proximityEvents() + { + ProximityFilter filter; diff --git a/qtwayland-update-wayland-xml-to-version-1.23.0.patch b/qtwayland-update-wayland-xml-to-version-1.23.0.patch new file mode 100644 index 0000000..e2acf59 --- /dev/null +++ b/qtwayland-update-wayland-xml-to-version-1.23.0.patch @@ -0,0 +1,491 @@ +From c2f61bc47baacf2e6a44c6c3c4e4cbf0abfa4095 Mon Sep 17 00:00:00 2001 +From: Liang Qi +Date: Wed, 3 Jul 2024 15:06:09 +0200 +Subject: Update wayland.xml to version 1.23.0 + +This updates only the protocol definition, implementations +will need additional commits to opt into using them. + +Change-Id: I5999e6dd75dfff7d904981fb1545d58c8b38ceb0 +Reviewed-by: David Edmundson +--- + src/3rdparty/protocol/wayland.xml | 215 +++++++++++++++++++++++++++----------- + 1 file changed, 156 insertions(+), 59 deletions(-) + +diff --git a/src/3rdparty/protocol/wayland/wayland.xml b/src/3rdparty/protocol/wayland/wayland.xml +index 10e039d6e..9418c62f3 100644 +--- a/src/3rdparty/protocol/wayland/wayland.xml ++++ b/src/3rdparty/protocol/wayland/wayland.xml +@@ -46,7 +46,7 @@ + compositor after the callback is fired and as such the client must not + attempt to use it after that point. + +- The callback_data passed in the callback is the event serial. ++ The callback_data passed in the callback is undefined and should be ignored. + + +@@ -212,7 +212,7 @@ + + + +- ++ + + The wl_shm_pool object encapsulates a piece of memory shared + between the compositor and client. Through the wl_shm_pool +@@ -262,17 +262,17 @@ + created, but using the new size. This request can only be + used to make the pool bigger. + +- This request only changes the amount of bytes that are mmapped +- by the server and does not touch the file corresponding to the +- file descriptor passed at creation time. It is the client's +- responsibility to ensure that the file is at least as big as +- the new pool size. ++ This request only changes the amount of bytes that are mmapped ++ by the server and does not touch the file corresponding to the ++ file descriptor passed at creation time. It is the client's ++ responsibility to ensure that the file is at least as big as ++ the new pool size. + + + + + +- ++ + + A singleton global object that provides support for shared + memory. +@@ -419,6 +419,21 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -442,6 +457,17 @@ + + + ++ ++ ++ ++ ++ ++ Using this request a client can tell the server that it is not going to ++ use the shm object anymore. ++ ++ Objects created via this interface remain unaffected. ++ ++ + + + +@@ -453,9 +479,11 @@ + client provides and updates the contents is defined by the buffer factory + interface. + +- If the buffer uses a format that has an alpha channel, the alpha channel +- is assumed to be premultiplied in the color channels unless otherwise +- specified. ++ Color channels are assumed to be electrical rather than optical (in other ++ words, encoded with a transfer function) unless otherwise specified. If ++ the buffer uses a format that has an alpha channel, the alpha channel is ++ assumed to be premultiplied into the electrical color channel values ++ (after transfer function encoding) unless otherwise specified. + + Note, because wl_buffer objects are created from multiple independent + factory interfaces, the wl_buffer interface is frozen at version 1. +@@ -847,6 +875,7 @@ + + + ++ + + + +@@ -868,7 +897,7 @@ + The icon surface is an optional (can be NULL) surface that + provides an icon to be moved around with the cursor. Initially, + the top-left corner of the icon surface is placed at the cursor +- hotspot, but subsequent wl_surface.attach request can move the ++ hotspot, but subsequent wl_surface.offset requests can move the + relative position. Attach requests must be confirmed with + wl_surface.commit as usual. The icon surface is given the role of + a drag-and-drop icon. If the icon surface already has another role, +@@ -876,6 +905,10 @@ + + The input region is ignored for wl_surfaces with the role of a + drag-and-drop icon. ++ ++ The given source may not be used in any further set_selection or ++ start_drag requests. Attempting to reuse a previously-used source ++ may send a used_source error. + + + +@@ -889,6 +922,10 @@ + to the data from the source on behalf of the client. + + To unset the selection, set the source to NULL. ++ ++ The given source may not be used in any further set_selection or ++ start_drag requests. Attempting to reuse a previously-used source ++ may send a used_source error. + + + +@@ -1411,7 +1448,7 @@ + + + ++ summary="surface was destroyed before its role object"/> + + + +@@ -1440,9 +1477,9 @@ + + When the bound wl_surface version is 5 or higher, passing any + non-zero x or y is a protocol violation, and will result in an +- 'invalid_offset' error being raised. The x and y arguments are ignored +- and do not change the pending state. To achieve equivalent semantics, +- use wl_surface.offset. ++ 'invalid_offset' error being raised. The x and y arguments are ignored ++ and do not change the pending state. To achieve equivalent semantics, ++ use wl_surface.offset. + + Surface contents are double-buffered state, see wl_surface.commit. + +@@ -1479,6 +1516,13 @@ + + If wl_surface.attach is sent with a NULL wl_buffer, the + following wl_surface.commit will remove the surface content. ++ ++ If a pending wl_buffer has been destroyed, the result is not specified. ++ Many compositors are known to remove the surface content on the following ++ wl_surface.commit, but this behaviour is not universal. Clients seeking to ++ maximise compatibility should not destroy pending buffers and should ++ ensure that they explicitly remove content from surfaces, even after ++ destroying buffers. + + +@@ -1618,16 +1662,18 @@ + + Surface state (input, opaque, and damage regions, attached buffers, + etc.) is double-buffered. Protocol requests modify the pending state, +- as opposed to the current state in use by the compositor. A commit +- request atomically applies all pending state, replacing the current +- state. After commit, the new pending state is as documented for each +- related request. ++ as opposed to the active state in use by the compositor. + +- On commit, a pending wl_buffer is applied first, and all other state +- second. This means that all coordinates in double-buffered state are +- relative to the new wl_buffer coming into use, except for +- wl_surface.attach itself. If there is no pending wl_buffer, the +- coordinates are relative to the current surface contents. ++ A commit request atomically creates a content update from the pending ++ state, even if the pending state has not been touched. The content ++ update is placed in a queue until it becomes active. After commit, the ++ new pending state is as documented for each related request. ++ ++ When the content update is applied, the wl_buffer is applied before all ++ other state. This means that all coordinates in double-buffered state ++ are relative to the newly attached wl_buffers, except for ++ wl_surface.attach itself. If there is no newly attached wl_buffer, the ++ coordinates are relative to the previous content update. + + All requests that need a commit to become effective are documented + to affect double-buffered state. +@@ -1666,10 +1712,12 @@ + + + +- This request sets an optional transformation on how the compositor +- interprets the contents of the buffer attached to the surface. The +- accepted values for the transform parameter are the values for +- wl_output.transform. ++ This request sets the transformation that the client has already applied ++ to the content of the buffer. The accepted values for the transform ++ parameter are the values for wl_output.transform. ++ ++ The compositor applies the inverse of this transformation whenever it ++ uses the buffer contents. + + Buffer transform is double-buffered state, see wl_surface.commit. + +@@ -1725,11 +1773,11 @@ + a buffer that is larger (by a factor of scale in each dimension) + than the desired surface size. + +- If scale is not positive the invalid_scale protocol error is ++ If scale is not greater than 0 the invalid_scale protocol error is + raised. + + ++ summary="scale for interpreting buffer contents"/> + + + +@@ -1802,10 +1850,15 @@ + This event indicates the preferred buffer scale for this surface. It is + sent whenever the compositor's preference changes. + ++ Before receiving this event the preferred buffer scale for this surface ++ is 1. ++ + It is intended that scaling aware clients use this event to scale their + content and use wl_surface.set_buffer_scale to indicate the scale they + have rendered with. This allows clients to supply a higher detail + buffer. ++ ++ The compositor shall emit a scale value greater than 0. + + + +@@ -1815,9 +1868,12 @@ + This event indicates the preferred buffer transform for this surface. + It is sent whenever the compositor's preference changes. + +- It is intended that transform aware clients use this event to apply the +- transform to their content and use wl_surface.set_buffer_transform to +- indicate the transform they have rendered with. ++ Before receiving this event the preferred buffer transform for this ++ surface is normal. ++ ++ Applying this transformation to the surface buffer contents and using ++ wl_surface.set_buffer_transform might allow the compositor to use the ++ surface buffer more efficiently. + + +@@ -1992,9 +2048,9 @@ + where (x, y) are the coordinates of the pointer location, in + surface-local coordinates. + +- On surface.attach requests to the pointer surface, hotspot_x ++ On wl_surface.offset requests to the pointer surface, hotspot_x + and hotspot_y are decremented by the x and y parameters +- passed to the request. Attach must be confirmed by ++ passed to the request. The offset must be applied by + wl_surface.commit as usual. + + The hotspot can also be updated by passing the currently set +@@ -2248,7 +2304,7 @@ + + + +- ++ + + Discrete step information for scroll and other axes. + +@@ -2374,6 +2430,16 @@ + + The wl_keyboard interface represents one or more keyboards + associated with a seat. ++ ++ Each wl_keyboard has the following logical state: ++ ++ - an active surface (possibly null), ++ - the keys currently logically down, ++ - the active modifiers, ++ - the active group. ++ ++ By default, the active surface is null, the keys currently logically down ++ are empty, the active modifiers and the active group are 0. + + + +@@ -2408,10 +2474,15 @@ + + The compositor must send the wl_keyboard.modifiers event after this + event. ++ ++ In the wl_keyboard logical state, this event sets the active surface to ++ the surface argument and the keys currently logically down to the keys ++ in the keys argument. The compositor must not send this event if the ++ wl_keyboard already had an active surface immediately before this event. + + + +- ++ + + + +@@ -2422,8 +2493,10 @@ + The leave notification is sent before the enter notification + for the new focus. + +- After this event client must assume that all keys, including modifiers, +- are lifted and also it must stop key repeating if there's some going on. ++ In the wl_keyboard logical state, this event resets all values to their ++ defaults. The compositor must not send this event if the active surface ++ of the wl_keyboard was not equal to the surface argument immediately ++ before this event. + + + +@@ -2448,6 +2521,15 @@ + + If this event produces a change in modifiers, then the resulting + wl_keyboard.modifiers event must be sent after this event. ++ ++ In the wl_keyboard logical state, this event adds the key to the keys ++ currently logically down (if the state argument is pressed) or removes ++ the key from the keys currently logically down (if the state argument is ++ released). The compositor must not send this event if the wl_keyboard ++ did not have an active surface immediately before this event. The ++ compositor must not send this event if state is pressed (resp. released) ++ and the key was already logically down (resp. was not logically down) ++ immediately before this event. + + + +@@ -2459,6 +2541,17 @@ + + Notifies clients that the modifier and/or group state has + changed, and it should update its local state. ++ ++ The compositor may send this event without a surface of the client ++ having keyboard focus, for example to tie modifier information to ++ pointer focus instead. If a modifier event with pressed modifiers is sent ++ without a prior enter event, the client can assume the modifier state is ++ valid until it receives the next wl_keyboard.modifiers event. In order to ++ reset the modifier state again, the compositor can send a ++ wl_keyboard.modifiers event with no pressed modifiers. ++ ++ In the wl_keyboard logical state, this event updates the modifiers and ++ group. + + + +@@ -2566,6 +2659,8 @@ + currently active on this client's surface. The client is + responsible for finalizing the touch points, future touch points on + this surface may reuse the touch point ID. ++ ++ No frame event is required after the cancel event. + + + +@@ -2665,10 +2760,9 @@ + + + +- +- This describes the transform that a compositor will apply to a +- surface to compensate for the rotation or mirroring of an +- output device. ++ ++ This describes transformations that clients and compositors apply to ++ buffer contents. + + The flipped values correspond to an initial flip around a + vertical axis followed by rotation. +@@ -2700,6 +2794,10 @@ + The geometry event will be followed by a done event (starting from + version 2). + ++ Clients should use wl_surface.preferred_buffer_transform instead of the ++ transform advertised by this event to find the preferred buffer ++ transform to use for a surface. ++ + Note: wl_output only advertises partial information about the output + position and identification. Some compositors, for instance those not + implementing a desktop-style output layout or those exposing virtual +@@ -2722,7 +2820,7 @@ + + ++ summary="additional transformation applied to buffer contents during presentation"/> + + + +@@ -2795,8 +2893,9 @@ + This event contains scaling geometry information + that is not in the geometry event. It may be sent after + binding the output object or if the output scale changes +- later. If it is not sent, the client should assume a +- scale of 1. ++ later. The compositor will emit a non-zero, positive ++ value for scale. If it is not sent, the client should ++ assume a scale of 1. + + A scale larger than 1 means that the compositor will + automatically scale surface buffers by this amount +@@ -2804,12 +2903,9 @@ + displays where applications rendering at the native + resolution would be too small to be legible. + +- It is intended that scaling aware clients track the +- current output of a surface, and if it is on a scaled +- output it should use wl_surface.set_buffer_scale with +- the scale of the output. That way the compositor can +- avoid scaling the surface, and the client can supply +- a higher detail image. ++ Clients should use wl_surface.preferred_buffer_scale ++ instead of this event to find the preferred buffer ++ scale to use for a surface. + + The scale event will be followed by a done event. + +@@ -3035,6 +3131,11 @@ + + If the parent wl_surface object is destroyed, the sub-surface is + unmapped. ++ ++ A sub-surface never has the keyboard focus of any seat. ++ ++ The wl_surface.offset request is ignored: clients must use set_position ++ instead to move the sub-surface. + + + +@@ -3060,9 +3161,7 @@ + surface area. Negative values are allowed. + + The scheduled coordinates will take effect whenever the state of the +- parent surface is applied. When this happens depends on whether the +- parent surface is in synchronized mode or not. See +- wl_subsurface.set_sync and wl_subsurface.set_desync for details. ++ parent surface is applied. + + If more than one set_position request is invoked by the client before + the commit of the parent surface, the position of a new request always +@@ -3085,9 +3184,7 @@ + The z-order is double-buffered. Requests are handled in order and + applied immediately to a pending state. The final pending state is + copied to the active state the next time the state of the parent +- surface is applied. When this happens depends on whether the parent +- surface is in synchronized mode or not. See wl_subsurface.set_sync and +- wl_subsurface.set_desync for details. ++ surface is applied. + + A new sub-surface is initially added as the top-most in the stack + of its siblings and parent. +-- +cgit v1.2.3 + diff --git a/sources b/sources index 1a343af..31e6556 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qtwayland-everywhere-src-6.7.1.tar.xz) = 9128ecd32319fd2ca154cb3d52726c80f96e2c906f9cf80cd67a3f91a4db49a853948489ec712061c6ef2c5abe70cd850c37f947659199678ac1482c77485a76 +SHA512 (qtwayland-everywhere-src-6.8.1.tar.xz) = 34885910532f6049cac09846aa4295e2ce82a1e6af2d3256391c835a406f0c6f679e9c94c06ca8921acf23036ccc747812631a91dc1f355ab20aafd8836e7312