diff --git a/autotests/client/test_wayland_subsurface.cpp b/autotests/client/test_wayland_subsurface.cpp --- a/autotests/client/test_wayland_subsurface.cpp +++ b/autotests/client/test_wayland_subsurface.cpp @@ -55,6 +55,7 @@ void testSyncMode(); void testDeSyncMode(); void testMainSurfaceFromTree(); + void testRemoveSurface(); private: KWayland::Server::Display *m_display; @@ -748,5 +749,38 @@ QCOMPARE(child3->surface()->childSubSurfaces().count(), 0); } +void TestSubSurface::testRemoveSurface() +{ + // this test verifies that removing the surface also removes the sub-surface from the parent + using namespace KWayland::Client; + using namespace KWayland::Server; + + QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(surfaceCreatedSpy.isValid()); + + QScopedPointer parentSurface(m_compositor->createSurface()); + QVERIFY(surfaceCreatedSpy.wait()); + auto parentServerSurface = surfaceCreatedSpy.last().first().value(); + QVERIFY(parentServerSurface); + QScopedPointer childSurface(m_compositor->createSurface()); + QVERIFY(surfaceCreatedSpy.wait()); + auto childServerSurface = surfaceCreatedSpy.last().first().value(); + QVERIFY(childServerSurface); + + QSignalSpy subSurfaceTreeChangedSpy(parentServerSurface, &SurfaceInterface::subSurfaceTreeChanged); + QVERIFY(subSurfaceTreeChangedSpy.isValid()); + + m_subCompositor->createSubSurface(childSurface.data(), parentSurface.data()); + parentSurface->commit(Surface::CommitFlag::None); + QVERIFY(subSurfaceTreeChangedSpy.wait()); + + QCOMPARE(parentServerSurface->childSubSurfaces().count(), 1); + + // destroy surface, takes place immediately + childSurface.reset(); + QVERIFY(subSurfaceTreeChangedSpy.wait()); + QCOMPARE(parentServerSurface->childSubSurfaces().count(), 0); +} + QTEST_GUILESS_MAIN(TestSubSurface) #include "test_wayland_subsurface.moc" diff --git a/src/server/subcompositor_interface.cpp b/src/server/subcompositor_interface.cpp --- a/src/server/subcompositor_interface.cpp +++ b/src/server/subcompositor_interface.cpp @@ -176,6 +176,18 @@ surface->d_func()->subSurfacePending.shadowIsSet = false; surface->d_func()->subSurfacePending.slideIsSet = false; parent->d_func()->addChild(QPointer(q)); + + QObject::connect(surface, &QObject::destroyed, q, + [this] { + // from spec: "If the wl_surface associated with the wl_subsurface is destroyed, + // the wl_subsurface object becomes inert. Note, that destroying either object + // takes effect immediately." + if (parent) { + Q_Q(SubSurfaceInterface); + reinterpret_cast(parent->d.data())->removeChild(QPointer(q)); + } + } + ); } void SubSurfaceInterface::Private::commit() diff --git a/src/server/surface_interface.cpp b/src/server/surface_interface.cpp --- a/src/server/surface_interface.cpp +++ b/src/server/surface_interface.cpp @@ -68,9 +68,11 @@ Q_Q(SurfaceInterface); emit q->subSurfaceTreeChanged(); QObject::disconnect(child.data(), &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged); - QObject::disconnect(child->surface().data(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged); - QObject::disconnect(child->surface().data(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged); - QObject::disconnect(child->surface().data(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged); + if (!child->surface().isNull()) { + QObject::disconnect(child->surface().data(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged); + QObject::disconnect(child->surface().data(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged); + QObject::disconnect(child->surface().data(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged); + } } bool SurfaceInterface::Private::raiseChild(QPointer subsurface, SurfaceInterface *sibling)