summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/client/qwaylandinputdevice.cpp52
-rw-r--r--tests/auto/client/seatv4/tst_seatv4.cpp62
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