From 9f92a05f52a98b78db4bd679fde92293cfa45ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 22 Aug 2016 14:18:23 +0200 Subject: [PATCH] Add support for Surface enter/leave events Summary: This change implements support for the wl_surface events enter and leave. Those events are emitted whenever a surface becomes visible on an output by e.g. mapping the surface, moving or resizing it. Similar the leave is sent whenever the surface is no longer on an output. The server side is not yet fully complete yet. It also needs to emit when the client binds the output another time and needs to send a leave before destroying the output. Reviewers: #plasma_on_wayland Subscribers: plasma-devel Tags: #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D2528 --- .../autotests/client/test_wayland_surface.cpp | 75 +++++++++++++++++++ src/wayland/output_interface.cpp | 12 +++ src/wayland/output_interface.h | 7 ++ src/wayland/surface_interface.cpp | 38 ++++++++++ src/wayland/surface_interface.h | 18 +++++ src/wayland/surface_interface_p.h | 2 + 6 files changed, 152 insertions(+) diff --git a/src/wayland/autotests/client/test_wayland_surface.cpp b/src/wayland/autotests/client/test_wayland_surface.cpp index 123e660dcc..d13db9a8f5 100644 --- a/src/wayland/autotests/client/test_wayland_surface.cpp +++ b/src/wayland/autotests/client/test_wayland_surface.cpp @@ -25,6 +25,7 @@ License along with this library. If not, see . #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" +#include "../../src/client/output.h" #include "../../src/client/surface.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" @@ -59,6 +60,7 @@ private Q_SLOTS: void testSurfaceAt(); void testDestroyAttachedBuffer(); void testDestroyWithPendingCallback(); + void testOutput(); void testDisconnect(); private: @@ -956,5 +958,78 @@ void TestWaylandSurface::testDisconnect() m_queue->destroy(); } +void TestWaylandSurface::testOutput() +{ + // This test verifies that the enter/leave are sent correctly to the Client + using namespace KWayland::Client; + using namespace KWayland::Server; + qRegisterMetaType(); + QScopedPointer s(m_compositor->createSurface()); + QVERIFY(!s.isNull()); + QVERIFY(s->isValid()); + QVERIFY(s->outputs().isEmpty()); + QSignalSpy enteredSpy(s.data(), &Surface::outputEntered); + QVERIFY(enteredSpy.isValid()); + QSignalSpy leftSpy(s.data(), &Surface::outputLeft); + QVERIFY(leftSpy.isValid()); + // wait for the surface on the Server side + QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(surfaceCreatedSpy.isValid()); + QVERIFY(surfaceCreatedSpy.wait()); + auto serverSurface = surfaceCreatedSpy.first().first().value(); + QVERIFY(serverSurface); + QCOMPARE(serverSurface->outputs(), QVector()); + + // create another registry to get notified about added outputs + Registry registry; + registry.setEventQueue(m_queue); + QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); + QVERIFY(allAnnounced.isValid()); + registry.create(m_connection); + QVERIFY(registry.isValid()); + registry.setup(); + QVERIFY(allAnnounced.wait()); + QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); + QVERIFY(outputAnnouncedSpy.isValid()); + + auto serverOutput = m_display->createOutput(m_display); + serverOutput->create(); + QVERIFY(outputAnnouncedSpy.wait()); + QScopedPointer clientOutput(registry.createOutput(outputAnnouncedSpy.first().first().value(), outputAnnouncedSpy.first().last().value())); + QVERIFY(clientOutput->isValid()); + m_connection->flush(); + m_display->dispatchEvents(); + + // now enter it + serverSurface->setOutputs(QVector{serverOutput}); + QCOMPARE(serverSurface->outputs(), QVector{serverOutput}); + QVERIFY(enteredSpy.wait()); + QCOMPARE(enteredSpy.count(), 1); + QCOMPARE(enteredSpy.first().first().value(), clientOutput.data()); + QCOMPARE(s->outputs(), QVector{clientOutput.data()}); + + // adding to same should not trigger + serverSurface->setOutputs(QVector{serverOutput}); + + // leave again + serverSurface->setOutputs(QVector()); + QCOMPARE(serverSurface->outputs(), QVector()); + QVERIFY(leftSpy.wait()); + QCOMPARE(enteredSpy.count(), 1); + QCOMPARE(leftSpy.count(), 1); + QCOMPARE(leftSpy.first().first().value(), clientOutput.data()); + QCOMPARE(s->outputs(), QVector()); + + // leave again should not trigger + serverSurface->setOutputs(QVector()); + + // and enter again, just to verify + serverSurface->setOutputs(QVector{serverOutput}); + QCOMPARE(serverSurface->outputs(), QVector{serverOutput}); + QVERIFY(enteredSpy.wait()); + QCOMPARE(enteredSpy.count(), 2); + QCOMPARE(leftSpy.count(), 1); +} + QTEST_GUILESS_MAIN(TestWaylandSurface) #include "test_wayland_surface.moc" diff --git a/src/wayland/output_interface.cpp b/src/wayland/output_interface.cpp index edbe79cb87..75cd1a6f35 100644 --- a/src/wayland/output_interface.cpp +++ b/src/wayland/output_interface.cpp @@ -506,6 +506,18 @@ bool OutputInterface::isDpmsSupported() const return d->dpms.supported; } +QVector OutputInterface::clientResources(ClientConnection *client) const +{ + Q_D(); + QVector ret; + for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) { + if (wl_resource_get_client((*it).resource) == client->client()) { + ret << (*it).resource; + } + } + return ret; +} + OutputInterface *OutputInterface::get(wl_resource* native) { return Private::get(native); diff --git a/src/wayland/output_interface.h b/src/wayland/output_interface.h index 17f79a7d3b..0cf9d4f952 100644 --- a/src/wayland/output_interface.h +++ b/src/wayland/output_interface.h @@ -36,6 +36,7 @@ namespace KWayland namespace Server { +class ClientConnection; class Display; /** @@ -125,6 +126,12 @@ public: **/ void setDpmsMode(DpmsMode mode); + /** + * @returns all wl_resources bound for the @p client + * @since 5.27 + **/ + QVector clientResources(ClientConnection *client) const; + static OutputInterface *get(wl_resource *native); Q_SIGNALS: diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp index f8fd5bbb36..1579072102 100644 --- a/src/wayland/surface_interface.cpp +++ b/src/wayland/surface_interface.cpp @@ -688,6 +688,44 @@ void SurfaceInterface::resetTrackedDamage() d->trackedDamage = QRegion(); } +QVector SurfaceInterface::outputs() const +{ + Q_D(); + return d->outputs; +} + +void SurfaceInterface::setOutputs(const QVector &outputs) +{ + Q_D(); + QVector removedOutputs = d->outputs; + for (auto it = outputs.constBegin(), end = outputs.constEnd(); it != end; ++it) { + const auto o = *it; + removedOutputs.removeOne(o); + } + for (auto it = removedOutputs.constBegin(), end = removedOutputs.constEnd(); it != end; ++it) { + const auto resources = (*it)->clientResources(client()); + for (wl_resource *r : resources) { + wl_surface_send_leave(d->resource, r); + } + } + // TODO: send leave when OutputInterface gets destroyed + + QVector addedOutputsOutputs = outputs; + for (auto it = d->outputs.constBegin(), end = d->outputs.constEnd(); it != end; ++it) { + const auto o = *it; + addedOutputsOutputs.removeOne(o); + } + for (auto it = addedOutputsOutputs.constBegin(), end = addedOutputsOutputs.constEnd(); it != end; ++it) { + const auto resources = (*it)->clientResources(client()); + for (wl_resource *r : resources) { + wl_surface_send_enter(d->resource, r); + } + } + // TODO: send enter when the client binds the OutputInterface another time + + d->outputs = outputs; +} + SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position) { if (!isMapped()) { diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h index 85b0805d4d..de74ef336a 100644 --- a/src/wayland/surface_interface.h +++ b/src/wayland/surface_interface.h @@ -205,6 +205,24 @@ public: **/ SurfaceInterface *surfaceAt(const QPointF &position); + /** + * Sets the @p outputs this SurfaceInterface overlaps with, may be empty. + * + * The compositor should update whenever the SurfaceInterface becomes visible on + * an OutputInterface by e.g. getting (un)mapped, resized, moved, etc. + * + * @see outputs + * @since 5.27 + **/ + void setOutputs(const QVector &outputs); + + /** + * @returns All OutputInterfaces the SurfaceInterface is on. + * @see setOutputs + * @since 5.27 + **/ + QVector outputs() const; + /** * @returns The SurfaceInterface for the @p native resource. **/ diff --git a/src/wayland/surface_interface_p.h b/src/wayland/surface_interface_p.h index 9890fc6f65..8d7362220e 100644 --- a/src/wayland/surface_interface_p.h +++ b/src/wayland/surface_interface_p.h @@ -89,6 +89,8 @@ public: // waiting on the frame callback of the never visible surface bool subSurfaceIsMapped = true; + QVector outputs; + private: SurfaceInterface *q_func() { return reinterpret_cast(q);