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 @@ -59,6 +59,7 @@ void testMappingOfSurfaceTree(); void testSurfaceAt(); void testDestroyAttachedBuffer(); + void testDestroyParentSurface(); private: KWayland::Server::Display *m_display; @@ -1041,5 +1042,51 @@ QVERIFY(destroySpy.wait()); } +void TestSubSurface::testDestroyParentSurface() +{ + // this test verifies that destroying a parent surface does not create problems + // see BUG 389231 + using namespace KWayland::Client; + using namespace KWayland::Server; + // create surface + QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(serverSurfaceCreated.isValid()); + QScopedPointer parent(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *serverParentSurface = serverSurfaceCreated.last().first().value(); + QScopedPointer child(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *serverChildSurface = serverSurfaceCreated.last().first().value(); + QScopedPointer grandChild(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + SurfaceInterface *serverGrandChildSurface = serverSurfaceCreated.last().first().value(); + // create sub-surface in desynchronized mode as Qt uses them + auto sub1 = m_subCompositor->createSubSurface(child.data(), parent.data()); + sub1->setMode(SubSurface::Mode::Desynchronized); + auto sub2 = m_subCompositor->createSubSurface(grandChild.data(), child.data()); + sub2->setMode(SubSurface::Mode::Desynchronized); + + // let's damage this surface + // and at the same time delete the parent surface + parent.reset(); + QSignalSpy parentDestroyedSpy(serverParentSurface, &QObject::destroyed); + QVERIFY(parentDestroyedSpy.isValid()); + QVERIFY(parentDestroyedSpy.wait()); + QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::red); + grandChild->attachBuffer(m_shm->createBuffer(image)); + grandChild->damage(QRect(0, 0, 100, 100)); + grandChild->commit(Surface::CommitFlag::None); + QSignalSpy damagedSpy(serverGrandChildSurface, &SurfaceInterface::damaged); + QVERIFY(damagedSpy.isValid()); + QVERIFY(damagedSpy.wait()); + + // Let's try to destroy it + QSignalSpy destroySpy(serverChildSurface, &QObject::destroyed); + QVERIFY(destroySpy.isValid()); + child.reset(); + QVERIFY(destroySpy.wait()); +} + 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 @@ -360,6 +360,9 @@ QPointer SubSurfaceInterface::mainSurface() const { Q_D(); + if (!d->parent) { + return QPointer(); + } if (d->parent->d_func()->subSurface) { return d->parent->d_func()->subSurface->mainSurface(); } 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 @@ -453,8 +453,11 @@ emit q->damaged(target->damage); // workaround for https://bugreports.qt.io/browse/QTBUG-52092 // if the surface is a sub-surface, but the main surface is not yet mapped, fake frame rendered - if (subSurface && !subSurface->mainSurface()->buffer()) { - q->frameRendered(0); + if (subSurface) { + const auto mainSurface = subSurface->mainSurface(); + if (!mainSurface || !mainSurface->buffer()) { + q->frameRendered(0); + } } } }