From 2df9d22a087f37dd48cffc53e955b8605dfeed8d Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 11 Jan 2019 22:48:04 +0000 Subject: [PATCH] Add high DPI support to internal KWin windows Summary: So far we didn't try to do high DPI on kwin internal windows, such as the user context menu and tab bars and whatever. Due to wayland scaling they were the correct phyiscal size but upscaled. This patch fixes our QPA to enable Qt's high-dpi support. BUG: 402853 Note icons are still low res. This is because the global QGuiApplication::devicePixelRatio which is the max of all connected screens is static for the duration of the app. QIcon uses this when determining the DPR to use. This will require a Qt change. Test Plan: Ran at 2x on my normal DPI screen (as that's easier to see anything) * User action menu is high DPI * Window deco tooltips are still fine * Tab switcher is high DPI * Overlay in present windows Desktop grid are still ok Reviewers: #kwin Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18042 --- autotests/integration/internal_window.cpp | 26 +++++++++++++++++++++++ autotests/integration/kwin_wayland_test.h | 4 ++-- main_wayland.cpp | 2 +- plugins/qpa/backingstore.cpp | 15 ++++++++++--- plugins/qpa/backingstore.h | 1 + plugins/qpa/window.cpp | 26 +++++++++++++++++++---- plugins/qpa/window.h | 4 ++++ shell_client.cpp | 4 +--- 8 files changed, 69 insertions(+), 13 deletions(-) diff --git a/autotests/integration/internal_window.cpp b/autotests/integration/internal_window.cpp index 0830452087..bacdc2030a 100644 --- a/autotests/integration/internal_window.cpp +++ b/autotests/integration/internal_window.cpp @@ -33,6 +33,8 @@ along with this program. If not, see . #include #include +#include + #include using namespace KWayland::Client; @@ -64,6 +66,7 @@ private Q_SLOTS: void testModifierClickUnrestrictedMove(); void testModifierScroll(); void testPopup(); + void testScale(); }; class HelperWindow : public QRasterWindow @@ -687,6 +690,29 @@ void InternalWindowTest::testPopup() QCOMPARE(internalClient->isPopupWindow(), true); } +void InternalWindowTest::testScale() +{ + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, + Q_ARG(int, 2), + Q_ARG(QVector, QVector({QRect(0,0,1280, 1024), QRect(1280/2, 0, 1280, 1024)})), + Q_ARG(QVector, QVector({2,2}))); + + QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(clientAddedSpy.isValid()); + HelperWindow win; + win.setGeometry(0, 0, 100, 100); + win.setFlags(win.flags() | Qt::Popup); + win.show(); + QCOMPARE(win.devicePixelRatio(), 2.0); + QVERIFY(clientAddedSpy.wait()); + QCOMPARE(clientAddedSpy.count(), 1); + auto internalClient = clientAddedSpy.first().first().value(); + QCOMPARE(internalClient->surface()->scale(), 2); + + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); +} + + } WAYLANDTEST_MAIN(KWin::InternalWindowTest) diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h index 4c7b183c3d..6d62e0a7c6 100644 --- a/autotests/integration/kwin_wayland_test.h +++ b/autotests/integration/kwin_wayland_test.h @@ -207,9 +207,9 @@ int main(int argc, char *argv[]) \ } #ifdef NO_XWAYLAND -#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling), KWin::Application::OperationModeWaylandOnly) +#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps), KWin::Application::OperationModeWaylandOnly) #else -#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling), KWin::Application::OperationModeXwayland) +#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps), KWin::Application::OperationModeXwayland) #endif #endif diff --git a/main_wayland.cpp b/main_wayland.cpp index f2cd475179..cd8a98c9f9 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -553,7 +553,7 @@ int main(int argc, char * argv[]) qunsetenv("QT_DEVICE_PIXEL_RATIO"); qputenv("QT_IM_MODULE", "qtvirtualkeyboard"); qputenv("QSG_RENDER_LOOP", "basic"); - QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); KWin::ApplicationWayland a(argc, argv); a.setupTranslator(); // reset QT_QPA_PLATFORM to a sane value for any processes started from KWin diff --git a/plugins/qpa/backingstore.cpp b/plugins/qpa/backingstore.cpp index 2ffe084cb8..a2f4fd2ac4 100644 --- a/plugins/qpa/backingstore.cpp +++ b/plugins/qpa/backingstore.cpp @@ -47,6 +47,7 @@ BackingStore::BackingStore(QWindow *w, KWayland::Client::ShmPool *shm) } const QSize size = m_backBuffer.size(); m_backBuffer = QImage(b->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); + m_backBuffer.setDevicePixelRatio(scale()); } ); } @@ -61,7 +62,7 @@ QPaintDevice *BackingStore::paintDevice() void BackingStore::resize(const QSize &size, const QRegion &staticContents) { Q_UNUSED(staticContents) - m_size = size; + m_size = size * scale(); if (!m_buffer) { return; } @@ -73,13 +74,15 @@ void BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &o { Q_UNUSED(region) Q_UNUSED(offset) - auto s = static_cast(window->handle())->surface(); + + auto w = static_cast(window->handle()); + auto s = w->surface(); if (!s) { return; } s->attachBuffer(m_buffer); // TODO: proper damage region - s->damage(QRect(QPoint(0, 0), m_backBuffer.size())); + s->damage(QRect(QPoint(0, 0), m_backBuffer.size() / scale())); s->commit(KWayland::Client::Surface::CommitFlag::None); waylandServer()->internalClientConection()->flush(); waylandServer()->dispatch(); @@ -108,6 +111,7 @@ void BackingStore::beginPaint(const QRegion&) auto b = m_buffer.toStrongRef(); b->setUsed(true); m_backBuffer = QImage(b->address(), m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied); + m_backBuffer.setDevicePixelRatio(scale()); if (oldBuffer) { b->copy(oldBuffer->address()); } else { @@ -115,5 +119,10 @@ void BackingStore::beginPaint(const QRegion&) } } +int BackingStore::scale() const +{ + return static_cast(window()->handle())->scale(); +} + } } diff --git a/plugins/qpa/backingstore.h b/plugins/qpa/backingstore.h index 0e00de6357..53c25a7788 100644 --- a/plugins/qpa/backingstore.h +++ b/plugins/qpa/backingstore.h @@ -48,6 +48,7 @@ public: void beginPaint(const QRegion &) override; private: + int scale() const; KWayland::Client::ShmPool *m_shm; QWeakPointer m_buffer; QImage m_backBuffer; diff --git a/plugins/qpa/window.cpp b/plugins/qpa/window.cpp index 37ce0e7254..04deadbe8b 100644 --- a/plugins/qpa/window.cpp +++ b/plugins/qpa/window.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . #define WL_EGL_PLATFORM 1 #include "integration.h" #include "window.h" +#include "screens.h" #include "../../shell_client.h" #include "../../wayland_server.h" #include @@ -44,7 +45,10 @@ Window::Window(QWindow *window, KWayland::Client::Surface *surface, KWayland::Cl , m_shellSurface(shellSurface) , m_windowId(++s_windowId) , m_integration(integration) + , m_scale(screens()->maxScale()) { + m_surface->setScale(m_scale); + QObject::connect(m_surface, &QObject::destroyed, window, [this] { m_surface = nullptr;}); QObject::connect(m_shellSurface, &QObject::destroyed, window, [this] { m_shellSurface = nullptr;}); waylandServer()->internalClientConection()->flush(); @@ -94,14 +98,17 @@ void Window::setGeometry(const QRect &rect) if (rect.height() != oldRect.height()) { emit window()->heightChanged(rect.height()); } + + const QSize nativeSize = rect.size() * m_scale; + if (m_contentFBO) { - if (m_contentFBO->width() != geometry().width() || m_contentFBO->height() != geometry().height()) { + if (m_contentFBO->size() != nativeSize) { m_resized = true; } } #if HAVE_WAYLAND_EGL if (m_eglWaylandWindow) { - wl_egl_window_resize(m_eglWaylandWindow, geometry().width(), geometry().height(), 0, 0); + wl_egl_window_resize(m_eglWaylandWindow, nativeSize.width(), nativeSize.height(), 0, 0); } #endif QWindowSystemInterface::handleGeometryChange(window(), geometry()); @@ -124,7 +131,7 @@ void Window::unmap() void Window::createEglSurface(EGLDisplay dpy, EGLConfig config) { #if HAVE_WAYLAND_EGL - const QSize size = window()->size(); + const QSize size = window()->size() * m_scale; m_eglWaylandWindow = wl_egl_window_create(*m_surface, size.width(), size.height()); if (!m_eglWaylandWindow) { return; @@ -158,7 +165,8 @@ void Window::createFBO() if (m_contentFBO && r.size().isEmpty()) { return; } - m_contentFBO.reset(new QOpenGLFramebufferObject(r.width(), r.height(), QOpenGLFramebufferObject::CombinedDepthStencil)); + const QSize nativeSize = r.size() * m_scale; + m_contentFBO.reset(new QOpenGLFramebufferObject(nativeSize.width(), nativeSize.height(), QOpenGLFramebufferObject::CombinedDepthStencil)); if (!m_contentFBO->isValid()) { qCWarning(KWIN_QPA) << "Content FBO is not valid"; } @@ -174,5 +182,15 @@ ShellClient *Window::shellClient() return m_shellClient; } +int Window::scale() const +{ + return m_scale; +} + +qreal Window::devicePixelRatio() const +{ + return m_scale; +} + } } diff --git a/plugins/qpa/window.h b/plugins/qpa/window.h index e8caedc6e0..bc334c359c 100644 --- a/plugins/qpa/window.h +++ b/plugins/qpa/window.h @@ -74,6 +74,9 @@ public: } void createEglSurface(EGLDisplay dpy, EGLConfig config); + int scale() const; + qreal devicePixelRatio() const override; + void bindContentFBO(); const QSharedPointer &contentFBO() const { return m_contentFBO; @@ -96,6 +99,7 @@ private: #endif quint32 m_windowId; const Integration *m_integration; + int m_scale = 1; }; } diff --git a/shell_client.cpp b/shell_client.cpp index 4255d6394d..810aa2fc71 100644 --- a/shell_client.cpp +++ b/shell_client.cpp @@ -510,9 +510,7 @@ void ShellClient::setInternalFramebufferObject(const QSharedPointersize(); + m_clientSize = fbo->size() / surface()->scale(); markAsMapped(); doSetGeometry(QRect(geom.topLeft(), m_clientSize)); Toplevel::setInternalFramebufferObject(fbo);