From 183e5160fe5a4cc9995f50836da2a3984c8d5b71 Mon Sep 17 00:00:00 2001 From: deepin-wm Date: Thu, 18 Jun 2026 20:50:10 +0800 Subject: [PATCH] fix: fix Layer Shell popup positioning race condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add parent geometry validity check in handleLayerShellPopup() with hasInitializeContainer and parentGeo.isEmpty() guards 2. Add hasInitializeContainer() check for Layer type parent in arrangePopupSurface() 3. Connect parent Layer Surface signals (normalGeometryChanged, hasInitializeContainerChanged) in addSurface() to trigger popup repositioning using QPointer for safety 4. Use QMetaObject::Connection with m_parentLayerConnections hash to manage signal connection lifecycle 5. Disconnect parent surface signals in removeSurface() for proper cleanup when popup is removed 6. Add #include explicit declaration Log: Fixed UOS ID login window intermittently displaying abnormally and becoming unclickable Influence: 1. Test UOS ID login window opens and displays correctly every time 2. Test Layer Shell popup positioning after parent surface resize 3. Test popup cleanup when parent surface is destroyed 4. Test other Layer Shell popups (menus, tooltips) still work fix: 修复Layer Shell弹窗定位时序竞态问题 1. 在handleLayerShellPopup()中增加父窗口几何有效性检查, 使用hasInitializeContainer和parentGeo.isEmpty()守卫 2. 在arrangePopupSurface()中对Layer类型父窗口增加 hasInitializeContainer()检查 3. 在addSurface()中连接父Layer Surface信号 (normalGeometryChanged, hasInitializeContainerChanged) 触发弹窗重新定位,使用QPointer保证安全 4. 使用QMetaObject::Connection配合m_parentLayerConnections 哈希表管理信号连接生命周期 5. 在removeSurface()中断开父表面信号连接,确保弹窗移除时 正确清理 6. 添加#include 显式声明 Log: 修复UOS ID登录窗口概率性显示异常不可点击的问题 Influence: 1. 测试UOS ID登录窗口每次打开均正常显示 2. 测试父表面调整大小后Layer Shell弹窗定位正确 3. 测试父表面销毁时弹窗正确清理 4. 测试其他Layer Shell弹窗(菜单、工具提示)正常工作 --- src/output/output.cpp | 61 ++++++++++++++++++++++++++++++++++++++++--- src/output/output.h | 1 + 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/output/output.cpp b/src/output/output.cpp index bf1be4f4b..c36684eba 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -454,6 +455,28 @@ void Output::addSurface(SurfaceWrapper *surface) // Reposition should ignore positionAutomatic arrangePopupSurface(surface); }); + + auto parentLayerSurface = surface->parentSurface(); + if (parentLayerSurface + && parentLayerSurface->type() == SurfaceWrapper::Type::Layer) { + QPointer surfacePtr(surface); + auto conn1 = connect(parentLayerSurface, &SurfaceWrapper::normalGeometryChanged, + this, [surfacePtr, this] { + if (!surfacePtr) return; + QMetaObject::invokeMethod(this, [surfacePtr, this] { + if (!surfacePtr) return; + arrangePopupSurface(surfacePtr); + }, Qt::QueuedConnection); + }); + auto conn2 = connect(parentLayerSurface, &SurfaceWrapper::hasInitializeContainerChanged, + this, [surfacePtr, this] { + if (!surfacePtr) return; + if (!surfacePtr->parentSurface() + || !surfacePtr->parentSurface()->hasInitializeContainer()) return; + arrangePopupSurface(surfacePtr); + }); + m_parentLayerConnections[surface] = {conn1, conn2}; + } } else if (surface->type() == SurfaceWrapper::Type::InputPopup) { auto inputPopupSurfaceItem = qobject_cast(surface->surfaceItem()); connect(inputPopupSurfaceItem, &WInputPopupSurfaceItem::referenceRectChanged, this, [surface, this] { @@ -469,6 +492,16 @@ void Output::removeSurface(SurfaceWrapper *surface) clearPopupCache(surface); m_initialWindowPositionRatio.remove(surface); Q_ASSERT(hasSurface(surface)); + + if (surface->type() == SurfaceWrapper::Type::XdgPopup) { + auto it = m_parentLayerConnections.find(surface); + if (it != m_parentLayerConnections.end()) { + for (auto &conn : it.value()) + QObject::disconnect(conn); + m_parentLayerConnections.erase(it); + } + } + SurfaceListModel::removeSurface(surface); surface->disconnect(this); @@ -846,7 +879,21 @@ void Output::handleLayerShellPopup(SurfaceWrapper *surface, const QRectF &normal return; } - auto parentOutput = surface->parentSurface()->ownsOutput()->outputItem(); + auto parentSurface = surface->parentSurface(); + if (!parentSurface->hasInitializeContainer()) { + qCInfo(lcTlOutput) << " LayerShell parent not initialized, deferring popup positioning" + << "surface=" << surface << "parent=" << parentSurface; + return; + } + + QRectF parentGeo = parentSurface->normalGeometry(); + if (parentGeo.isEmpty()) { + qCInfo(lcTlOutput) << " LayerShell parent geometry is empty, deferring popup positioning" + << "surface=" << surface << "parentGeo=" << parentGeo; + return; + } + + auto parentOutput = parentSurface->ownsOutput()->outputItem(); auto dPos = popupDPos(surface); if (!dPos.has_value()) return; @@ -917,13 +964,21 @@ void Output::arrangePopupSurface(SurfaceWrapper *surface) { SurfaceWrapper *parentSurfaceWrapper = surface->parentSurface(); if (!parentSurfaceWrapper) { - // When an input popup is still alive while its parent text-input client is being torn down, - // arrangePopupSurface() can run in a transient state where parentSurface is temporarily unavailable. + // When an input popup is still alive while its parent text-input client is being torn down, + // arrangePopupSurface() can run in a transient state where parentSurface is temporarily unavailable. qCWarning(lcTlSurface) << "[popup] skip arrangePopupSurface: missing parent surface" << "surface=" << surface; return; } + if (parentSurfaceWrapper->type() == SurfaceWrapper::Type::Layer + && !parentSurfaceWrapper->hasInitializeContainer()) { + qCInfo(lcTlSurface) << "[popup] skip arrangePopupSurface: LayerShell parent not initialized" + << "surface=" << surface + << "parentSurface=" << parentSurfaceWrapper; + return; + } + QRectF normalGeo = surface->normalGeometry(); if (normalGeo.isEmpty()) { return; diff --git a/src/output/output.h b/src/output/output.h index b0bfa5fb2..e00905744 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -158,6 +158,7 @@ public Q_SLOTS: QMap> m_positionCache; QHash m_initialWindowPositionRatio; + QHash> m_parentLayerConnections; std::unique_ptr m_backlight = nullptr; OutputConfig *m_config;