From 1e4703a719599d1587f5e43c0650bcac471450e7 Mon Sep 17 00:00:00 2001 From: Vlad Zagorodniy Date: Tue, 10 Jul 2018 21:09:22 +0300 Subject: [PATCH] [wayland] Confine pointer to screen geometry Summary: If the new pointer position is "off screen", PointerInputRedirection just ignores that new position. So, pointer remains on its previous position. In some particular cases, like reaching default panel, it degrades desktop experience because one have to slowly move pointer in order to reach what he/she wants. This change addresses that problem by confining the new pointer position to screen geometry. BUG: 374867 FIXED-IN: 5.13.4 Test Plan: Ran tests Reviewers: #kwin, graesslin Reviewed By: #kwin, graesslin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D14036 --- autotests/integration/pointer_input.cpp | 93 +++++++++++++++++++++++++ pointer_input.cpp | 18 +++-- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp index b5d61713e3..f3eec594e5 100644 --- a/autotests/integration/pointer_input.cpp +++ b/autotests/integration/pointer_input.cpp @@ -76,6 +76,8 @@ private Q_SLOTS: void testPopup(); void testDecoCancelsPopup(); void testWindowUnderCursorWhileButtonPressed(); + void testConfineToScreenGeometry_data(); + void testConfineToScreenGeometry(); private: void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); @@ -1192,6 +1194,97 @@ void PointerInputTest::testWindowUnderCursorWhileButtonPressed() QCOMPARE(enteredSpy.count(), 2); } +void PointerInputTest::testConfineToScreenGeometry_data() +{ + QTest::addColumn("startPos"); + QTest::addColumn("targetPos"); + QTest::addColumn("expectedPos"); + + // screen layout: + // + // +----------+----------+---------+ + // | left | top | right | + // +----------+----------+---------+ + // | bottom | + // +----------+ + // + + QTest::newRow("move top-left - left screen") << QPoint(640, 512) << QPoint(-100, -100) << QPoint(0, 0); + QTest::newRow("move top - left screen") << QPoint(640, 512) << QPoint(640, -100) << QPoint(640, 0); + QTest::newRow("move top-right - left screen") << QPoint(640, 512) << QPoint(1380, -100) << QPoint(1380, 0); + QTest::newRow("move right - left screen") << QPoint(640, 512) << QPoint(1380, 512) << QPoint(1380, 512); + QTest::newRow("move bottom-right - left screen") << QPoint(640, 512) << QPoint(1380, 1124) << QPoint(1380, 1124); + QTest::newRow("move bottom - left screen") << QPoint(640, 512) << QPoint(640, 1124) << QPoint(640, 1023); + QTest::newRow("move bottom-left - left screen") << QPoint(640, 512) << QPoint(-100, 1124) << QPoint(0, 1023); + QTest::newRow("move left - left screen") << QPoint(640, 512) << QPoint(-100, 512) << QPoint(0, 512); + + QTest::newRow("move top-left - top screen") << QPoint(1920, 512) << QPoint(1180, -100) << QPoint(1180, 0); + QTest::newRow("move top - top screen") << QPoint(1920, 512) << QPoint(1920, -100) << QPoint(1920, 0); + QTest::newRow("move top-right - top screen") << QPoint(1920, 512) << QPoint(2660, -100) << QPoint(2660, 0); + QTest::newRow("move right - top screen") << QPoint(1920, 512) << QPoint(2660, 512) << QPoint(2660, 512); + QTest::newRow("move bottom-right - top screen") << QPoint(1920, 512) << QPoint(2660, 1124) << QPoint(2559, 1023); + QTest::newRow("move bottom - top screen") << QPoint(1920, 512) << QPoint(1920, 1124) << QPoint(1920, 1124); + QTest::newRow("move bottom-left - top screen") << QPoint(1920, 512) << QPoint(1180, 1124) << QPoint(1280, 1023); + QTest::newRow("move left - top screen") << QPoint(1920, 512) << QPoint(1180, 512) << QPoint(1180, 512); + + QTest::newRow("move top-left - right screen") << QPoint(3200, 512) << QPoint(2460, -100) << QPoint(2460, 0); + QTest::newRow("move top - right screen") << QPoint(3200, 512) << QPoint(3200, -100) << QPoint(3200, 0); + QTest::newRow("move top-right - right screen") << QPoint(3200, 512) << QPoint(3940, -100) << QPoint(3839, 0); + QTest::newRow("move right - right screen") << QPoint(3200, 512) << QPoint(3940, 512) << QPoint(3839, 512); + QTest::newRow("move bottom-right - right screen") << QPoint(3200, 512) << QPoint(3940, 1124) << QPoint(3839, 1023); + QTest::newRow("move bottom - right screen") << QPoint(3200, 512) << QPoint(3200, 1124) << QPoint(3200, 1023); + QTest::newRow("move bottom-left - right screen") << QPoint(3200, 512) << QPoint(2460, 1124) << QPoint(2460, 1124); + QTest::newRow("move left - right screen") << QPoint(3200, 512) << QPoint(2460, 512) << QPoint(2460, 512); + + QTest::newRow("move top-left - bottom screen") << QPoint(1920, 1536) << QPoint(1180, 924) << QPoint(1180, 924); + QTest::newRow("move top - bottom screen") << QPoint(1920, 1536) << QPoint(1920, 924) << QPoint(1920, 924); + QTest::newRow("move top-right - bottom screen") << QPoint(1920, 1536) << QPoint(2660, 924) << QPoint(2660, 924); + QTest::newRow("move right - bottom screen") << QPoint(1920, 1536) << QPoint(2660, 1536) << QPoint(2559, 1536); + QTest::newRow("move bottom-right - bottom screen") << QPoint(1920, 1536) << QPoint(2660, 2148) << QPoint(2559, 2047); + QTest::newRow("move bottom - bottom screen") << QPoint(1920, 1536) << QPoint(1920, 2148) << QPoint(1920, 2047); + QTest::newRow("move bottom-left - bottom screen") << QPoint(1920, 1536) << QPoint(1180, 2148) << QPoint(1280, 2047); + QTest::newRow("move left - bottom screen") << QPoint(1920, 1536) << QPoint(1180, 1536) << QPoint(1280, 1536); +} + +void PointerInputTest::testConfineToScreenGeometry() +{ + // this test verifies that pointer belongs to at least one screen + // after moving it to off-screen area + + // unload the Present Windows effect because it pushes back + // pointer if it's at (0, 0) + static_cast(effects)->unloadEffect(QStringLiteral("presentwindows")); + + // setup screen layout + const QVector geometries { + QRect(0, 0, 1280, 1024), + QRect(1280, 0, 1280, 1024), + QRect(2560, 0, 1280, 1024), + QRect(1280, 1024, 1280, 1024) + }; + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", + Qt::DirectConnection, + Q_ARG(int, geometries.count()), + Q_ARG(QVector, geometries)); + QCOMPARE(screens()->count(), geometries.count()); + QCOMPARE(screens()->geometry(0), geometries.at(0)); + QCOMPARE(screens()->geometry(1), geometries.at(1)); + QCOMPARE(screens()->geometry(2), geometries.at(2)); + QCOMPARE(screens()->geometry(3), geometries.at(3)); + + // move pointer to initial position + QFETCH(QPoint, startPos); + Cursor::setPos(startPos); + QCOMPARE(Cursor::pos(), startPos); + + // perform movement + QFETCH(QPoint, targetPos); + kwinApp()->platform()->pointerMotion(targetPos, 1); + + QFETCH(QPoint, expectedPos); + QCOMPARE(Cursor::pos(), expectedPos); +} + } WAYLANDTEST_MAIN(KWin::PointerInputTest) diff --git a/pointer_input.cpp b/pointer_input.cpp index 50eb9e7495..f3a9fcb85e 100644 --- a/pointer_input.cpp +++ b/pointer_input.cpp @@ -113,6 +113,14 @@ static bool screenContainsPos(const QPointF &pos) return false; } +static QPointF confineToBoundingBox(const QPointF &pos, const QRectF &boundingBox) +{ + return QPointF( + qBound(boundingBox.left(), pos.x(), boundingBox.right() - 1.0), + qBound(boundingBox.top(), pos.y(), boundingBox.bottom() - 1.0) + ); +} + PointerInputRedirection::PointerInputRedirection(InputRedirection* parent) : InputDeviceHandler(parent) , m_cursor(nullptr) @@ -745,13 +753,11 @@ void PointerInputRedirection::updatePosition(const QPointF &pos) // verify that at least one screen contains the pointer position QPointF p = pos; if (!screenContainsPos(p)) { - // allow either x or y to pass - p = QPointF(m_pos.x(), pos.y()); + const QRectF unitedScreensGeometry = screens()->geometry(); + p = confineToBoundingBox(p, unitedScreensGeometry); if (!screenContainsPos(p)) { - p = QPointF(pos.x(), m_pos.y()); - if (!screenContainsPos(p)) { - return; - } + const QRectF currentScreenGeometry = screens()->geometry(screens()->number(m_pos.toPoint())); + p = confineToBoundingBox(p, currentScreenGeometry); } } p = applyPointerConfinement(p);