Skip to content

chore: port more behaviors from dde-daemon to dde-tray-loader#460

Merged
BLumia merged 1 commit into
linuxdeepin:masterfrom
BLumia:tray-daemon
May 20, 2026
Merged

chore: port more behaviors from dde-daemon to dde-tray-loader#460
BLumia merged 1 commit into
linuxdeepin:masterfrom
BLumia:tray-daemon

Conversation

@BLumia
Copy link
Copy Markdown
Member

@BLumia BLumia commented May 19, 2026

为 dde-tray-loader 补齐更多与 dde-daemon 提供的 xembed 托盘相关支持的行为差异,包括:

  1. 仅当托盘图标实际变化时才发送变化信号
  2. 补充 _NET_SYSTEM_TRAY_ORIENTATION 设置(目前写死其值)
  3. undock() buchong xcb_damage_destroy
  4. 增加 checkValid 机制
  5. 补充 Inited 信号(尽管目前未发现消费者)
  6. GetName 增加与 dde-daemon 一致的 fallback 行为

为 dde-tray-loader 补齐更多与 xembed 托盘相关的行为差异,包括:

1. 仅当托盘图标实际变化时才发送变化信号
2. 补充 _NET_SYSTEM_TRAY_ORIENTATION 设置(目前写死其值)
3. undock() buchong  xcb_damage_destroy
4. 增加 checkValid 机制
5. 补充 Inited 信号(尽管目前未发现消费者)
6. GetName 增加与 dde-daemon 一致的 fallback 行为

Log:
@BLumia BLumia requested a review from tsic404 May 19, 2026 12:28
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Sorry @BLumia, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@BLumia BLumia requested a review from fly602 May 19, 2026 12:29
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这份 Git Diff 对系统托盘管理器进行了多项改进,包括修复 X11 Damage 对象的内存泄漏、增加窗口有效性校验、优化托盘图标变更通知逻辑(避免冗余信号发射)、扩展窗口名称获取方式以及完善系统托盘属性设置。

以下是对该代码的详细审查意见,分为语法逻辑、代码质量、代码性能和代码安全四个方面:

一、 语法与逻辑

  1. 拼写错误:X11 原子名称拼写错误

    • 位置fdoselectionmanager.cpp 第263行
    • 问题:代码中写成了 NET_SYSTEM_TRAY_ORIENTAION,缺少了 T,正确的 EWMH 规范名称应为 _NET_SYSTEM_TRAY_ORIENTATION。X11 原子名称是严格匹配的,拼写错误会导致该属性设置无效,其他符合规范的客户端可能无法正确读取该属性。
    • 建议:修正为 UTIL->getAtomByName("_NET_SYSTEM_TRAY_ORIENTATION")
  2. 逻辑漏洞:isValidX11Window 判断不严谨

    • 位置util.cpp isValidX11Window 函数
    • 问题:当 replyError 存在时(即发生了 X11 错误),当前逻辑返回 reply && !replyError。但是,如果请求一个不存在的窗口,X11 会返回 BadWindow 错误,此时 reply 可能为 nullptrreplyError 不为空,该逻辑能正确返回 false。然而,如果 XCB 返回了其他类型的错误(虽然罕见),或者 reply 分配失败,这种判断方式可能掩盖真实的错误类型。更重要的是,如果窗口存在但状态异常,reply 不为空且 replyError 为空,会返回 true,这是符合预期的。但更标准的做法是明确检查是否发生了 BadWindow 错误。
    • 建议:如果仅为了判断窗口是否存在,建议检查 replyError 是否为空,或者明确判断错误码不是 BadWindow。当前逻辑在大多数情况下能工作,但建议简化为:return reply != nullptr;(因为如果窗口无效,XCB 通常会返回错误并导致 reply 为空)或者更严谨地检查 error->error_code != XCB_WINDOW
  3. 逻辑隐患:getX11WindowName 返回局部临时对象的指针

    • 位置util.cpp getX11WindowName 函数末尾
    • 问题:在函数末尾,如果前面的 if (!ret.empty()) 都未命中,执行到了 return ret.c_str();。此时 ret 是函数内的局部变量,函数结束后其生命周期结束,返回指向它的指针属于严重的悬挂指针问题,会导致未定义行为或程序崩溃。
    • 建议:将末尾的 return ret.c_str(); 修改为 return QString::fromUtf8(ret.c_str()); 或者直接 return QString();

二、 代码质量

  1. 命名规范与拼写:reclainRequested

    • 位置fdoselectionmanager.cpp 第284行
    • 问题:信号名 reclainRequested 存在拼写错误,应为 reclaimRequested(收回/重新要求)。虽然这可能是历史遗留的信号名,但在新代码中关联时应保持警惕,如果可以修改信号定义,建议一并纠正。
  2. 代码一致性:X11 连接的获取方式

    • 位置fdoselectionmanager.cpp undock 函数 与 addDamageWatch 函数
    • 问题:在 addDamageWatch 中,获取 X11 连接使用的是局部变量 c(通过 Util::instance()->getX11Connection() 获取)。而在 undock 中,直接使用了 Util::instance()->getX11Connection()。虽然功能等价,但同一类中风格不一致。
    • 建议:统一风格,建议在 undock 中也先获取局部变量 c,使代码更简洁易读。
  3. 资源管理:m_damageWatches 的清理时机

    • 位置fdoselectionmanager.cpp undock 函数
    • 问题:在 undock 中增加了 m_damageWatches.take(winId) 并销毁 Damage 对象,这是很好的资源回收逻辑。但需要确认:当托盘管理器失去所有权(onLostOwnership)或应用退出时,是否遍历了 m_damageWatches 并清理了所有的 X11 Damage 资源?如果仅依赖 undock,在异常退出或批量失去所有权时可能仍会泄漏。

三、 代码性能

  1. 性能瓶颈:高频操作中的重量级图像对比

    • 位置traymanager1.cpp notifyIconChanged 函数
    • 问题:每次托盘图标通知变更时,都会调用 UTIL->getX11WindowPixmapData 获取完整的像素数据,并将其与旧数据进行对比。xcb_get_image 是一次同步的 X11 请求,涉及内核态与用户态的切换以及图形数据的完整拷贝。对于高频更新的托盘图标(如网络监控、CPU监控),这会带来显著的 CPU 和总线开销。
    • 建议
      • 如果 X11 Damage 扩展已经正确监听(如 addDamageWatch 所做),通常不需要再通过对比完整像素数据来判断是否更新。Damage 事件本身就代表了区域内容发生了改变。
      • 如果必须进行防抖/去重,建议仅比较窗口的尺寸或使用更轻量级的校验和(如 X11 的 XRender 扩展提供的特性),而不是每次都拉取全量像素数据。
      • QByteArray 的比较(==)是逐字节比较的,对于几十KB的图标数据来说,这本身也是一笔不小的 CPU 开销。
  2. 冗余遍历:dock 时的 checkValid

    • 位置fdoselectionmanager.cpp dock 函数
    • 问题:每次有新窗口准备 dock 时,都会调用 checkValid() 遍历当前所有已存在的托盘图标并校验其有效性。这导致时间复杂度从 O(1) 变为了 O(N)。
    • 建议:窗口失效(被销毁)通常会触发 StructureNotify 中的 DestroyNotify 事件。应该优先依赖事件驱动来 undock 失效窗口,而不是在每次新窗口 dock 时进行全量扫描。如果是为了防止漏报,建议降低 checkValid 的调用频率,或者采用定时器惰性检查,而不是阻塞在 dock 流程中。

四、 代码安全

  1. 缓冲区越界风险:xcb_get_image_data_length

    • 位置util.cpp getX11WindowPixmapData 函数
    • 问题xcb_get_image_data_length 返回的是字节数,但在某些 XCB 版本或特定情况下,如果 X 服务器返回的数据长度与预期的 width * height * bytes_per_pixel 不符,直接使用该长度构造 QByteArray 可能会导致读取越界。
    • 建议:建议增加一层校验,例如计算预期的数据长度 expectedSize = geometry.width() * geometry.height() * 4(假设是 32位 RGBA),并与 xcb_get_image_data_length 返回值取较小值,或者至少确保 QByteArray 不会因为异常的 X11 响应而分配过大内存。
  2. 内存分配异常风险:无限制的 Pixmap 数据缓存

    • 位置traymanager1.h IconState 结构体
    • 问题pixmapData 会持久缓存在 m_icons 哈希表中。如果由于 X11 通信异常或逻辑漏洞,导致每次 notifyIconChanged 都认为数据不同并更新 pixmapData,这会导致内存持续增长。
    • 建议:考虑对 pixmapData 的大小进行限制,或者如果该数据仅用于变更比对,可以考虑使用哈希摘要(如 QCryptographicHash)代替完整的像素数据存储,这样既能比对又极大地节省内存。
  3. X11 Display 线程安全

    • 位置util.cpp getX11WindowName 中使用了 m_display (Xlib)
    • 问题:Xlib 的 Display 结构体默认不是线程安全的。如果在多线程环境下同时调用 XFetchNameXGetClassHint,可能会导致程序崩溃。而 XCB (m_x11connection) 在设计上对多线程更友好。
    • 建议:确保所有使用 m_display 的代码都在同一个线程(通常是主事件循环所在线程)中执行,或者使用 XInitThreads() 并在访问时加锁。长远来看,建议将 Xlib 调用迁移到 XCB 等价实现。

总结与修改建议代码片段

针对最严重的几个问题(逻辑崩溃和拼写错误),建议进行如下修改:

1. 修复 fdoselectionmanager.cpp 中的拼写错误:

// 修改前:
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("NET_SYSTEM_TRAY_ORIENTAION"), XCB_ATOM_CARDINAL, 32, 1, &orientation);

// 修改后:
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_ORIENTATION"), XCB_ATOM_CARDINAL, 32, 1, &orientation);

2. 修复 util.cppgetX11WindowName 的悬挂指针问题:

// 修改前(文件末尾):
    if (!ret.empty()) {
        return QString::fromLocal8Bit(ret.c_str(), static_cast<int>(ret.size()));
    }
    return ret.c_str(); // 严重危险:返回局部变量的指针

// 修改后:
    if (!ret.empty()) {
        return QString::fromLocal8Bit(ret.c_str(), static_cast<int>(ret.size()));
    }
    return QString(); // 安全:返回空字符串对象

3. 优化 util.cppgetX11WindowPixmapData 的安全性:

// 在构造 QByteArray 时增加安全校验
int dataLen = xcb_get_image_data_length(imageReply.get());
// 简单防备异常超大分配或不足
int expectedLen = geometry.width() * geometry.height() * 4; 
if (dataLen < expectedLen) {
    return false; // 数据不完整
}
// 如果数据过大,也可以做截断或报错处理
*data = QByteArray(reinterpret_cast<const char *>(xcb_get_image_data(imageReply.get())), 
                   qMin(dataLen, expectedLen)); // 取较小值保底
return true;

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia, tsic404

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

@BLumia BLumia merged commit 54ab4a3 into linuxdeepin:master May 20, 2026
11 checks passed
@BLumia BLumia deleted the tray-daemon branch May 20, 2026 02:27
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.

3 participants