diff --git a/qwlroots/src/types/qwxwayland.h b/qwlroots/src/types/qwxwayland.h index e173cd257..d4eb15121 100644 --- a/qwlroots/src/types/qwxwayland.h +++ b/qwlroots/src/types/qwxwayland.h @@ -30,6 +30,9 @@ class QW_CLASS_OBJECT(xwayland) QW_FUNC_MEMBER(xwayland, set_seat, void, wlr_seat *seat) QW_FUNC_MEMBER(xwayland, get_xwm_connection, xcb_connection_t*) + +protected: + QW_FUNC_MEMBER(xwayland, destroy, void) }; QW_END_NAMESPACE diff --git a/src/core/treeland.cpp b/src/core/treeland.cpp index 4d4d215b1..07aaa953c 100644 --- a/src/core/treeland.cpp +++ b/src/core/treeland.cpp @@ -103,6 +103,10 @@ class TreelandPrivate : public QObject it->second->deleteLater(); pluginTs.erase(it++); } + // UserModel must be deleted before Helper, to remove client surfaces before rendering ends. + delete qmlEngine->singletonInstance("Treeland", "UserModel"); + // Helper must be deleted before QmlEngine, for surfaces to be cleanly removed. + qmlEngine->clearSingletons(); } #ifndef DISABLE_DDM @@ -490,6 +494,7 @@ void Treeland::quit() // make sure all deleted before app exit d_ptr.reset(); qApp->quit(); + // TODO: release drm master and return tty to text mode } } // namespace Treeland diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index fb5bd960d..17e7652f4 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -242,6 +242,8 @@ Helper::Helper(QObject *parent) Helper::~Helper() { + Q_ASSERT(m_instance == this); + m_instance = nullptr; for (auto s : m_rootSurfaceContainer->surfaces()) { m_rootSurfaceContainer->destroyForSurface(s); } @@ -249,8 +251,6 @@ Helper::~Helper() // destroy before m_rootSurfaceContainer delete m_shellHandler; delete m_rootSurfaceContainer; - Q_ASSERT(m_instance == this); - m_instance = nullptr; } Helper *Helper::instance() @@ -1609,12 +1609,18 @@ bool Helper::beforeDisposeEvent(WSeat *seat, QWindow *, QInputEvent *event) if (event->type() == QEvent::KeyPress) { auto kevent = static_cast(event); +#ifndef QT_NO_DEBUG + if (QKeySequence(kevent->keyCombination()) == + QKeySequence(Qt::MetaModifier | Qt::Key_F12)) { + std::terminate(); + } // The debug view shortcut should always handled first if (QKeySequence(kevent->keyCombination()) == QKeySequence(Qt::ControlModifier | Qt::ShiftModifier | Qt::MetaModifier | Qt::Key_F11)) { if (toggleDebugMenuBar()) return true; } +#endif // Switch TTY with Ctrl + Alt + F1-F12 if (kevent->modifiers() == (Qt::ControlModifier | Qt::AltModifier)) { diff --git a/src/seat/helper.h b/src/seat/helper.h index 12599129a..c644896d1 100644 --- a/src/seat/helper.h +++ b/src/seat/helper.h @@ -183,7 +183,6 @@ class Helper : public WSeatEventFilter void addSocket(WSocket *socket); [[nodiscard]] WXWayland *createXWayland(); - void removeXWayland(WXWayland *xwayland); PersonalizationV1 *personalization() const; diff --git a/src/session/session.cpp b/src/session/session.cpp index a91352afd..e743c9bf6 100644 --- a/src/session/session.cpp +++ b/src/session/session.cpp @@ -49,10 +49,16 @@ Session::~Session() delete m_settingManager; m_settingManager = nullptr; } - if (m_xwayland) - Helper::instance()->shellHandler()->removeXWayland(m_xwayland); - if (m_socket) + if (m_xwayland) { + // if shellHandler is already destructed, wait for WServer to clean up the interface. + if (auto *helper = Helper::instance()) + helper->shellHandler()->removeXWayland(m_xwayland); + m_xwayland = nullptr; + } + if (m_socket) { delete m_socket; + m_socket = nullptr; + } } int Session::id() const diff --git a/src/treeland-shortcut/shortcuts/_treeland-quit.ini b/src/treeland-shortcut/shortcuts/_treeland-quit.ini index 7a73fff6d..29315bf2a 100644 --- a/src/treeland-shortcut/shortcuts/_treeland-quit.ini +++ b/src/treeland-shortcut/shortcuts/_treeland-quit.ini @@ -1,5 +1,5 @@ [Shortcut] -Shortcut=Meta+F12 +Shortcut=Meta+Shift+Q Type="Action" [Type.Action] diff --git a/waylib/src/server/protocols/wxwayland.cpp b/waylib/src/server/protocols/wxwayland.cpp index 3b19101a9..d0964653b 100644 --- a/waylib/src/server/protocols/wxwayland.cpp +++ b/waylib/src/server/protocols/wxwayland.cpp @@ -56,6 +56,9 @@ class Q_DECL_HIDDEN WXWaylandPrivate : public WWrapObjectPrivate QList toplevelSurfaces; WSocket *socket = nullptr; + +protected: + void instantRelease() override; }; void WXWaylandPrivate::init() @@ -91,6 +94,10 @@ void WXWaylandPrivate::init() } } +void WXWaylandPrivate::instantRelease() { + delete handle(); +} + void WXWaylandPrivate::on_new_surface(wlr_xwayland_surface *xwl_surface) { W_Q(WXWayland); @@ -347,6 +354,9 @@ void WXWayland::destroy([[maybe_unused]] WServer *server) d->screen = nullptr; for (auto surface : std::as_const(list)) { + // disconnect from on_surface_destroy + disconnect(surface->handle(), &qw_xwayland_surface::before_destroy, + this, nullptr); removeSurface(surface); surface->safeDeleteLater(); } diff --git a/waylib/src/server/qtquick/private/wbufferrenderer.cpp b/waylib/src/server/qtquick/private/wbufferrenderer.cpp index 4bf504539..5ae0682e3 100644 --- a/waylib/src/server/qtquick/private/wbufferrenderer.cpp +++ b/waylib/src/server/qtquick/private/wbufferrenderer.cpp @@ -85,7 +85,17 @@ WBufferRenderer::WBufferRenderer(QQuickItem *parent) , m_cacheBuffer(true) , m_hideSource(false) { - + // ensure graphical resources are released before scene graph is invalidated + // since WBufferRenderer's ItemHasContent bit is unset + // the invalidateSceneGraph slot will not be called through QQuickWindowPrivate::cleanupNodesOnShutdown + QMetaObject::Connection windowConn; + if (window()) + windowConn = connect(window(), &QQuickWindow::sceneGraphInvalidated, this, &WBufferRenderer::invalidateSceneGraph); + connect(this, &QQuickItem::windowChanged, this, [this, windowConn](auto *window) mutable { + disconnect(windowConn); + if (window) + windowConn = connect(window, &QQuickWindow::sceneGraphInvalidated, this, &WBufferRenderer::invalidateSceneGraph); + }); } WBufferRenderer::~WBufferRenderer() @@ -142,7 +152,6 @@ void WBufferRenderer::setSourceList(QList sources, bool hideSource) return; resetSources(); - m_sourceList.clear(); m_hideSource = hideSource; for (auto s : std::as_const(sources)) { @@ -154,7 +163,7 @@ void WBufferRenderer::setSourceList(QList sources, bool hideSource) connect(s, &QQuickItem::destroyed, this, [this] { const int index = indexOfSource(static_cast(sender())); Q_ASSERT(index >= 0); - removeSource(index); + destroySource(index); m_sourceList.removeAt(index); }); @@ -687,11 +696,13 @@ void WBufferRenderer::invalidateSceneGraph() { if (m_textureProvider) m_textureProvider.reset(); + resetSources(); } void WBufferRenderer::releaseResources() { cleanTextureProvider(); + resetSources(); } void WBufferRenderer::cleanTextureProvider() @@ -723,19 +734,23 @@ void WBufferRenderer::cleanTextureProvider() void WBufferRenderer::resetSources() { for (int i = 0; i < m_sourceList.size(); ++i) { - removeSource(i); + destroySource(i); } + m_sourceList.clear(); } -void WBufferRenderer::removeSource(int index) +void WBufferRenderer::destroySource(int index) { - auto s = m_sourceList.at(index); + auto &s = m_sourceList[index]; if (isRootItem(s.source)) return; // Renderer of source is delay initialized in ensureRenderer. It might be null here. - if (s.renderer) - s.renderer->deleteLater(); + if (s.renderer) { + delete s.renderer; + s.renderer = nullptr; + } + auto d = QQuickItemPrivate::get(s.source); if (d->inDestructor) return; diff --git a/waylib/src/server/qtquick/private/wbufferrenderer_p.h b/waylib/src/server/qtquick/private/wbufferrenderer_p.h index a3e455657..8acfd09c4 100644 --- a/waylib/src/server/qtquick/private/wbufferrenderer_p.h +++ b/waylib/src/server/qtquick/private/wbufferrenderer_p.h @@ -106,7 +106,7 @@ class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem private: inline WOutputRenderWindow *renderWindow() const { - return qobject_cast(parent()); + return qobject_cast(window()); } inline bool shouldCacheBuffer() const { @@ -125,7 +125,7 @@ class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem } void resetSources(); - void removeSource(int index); + void destroySource(int index); int indexOfSource(QQuickItem *item); QSGRenderer *ensureRenderer(int sourceIndex, QSGRenderContext *rc); diff --git a/waylib/src/server/qtquick/private/wrenderbuffernode.cpp b/waylib/src/server/qtquick/private/wrenderbuffernode.cpp index e5d4a2370..460049d96 100644 --- a/waylib/src/server/qtquick/private/wrenderbuffernode.cpp +++ b/waylib/src/server/qtquick/private/wrenderbuffernode.cpp @@ -37,7 +37,17 @@ class Q_DECL_HIDDEN DataManagerBase : public QObject mutable QAtomicInt ref; explicit DataManagerBase(QQuickWindow *owner) - : QObject(owner) {} + : QObject(owner) + { + Q_ASSERT(owner->isSceneGraphInitialized()); + connect(owner, &QQuickWindow::sceneGraphInvalidated, this, [this]() { + setParent(nullptr); + // per request from zccrs. + // Be Warned: objects may not be expected to be deleted in the rendering thread. + delete this; + }, static_cast(Qt::DirectConnection | Qt::SingleShotConnection)); + } + virtual ~DataManagerBase() {}; }; template @@ -260,7 +270,7 @@ class Q_DECL_HIDDEN DataManager : public DataManagerBase } using QObject::deleteLater; - ~DataManager() { + ~DataManager() override { for (auto data : std::as_const(dataList)) { Derive::destroy(data->data); } @@ -452,7 +462,7 @@ class Q_DECL_HIDDEN RhiManager : public DataManager isBatchRenderer = dynamic_cast(renderer); } - ~RhiManager() { + ~RhiManager() override { delete renderer; }