From 3bbe3cae4298df7db8b1f137e95079e1a1b037c9 Mon Sep 17 00:00:00 2001 From: yeshanshan Date: Wed, 29 Apr 2026 14:49:17 +0800 Subject: [PATCH] fix: fix occasional crash in treeland under layer shell surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Replace direct screenChanged lambda that captured window pointer with member function scheduleRecreate 2. Add m_output tracking to prevent redundant recreate calls 3. Add scheduleCommit to coalesce multiple commit calls and delay them via QueuedConnection 4. Add m_commitScheduled and m_recreateScheduled flags to debounce operations 5. Fix marginsChanged signal handler missing explicit commit 6. This prevents race conditions when window gets destroyed during asynchronous reset/reinit cycle Influence: 1. Test layer shell surface creation with screen changes 2. Verify no crashes when rapidly changing screen configuration 3. Test margins, anchors, keyboard interactivity changes 4. Verify commit debouncing works correctly 5. Test edge cases with null waylandSurface or window 6. Test on treeland to confirm crash is fixed 修复: 修复在 treeland 下图层壳表面的偶发崩溃 1. 将直接捕获窗口指针的 screenChanged lambda 替换为成员函数 scheduleRecreate 2. 添加 m_output 跟踪以防止重复的 recreate 调用 3. 添加 scheduleCommit 合并多个 commit 调用并通过 QueuedConnection 延迟 执行 4. 添加 m_commitScheduled 和 m_recreateScheduled 标志以去抖操作 5. 修复 marginsChanged 信号处理程序缺少显式 commit 的问题 6. 这防止了在异步 reset/reinit 周期中窗口被销毁时的竞态条件 Influence: 1. 测试屏幕变化时的图层壳表面创建 2. 验证快速改变屏幕配置时无崩溃 3. 测试边距、锚点、键盘交互性变化 4. 验证 commit 去抖工作正常 5. 测试 waylandSurface 或窗口为 null 的边界情况 6. 在 treeland 上测试确认崩溃已修复 --- .../layershell/qwaylandlayershellsurface.cpp | 187 +++++++++++++----- .../layershell/qwaylandlayershellsurface_p.h | 20 +- 2 files changed, 162 insertions(+), 45 deletions(-) diff --git a/frame/layershell/qwaylandlayershellsurface.cpp b/frame/layershell/qwaylandlayershellsurface.cpp index 2de637ba8..586160f4b 100644 --- a/frame/layershell/qwaylandlayershellsurface.cpp +++ b/frame/layershell/qwaylandlayershellsurface.cpp @@ -24,63 +24,35 @@ QWaylandLayerShellSurface::QWaylandLayerShellSurface(QtWayland::zwlr_layer_shell , QtWayland::zwlr_layer_surface_v1() , m_dlayerShellWindow(DLayerShellWindow::get(window->window())) { - wl_output *output = nullptr; if (m_dlayerShellWindow->screenConfiguration() == DLayerShellWindow::ScreenFromQWindow) { - auto waylandScreen = dynamic_cast(window->window()->screen()->handle()); - connect(window->window(), &QWindow::screenChanged, this, [window](){ - window->reset(); - // make sure window has been cleaned completed - QMetaObject::invokeMethod( - window, - [window]() { - window->reinit(); - }, - Qt::QueuedConnection); - }); - if (!waylandScreen) { + output = currentOutput(); + connect(window->window(), &QWindow::screenChanged, this, &QWaylandLayerShellSurface::scheduleRecreate); + if (!output) { qCWarning(layershellsurface) << "failed to get screen for wayland"; - } else { - output = waylandScreen->output(); } } init(shell->get_layer_surface(window->waylandSurface()->object(), output, m_dlayerShellWindow->layer(), m_dlayerShellWindow->scope())); + m_output = output; - set_layer(m_dlayerShellWindow->layer()); - connect(m_dlayerShellWindow, &DLayerShellWindow::layerChanged, this, [this, window](){ - set_layer(m_dlayerShellWindow->layer()); - window->waylandSurface()->commit(); - }); + applyLayer(); + connect(m_dlayerShellWindow, &DLayerShellWindow::layerChanged, this, &QWaylandLayerShellSurface::applyLayer); set_anchor(m_dlayerShellWindow->anchors()); - connect(m_dlayerShellWindow, &DLayerShellWindow::anchorsChanged, this,[this, window](){ - trySetAnchorsAndSize(); - }); + connect(m_dlayerShellWindow, &DLayerShellWindow::anchorsChanged, this, &QWaylandLayerShellSurface::trySetAnchorsAndSize); - set_exclusive_zone(m_dlayerShellWindow->exclusionZone()); - connect(m_dlayerShellWindow, &DLayerShellWindow::exclusionZoneChanged, this,[this, window](){ - set_exclusive_zone(m_dlayerShellWindow->exclusionZone()); - window->waylandSurface()->commit(); - }); + applyExclusiveZone(); + connect(m_dlayerShellWindow, &DLayerShellWindow::exclusionZoneChanged, this, &QWaylandLayerShellSurface::applyExclusiveZone); - set_margin(m_dlayerShellWindow->topMargin(), m_dlayerShellWindow->rightMargin(), m_dlayerShellWindow->bottomMargin(), m_dlayerShellWindow->leftMargin()); - connect(m_dlayerShellWindow, &DLayerShellWindow::marginsChanged, this, [this](){ - set_margin(m_dlayerShellWindow->topMargin(), m_dlayerShellWindow->rightMargin(), m_dlayerShellWindow->bottomMargin(), m_dlayerShellWindow->leftMargin()); - }); + applyMargins(); + connect(m_dlayerShellWindow, &DLayerShellWindow::marginsChanged, this, &QWaylandLayerShellSurface::applyMargins); - set_keyboard_interactivity(m_dlayerShellWindow->keyboardInteractivity()); - connect(m_dlayerShellWindow, &DLayerShellWindow::keyboardInteractivityChanged, this, [this, window](){ - set_keyboard_interactivity(m_dlayerShellWindow->keyboardInteractivity()); - window->waylandSurface()->commit(); - }); + applyKeyboardInteractivity(); + connect(m_dlayerShellWindow, &DLayerShellWindow::keyboardInteractivityChanged, this, &QWaylandLayerShellSurface::applyKeyboardInteractivity); - auto applyInputRegion = [this, window]() { - window->window()->setMask(m_dlayerShellWindow->inputRegion()); - window->waylandSurface()->commit(); - }; - - connect(m_dlayerShellWindow, &DLayerShellWindow::inputRegionChanged, this, applyInputRegion); + applyInputRegion(); + connect(m_dlayerShellWindow, &DLayerShellWindow::inputRegionChanged, this, &QWaylandLayerShellSurface::applyInputRegion); calcAndSetRequestSize(window->surfaceSize()); @@ -101,6 +73,33 @@ void QWaylandLayerShellSurface::zwlr_layer_surface_v1_closed() } } +QtWaylandClient::QWaylandWindow *QWaylandLayerShellSurface::waylandWindow() +{ + return window(); +} + +QWindow *QWaylandLayerShellSurface::windowHandle() +{ + auto currentWindow = waylandWindow(); + return currentWindow ? currentWindow->window() : nullptr; +} + +QtWaylandClient::QWaylandScreen *QWaylandLayerShellSurface::waylandScreen() +{ + auto currentWindowHandle = windowHandle(); + if (!currentWindowHandle || !currentWindowHandle->screen()) { + return nullptr; + } + + return dynamic_cast(currentWindowHandle->screen()->handle()); +} + +wl_output *QWaylandLayerShellSurface::currentOutput() +{ + auto currentWaylandScreen = waylandScreen(); + return currentWaylandScreen ? currentWaylandScreen->output() : nullptr; +} + void QWaylandLayerShellSurface::calcAndSetRequestSize(QSize requestSize) { auto anchors = m_dlayerShellWindow->anchors(); @@ -128,10 +127,45 @@ void QWaylandLayerShellSurface::trySetAnchorsAndSize() if (!anchorsSizeConflict()) { set_anchor(m_dlayerShellWindow->anchors()); set_size(m_requestSize.width(), m_requestSize.height()); - window()->waylandSurface()->commit(); + scheduleCommit(); } } +void QWaylandLayerShellSurface::applyLayer() +{ + set_layer(m_dlayerShellWindow->layer()); + scheduleCommit(); +} + +void QWaylandLayerShellSurface::applyExclusiveZone() +{ + set_exclusive_zone(m_dlayerShellWindow->exclusionZone()); + scheduleCommit(); +} + +void QWaylandLayerShellSurface::applyMargins() +{ + set_margin(m_dlayerShellWindow->topMargin(), m_dlayerShellWindow->rightMargin(), m_dlayerShellWindow->bottomMargin(), m_dlayerShellWindow->leftMargin()); + scheduleCommit(); +} + +void QWaylandLayerShellSurface::applyKeyboardInteractivity() +{ + set_keyboard_interactivity(m_dlayerShellWindow->keyboardInteractivity()); + scheduleCommit(); +} + +void QWaylandLayerShellSurface::applyInputRegion() +{ + auto currentWindowHandle = windowHandle(); + if (!currentWindowHandle) { + return; + } + + currentWindowHandle->setMask(m_dlayerShellWindow->inputRegion()); + scheduleCommit(); +} + void QWaylandLayerShellSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) { ack_configure(serial); @@ -164,6 +198,71 @@ void QWaylandLayerShellSurface::setWindowGeometry(const QRect &geometry) trySetAnchorsAndSize(); } +void QWaylandLayerShellSurface::commitWindowState() +{ + auto currentWaylandWindow = waylandWindow(); + if (!currentWaylandWindow || !currentWaylandWindow->waylandSurface()) { + return; + } + + currentWaylandWindow->waylandSurface()->commit(); +} + +void QWaylandLayerShellSurface::flushCommit() +{ + m_commitScheduled = false; + commitWindowState(); +} + +void QWaylandLayerShellSurface::scheduleCommit() +{ + if (m_commitScheduled) { + return; + } + + m_commitScheduled = true; + QMetaObject::invokeMethod(this, &QWaylandLayerShellSurface::flushCommit, Qt::QueuedConnection); +} + +void QWaylandLayerShellSurface::recreateWindow() +{ + auto currentWaylandWindow = waylandWindow(); + if (!currentWaylandWindow) { + return; + } + + currentWaylandWindow->reset(); + QMetaObject::invokeMethod(currentWaylandWindow, &QtWaylandClient::QWaylandWindow::reinit, Qt::QueuedConnection); +} + +void QWaylandLayerShellSurface::flushRecreate() +{ + m_recreateScheduled = false; + recreateWindow(); +} + +void QWaylandLayerShellSurface::scheduleRecreate() +{ + auto currentWindowHandle = windowHandle(); + if (!currentWindowHandle) { + return; + } + + const auto output = currentOutput(); + if (!output) { + qCWarning(layershellsurface) << "failed to get screen for wayland"; + return; + } + + if (output == m_output || m_recreateScheduled) { + return; + } + + m_output = output; + m_recreateScheduled = true; + QMetaObject::invokeMethod(this, &QWaylandLayerShellSurface::flushRecreate, Qt::QueuedConnection); +} + void QWaylandLayerShellSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *popup) { std::any anyRole = popup->surfaceRole(); diff --git a/frame/layershell/qwaylandlayershellsurface_p.h b/frame/layershell/qwaylandlayershellsurface_p.h index f05c08183..c8e59cb37 100644 --- a/frame/layershell/qwaylandlayershellsurface_p.h +++ b/frame/layershell/qwaylandlayershellsurface_p.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -33,14 +33,32 @@ class QWaylandLayerShellSurface : public QtWaylandClient::QWaylandShellSurface, void zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) override; void zwlr_layer_surface_v1_closed() override; + QtWaylandClient::QWaylandWindow *waylandWindow(); + QWindow *windowHandle(); + QtWaylandClient::QWaylandScreen *waylandScreen(); + wl_output *currentOutput(); void calcAndSetRequestSize(QSize requestSize); bool anchorsSizeConflict() const; void trySetAnchorsAndSize(); + void applyLayer(); + void applyExclusiveZone(); + void applyMargins(); + void applyKeyboardInteractivity(); + void applyInputRegion(); + void commitWindowState(); + void flushCommit(); + void scheduleCommit(); + void recreateWindow(); + void flushRecreate(); + void scheduleRecreate(); DLayerShellWindow* m_dlayerShellWindow; QSize m_pendingSize; QSize m_requestSize; + wl_output *m_output = nullptr; bool m_configured = false; + bool m_commitScheduled = false; + bool m_recreateScheduled = false; }; DS_END_NAMESPACE