diff options
author | Johan Klokkhammer Helsing <johan.helsing@qt.io> | 2019-01-24 14:42:28 +0100 |
---|---|---|
committer | Johan Helsing <johan.helsing@qt.io> | 2019-02-27 11:48:30 +0000 |
commit | 021bd4d7ed1f4221a0132e21ef0ee5b07f74be3e (patch) | |
tree | 67bf87802a099743279b50fe62d05628dbd2bee0 | |
parent | 2f0e6d773647d2112fcf34ab0d9d724f8278477c (diff) | |
download | qtwayland-021bd4d7ed1f4221a0132e21ef0ee5b07f74be3e.tar.gz |
Client: Decrease buffer_scale for small cursor themes
Not all setups or themes have cursors with a matching DPI. In such cases,
wl_cursor_load_theme will then return a theme that is the closest resolution it
can get.
With the previous implementation, cursors themes without a high dpi version
would become become really tiny on high DPI displays.
This patch prevents it by setting a lower wl_surface.scale for those themes.
This also implements proper tracking of the cursor surface's entered outputs
(i.e. if the entered surface is destroyed, the scale is reset, and similarly
the following sequence of events should also be handled:
wl_surface.enter(wl_output@1)
wl_surface.enter(wl_output@2)
wl_surface.leave(wl_output@2)
In the old implementation, we would be stuck with the scale from wl_output@2,
but now we now should correctly get the scale of wl_output@1.
[ChangeLog][QPA plugin] Cursors on high DPI screens are now scaled up if the
cursor theme does not have an appropriate high resolution version.
Change-Id: Ic87d00e35612b5afdf8c2e3a4463fcfef1f1f09d
Reviewed-by: Giulio Camuffo <giulio.camuffo@kdab.com>
-rw-r--r-- | src/client/qwaylandinputdevice.cpp | 52 | ||||
-rw-r--r-- | tests/auto/client/seatv4/tst_seatv4.cpp | 62 |
2 files changed, 103 insertions, 11 deletions
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 43ca6dbd..65267869 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -188,7 +188,8 @@ QWaylandInputDevice::Pointer::~Pointer() #if QT_CONFIG(cursor) -class CursorSurface : public QtWayland::wl_surface { +class CursorSurface : public QObject, public QtWayland::wl_surface +{ public: explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display) : m_pointer(pointer) @@ -196,6 +197,14 @@ public: init(display->createSurface(this)); //TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead. m_version = display->compositorVersion(); + connect(qApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) { + int oldScale = outputScale(); + if (!m_screens.removeOne(static_cast<QWaylandScreen *>(screen->handle()))) + return; + + if (outputScale() != oldScale) + m_pointer->updateCursor(); + }); } void hide() @@ -225,18 +234,38 @@ public: commit(); } - int outputScale() const { return m_outputScale; } + int outputScale() const + { + int scale = 0; + for (auto *screen : m_screens) + scale = qMax(scale, screen->scale()); + return scale; + } protected: void surface_enter(struct ::wl_output *output) override { - //TODO: Can be improved to keep track of all entered screens - int scale = QWaylandScreen::fromWlOutput(output)->scale(); - if (scale == m_outputScale) + int oldScale = outputScale(); + auto *screen = QWaylandScreen::fromWlOutput(output); + if (m_screens.contains(screen)) + return; + + m_screens.append(screen); + + if (outputScale() != oldScale) + m_pointer->updateCursor(); + } + + void surface_leave(struct ::wl_output *output) override + { + int oldScale = outputScale(); + auto *screen = QWaylandScreen::fromWlOutput(output); + + if (!m_screens.removeOne(screen)) return; - m_outputScale = scale; - m_pointer->updateCursor(); + if (outputScale() != oldScale) + m_pointer->updateCursor(); } private: @@ -244,7 +273,7 @@ private: uint m_version = 0; uint m_setSerial = 0; QPoint m_hotspot; - int m_outputScale = 0; + QVector<QWaylandScreen *> m_screens; }; QString QWaylandInputDevice::Pointer::cursorThemeName() const @@ -280,6 +309,13 @@ void QWaylandInputDevice::Pointer::updateCursorTheme() int pixelSize = cursorSize() * scale; auto *display = seat()->mQDisplay; mCursor.theme = display->loadCursorTheme(cursorThemeName(), pixelSize); + if (auto *arrow = mCursor.theme->cursorImage(Qt::ArrowCursor)) { + int arrowPixelSize = qMax(arrow->width, arrow->height); // Not all cursor themes are square + while (scale > 1 && arrowPixelSize / scale < cursorSize()) + --scale; + } else { + qWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor"; + } mCursor.themeBufferScale = scale; } diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp index bc768fa6..8fa81b60 100644 --- a/tests/auto/client/seatv4/tst_seatv4.cpp +++ b/tests/auto/client/seatv4/tst_seatv4.cpp @@ -75,6 +75,7 @@ private slots: void simpleAxis(); void invalidPointerEvents(); void scaledCursor(); + void unscaledFallbackCursor(); void bitmapCursor(); void hidpiBitmapCursor(); void hidpiBitmapCursorNonInt(); @@ -290,7 +291,7 @@ void tst_seatv4::invalidPointerEvents() static bool supportsCursorSize(uint size, wl_shm *shm) { - auto *theme = wl_cursor_theme_load(nullptr, size, shm); + auto *theme = wl_cursor_theme_load(qgetenv("XCURSOR_THEME"), size, shm); if (!theme) return false; @@ -313,10 +314,16 @@ static bool supportsCursorSizes(const QVector<uint> &sizes) }); } +static uint defaultCursorSize() { + const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); + return xCursorSize > 0 ? uint(xCursorSize) : 32; +} + void tst_seatv4::scaledCursor() { - if (!supportsCursorSizes({32, 64})) - QSKIP("Cursor themes with sizes 32 and 64 not found."); + const uint defaultSize = defaultCursorSize(); + if (!supportsCursorSizes({defaultSize, defaultSize * 2})) + QSKIP("Cursor themes with default size and 2x default size not found."); // Add a highdpi output exec([&] { @@ -351,6 +358,55 @@ void tst_seatv4::scaledCursor() exec([&] { remove(output(1)); }); } +void tst_seatv4::unscaledFallbackCursor() +{ + const uint defaultSize = defaultCursorSize(); + if (!supportsCursorSizes({defaultSize})) + QSKIP("Default cursor size not supported"); + + const int screens = 4; // with scales 1, 2, 4, 8 + + exec([=] { + for (int i = 1; i < screens; ++i) { + OutputData d; + d.scale = int(qPow(2, i)); + d.position = {1920 * i, 0}; + add<Output>(d); + } + }); + + QRasterWindow window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); + QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()); + QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()->m_committed.buffer); + QCOMPOSITOR_TRY_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 1); + QSize unscaledPixelSize = exec([=] { + return pointer()->cursorSurface()->m_committed.buffer->size(); + }); + + QCOMPARE(unscaledPixelSize.width(), int(defaultSize)); + QCOMPARE(unscaledPixelSize.height(), int(defaultSize)); + + for (int i = 1; i < screens; ++i) { + exec([=] { + auto *surface = pointer()->cursorSurface(); + surface->sendEnter(getAll<Output>()[i]); + surface->sendLeave(getAll<Output>()[i-1]); + }); + + xdgPingAndWaitForPong(); // Give the client a chance to mess up + + // Surface size (buffer size / scale) should stay constant + QCOMPOSITOR_TRY_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size() / pointer()->cursorSurface()->m_committed.bufferScale, unscaledPixelSize); + } + + // Remove the extra outputs to clean up for the next test + exec([&] { while (auto *o = output(1)) remove(o); }); +} + void tst_seatv4::bitmapCursor() { // Add a highdpi output |