summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2019-01-24 14:42:28 +0100
committerJohan Helsing <johan.helsing@qt.io>2019-02-27 11:48:30 +0000
commit021bd4d7ed1f4221a0132e21ef0ee5b07f74be3e (patch)
tree67bf87802a099743279b50fe62d05628dbd2bee0
parent2f0e6d773647d2112fcf34ab0d9d724f8278477c (diff)
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.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