From f14c04a021d2e433ad6226b51c75ca935f70f362 Mon Sep 17 00:00:00 2001 From: rewine Date: Tue, 28 Oct 2025 15:13:53 +0800 Subject: [PATCH] fix: fix corner radius and memory leak issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Fixed corner radius inheritance by passing initial radius parameter to prelaunch splash 2. Resolved memory leak by replacing direct destroy() call with signal- based destruction request 3. Improved surface item handling by adding proper change notifications 4. Enhanced type checking for mouse area to exclude undetermined surface types Log: Fixed window corner radius display and memory leak during splash screen cleanup Influence: 1. Test window creation with different corner radius values 2. Verify prelaunch splash screen properly disappears without memory leaks 3. Check mouse interaction works correctly on all surface types 4. Validate window decoration borders follow proper radius inheritance 5. Test surface type transitions from undetermined to normal state fix: 修复圆角和内存泄漏问题 1. 通过向预启动闪屏传递初始半径参数修复圆角继承问题 2. 使用基于信号的销毁请求替换直接destroy()调用解决内存泄漏 3. 添加适当的变更通知改进surface item处理 4. 增强鼠标区域的类型检查以排除未确定表面类型 Log: 修复窗口圆角显示和闪屏清理时的内存泄漏问题 Influence: 1. 测试不同圆角值下的窗口创建 2. 验证预启动闪屏正确消失且无内存泄漏 3. 检查所有表面类型上的鼠标交互是否正常 4. 验证窗口装饰边框是否遵循正确的圆角继承 5. 测试从未确定状态到正常状态的表面类型转换 --- src/core/qml/Decoration.qml | 7 +- src/core/qml/PrelaunchSplash.qml | 13 +-- src/core/qmlengine.cpp | 11 ++- src/core/qmlengine.h | 4 +- src/core/shellhandler.cpp | 2 +- src/surface/surfacewrapper.cpp | 149 ++++++++++++++++++------------- src/surface/surfacewrapper.h | 10 ++- 7 files changed, 117 insertions(+), 79 deletions(-) diff --git a/src/core/qml/Decoration.qml b/src/core/qml/Decoration.qml index ad421e5ef..37940f973 100644 --- a/src/core/qml/Decoration.qml +++ b/src/core/qml/Decoration.qml @@ -10,7 +10,6 @@ Item { id: root required property SurfaceWrapper surface - readonly property SurfaceItem surfaceItem: surface.surfaceItem visible: surface && surface.visibleDecoration && surface.visible x: shadow.boundingRect.x @@ -19,7 +18,9 @@ Item { height: shadow.boundingRect.height MouseArea { - enabled: surface.type !== SurfaceWrapper.Type.XdgPopup && surface.type !== SurfaceWrapper.Type.Layer + enabled: surface.type !== SurfaceWrapper.Type.XdgPopup + && surface.type !== SurfaceWrapper.Type.Layer + && surface.type !== SurfaceWrapper.Type.Undetermined property int edges: 0 anchors { @@ -73,7 +74,7 @@ Item { Border { visible: surface.visibleDecoration - parent: surfaceItem + parent: surface.surfaceItem ? surface.surfaceItem : surface.prelaunchSplash z: SurfaceItem.ZOrder.ContentItem + 1 anchors.fill: parent radius: surface.radius diff --git a/src/core/qml/PrelaunchSplash.qml b/src/core/qml/PrelaunchSplash.qml index 05c161ce8..0de3deaec 100644 --- a/src/core/qml/PrelaunchSplash.qml +++ b/src/core/qml/PrelaunchSplash.qml @@ -9,18 +9,19 @@ import QtQuick.Controls Item { id: splash - property string logoPath: "" + required property string logoPath + required property real initialRadius property bool destroyAfterFade: false + signal destroyRequested // Fill the entire parent (SurfaceWrapper) anchors.fill: parent Rectangle { - radius: 10 // TODO: use Decoration's radius id: background color: "#ffffff" - // Fill parent; size is dictated by parent anchors.fill: parent + radius: initialRadius // Centered logo Image { @@ -55,12 +56,14 @@ Item { target: splash from: 1.0 to: 0.0 - duration: 500 + duration: 400 onFinished: { splash.visible = false if (splash.destroyAfterFade) { - splash.destroy(); + // Request C++ side to destroy this item to avoid calling destroy() + // on an object owned by C++. + splash.destroyRequested(); } } } diff --git a/src/core/qmlengine.cpp b/src/core/qmlengine.cpp index fe74c2f9a..64940eb6d 100644 --- a/src/core/qmlengine.cpp +++ b/src/core/qmlengine.cpp @@ -244,11 +244,10 @@ QQuickItem *QmlEngine::createWindowPicker(QQuickItem *parent) return createComponent(windowPickerComponent, parent); } -QQuickItem *QmlEngine::createPrelaunchSplash(QQuickItem *parent, const QString &logoPath) +QQuickItem *QmlEngine::createPrelaunchSplash(QQuickItem *parent, const QString &logoPath, qreal initialRadius) { - QVariantMap properties; - if (!logoPath.isEmpty()) { - properties["logoPath"] = logoPath; - } - return createComponent(prelaunchSplashComponent, parent, properties); + return createComponent(prelaunchSplashComponent, parent, { + { "logoPath", QVariant::fromValue(logoPath) }, + { "initialRadius", QVariant::fromValue(initialRadius) }, + }); } diff --git a/src/core/qmlengine.h b/src/core/qmlengine.h index 56d4bf2f6..7a52dad03 100644 --- a/src/core/qmlengine.h +++ b/src/core/qmlengine.h @@ -63,7 +63,9 @@ class QmlEngine : public QQmlApplicationEngine QQuickItem *createShowDesktopAnimation(SurfaceWrapper *surface, QQuickItem *parent, bool show); QQuickItem *createCaptureSelector(QQuickItem *parent, CaptureManagerV1 *captureManager); QQuickItem *createWindowPicker(QQuickItem *parent); - QQuickItem *createPrelaunchSplash(QQuickItem *parent, const QString &logoPath = QString()); + QQuickItem *createPrelaunchSplash(QQuickItem *parent, + const QString &logoPath, + qreal initialRadius); QQmlComponent *surfaceContentComponent() { diff --git a/src/core/shellhandler.cpp b/src/core/shellhandler.cpp index e5d50bfc4..57ab50c98 100644 --- a/src/core/shellhandler.cpp +++ b/src/core/shellhandler.cpp @@ -85,7 +85,7 @@ void ShellHandler::updateWrapperContainer(SurfaceWrapper *wrapper, if (qobject_cast(oldContainer) == nullptr) { oldContainer->removeSurface(wrapper); m_workspace->addSurface(wrapper); - } + } // else do nothing, already in workspace } else { m_workspace->addSurface(wrapper); diff --git a/src/surface/surfacewrapper.cpp b/src/surface/surfacewrapper.cpp index dc35eb48b..4b36633bf 100644 --- a/src/surface/surfacewrapper.cpp +++ b/src/surface/surfacewrapper.cpp @@ -3,14 +3,12 @@ #include "surface/surfacewrapper.h" -#include "seat/helper.h" -#include "treelandconfig.hpp" +#include "common/treelandlogging.h" #include "core/qmlengine.h" #include "output/output.h" +#include "seat/helper.h" +#include "treelandconfig.hpp" #include "workspace/workspace.h" -#include "common/treelandlogging.h" - -#include #include #include @@ -91,23 +89,21 @@ SurfaceWrapper::SurfaceWrapper(QmlEngine *qmlEngine, QQuickItem *parent, const Q { QQmlEngine::setContextForObject(this, qmlEngine->rootContext()); if (initialSize.isValid() && initialSize.width() > 0 && initialSize.height() > 0) { - // Also set implicit size to keep QML layout consistent + // Also set implicit size to keep QML layout consistent setImplicitSize(initialSize.width(), initialSize.height()); qInfo() << "Prelaunch Splash: set initial size to" << initialSize; } else { setImplicitSize(800, 600); } - setNoDecoration(false); - - m_prelaunchSplash = m_engine->createPrelaunchSplash(this); - m_prelaunchSplash->setZ(9999999999); + m_prelaunchSplash = m_engine->createPrelaunchSplash(this, QString(), radius()); + m_prelaunchSplash->setZ(99999); + // Connect to QML signal so C++ can destroy the QML item when requested + connect(m_prelaunchSplash, + SIGNAL(destroyRequested()), + this, + SLOT(onPrelaunchSplashDestroyRequested())); - connect(m_prelaunchSplash, &QQuickItem::visibleChanged, - this, [this] { - if (m_surfaceItem) - setImplicitSize(m_surfaceItem->implicitWidth(), m_surfaceItem->implicitHeight()); - updateVisible(); - }); + setNoDecoration(false); } SurfaceWrapper::~SurfaceWrapper() @@ -183,53 +179,55 @@ void SurfaceWrapper::setup() requestCancelMinimize(); }); m_shellSurface->safeConnect(&WToplevelSurface::requestMaximize, - this, - &SurfaceWrapper::requestMaximize); + this, + &SurfaceWrapper::requestMaximize); m_shellSurface->safeConnect(&WToplevelSurface::requestCancelMaximize, - this, - &SurfaceWrapper::requestCancelMaximize); + this, + &SurfaceWrapper::requestCancelMaximize); m_shellSurface->safeConnect(&WToplevelSurface::requestMove, - this, - &SurfaceWrapper::requestMove); + this, + &SurfaceWrapper::requestMove); m_shellSurface->safeConnect(&WToplevelSurface::requestResize, - this, - [this](WSeat *, Qt::Edges edge, quint32) { - Q_EMIT requestResize(edge); - }); + this, + [this](WSeat *, Qt::Edges edge, quint32) { + Q_EMIT requestResize(edge); + }); m_shellSurface->safeConnect(&WToplevelSurface::requestFullscreen, - this, - &SurfaceWrapper::requestFullscreen); + this, + &SurfaceWrapper::requestFullscreen); m_shellSurface->safeConnect(&WToplevelSurface::requestCancelFullscreen, - this, - &SurfaceWrapper::requestCancelFullscreen); + this, + &SurfaceWrapper::requestCancelFullscreen); if (m_type == Type::XdgToplevel) { m_shellSurface->safeConnect(&WToplevelSurface::requestShowWindowMenu, - this, - [this](WSeat *, QPoint pos, quint32) { - Q_EMIT requestShowWindowMenu( - { pos.x() + m_surfaceItem->leftPadding(), - pos.y() + m_surfaceItem->topPadding() }); - }); + this, + [this](WSeat *, QPoint pos, quint32) { + Q_EMIT requestShowWindowMenu( + { pos.x() + m_surfaceItem->leftPadding(), + pos.y() + m_surfaceItem->topPadding() }); + }); } } m_shellSurface->surface()->safeConnect(&WSurface::mappedChanged, - this, - &SurfaceWrapper::onMappedChanged); + this, + &SurfaceWrapper::onMappedChanged); - connect(m_surfaceItem, - &WSurfaceItem::boundingRectChanged, - this, - &SurfaceWrapper::updateBoundingRect); - connect(m_surfaceItem, &WSurfaceItem::implicitWidthChanged, this, [this] { - setImplicitWidth(m_surfaceItem->implicitWidth()); - }); - connect(m_surfaceItem, &WSurfaceItem::implicitHeightChanged, this, [this] { - setImplicitHeight(m_surfaceItem->implicitHeight()); - }); - - if (!m_prelaunchSplash || !m_prelaunchSplash->isVisible()) + Q_EMIT surfaceItemChanged(); + + if (!m_prelaunchSplash || !m_prelaunchSplash->isVisible()) { setImplicitSize(m_surfaceItem->implicitWidth(), m_surfaceItem->implicitHeight()); + connect(m_surfaceItem, &WSurfaceItem::implicitWidthChanged, this, [this] { + setImplicitWidth(m_surfaceItem->implicitWidth()); + }); + connect(m_surfaceItem, &WSurfaceItem::implicitHeightChanged, this, [this] { + setImplicitHeight(m_surfaceItem->implicitHeight()); + }); + connect(m_surfaceItem, + &WSurfaceItem::boundingRectChanged, + this, + &SurfaceWrapper::updateBoundingRect); + } if (auto client = m_shellSurface->waylandClient()) { connect(client->socket(), @@ -322,14 +320,13 @@ void SurfaceWrapper::convertToNormalSurface(WToplevelSurface *shellSurface, Type // Assign new shell surface (QPointer auto-detects destruction) m_shellSurface = shellSurface; - m_type = type; // Call setup() to initialize surfaceItem related features setup(); + // setNoDecoration not called updateTitleBar when type is Undetermined + updateTitleBar(); - // After conversion refresh visibility and bounding rect - updateBoundingRect(); QMetaObject::invokeMethod(m_prelaunchSplash, "hideAndDestroy", Qt::QueuedConnection); } @@ -373,6 +370,32 @@ void SurfaceWrapper::setFocus(bool focus, Qt::FocusReason reason) m_surfaceItem->setFocus(false, reason); } +void SurfaceWrapper::onPrelaunchSplashDestroyRequested() +{ + if (m_surfaceItem) { + setImplicitSize(m_surfaceItem->implicitWidth(), m_surfaceItem->implicitHeight()); + connect(m_surfaceItem, &WSurfaceItem::implicitWidthChanged, this, [this] { + setImplicitWidth(m_surfaceItem->implicitWidth()); + }); + connect(m_surfaceItem, &WSurfaceItem::implicitHeightChanged, this, [this] { + setImplicitHeight(m_surfaceItem->implicitHeight()); + }); + connect(m_surfaceItem, + &WSurfaceItem::boundingRectChanged, + this, + &SurfaceWrapper::updateBoundingRect); + } + if (m_decoration) + m_decoration->stackBefore(m_surfaceItem); + updateVisible(); + + if (!m_prelaunchSplash) + return; + m_prelaunchSplash->deleteLater(); + m_prelaunchSplash = nullptr; + Q_EMIT prelaunchSplashChanged(); +} + WSurface *SurfaceWrapper::surface() const { if (!m_shellSurface) @@ -391,6 +414,11 @@ WSurfaceItem *SurfaceWrapper::surfaceItem() const return m_surfaceItem; } +QQuickItem *SurfaceWrapper::prelaunchSplash() const +{ + return m_prelaunchSplash; +} + bool SurfaceWrapper::resize(const QSizeF &size) { // No surfaceItem in prelaunch mode -> return false @@ -744,7 +772,7 @@ void SurfaceWrapper::setNoDecoration(bool newNoDecoration) return; m_noDecoration = newNoDecoration; - if (m_titleBarState == TitleBarState::Default) + if (m_titleBarState == TitleBarState::Default && m_type != Type::Undetermined) updateTitleBar(); if (m_noDecoration) { @@ -755,7 +783,7 @@ void SurfaceWrapper::setNoDecoration(bool newNoDecoration) } else { Q_ASSERT(!m_decoration); m_decoration = m_engine->createDecoration(this, this); - m_decoration->stackBefore(m_surfaceItem); + m_decoration->stackBefore(m_surfaceItem ? m_surfaceItem : m_prelaunchSplash); connect(m_decoration, &QQuickItem::xChanged, this, &SurfaceWrapper::updateBoundingRect); connect(m_decoration, &QQuickItem::yChanged, this, &SurfaceWrapper::updateBoundingRect); connect(m_decoration, &QQuickItem::widthChanged, this, &SurfaceWrapper::updateBoundingRect); @@ -775,8 +803,7 @@ void SurfaceWrapper::updateTitleBar() return; // No surfaceItem in prelaunch mode -> early return - if (!m_surfaceItem) - return; + Q_ASSERT(m_surfaceItem); if (noTitleBar() == !m_titleBar) return; @@ -951,7 +978,8 @@ void SurfaceWrapper::doSetSurfaceState(State newSurfaceState) if (m_wrapperAboutToRemove) return; - // In prelaunch mode there is no shellSurface; update state only without calling shellSurface methods + // In prelaunch mode there is no shellSurface; update state only without calling shellSurface + // methods if (!m_shellSurface) { m_previousSurfaceState.setValueBypassingBindings(m_surfaceState); m_surfaceState.setValueBypassingBindings(newSurfaceState); @@ -1097,7 +1125,7 @@ void SurfaceWrapper::onMappedChanged() bool mapped = surface()->mapped() && !m_hideByLockScreen; if (!m_isProxy) { if (mapped) { - //createNewOrClose(OPEN_ANIMATION); + // createNewOrClose(OPEN_ANIMATION); if (m_coverContent) { m_coverContent->setVisible(true); } @@ -1202,7 +1230,8 @@ qreal SurfaceWrapper::radius() const qreal radius = m_radius; - // TODO: Handle: XdgToplevel, popup, InputPopup, XWayland (bypass, window type: menu/normal/popup) + // TODO: Handle: XdgToplevel, popup, InputPopup, XWayland (bypass, window type: + // menu/normal/popup) if (radius < 1 && m_type != Type::Layer) { radius = Helper::instance()->config()->windowRadius(); } diff --git a/src/surface/surfacewrapper.h b/src/surface/surfacewrapper.h index bff498c87..21458ef49 100644 --- a/src/surface/surfacewrapper.h +++ b/src/surface/surfacewrapper.h @@ -6,7 +6,6 @@ #include #include -// 交叉淡出需要动画类 #include Q_MOC_INCLUDE() @@ -27,13 +26,14 @@ class SurfaceWrapper : public QQuickItem Q_OBJECT QML_ELEMENT QML_UNCREATABLE("SurfaceWrapper objects are created by c++") - Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(Type type READ type NOTIFY surfaceItemChanged) // make to read only Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged FINAL) Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged FINAL) Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WSurface* surface READ surface CONSTANT) Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WToplevelSurface* shellSurface READ shellSurface CONSTANT) - Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WSurfaceItem* surfaceItem READ surfaceItem CONSTANT) + Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WSurfaceItem* surfaceItem READ surfaceItem NOTIFY surfaceItemChanged) + Q_PROPERTY(QQuickItem* prelaunchSplash READ prelaunchSplash NOTIFY prelaunchSplashChanged) Q_PROPERTY(QRectF boundingRect READ boundingRect NOTIFY boundingRectChanged) Q_PROPERTY(QRectF geometry READ geometry NOTIFY geometryChanged FINAL) Q_PROPERTY(QRectF normalGeometry READ normalGeometry NOTIFY normalGeometryChanged FINAL) @@ -127,6 +127,7 @@ class SurfaceWrapper : public QQuickItem WSurface *surface() const; WToplevelSurface *shellSurface() const; WSurfaceItem *surfaceItem() const; + QQuickItem *prelaunchSplash() const; bool resize(const QSizeF &size); QRectF titlebarGeometry() const; @@ -307,6 +308,8 @@ public Q_SLOTS: void coverEnabledChanged(); void aboutToBeInvalidated(); void acceptKeyboardFocusChanged(); + void surfaceItemChanged(); + void prelaunchSplashChanged(); private: ~SurfaceWrapper() override; @@ -335,6 +338,7 @@ public Q_SLOTS: void doSetSurfaceState(State newSurfaceState); Q_SLOT void onAnimationReady(); Q_SLOT void onAnimationFinished(); + Q_SLOT void onPrelaunchSplashDestroyRequested(); bool startStateChangeAnimation(SurfaceWrapper::State targetState, const QRectF &targetGeometry); void onWindowAnimationFinished(); Q_SLOT void onShowAnimationFinished();