Skip to content

fix: fix application tray crash due to parent-child ownership and null pointer issues#464

Open
18202781743 wants to merge 7 commits into
linuxdeepin:masterfrom
18202781743:agent/agent/b3d3b5cf
Open

fix: fix application tray crash due to parent-child ownership and null pointer issues#464
18202781743 wants to merge 7 commits into
linuxdeepin:masterfrom
18202781743:agent/agent/b3d3b5cf

Conversation

@18202781743

@18202781743 18202781743 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

问题描述

应用程序托盘在 SNI (StatusNotifierItem) 托盘项注销时崩溃。

根本原因

  1. 双重生命周期管理SniTrayProtocolHandler 同时被 QSharedPointer 和 QObject 父子关系管理,导致双重删除
  2. 悬空指针m_tooltip 中存储的 widget 指针在 handler 销毁后变成悬空指针
  3. 空指针访问eventFilter 中使用 parent() 获取 widget,但移除 setParent(this) 后返回错误对象

修复内容

traywidget.cpp

  • 移除 m_handler->setParent(this) 避免双重生命周期管理
  • showEventpaintEvent 中添加 m_handler 空指针检查

sniprotocolhandler.cpp

  • eventFilter 中将 parent() 替换为 window()
  • 添加 windowHandle() 的空指针检查

ddeindicatortrayprotocol.cpp

  • updateContent 中将 q->parent() 替换为 q->window()
  • eventFilter 中将 parent() 替换为 window(),添加空指针检查

trayplugin.cpp

  • remove lambda 中,先清理 m_tooltip,再调用 itemRemoved,避免访问悬空指针

测试

修复后需要验证:

  1. SNI 托盘项正常显示和移除
  2. 右键菜单正常弹出
  3. 左键激活功能正常
  4. 不再出现崩溃

🤖 Generated with Claude Code

Summary by Sourcery

Integrate Wayland xdg-activation support into the application tray and harden tray item lifecycle handling to prevent crashes when SNI items are removed.

New Features:

  • Add Wayland xdg-activation client for tray, requesting and forwarding activation tokens before activating SNI items.

Bug Fixes:

  • Prevent application tray crashes by avoiding double ownership of tray protocol handlers, cleaning up tooltip pointers before handler destruction, and guarding against null windows in event handling.

Enhancements:

  • Use window() instead of parent() for tray widgets and indicator handlers, adding null checks to make painting and context menu handling more robust.

Build:

  • Extend CMake configuration to locate Wayland protocols via pkg-config, generate xdg-activation client sources, and link against Qt Wayland and Wayland client libraries.

18202781743 and others added 7 commits June 4, 2026 11:44
Add XdgActivationClient that uses QWaylandClientExtension to bind
dde-shell's xdg_activation_v1 global and request activation tokens
via standard Wayland protocol before calling SNI Activate.

Modified sniprotocolhandler to request token asynchronously before
calling Activate, setting XDG_ACTIVATION_TOKEN env var.

Log: SNI托盘图标Wayland下点击时申请xdgactivation token激活DBus窗口
Issue: linuxdeepin#6
Use captured provider pointer instead of sender() which returns
nullptr in lambda context and causes crash when calling deleteLater().

Fixes: sender()->deleteLater() crash
pointer issues

1. Fix crash in DDEindicatorTrayProtocol by using window() instead of
parent() for event filter and content updates
2. Add null pointer checks in DDEindicatorTrayProtocol event filter
3. Fix crash in SniTrayProtocolHandler by using window() instead of
parent() and adding null checks
4. Fix tooltip removal order in TrayPlugin to avoid dangling pointer
references
5. Fix double-delete crash in TrayWidget by not setting handler as child
widget
6. Add null handler checks in TrayWidget showEvent and paintEvent

Log: Fixed application tray crashes when tray items are added/removed

Influence:
1. Test adding and removing multiple tray items
2. Test tray indicator protocol operations
3. Test sni protocol tray operations
4. Test tray widget paint and show events
5. Test tray icon updates and tooltip display
6. Test application tray with various indicator types

feat: 修复应用托盘因父子关系和空指针导致的崩溃问题

1. 修复 DDEindicatorTrayProtocol 中,使用 window() 替代 parent() 进行事
件过滤和内容更新
2. 在 DDEindicatorTrayProtocol 的事件过滤器中添加空指针检查
3. 修复 SniTrayProtocolHandler 中,使用 window() 替代 parent() 并添加空
指针检查
4. 修复 TrayPlugin 中 tooltip 移除顺序,避免悬空指针引用
5. 修复 TrayWidget 中不将 handler 设置为子控件导致的二次删除崩溃
6. 在 TrayWidget 的 showEvent 和 paintEvent 中添加 handler 空指针检查

Log: 修复添加/移除托盘项时应用托盘崩溃的问题

Influence:
1. 测试添加和移除多个托盘项
2. 测试托盘指示协议操作
3. 测试 sni 协议托盘操作
4. 测试托盘控件的绘制和显示事件
5. 测试托盘图标更新和提示信息显示
6. 测试各种指示类型下的应用托盘功能
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors application tray SNI handling to avoid parent/child vs smart-pointer double-ownership, fixes null/dangling pointer paths in event handling and painting, and adds Wayland xdg-activation integration so left-click activation is properly tokenized on Wayland while keeping X11 behavior intact.

Sequence diagram for SNI left-click activation with Wayland xdg-activation

sequenceDiagram
    actor User
    participant TrayWidget
    participant SniTrayProtocolHandler
    participant XdgActivationClient
    participant StatusNotifierItem

    User->>TrayWidget: [Left click]
    TrayWidget->>SniTrayProtocolHandler: eventFilter(QEvent)

    alt [activationClient isActive]
        SniTrayProtocolHandler->>XdgActivationClient: instance()
        SniTrayProtocolHandler->>XdgActivationClient: requestToken(QWindow, QString, TokenCallback)
        XdgActivationClient-->>SniTrayProtocolHandler: TokenCallback(token)
        alt [token not empty]
            SniTrayProtocolHandler->>StatusNotifierItem: ProvideXdgActivationToken(token)
        end
        SniTrayProtocolHandler->>StatusNotifierItem: Activate(0, 0)
    else [activationClient not active]
        SniTrayProtocolHandler->>StatusNotifierItem: Activate(0, 0)
    end
Loading

File-Level Changes

Change Details Files
Avoid double-deletion and dangling pointers in tray widget/handler lifecycle and tooltip management.
  • Stopped assigning TrayWidget as QObject parent of tray protocol handlers since their lifetime is owned by QSharedPointer.
  • Added null-checks around m_handler before using it in TrayWidget::showEvent and paintEvent to prevent access after handler destruction.
  • Adjusted TrayPlugin removal lambda to remove tooltip entries before emitting itemRemoved and destroying widgets, eliminating use of freed tooltip widgets.
plugins/application-tray/traywidget.cpp
plugins/application-tray/trayplugin.cpp
Harden SNI and DDE indicator event handling against null widgets and incorrect parent usage.
  • Replaced uses of parent() with window()/window()->windowHandle() in SniTrayProtocolHandler and DDEindicatorProtocolHandler to rely on the actual window instead of QObject parenting.
  • Added null checks on window()/windowHandle() in SNI left-click activation and context menu paths, early-returning when no valid window is available.
  • Updated DDEindicatorProtocolHandler painting and content updates to guard against null window pointers and use window() consistently as the watched object.
plugins/application-tray/sniprotocolhandler.cpp
plugins/application-tray/ddeindicatortrayprotocol.cpp
Integrate Wayland xdg-activation protocol for tray icon activation and wire it into the SNI handler.
  • Introduced tray::XdgActivationClient and supporting XdgActivationV1/XdgActivationTokenV1 classes to wrap the xdg-activation-v1 Wayland protocol and request activation tokens asynchronously.
  • Extended the SNI D-Bus interface with ProvideXdgActivationToken() so tokens can be forwarded to the StatusNotifierItem implementation before Activate().
  • Modified SniTrayProtocolHandler left-click handling to request an xdg-activation token (when the client extension is active) and call ProvideXdgActivationToken + Activate once the token is received, falling back to direct Activate on non-Wayland/non-active setups.
src/tray-wayland-integration/xdgactivationclient.h
src/tray-wayland-integration/xdgactivationclient.cpp
plugins/application-tray/sniprotocolhandler.cpp
plugins/application-tray/api/dbus/org.kde.StatusNotifierItem.xml
Update CMake build system to pull in Wayland/xgd-activation dependencies and share xdg-activation client sources with the application tray plugin.
  • Required Qt WaylandClient (and private Gui/WaylandClient components for newer Qt) and WaylandClient pkg-config module for the application-tray plugin, and linked the new libraries.
  • Hooked qt_generate_wayland_protocol_client_sources for xdg-activation-v1 in both the main tray-wayland-integration library and the application-tray plugin.
  • Exposed WAYLAND_PROTOCOLS_DATADIR via pkg-config in the top-level CMake so both components can locate the xdg-activation protocol XML, and added xdgactivationclient.{h,cpp} to both dockpluginmanager and application-tray builds.
CMakeLists.txt
src/tray-wayland-integration/CMakeLists.txt
plugins/application-tray/CMakeLists.txt
Minor metadata and licensing header adjustments.
  • Normalized SPDX copyright headers to include year ranges where missing or inconsistent across tray-related sources.
plugins/application-tray/ddeindicatortrayprotocol.cpp
plugins/application-tray/traywidget.cpp
plugins/application-tray/trayplugin.cpp
CMakeLists.txt
src/tray-wayland-integration/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

这份代码变更主要实现了在 Wayland 环境下通过 xdg-activation-v1 协议为系统托盘(SNI/XDG 规范)提供激活令牌的功能,同时修复了部分 Qt 对象生命周期管理(如 parent 设置导致的 double-delete)和悬空指针的问题。

整体来看,代码逻辑清晰,对 Wayland 底层协议的封装和使用基本正确。但经过仔细审查,在语法逻辑、代码质量、性能和安全方面仍有以下改进意见:

1. 语法与逻辑

1.1 单例模式存在线程安全与内存泄漏风险

文件: xdgactivationclient.cpp

XdgActivationClient *XdgActivationClient::instance()
{
    static XdgActivationClient *s_instance = nullptr;
    if (!s_instance) {
        s_instance = new XdgActivationClient(qApp);
    }
    return s_instance;
}

问题:

  1. 线程安全:此处的 new 操作不是线程安全的,如果多个线程同时首次调用 instance(),可能会创建多个实例。
  2. 内存泄漏/重复析构:虽然将 qApp 作为 parent 理论上可以让应用退出时自动释放,但在 C++11 及以上标准中,更推荐使用原生线程安全且无需显式 new 的静态局部变量。

改进建议:

XdgActivationClient *XdgActivationClient::instance()
{
    static XdgActivationClient s_instance{qApp};
    return &s_instance;
}

注意:如果改为这种方式,需要将构造函数和析构函数从 private 移到 public(或保持 private 并通过 friend 访问),并删除类声明中的 ~XdgActivationClient() override; 实现以使用编译器默认生成的。

1.2 requestToken 不支持并发请求

文件: xdgactivationclient.cpp

if (m_pendingProvider) {
    qCWarning(trayXdgActivation) << "Token request already in progress";
    if (callback) callback(QString());
    return;
}

问题: 如果在极短时间内有多个托盘图标同时请求激活,由于 m_pendingProvider 只能保存一个,后续的请求会被直接拒绝并返回空 Token,导致部分托盘图标点击无响应。

改进建议: 使用 QMap/QHashQQueue 管理并发的 XdgActivationTokenV1*,或者至少在注释中明确说明当前设计不支持并发,以便后续维护者知晓。如果业务逻辑保证不会并发(例如 UI 主线程单点击事件),则当前逻辑可行,但建议补充注释。

1.3 Lambda 捕获 sniInter 存在潜在悬空指针风险

文件: sniprotocolhandler.cpp

auto sniInter = m_sniInter;
activationClient->requestToken(win, QString(), [sniInter](const QString &token) {
    if (!token.isEmpty()) {
        sniInter->ProvideXdgActivationToken(token);
    }
    sniInter->Activate(0, 0);
});

问题: requestToken 是异步的,当回调执行时,m_sniInter 指向的对象可能已经被销毁。虽然通过值捕获了指针,但如果原始对象被删除,捕获的指针将成为悬空指针。

改进建议: 如果 m_sniInterQPointer,建议在 Lambda 中使用 QPointer 捕获并在调用前检查有效性;如果不是,建议评估其生命周期,使用 QPointer 是最安全的做法:

QPointer<YourSniInterType> sniInter = m_sniInter; // 假设 m_sniInter 原本是裸指针
activationClient->requestToken(win, QString(), [sniInter](const QString &token) {
    if (!sniInter) return; // 防止悬空指针
    if (!token.isEmpty()) {
        sniInter->ProvideXdgActivationToken(token);
    }
    sniInter->Activate(0, 0);
});

2. 代码质量

2.1 跨模块引用源文件破坏了模块封装

文件: plugins/application-tray/CMakeLists.txt

set(TRAY_SOURCES
    ...
    ${CMAKE_SOURCE_DIR}/src/tray-wayland-integration/xdgactivationclient.h
    ${CMAKE_SOURCE_DIR}/src/tray-wayland-integration/xdgactivationclient.cpp
)

问题: 直接跨目录引用另一个 CMake 目标的 .cpp 文件是一种反模式。这会导致 xdgactivationclient.cpp 被编译两次(一次在 dockpluginmanager,一次在 application-tray),不仅增加了编译时间,还可能引发 ODR(One Definition Rule)问题或符号冲突。

改进建议:

  1. 推荐方案:将 XdgActivationClient 提取为一个独立的共享库(如 XdgActivationClientLib),然后让 application-traydockpluginmanager 都链接这个库。
  2. 备选方案:如果不想增加动态库,可以将头文件和源文件移到一个公共的 commonutils 目录,并在两个 CMakeLists 中分别包含。

2.2 Wayland 私有 API 的版本兼容性处理不足

文件: plugins/application-tray/CMakeLists.txt

if(Qt${QT_VERSION_MAJOR}_VERSION VERSION_GREATER_EQUAL 6.10)
  find_package(Qt${QT_VERSION_MAJOR} COMPONENTS GuiPrivate WaylandClientPrivate REQUIRED)
endif()

问题: xdgactivationclient.cpp 中大量使用了 Wayland 私有 API(如 QWaylandWindow, QWaylandDisplay),这些 API 在 Qt 6.10 之前也是必需的。CMake 中仅判断 >= 6.10 才引入 Private 模块,但在 C++ 代码中却无条件包含了私有头文件,这在低于 6.10 的 Qt 版本上会导致编译失败。

改进建议: 无论 Qt 版本如何,只要使用了 Wayland 私有 API,就必须链接 GuiPrivateWaylandClientPrivate。建议移除 if(Qt6_VERSION ...) 判断,直接 find_package 这两个 Private 组件。

2.3 代码逻辑优化:ddeindicatortrayprotocol.cpp 中的冗余判断

文件: ddeindicatortrayprotocol.cpp

auto widget = window();
if (!widget)
    return false;

问题: window() 返回的是 QWindow*,而 QPainter painter(widget) 需要的是 QWidget*。直接用 QWindow 构造 QPainter 是无法在 Widget 体系下正常绘制的。原代码使用的是 static_cast<QWidget*>(parent())

改进建议: 确认 window() 在此上下文中的真实意图。如果是想获取所在的 QWidget 窗口,应该使用 windowWidget() 或类似方法,或者确认这里是否是重构导致的类型误用。如果是在 QWidget 上绘制,必须拿到对应的 QWidget* 指针。

3. 代码性能

3.1 showEvent 中的重复操作

文件: traywidget.cpp

void TrayWidget::showEvent(QShowEvent* event)
{
    Q_UNUSED(event)
    if (!m_handler) return;

    m_handler->setWindow(window());
    window()->installEventFilter(m_handler);
    window()->setMouseTracking(true);
}

问题: showEvent 在窗口每次显示时都会触发(例如切换虚拟桌面、取消最小化)。每次都调用 installEventFiltersetMouseTracking(true) 是不必要的性能开销,且可能导致事件过滤器被重复安装。

改进建议: 增加状态判断,确保只安装一次:

void TrayWidget::showEvent(QShowEvent* event)
{
    Q_UNUSED(event)
    if (!m_handler) return;

    QWindow* win = window();
    m_handler->setWindow(win);
    if (!win->testAttribute(Qt::WA_Hover)) { // 或使用自定义 bool 标记
        win->installEventFilter(m_handler);
        win->setMouseTracking(true);
    }
}

4. 代码安全

4.1 C++ 风格的类型转换

文件: sniprotocolhandler.cpp (旧代码也有此问题,但重构时未修复)

auto plugin = Plugin::EmbedPlugin::get(win);

虽然此处已将 static_cast<QWidget*>(parent()) 改为了更安全的获取 win 的方式,但在涉及 QWindow 和 QWidget 转换的地方,务必确保不使用 reinterpret_cast 或 C-style 强转,以防止类型混淆漏洞。

4.2 Wayland serialseat 的有效性检查

文件: xdgactivationclient.cpp

if (auto *inputDevice = waylandWindow->display()->lastInputDevice()) {
    provider->set_serial(inputDevice->serial(), inputDevice->wl_seat());
}

问题: 在某些边界情况下(如无输入设备连接或启动阶段),inputDevice->serial() 可能是无效的,wl_seat() 可能为空。向 Wayland 合成器提交无效的 serial 可能导致协议错误进而让客户端崩溃。

改进建议: 增加对 wl_seat 的非空判断:

if (auto *inputDevice = waylandWindow->display()->lastInputDevice()) {
    auto *seat = inputDevice->wl_seat();
    if (seat) {
        provider->set_serial(inputDevice->serial(), seat);
    }
}

总结

本次提交的核心逻辑(Wayland XdgActivation 集成)是正确的,解决了 Linux Wayland 桌面下点击托盘无法唤起应用的关键痛点。但在工程结构(跨模块引用源码)、异步回调的内存安全以及单例模式等方面需要进行微调,以保证代码的健壮性和可维护性。建议优先修复 1.3 (Lambda悬空指针)2.1 (跨模块引用源码) 的问题。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In the left-button branch of SniTrayProtocolHandler::eventFilter, window()->windowHandle() is used without a null check on window(), unlike the right-click path and other handlers; consider guarding window() itself to avoid a null dereference when no window is associated.
  • The new XdgActivationClient class is declared in the tray namespace, but the call sites (e.g., in sniprotocolhandler.cpp) reference XdgActivationClient::instance() without a namespace qualifier; either add using namespace tray; locally or fully qualify the type to ensure this compiles and avoids namespace ambiguity.
  • In plugins/application-tray/CMakeLists.txt, Qt${QT_VERSION_MAJOR}::GuiPrivate and Qt${QT_VERSION_MAJOR}::WaylandClientPrivate are linked unconditionally, but the corresponding find_package calls are gated on Qt${QT_VERSION_MAJOR}_VERSION >= 6.10; for earlier Qt versions these targets may not exist, so consider guarding their usage or linking only when the private components are actually found.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the left-button branch of `SniTrayProtocolHandler::eventFilter`, `window()->windowHandle()` is used without a null check on `window()`, unlike the right-click path and other handlers; consider guarding `window()` itself to avoid a null dereference when no window is associated.
- The new `XdgActivationClient` class is declared in the `tray` namespace, but the call sites (e.g., in `sniprotocolhandler.cpp`) reference `XdgActivationClient::instance()` without a namespace qualifier; either add `using namespace tray;` locally or fully qualify the type to ensure this compiles and avoids namespace ambiguity.
- In `plugins/application-tray/CMakeLists.txt`, `Qt${QT_VERSION_MAJOR}::GuiPrivate` and `Qt${QT_VERSION_MAJOR}::WaylandClientPrivate` are linked unconditionally, but the corresponding `find_package` calls are gated on `Qt${QT_VERSION_MAJOR}_VERSION >= 6.10`; for earlier Qt versions these targets may not exist, so consider guarding their usage or linking only when the private components are actually found.

## Individual Comments

### Comment 1
<location path="plugins/application-tray/sniprotocolhandler.cpp" line_range="327-328" />
<code_context>
+                    if (!win)
+                        return false;
+
+                    auto sniInter = m_sniInter;
+                    activationClient->requestToken(win, QString(), [sniInter](const QString &token) {
+                        if (!token.isEmpty()) {
+                            sniInter->ProvideXdgActivationToken(token);
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against a potentially dangling `sniInter` in the async activation callback.

The lambda captures `sniInter` as a raw pointer and may run after the tray item is removed, leading to undefined behavior when calling `ProvideXdgActivationToken` / `Activate` on a freed object.

Use a guarded pointer and check it before use, e.g.:

```cpp
QPointer<decltype(m_sniInter)> sniInter = m_sniInter;
activationClient->requestToken(win, QString(), [sniInter](const QString &token) {
    if (!sniInter)
        return;
    if (!token.isEmpty()) {
        sniInter->ProvideXdgActivationToken(token);
    }
    sniInter->Activate(0, 0);
});
```

This prevents the async callback from accessing freed memory if the SNI interface disappears while the request is in flight.
</issue_to_address>

### Comment 2
<location path="plugins/application-tray/sniprotocolhandler.cpp" line_range="323-324" />
<code_context>
-                m_sniInter->Activate(0, 0);
+                auto *activationClient = XdgActivationClient::instance();
+                if (activationClient->isActive()) {
+                    auto *win = window()->windowHandle();
+                    if (!win)
+                        return false;
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider a fallback path when `windowHandle()` is null to avoid silently dropping clicks.

When `window()->windowHandle()` is null, this now returns `false` and drops the click, whereas before we still activated via `Activate(0, 0)`. This can occur when the window isn’t yet backed by a native handle. To avoid a behavior regression and lost clicks, consider either falling back to the previous activation path when `win` is null, or at least logging this case and still calling `Activate(0, 0)` without a token.

Suggested implementation:

```cpp
                    auto *win = window()->windowHandle();
                    if (!win) {
                        qCWarning(LOG_SNI) << "No native window handle available for XDG activation, falling back to legacy SNI activation";
                        m_sniInter->Activate(0, 0);
                        return false;
                    }

```

These changes assume:
1. `LOG_SNI` is an existing logging category defined elsewhere (commonly in `util.h` in this codebase). If it is named differently, replace `LOG_SNI` with the correct category.
2. `m_sniInter` is the same interface previously used for `Activate(0, 0)` and is still available in this context. If the member has a different name, adjust `m_sniInter` accordingly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +327 to +328
auto sniInter = m_sniInter;
activationClient->requestToken(win, QString(), [sniInter](const QString &token) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Guard against a potentially dangling sniInter in the async activation callback.

The lambda captures sniInter as a raw pointer and may run after the tray item is removed, leading to undefined behavior when calling ProvideXdgActivationToken / Activate on a freed object.

Use a guarded pointer and check it before use, e.g.:

QPointer<decltype(m_sniInter)> sniInter = m_sniInter;
activationClient->requestToken(win, QString(), [sniInter](const QString &token) {
    if (!sniInter)
        return;
    if (!token.isEmpty()) {
        sniInter->ProvideXdgActivationToken(token);
    }
    sniInter->Activate(0, 0);
});

This prevents the async callback from accessing freed memory if the SNI interface disappears while the request is in flight.

Comment on lines +323 to +324
auto *win = window()->windowHandle();
if (!win)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider a fallback path when windowHandle() is null to avoid silently dropping clicks.

When window()->windowHandle() is null, this now returns false and drops the click, whereas before we still activated via Activate(0, 0). This can occur when the window isn’t yet backed by a native handle. To avoid a behavior regression and lost clicks, consider either falling back to the previous activation path when win is null, or at least logging this case and still calling Activate(0, 0) without a token.

Suggested implementation:

                    auto *win = window()->windowHandle();
                    if (!win) {
                        qCWarning(LOG_SNI) << "No native window handle available for XDG activation, falling back to legacy SNI activation";
                        m_sniInter->Activate(0, 0);
                        return false;
                    }

These changes assume:

  1. LOG_SNI is an existing logging category defined elsewhere (commonly in util.h in this codebase). If it is named differently, replace LOG_SNI with the correct category.
  2. m_sniInter is the same interface previously used for Activate(0, 0) and is still available in this context. If the member has a different name, adjust m_sniInter accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants