diff --git a/abstract_client.cpp b/abstract_client.cpp index f3fcb50d7c..8969d291a1 100644 --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -2575,6 +2575,16 @@ QRect AbstractClient::inputGeometry() const return Toplevel::inputGeometry(); } +bool AbstractClient::hitTest(const QPoint &point) const +{ + if (isDecorated()) { + if (m_decoration.inputRegion.contains(mapToFrame(point))) { + return true; + } + } + return Toplevel::hitTest(point); +} + QRect AbstractClient::virtualKeyboardGeometry() const { return m_virtualKeyboardGeometry; diff --git a/abstract_client.h b/abstract_client.h index 79ab9ba0c4..9aecb96110 100644 --- a/abstract_client.h +++ b/abstract_client.h @@ -754,6 +754,7 @@ public: virtual void showContextHelp(); QRect inputGeometry() const override; + bool hitTest(const QPoint &point) const override; /** * @returns the geometry of the virtual keyboard diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp index 63f27ade3f..89b0451f8d 100644 --- a/autotests/integration/pointer_input.cpp +++ b/autotests/integration/pointer_input.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,8 @@ private Q_SLOTS: void testResizeCursor(); void testMoveCursor(); void testHideShowCursor(); + void testDefaultInputRegion(); + void testEmptyInputRegion(); private: void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); @@ -1619,6 +1622,52 @@ void PointerInputTest::testHideShowCursor() QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); } +void PointerInputTest::testDefaultInputRegion() +{ + // This test verifies that a surface that hasn't specified the input region can be focused. + + // Create a test client. + using namespace KWayland::Client; + QScopedPointer surface(Test::createSurface()); + QVERIFY(!surface.isNull()); + QScopedPointer shellSurface(Test::createXdgShellStableSurface(surface.data())); + QVERIFY(!shellSurface.isNull()); + AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(client); + + // Move the point to the center of the surface. + Cursors::self()->mouse()->setPos(client->frameGeometry().center()); + QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), client->surface()); + + // Destroy the test client. + shellSurface.reset(); + QVERIFY(Test::waitForWindowDestroyed(client)); +} + +void PointerInputTest::testEmptyInputRegion() +{ + // This test verifies that a surface that has specified an empty input region can't be focused. + + // Create a test client. + using namespace KWayland::Client; + QScopedPointer surface(Test::createSurface()); + QVERIFY(!surface.isNull()); + std::unique_ptr inputRegion(m_compositor->createRegion(QRegion())); + surface->setInputRegion(inputRegion.get()); + QScopedPointer shellSurface(Test::createXdgShellStableSurface(surface.data())); + QVERIFY(!shellSurface.isNull()); + AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(client); + + // Move the point to the center of the surface. + Cursors::self()->mouse()->setPos(client->frameGeometry().center()); + QVERIFY(!waylandServer()->seat()->focusedPointerSurface()); + + // Destroy the test client. + shellSurface.reset(); + QVERIFY(Test::waitForWindowDestroyed(client)); +} + } WAYLANDTEST_MAIN(KWin::PointerInputTest) diff --git a/input.cpp b/input.cpp index 0fbc2871d6..6f44cd7d12 100644 --- a/input.cpp +++ b/input.cpp @@ -2418,17 +2418,6 @@ Qt::MouseButtons InputRedirection::qtButtonStates() const return m_pointer->buttons(); } -static bool acceptsInput(Toplevel *t, const QPoint &pos) -{ - const QRegion input = t->inputShape(); - if (input.isEmpty()) { - return true; - } - // TODO: What about sub-surfaces sticking outside the main surface? - const QPoint localPoint = pos - t->bufferGeometry().topLeft(); - return input.contains(localPoint); -} - Toplevel *InputRedirection::findToplevel(const QPoint &pos) { if (!Workspace::self()) { @@ -2443,7 +2432,7 @@ Toplevel *InputRedirection::findToplevel(const QPoint &pos) } const QList &unmanaged = Workspace::self()->unmanagedList(); foreach (Unmanaged *u, unmanaged) { - if (u->inputGeometry().contains(pos) && acceptsInput(u, pos)) { + if (u->hitTest(pos)) { return u; } } @@ -2482,7 +2471,7 @@ Toplevel *InputRedirection::findManagedToplevel(const QPoint &pos) continue; } } - if (t->inputGeometry().contains(pos) && acceptsInput(t, pos)) { + if (t->hitTest(pos)) { return t; } } while (it != stacking.begin()); diff --git a/inputpanelv1client.cpp b/inputpanelv1client.cpp index 4d56d94866..50bbbe86c3 100644 --- a/inputpanelv1client.cpp +++ b/inputpanelv1client.cpp @@ -107,9 +107,6 @@ NET::WindowType InputPanelV1Client::windowType(bool, int) const QRect InputPanelV1Client::inputGeometry() const { - if (surface()->inputIsInfinite()) { - return frameGeometry(); - } return surface()->input().boundingRect().translated(pos()); } diff --git a/pointer_input.cpp b/pointer_input.cpp index 4812a11d37..6966e8923a 100644 --- a/pointer_input.cpp +++ b/pointer_input.cpp @@ -620,8 +620,7 @@ template static QRegion getConstraintRegion(Toplevel *t, T *constraint) { const QRegion windowShape = t->inputShape(); - const QRegion windowRegion = windowShape.isEmpty() ? QRegion(0, 0, t->clientSize().width(), t->clientSize().height()) : windowShape; - const QRegion intersected = constraint->region().isEmpty() ? windowRegion : windowRegion.intersected(constraint->region()); + const QRegion intersected = constraint->region().isEmpty() ? windowShape : windowShape.intersected(constraint->region()); return intersected.translated(t->pos() + t->clientPos()); } diff --git a/toplevel.cpp b/toplevel.cpp index 0694361185..126c8165ed 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -789,6 +789,14 @@ QMatrix4x4 Toplevel::inputTransformation() const return m; } +bool Toplevel::hitTest(const QPoint &point) const +{ + if (m_surface && m_surface->isMapped()) { + return m_surface->inputSurfaceAt(mapToLocal(point)); + } + return inputGeometry().contains(point); +} + QPoint Toplevel::mapToFrame(const QPoint &point) const { return point - frameGeometry().topLeft(); diff --git a/toplevel.h b/toplevel.h index e16cd2176b..a7674928e0 100644 --- a/toplevel.h +++ b/toplevel.h @@ -540,6 +540,11 @@ public: */ virtual QMatrix4x4 inputTransformation() const; + /** + * Returns @c true if the toplevel can accept input at the specified position @a point. + */ + virtual bool hitTest(const QPoint &point) const; + /** * The window has a popup grab. This means that when it got mapped the * parent window had an implicit (pointer) grab.