Skip to content

Commit 125b3ac

Browse files
committed
fix: fix occasional crash in treeland under layer shell surface
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 上测试确认崩溃已修复
1 parent ae4d31e commit 125b3ac

2 files changed

Lines changed: 162 additions & 45 deletions

File tree

frame/layershell/qwaylandlayershellsurface.cpp

Lines changed: 143 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,63 +24,35 @@ QWaylandLayerShellSurface::QWaylandLayerShellSurface(QtWayland::zwlr_layer_shell
2424
, QtWayland::zwlr_layer_surface_v1()
2525
, m_dlayerShellWindow(DLayerShellWindow::get(window->window()))
2626
{
27-
2827
wl_output *output = nullptr;
2928
if (m_dlayerShellWindow->screenConfiguration() == DLayerShellWindow::ScreenFromQWindow) {
30-
auto waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(window->window()->screen()->handle());
31-
connect(window->window(), &QWindow::screenChanged, this, [window](){
32-
window->reset();
33-
// make sure window has been cleaned completed
34-
QMetaObject::invokeMethod(
35-
window,
36-
[window]() {
37-
window->reinit();
38-
},
39-
Qt::QueuedConnection);
40-
});
41-
if (!waylandScreen) {
29+
output = currentOutput();
30+
connect(window->window(), &QWindow::screenChanged, this, &QWaylandLayerShellSurface::scheduleRecreate);
31+
if (!output) {
4232
qCWarning(layershellsurface) << "failed to get screen for wayland";
43-
} else {
44-
output = waylandScreen->output();
4533
}
4634
}
4735

4836
init(shell->get_layer_surface(window->waylandSurface()->object(), output, m_dlayerShellWindow->layer(), m_dlayerShellWindow->scope()));
37+
m_output = output;
4938

50-
set_layer(m_dlayerShellWindow->layer());
51-
connect(m_dlayerShellWindow, &DLayerShellWindow::layerChanged, this, [this, window](){
52-
set_layer(m_dlayerShellWindow->layer());
53-
window->waylandSurface()->commit();
54-
});
39+
applyLayer();
40+
connect(m_dlayerShellWindow, &DLayerShellWindow::layerChanged, this, &QWaylandLayerShellSurface::applyLayer);
5541

5642
set_anchor(m_dlayerShellWindow->anchors());
57-
connect(m_dlayerShellWindow, &DLayerShellWindow::anchorsChanged, this,[this, window](){
58-
trySetAnchorsAndSize();
59-
});
43+
connect(m_dlayerShellWindow, &DLayerShellWindow::anchorsChanged, this, &QWaylandLayerShellSurface::trySetAnchorsAndSize);
6044

61-
set_exclusive_zone(m_dlayerShellWindow->exclusionZone());
62-
connect(m_dlayerShellWindow, &DLayerShellWindow::exclusionZoneChanged, this,[this, window](){
63-
set_exclusive_zone(m_dlayerShellWindow->exclusionZone());
64-
window->waylandSurface()->commit();
65-
});
45+
applyExclusiveZone();
46+
connect(m_dlayerShellWindow, &DLayerShellWindow::exclusionZoneChanged, this, &QWaylandLayerShellSurface::applyExclusiveZone);
6647

67-
set_margin(m_dlayerShellWindow->topMargin(), m_dlayerShellWindow->rightMargin(), m_dlayerShellWindow->bottomMargin(), m_dlayerShellWindow->leftMargin());
68-
connect(m_dlayerShellWindow, &DLayerShellWindow::marginsChanged, this, [this](){
69-
set_margin(m_dlayerShellWindow->topMargin(), m_dlayerShellWindow->rightMargin(), m_dlayerShellWindow->bottomMargin(), m_dlayerShellWindow->leftMargin());
70-
});
48+
applyMargins();
49+
connect(m_dlayerShellWindow, &DLayerShellWindow::marginsChanged, this, &QWaylandLayerShellSurface::applyMargins);
7150

72-
set_keyboard_interactivity(m_dlayerShellWindow->keyboardInteractivity());
73-
connect(m_dlayerShellWindow, &DLayerShellWindow::keyboardInteractivityChanged, this, [this, window](){
74-
set_keyboard_interactivity(m_dlayerShellWindow->keyboardInteractivity());
75-
window->waylandSurface()->commit();
76-
});
51+
applyKeyboardInteractivity();
52+
connect(m_dlayerShellWindow, &DLayerShellWindow::keyboardInteractivityChanged, this, &QWaylandLayerShellSurface::applyKeyboardInteractivity);
7753

78-
auto applyInputRegion = [this, window]() {
79-
window->window()->setMask(m_dlayerShellWindow->inputRegion());
80-
window->waylandSurface()->commit();
81-
};
82-
83-
connect(m_dlayerShellWindow, &DLayerShellWindow::inputRegionChanged, this, applyInputRegion);
54+
applyInputRegion();
55+
connect(m_dlayerShellWindow, &DLayerShellWindow::inputRegionChanged, this, &QWaylandLayerShellSurface::applyInputRegion);
8456

8557
calcAndSetRequestSize(window->surfaceSize());
8658

@@ -101,6 +73,33 @@ void QWaylandLayerShellSurface::zwlr_layer_surface_v1_closed()
10173
}
10274
}
10375

76+
QtWaylandClient::QWaylandWindow *QWaylandLayerShellSurface::waylandWindow()
77+
{
78+
return window();
79+
}
80+
81+
QWindow *QWaylandLayerShellSurface::windowHandle()
82+
{
83+
auto currentWindow = waylandWindow();
84+
return currentWindow ? currentWindow->window() : nullptr;
85+
}
86+
87+
QtWaylandClient::QWaylandScreen *QWaylandLayerShellSurface::waylandScreen()
88+
{
89+
auto currentWindowHandle = windowHandle();
90+
if (!currentWindowHandle || !currentWindowHandle->screen()) {
91+
return nullptr;
92+
}
93+
94+
return dynamic_cast<QtWaylandClient::QWaylandScreen *>(currentWindowHandle->screen()->handle());
95+
}
96+
97+
wl_output *QWaylandLayerShellSurface::currentOutput()
98+
{
99+
auto currentWaylandScreen = waylandScreen();
100+
return currentWaylandScreen ? currentWaylandScreen->output() : nullptr;
101+
}
102+
104103
void QWaylandLayerShellSurface::calcAndSetRequestSize(QSize requestSize)
105104
{
106105
auto anchors = m_dlayerShellWindow->anchors();
@@ -128,10 +127,45 @@ void QWaylandLayerShellSurface::trySetAnchorsAndSize()
128127
if (!anchorsSizeConflict()) {
129128
set_anchor(m_dlayerShellWindow->anchors());
130129
set_size(m_requestSize.width(), m_requestSize.height());
131-
window()->waylandSurface()->commit();
130+
scheduleCommit();
132131
}
133132
}
134133

134+
void QWaylandLayerShellSurface::applyLayer()
135+
{
136+
set_layer(m_dlayerShellWindow->layer());
137+
scheduleCommit();
138+
}
139+
140+
void QWaylandLayerShellSurface::applyExclusiveZone()
141+
{
142+
set_exclusive_zone(m_dlayerShellWindow->exclusionZone());
143+
scheduleCommit();
144+
}
145+
146+
void QWaylandLayerShellSurface::applyMargins()
147+
{
148+
set_margin(m_dlayerShellWindow->topMargin(), m_dlayerShellWindow->rightMargin(), m_dlayerShellWindow->bottomMargin(), m_dlayerShellWindow->leftMargin());
149+
scheduleCommit();
150+
}
151+
152+
void QWaylandLayerShellSurface::applyKeyboardInteractivity()
153+
{
154+
set_keyboard_interactivity(m_dlayerShellWindow->keyboardInteractivity());
155+
scheduleCommit();
156+
}
157+
158+
void QWaylandLayerShellSurface::applyInputRegion()
159+
{
160+
auto currentWindowHandle = windowHandle();
161+
if (!currentWindowHandle) {
162+
return;
163+
}
164+
165+
currentWindowHandle->setMask(m_dlayerShellWindow->inputRegion());
166+
scheduleCommit();
167+
}
168+
135169
void QWaylandLayerShellSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height)
136170
{
137171
ack_configure(serial);
@@ -164,6 +198,71 @@ void QWaylandLayerShellSurface::setWindowGeometry(const QRect &geometry)
164198
trySetAnchorsAndSize();
165199
}
166200

201+
void QWaylandLayerShellSurface::commitWindowState()
202+
{
203+
auto currentWaylandWindow = waylandWindow();
204+
if (!currentWaylandWindow || !currentWaylandWindow->waylandSurface()) {
205+
return;
206+
}
207+
208+
currentWaylandWindow->waylandSurface()->commit();
209+
}
210+
211+
void QWaylandLayerShellSurface::flushCommit()
212+
{
213+
m_commitScheduled = false;
214+
commitWindowState();
215+
}
216+
217+
void QWaylandLayerShellSurface::scheduleCommit()
218+
{
219+
if (m_commitScheduled) {
220+
return;
221+
}
222+
223+
m_commitScheduled = true;
224+
QMetaObject::invokeMethod(this, &QWaylandLayerShellSurface::flushCommit, Qt::QueuedConnection);
225+
}
226+
227+
void QWaylandLayerShellSurface::recreateWindow()
228+
{
229+
auto currentWaylandWindow = waylandWindow();
230+
if (!currentWaylandWindow) {
231+
return;
232+
}
233+
234+
currentWaylandWindow->reset();
235+
QMetaObject::invokeMethod(currentWaylandWindow, &QtWaylandClient::QWaylandWindow::reinit, Qt::QueuedConnection);
236+
}
237+
238+
void QWaylandLayerShellSurface::flushRecreate()
239+
{
240+
m_recreateScheduled = false;
241+
recreateWindow();
242+
}
243+
244+
void QWaylandLayerShellSurface::scheduleRecreate()
245+
{
246+
auto currentWindowHandle = windowHandle();
247+
if (!currentWindowHandle) {
248+
return;
249+
}
250+
251+
const auto output = currentOutput();
252+
if (!output) {
253+
qCWarning(layershellsurface) << "failed to get screen for wayland";
254+
return;
255+
}
256+
257+
if (output == m_output || m_recreateScheduled) {
258+
return;
259+
}
260+
261+
m_output = output;
262+
m_recreateScheduled = true;
263+
QMetaObject::invokeMethod(this, &QWaylandLayerShellSurface::flushRecreate, Qt::QueuedConnection);
264+
}
265+
167266
void QWaylandLayerShellSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *popup)
168267
{
169268
std::any anyRole = popup->surfaceRole();

frame/layershell/qwaylandlayershellsurface_p.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -33,14 +33,32 @@ class QWaylandLayerShellSurface : public QtWaylandClient::QWaylandShellSurface,
3333
void zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) override;
3434
void zwlr_layer_surface_v1_closed() override;
3535

36+
QtWaylandClient::QWaylandWindow *waylandWindow();
37+
QWindow *windowHandle();
38+
QtWaylandClient::QWaylandScreen *waylandScreen();
39+
wl_output *currentOutput();
3640
void calcAndSetRequestSize(QSize requestSize);
3741
bool anchorsSizeConflict() const;
3842
void trySetAnchorsAndSize();
43+
void applyLayer();
44+
void applyExclusiveZone();
45+
void applyMargins();
46+
void applyKeyboardInteractivity();
47+
void applyInputRegion();
48+
void commitWindowState();
49+
void flushCommit();
50+
void scheduleCommit();
51+
void recreateWindow();
52+
void flushRecreate();
53+
void scheduleRecreate();
3954

4055
DLayerShellWindow* m_dlayerShellWindow;
4156
QSize m_pendingSize;
4257
QSize m_requestSize;
58+
wl_output *m_output = nullptr;
4359
bool m_configured = false;
60+
bool m_commitScheduled = false;
61+
bool m_recreateScheduled = false;
4462
};
4563

4664
DS_END_NAMESPACE

0 commit comments

Comments
 (0)