diff options
author | David Edmundson <davidedmundson@kde.org> | 2022-03-23 15:35:50 +0000 |
---|---|---|
committer | David Edmundson <davidedmundson@kde.org> | 2022-12-07 20:36:40 +0000 |
commit | 23d3fc712cdf7fc0a4ff837082de9ba773e8605c (patch) | |
tree | 5b4866d8c3c0b7eb524a8f70050b3c2868b6ba01 /src/client | |
parent | 5bae5debdf94f6bbec61f4ff50ecbf3c1231e0e3 (diff) |
Implement fractional_scale_v1 and wp_viewport
This allows compositors to hint a non-integer scale to use on a window
which we can hook to Qt's existing fractional scaling support.
The viewport is used to communicate the relationship between buffer size
and logical size to the compositor. It is a non-integer alternative to
wl_buffer_scale
Change-Id: I1a850f1bcd40e8d04e241e18a538b11f18bc671c
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: David Edmundson <davidedmundson@kde.org>
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/client/qwaylanddisplay.cpp | 17 | ||||
-rw-r--r-- | src/client/qwaylanddisplay_p.h | 8 | ||||
-rw-r--r-- | src/client/qwaylandfractionalscale.cpp | 36 | ||||
-rw-r--r-- | src/client/qwaylandfractionalscale_p.h | 50 | ||||
-rw-r--r-- | src/client/qwaylandviewport.cpp | 35 | ||||
-rw-r--r-- | src/client/qwaylandviewport_p.h | 42 | ||||
-rw-r--r-- | src/client/qwaylandwindow.cpp | 61 | ||||
-rw-r--r-- | src/client/qwaylandwindow_p.h | 7 |
9 files changed, 253 insertions, 7 deletions
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 820c9eada..47312010f 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -32,6 +32,7 @@ qt_internal_add_module(WaylandClient qwaylanddecorationplugin.cpp qwaylanddecorationplugin_p.h qwaylanddisplay.cpp qwaylanddisplay_p.h qwaylandextendedsurface.cpp qwaylandextendedsurface_p.h + qwaylandfractionalscale.cpp qwaylandfractionalscale_p.h qwaylandinputcontext.cpp qwaylandinputcontext_p.h qwaylandtextinputv1.cpp qwaylandtextinputv1_p.h qwaylandtextinputv2.cpp qwaylandtextinputv2_p.h @@ -50,6 +51,7 @@ qt_internal_add_module(WaylandClient qwaylandsubsurface.cpp qwaylandsubsurface_p.h qwaylandsurface.cpp qwaylandsurface_p.h qwaylandtouch.cpp qwaylandtouch_p.h + qwaylandviewport.cpp qwaylandviewport_p.h qwaylandwindow.cpp qwaylandwindow_p.h qwaylandwindowmanagerintegration.cpp qwaylandwindowmanagerintegration_p.h shellintegration/qwaylandclientshellapi_p.h @@ -88,6 +90,8 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wayland.xml ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wp-primary-selection-unstable-v1.xml ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/fractional-scale-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/viewporter.xml ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-key-unstable-v1.xml ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-text-input-method-unstable-v1.xml ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-windowmanager.xml diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 18eb71c36..52ea64929 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -50,6 +50,8 @@ #include <QtWaylandClient/private/qwayland-text-input-unstable-v4-wip.h> #include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h> #include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h> +#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> +#include <QtWaylandClient/private/qwayland-viewporter.h> #include <QtCore/private/qcore_unix_p.h> @@ -294,6 +296,17 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface()); } +::wp_viewport *QWaylandDisplay::createViewport(QWaylandWindow *window) +{ + if (!mViewporter) { + qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor."; + return nullptr; + } + + Q_ASSERT(window->wlSurface()); + return mViewporter->get_viewport(window->wlSurface()); +} + QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const { return mWaylandIntegration->shellIntegration(); @@ -597,6 +610,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mXdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version)); for (auto *screen : std::as_const(mWaitingScreens)) screen->initXdgOutput(xdgOutputManager()); + } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) { + mFractionalScaleManager.reset(new QtWayland::wp_fractional_scale_manager_v1(registry, id, 1)); + } else if (interface == QLatin1String("wp_viewporter")) { + mViewporter.reset(new QtWayland::wp_viewporter(registry, id, qMin(1u, version))); } mGlobals.append(RegistryGlobal(id, interface, version, registry)); diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 473016f1e..640f33b8e 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -36,6 +36,7 @@ #endif struct wl_cursor_image; +struct wp_viewport; QT_BEGIN_NAMESPACE @@ -50,6 +51,8 @@ namespace QtWayland { class zwp_text_input_manager_v2; class zwp_text_input_manager_v4; class qt_text_input_method_manager_v1; + class wp_viewporter; + class wp_fractional_scale_manager_v1; } namespace QtWaylandClient { @@ -110,6 +113,7 @@ public: struct wl_surface *createSurface(void *handle); struct ::wl_region *createRegion(const QRegion &qregion); struct ::wl_subsurface *createSubSurface(QWaylandWindow *window, QWaylandWindow *parent); + struct ::wp_viewport *createViewport(QWaylandWindow *window); QWaylandShellIntegration *shellIntegration() const; QWaylandClientBufferIntegration *clientBufferIntegration() const; @@ -146,6 +150,8 @@ public: QtWayland::zwp_text_input_manager_v4 *textInputManagerv4() const { return mTextInputManagerv4.data(); } QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); } QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); } + QtWayland::wp_fractional_scale_manager_v1 *fractionalScaleManager() const { return mFractionalScaleManager.data(); } + QtWayland::wp_viewporter *viewporter() const { return mViewporter.data(); } struct RegistryGlobal { uint32_t id; @@ -265,6 +271,8 @@ private: QScopedPointer<QtWayland::zwp_text_input_manager_v4> mTextInputManagerv4; QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration; QScopedPointer<QWaylandXdgOutputManagerV1> mXdgOutputManager; + QScopedPointer<QtWayland::wp_viewporter> mViewporter; + QScopedPointer<QtWayland::wp_fractional_scale_manager_v1> mFractionalScaleManager; int mFd = -1; int mWritableNotificationFd = -1; QList<RegistryGlobal> mGlobals; diff --git a/src/client/qwaylandfractionalscale.cpp b/src/client/qwaylandfractionalscale.cpp new file mode 100644 index 000000000..324a0b729 --- /dev/null +++ b/src/client/qwaylandfractionalscale.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandfractionalscale_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandFractionalScale::QWaylandFractionalScale(struct ::wp_fractional_scale_v1 *object) + : QtWayland::wp_fractional_scale_v1(object) +{} + + +QWaylandFractionalScale::~QWaylandFractionalScale() +{ + destroy(); +} + +qreal QWaylandFractionalScale::preferredScale() const +{ + return mPreferredScale; +} + +void QWaylandFractionalScale::wp_fractional_scale_v1_preferred_scale(uint scale) +{ + qreal preferredScale = scale / 120.0; // hardcoded denominator determined in the spec + if (preferredScale != mPreferredScale) { + mPreferredScale = preferredScale; + Q_EMIT preferredScaleChanged(); + } +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandfractionalscale_p.h b/src/client/qwaylandfractionalscale_p.h new file mode 100644 index 000000000..0483eb338 --- /dev/null +++ b/src/client/qwaylandfractionalscale_p.h @@ -0,0 +1,50 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDFRACTIONALSCALE_P_H +#define QWAYLANDFRACTIONALSCALE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> +#include <QtWaylandClient/qtwaylandclientglobal.h> + +#include <QObject> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandFractionalScale : public QObject, public QtWayland::wp_fractional_scale_v1 +{ + Q_OBJECT +public: + explicit QWaylandFractionalScale(struct ::wp_fractional_scale_v1 *object); + ~QWaylandFractionalScale(); + + qreal preferredScale() const; + +Q_SIGNALS: + void preferredScaleChanged(); + +protected: + void wp_fractional_scale_v1_preferred_scale(uint scale) override; + +private: + qreal mPreferredScale = 1.0; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/client/qwaylandviewport.cpp b/src/client/qwaylandviewport.cpp new file mode 100644 index 000000000..3252718c0 --- /dev/null +++ b/src/client/qwaylandviewport.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandviewport_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandViewport::QWaylandViewport(::wp_viewport *viewport) + : QtWayland::wp_viewport(viewport) +{ +} + +QWaylandViewport::~QWaylandViewport() +{ + destroy(); +} + +void QWaylandViewport::setSource(const QRectF &source) +{ + set_source(wl_fixed_from_double(source.x()), + wl_fixed_from_double(source.y()), + wl_fixed_from_double(source.width()), + wl_fixed_from_double(source.height())); +} + +void QWaylandViewport::setDestination(const QSize &destination) +{ + set_destination(destination.width(), destination.height()); +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandviewport_p.h b/src/client/qwaylandviewport_p.h new file mode 100644 index 000000000..e1dfeb3a7 --- /dev/null +++ b/src/client/qwaylandviewport_p.h @@ -0,0 +1,42 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDVIEWPORT_P_H +#define QWAYLANDVIEWPORT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandClient/private/qwayland-viewporter.h> +#include <QtWaylandClient/qtwaylandclientglobal.h> + +#include <QRect> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandViewport : public QtWayland::wp_viewport +{ +public: + explicit QWaylandViewport(::wp_viewport *viewport); + ~QWaylandViewport() override; + + void setSource(const QRectF &source); + void setDestination(const QSize &destination); + +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDVIEWPORT_P_H diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index f4d49c84d..56b9af282 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -7,6 +7,7 @@ #include "qwaylanddisplay_p.h" #include "qwaylandsurface_p.h" #include "qwaylandinputdevice_p.h" +#include "qwaylandfractionalscale_p.h" #include "qwaylandscreen_p.h" #include "qwaylandshellsurface_p.h" #include "qwaylandsubsurface_p.h" @@ -16,6 +17,7 @@ #include "qwaylanddecorationfactory_p.h" #include "qwaylandshmbackingstore_p.h" #include "qwaylandshellintegration_p.h" +#include "qwaylandviewport_p.h" #include <QtCore/QFileInfo> #include <QtCore/QPointer> @@ -29,6 +31,8 @@ #include <QtCore/QDebug> #include <QtCore/QThread> +#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -91,6 +95,26 @@ void QWaylandWindow::initWindow() initializeWlSurface(); } + if (mDisplay->fractionalScaleManager() && qApp->highDpiScaleFactorRoundingPolicy() == Qt::HighDpiScaleFactorRoundingPolicy::PassThrough) { + mFractionalScale.reset(new QWaylandFractionalScale(mDisplay->fractionalScaleManager()->get_fractional_scale(mSurface->object()))); + + mScale = mFractionalScale->preferredScale(); + connect(mFractionalScale.data(), &QWaylandFractionalScale::preferredScaleChanged, this, [this]() { + if (mScale == mFractionalScale->preferredScale()) { + return; + } + mScale = mFractionalScale->preferredScale(); + ensureSize(); + if (mViewport) + updateViewport(); + if (isExposed()) { + // redraw at the new DPR + window()->requestUpdate(); + sendExposeEvent(QRect(QPoint(), geometry().size())); + } + }); + } + if (shouldCreateSubSurface()) { Q_ASSERT(!mSubSurfaceWindow); @@ -146,11 +170,17 @@ void QWaylandWindow::initWindow() } } + if (display()->viewporter() && !window()->flags().testFlag(Qt::BypassWindowManagerHint)) { + mViewport.reset(new QWaylandViewport(display()->createViewport(this))); + } + // Enable high-dpi rendering. Scale() returns the screen scale factor and will // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale() // to inform the compositor that high-resolution buffers will be provided. - if (mSurface->version() >= 3) - mSurface->set_buffer_scale(mScale); + if (mViewport) + updateViewport(); + else if (mSurface->version() >= 3) + mSurface->set_buffer_scale(std::ceil(scale())); setWindowFlags(window()->flags()); QRect geometry = windowGeometry(); @@ -220,6 +250,8 @@ void QWaylandWindow::reset() mShellSurface = nullptr; delete mSubSurfaceWindow; mSubSurfaceWindow = nullptr; + mViewport.reset(); + mFractionalScale.reset(); if (mSurface) { emit wlSurfaceDestroyed(); @@ -322,6 +354,8 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect) QPlatformWindow::setGeometry(QRect(rect.x(), rect.y(), qBound(minimum.width(), rect.width(), maximum.width()), qBound(minimum.height(), rect.height(), maximum.height()))); + if (mViewport) + updateViewport(); if (mSubSurfaceWindow) { QMargins m = static_cast<QWaylandWindow *>(QPlatformWindow::parent())->clientSideMargins(); @@ -370,6 +404,12 @@ void QWaylandWindow::setGeometry(const QRect &r) setOpaqueArea(QRect(QPoint(0, 0), rect.size())); } +void QWaylandWindow::updateViewport() +{ + if (!surfaceSize().isEmpty()) + mViewport->setDestination(surfaceSize()); +} + void QWaylandWindow::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins) { QMargins margins = clientSideMargins(); @@ -1242,6 +1282,7 @@ void QWaylandWindow::handleScreensChanged() return; QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); + mLastReportedScreen = newScreen; if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup && window()->type() != Qt::ToolTip @@ -1251,11 +1292,19 @@ void QWaylandWindow::handleScreensChanged() setGeometry(geometry); } - int scale = newScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(newScreen)->scale(); + if (mFractionalScale) + return; + + int scale = mLastReportedScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(mLastReportedScreen)->scale(); + if (scale != mScale) { mScale = scale; - if (mSurface && mSurface->version() >= 3) - mSurface->set_buffer_scale(mScale); + if (mSurface) { + if (mViewport) + updateViewport(); + else if (mSurface->version() >= 3) + mSurface->set_buffer_scale(std::ceil(mScale)); + } ensureSize(); } } @@ -1558,4 +1607,4 @@ void QWaylandWindow::closeChildPopups() { QT_END_NAMESPACE -#include "moc_qwaylandwindow_p.cpp" +#include "qwaylandwindow.moc" diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index 0f8c55151..6531606a7 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -53,6 +53,8 @@ class QWaylandPointerEvent; class QWaylandPointerGestureSwipeEvent; class QWaylandPointerGesturePinchEvent; class QWaylandSurface; +class QWaylandFractionalScale; +class QWaylandViewport; class Q_WAYLANDCLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow { @@ -233,6 +235,8 @@ protected: // mSurface can be written by the main thread. Other threads should claim a read lock for access mutable QReadWriteLock mSurfaceLock; QScopedPointer<QWaylandSurface> mSurface; + QScopedPointer<QWaylandFractionalScale> mFractionalScale; + QScopedPointer<QWaylandViewport> mViewport; QWaylandShellSurface *mShellSurface = nullptr; QWaylandSubSurface *mSubSurfaceWindow = nullptr; @@ -284,7 +288,7 @@ protected: bool mSentInitialResize = false; QPoint mOffset; - int mScale = 1; + qreal mScale = 1; QPlatformScreen *mLastReportedScreen = nullptr; QIcon mWindowIcon; @@ -314,6 +318,7 @@ private: QPlatformScreen *calculateScreenFromSurfaceEvents() const; void setOpaqueArea(const QRegion &opaqueArea); bool isOpaque() const; + void updateViewport(); void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleScreensChanged(); |