From 37851e2e081ed148390bad1bc38593c9af0b7010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 5 Apr 2016 14:26:53 +0200 Subject: [PATCH] [server] Add a method SurfaceInterface::surfaceAt(const QPointF&) -> SurfaceInterface* Summary: The new method returns the (child) surface at a given surface position taking care of stacking order, whether surfaces are mapped, etc. Reviewers: #plasma Subscribers: plasma-devel Projects: #plasma Differential Revision: https://phabricator.kde.org/D1319 --- .../client/test_wayland_subsurface.cpp | 80 +++++++++++++++++++ .../autotests/client/test_wayland_surface.cpp | 36 +++++++++ src/wayland/surface_interface.cpp | 28 +++++++ src/wayland/surface_interface.h | 14 ++++ 4 files changed, 158 insertions(+) diff --git a/src/wayland/autotests/client/test_wayland_subsurface.cpp b/src/wayland/autotests/client/test_wayland_subsurface.cpp index 40bbad59b4..52800cdaa6 100644 --- a/src/wayland/autotests/client/test_wayland_subsurface.cpp +++ b/src/wayland/autotests/client/test_wayland_subsurface.cpp @@ -57,6 +57,7 @@ private Q_SLOTS: void testMainSurfaceFromTree(); void testRemoveSurface(); void testMappingOfSurfaceTree(); + void testSurfaceAt(); private: KWayland::Server::Display *m_display; @@ -920,5 +921,84 @@ void TestSubSurface::testMappingOfSurfaceTree() QVERIFY(!child3->surface()->isMapped()); } +void TestSubSurface::testSurfaceAt() +{ + // this test verifies that the correct surface is picked in a sub-surface tree + using namespace KWayland::Client; + using namespace KWayland::Server; + // first create a parent surface and map it + QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(serverSurfaceCreated.isValid()); + QScopedPointer parent(m_compositor->createSurface()); + QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::red); + parent->attachBuffer(m_shm->createBuffer(image)); + parent->damage(QRect(0, 0, 100, 100)); + parent->commit(Surface::CommitFlag::None); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *parentServerSurface = serverSurfaceCreated.last().first().value(); + + // create two child sub surfaces, those won't be mapped, just added to the parent + // this is to simulate the behavior of QtWayland + QScopedPointer directChild1(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *directChild1ServerSurface = serverSurfaceCreated.last().first().value(); + QScopedPointer directChild2(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *directChild2ServerSurface = serverSurfaceCreated.last().first().value(); + + // create the sub surfaces for them + QScopedPointer directChild1SubSurface(m_subCompositor->createSubSurface(directChild1.data(), parent.data())); + directChild1SubSurface->setMode(SubSurface::Mode::Desynchronized); + QScopedPointer directChild2SubSurface(m_subCompositor->createSubSurface(directChild2.data(), parent.data())); + directChild2SubSurface->setMode(SubSurface::Mode::Desynchronized); + + // each of the children gets a child + QScopedPointer childFor1(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *childFor1ServerSurface = serverSurfaceCreated.last().first().value(); + QScopedPointer childFor2(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *childFor2ServerSurface = serverSurfaceCreated.last().first().value(); + + // create sub surfaces for them + QScopedPointer childFor1SubSurface(m_subCompositor->createSubSurface(childFor1.data(), directChild1.data())); + childFor1SubSurface->setMode(SubSurface::Mode::Desynchronized); + QScopedPointer childFor2SubSurface(m_subCompositor->createSubSurface(childFor2.data(), directChild2.data())); + childFor2SubSurface->setMode(SubSurface::Mode::Desynchronized); + + // both get a quarter of the grand-parent surface + childFor2SubSurface->setPosition(QPoint(50, 50)); + childFor2->commit(Surface::CommitFlag::None); + directChild2->commit(Surface::CommitFlag::None); + parent->commit(Surface::CommitFlag::None); + + // now let's render both grand children + QImage partImage(QSize(50, 50), QImage::Format_ARGB32_Premultiplied); + partImage.fill(Qt::green); + childFor1->attachBuffer(m_shm->createBuffer(partImage)); + childFor1->damage(QRect(0, 0, 50, 50)); + childFor1->commit(Surface::CommitFlag::None); + partImage.fill(Qt::blue); + childFor2->attachBuffer(m_shm->createBuffer(partImage)); + childFor2->damage(QRect(0, 0, 50, 50)); + childFor2->commit(Surface::CommitFlag::None); + + QSignalSpy treeChangedSpy(parentServerSurface, &SurfaceInterface::subSurfaceTreeChanged); + QVERIFY(treeChangedSpy.isValid()); + QVERIFY(treeChangedSpy.wait()); + + // now let's test a few positions + QCOMPARE(parentServerSurface->surfaceAt(QPointF(0, 0)), childFor1ServerSurface); + QCOMPARE(parentServerSurface->surfaceAt(QPointF(49, 49)), childFor1ServerSurface); + QCOMPARE(parentServerSurface->surfaceAt(QPointF(50, 50)), childFor2ServerSurface); + QCOMPARE(parentServerSurface->surfaceAt(QPointF(100, 100)), childFor2ServerSurface); + QCOMPARE(parentServerSurface->surfaceAt(QPointF(25, 75)), parentServerSurface); + QCOMPARE(parentServerSurface->surfaceAt(QPointF(75, 25)), parentServerSurface); + // outside the geometries should be no surface + QVERIFY(!parentServerSurface->surfaceAt(QPointF(-1, -1))); + QVERIFY(!parentServerSurface->surfaceAt(QPointF(101, 101))); +} + QTEST_GUILESS_MAIN(TestSubSurface) #include "test_wayland_subsurface.moc" diff --git a/src/wayland/autotests/client/test_wayland_surface.cpp b/src/wayland/autotests/client/test_wayland_surface.cpp index dd3e34c3e1..ff88e09a9e 100644 --- a/src/wayland/autotests/client/test_wayland_surface.cpp +++ b/src/wayland/autotests/client/test_wayland_surface.cpp @@ -56,6 +56,7 @@ private Q_SLOTS: void testDestroy(); void testUnmapOfNotMappedSurface(); void testDamageTracking(); + void testSurfaceAt(); private: KWayland::Server::Display *m_display; @@ -797,5 +798,40 @@ void TestWaylandSurface::testDamageTracking() QCOMPARE(serverSurface->damage(), QRegion(50, 40, 20, 30)); } +void TestWaylandSurface::testSurfaceAt() +{ + // this test verifies that surfaceAt(const QPointF&) works as expected for the case of no children + using namespace KWayland::Client; + using namespace KWayland::Server; + // create surface + QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(serverSurfaceCreated.isValid()); + QScopedPointer s(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); + + // a newly created surface should not be mapped and not provide a surface at a position + QVERIFY(!serverSurface->isMapped()); + QVERIFY(!serverSurface->surfaceAt(QPointF(0, 0))); + + // let's damage this surface + QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged); + QVERIFY(sizeChangedSpy.isValid()); + QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::red); + s->attachBuffer(m_shm->createBuffer(image)); + s->damage(QRect(0, 0, 100, 100)); + s->commit(Surface::CommitFlag::None); + QVERIFY(sizeChangedSpy.wait()); + + // now the surface is mapped and surfaceAt should give the surface + QVERIFY(serverSurface->isMapped()); + QCOMPARE(serverSurface->surfaceAt(QPointF(0, 0)), serverSurface); + QCOMPARE(serverSurface->surfaceAt(QPointF(100, 100)), serverSurface); + // outside the geometry it should not give a surface + QVERIFY(!serverSurface->surfaceAt(QPointF(101, 101))); + QVERIFY(!serverSurface->surfaceAt(QPointF(-1, -1))); +} + QTEST_GUILESS_MAIN(TestWaylandSurface) #include "test_wayland_surface.moc" diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp index b0b5920044..1784b6f8f7 100644 --- a/src/wayland/surface_interface.cpp +++ b/src/wayland/surface_interface.cpp @@ -25,6 +25,8 @@ License along with this library. If not, see . #include "region_interface.h" #include "subcompositor_interface.h" #include "subsurface_interface_p.h" +// Qt +#include // Wayland #include // std @@ -692,6 +694,32 @@ void SurfaceInterface::resetTrackedDamage() d->trackedDamage = QRegion(); } +SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position) +{ + if (!isMapped()) { + return nullptr; + } + Q_D(); + // go from top to bottom. Top most child is last in list + QListIterator> it(d->current.children); + it.toBack(); + while (it.hasPrevious()) { + const auto ¤t = it.previous(); + auto surface = current->surface(); + if (surface.isNull()) { + continue; + } + if (auto s = surface->surfaceAt(position - current->position())) { + return s; + } + } + // check whether the geometry contains the pos + if (!size().isEmpty() && QRectF(QPoint(0, 0), size()).contains(position)) { + return this; + } + return nullptr; +} + SurfaceInterface::Private *SurfaceInterface::d_func() const { return reinterpret_cast(d.data()); diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h index 6565bc7f83..f78e6c680a 100644 --- a/src/wayland/surface_interface.h +++ b/src/wayland/surface_interface.h @@ -191,6 +191,20 @@ public: **/ void resetTrackedDamage(); + /** + * Finds the SurfaceInterface at the given @p position in surface-local coordinates. + * This can be either a descendant SurfaceInterface honoring the stacking order or + * the SurfaceInterface itself if its geometry contains the given @p position. + * + * If no such SurfaceInterface is found, e.g. because the SurfaceInterface is unmapped, + * @c nullptr is returned. + * + * @param position The position in surface-local coordinates + * @returns Child surface at the given @p position or surface itself at the position, might be @c nullptr + * @since 5.7 + **/ + SurfaceInterface *surfaceAt(const QPointF &position); + /** * @returns The SurfaceInterface for the @p native resource. **/