diff options
-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 43ca6dbd0..65267869f 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 bc768fa61..8fa81b608 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 |