Files
xembed-fluff/doc.md
2025-12-18 21:10:53 +08:00

207 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# xembed-traymanager-proxy — 改造文档
此文档描述从 `xembed-sni-proxy` 改造而来的 `xembed-traymanager-proxy` 项目。改造的核心是将 X11 Xembed 系统托盘管理功能从 Go daemon 分离到独立的 C++/Qt 组件,并通过 DBus 与后端 TrayManager1 通信。
**项目概述**
- 原名:`xembed-sni-proxy`
- 现名:`xembed-traymanager-proxy`
- 原作用:在 X11/XEmbed 客户端与 KDE/StatusNotifierItem (SNI) 系统之间作桥接
- 现作用:在 X11/XEmbed 客户端与 Deepin TrayManager1 DBus 后端之间作桥接
- 语言C++(使用 Qt 框架)
- 构建系统CMake
**架构改造对比**
| 方面 | 原 xembed-sni-proxy | 新 xembed-traymanager-proxy |
|-----|-------------------|---------------------------|
| **Selection Owner** | 注册并持有 | 注册并持有(保留) |
| **Icon 处理** | SNIProxy → 注册 SNI DBus 项 | TrayManagerProxy → 调用 TrayManager1 DBus |
| **DBus 暴露** | 每个 icon 一个 StatusNotifierItem 对象 | 通过 TrayManager1 后端统一管理 |
| **DBus 服务名** | org.kde.StatusNotifierWatcher | org.deepin.dde.TrayManager1 |
| **消息格式** | KDbusImageStruct, KDbusToolTipStruct | uint32 窗口ID + 图像数据 |
| **后端支持** | KDE Plasma SNI 管理器 | dde-daemon trayicon1 组件 |
**源码布局(改造后)**
- `main.cpp` — 启动代码,简化了 SNI 初始化
- `fdoselectionmanager.cpp` / `fdoselectionmanager.h` — 保留 selection owner 管理逻辑,改为创建 TrayManagerProxy
- `traymanagerproxy.cpp` / `traymanagerproxy.h` — ✨ **新增**,替代 sniproxy通过 DBus 与 TrayManager1 通信
- `xtestsender.cpp` / `xtestsender.h` — 保留,用于点击事件注入
- `xcbutils.h` — 保留X11/xcb 辅助函数
- `org.deepin.dde.TrayManager1.xml` — ✨ **新增**TrayManager1 DBus 接口定义
- `org.kde.StatusNotifierItem.xml`, `org.kde.StatusNotifierWatcher.xml` — ❌ **删除**
- `snidbus.cpp` / `snidbus.h` — ❌ **删除**
- `sniproxy.cpp` / `sniproxy.h` — ❌ **删除**(替代为 traymanagerproxy
- `statusnotifieritemadaptor.*` — ❌ **删除**CMake 生成)
- `statusnotifierwatcher_interface.*` — ❌ **删除**CMake 生成)
- `CMakeLists.txt` — 改为生成 TrayManager1 接口代码
**关键改造点详解**
1. **TrayManagerProxy新增**
- 替代原有的 SNIProxy
- 职责:
- 接收 X11 tray icon 窗口
- 创建容器窗口reparent icon
- 监听 XDamage 事件,抓取 icon 窗口图像
- **通过 DBus 调用** `org.deepin.dde.TrayManager1` 接口:
- 获取窗口名
- 注册/更新/移除 icon
- 与原 SNIProxy 的区别:
- SNIProxy: 自己注册 StatusNotifierItem DBus 对象
- TrayManagerProxy: 将数据发送给后端 TrayManager1
2. **FdoSelectionManager改造**
- 保留selection owner 管理、X11 事件处理
- 改变:创建 `new TrayManagerProxy(winId)` 而非 `new SNIProxy(winId)`
- 新增:`initTrayManager()` 方法(目前为桩)
3. **org.deepin.dde.TrayManager1.xml新增**
```xml
<interface name="org.deepin.dde.TrayManager1">
<property name="TrayIcons" type="au" access="read"/>
<method name="Manage"/>, <method name="GetName"/>, <method name="EnableNotification"/>
<signal name="Added"/>, <signal name="Removed"/>, <signal name="Changed"/>, <signal name="Inited"/>
</interface>
```
4. **main.cpp简化**
- 删除:`qDBusRegisterMetaType<KDbusImageStruct>` 等 SNI 类型注册
- 删除:`#include "snidbus.h"`
- 保留:基础 Qt/X11 初始化
**运行时交互流程**
```
应用窗口尝试 dock
xembed-traymanager-proxy (C++)
├─ 监听到 _NET_SYSTEM_TRAY_OPCODE ClientMessage
├─ 创建 TrayManagerProxy 对象
├─ TrayManagerProxy 连接到 TrayManager1 DBus
├─ 调用 TrayManager1::RegisterTrayIcon(windowId, iconPath, name)
├─ 监听 XDamage 事件
└─ 定期更新TrayManager1::UpdateTrayIcon(windowId, ...)
go daemon (dde-daemon/trayicon1)
├─ 接收 DBus 调用
├─ 维护内部 icon 列表
├─ 发出 Added/Changed/Removed 信号
└─ 暴露 TrayIcons 属性
上层 UI (如 shell 面板)
├─ 监听 TrayManager1 信号
├─ 读取 TrayIcons 属性
└─ 展示图标
```
**构建和部署**
```bash
cd /path/to/xembed-sni-proxy
mkdir build && cd build
cmake ..
make
sudo make install
```
**与 dde-daemon 的协作**
1. **dde-daemon/trayicon1/daemon.go**
- `enableTraySelectionManager` 应保持为 `false`
- 不处理 X11 tray events
- 只维护 TrayManager1 DBus 对象和 StatusNotifierWatcher
2. **何时启用**
- 当 xembed-traymanager-proxy 运行时,所有 X11 tray 请求由它处理
- 当 xembed-traymanager-proxy 不运行时,可选择由 Go daemon 处理(设置 `enableTraySelectionManager = true`
**注意事项**
- TrayManager1 接口目前是 **代理接口**C++ 项目作为客户端调用)
- 完整的 TrayManager1 实现应在 `dde-daemon/trayicon1` 中添加对应的 DBus 方法处理
- 当前实现假设 TrayManager1 后端已启动并可用
4. 当用户点击图标时SNI 管理器可能向 `StatusNotifierItem` 发送激活请求,代理将此映射为对 XEmbed 客户端发送激活事件(例如合成鼠标事件或发送客户端消息)。
5. 如果客户端更新图标或 tooltip代理侦测变化并通过 DBus 发出属性更改以通知 SNI 管理器更新显示。
**DBus 接口与方法(重点)**
- `org.kde.StatusNotifierItem`(导出在每个托盘项对象上):
- 属性:`IconPixmap`, `IconName`, `Category`, `Id`, `Title`, `Status`, `WindowId` 等。
- 方法/信号:`Activate`, `SecondaryActivate`, `ContextMenu`, `NewTitle`, `NewIcon` 等(具体以 xml 文件 / 项目实现为准)。
- `org.kde.StatusNotifierWatcher`(用于注册):
- `RegisterStatusNotifierItem` / `RegisterStatusNotifierWatcher` — 通知 watcher 新的 item 注册或 watcher 本身注册至全局。
请参见 `org.kde.StatusNotifierItem.xml` 与 `org.kde.StatusNotifierWatcher.xml` 以获得精确的方法/信号签名。
**构建与依赖**
- 必要依赖:
- Qt5/Qt6QtCore, QtDBus, QtWidgets, QtGui
- libxcb / X11 开发头文件(用于处理 XEmbed
- CMake构建系统
- 构建步骤(开发机示例):
```bash
mkdir -p build && cd build
cmake ..
cmake --build .
```
- 开发辅助:`compile_commands.json` 可用于代码导航与静态分析。
**部署与打包**
- 二进制通常安装到系统的 `/usr/bin` 或相应位置。
- `plasma-xembedsniproxy.service.in` 用于生成一个 systemd 用户服务或系统服务(视打包策略),确保在会话启动时自动运行代理。
- `xembedsniproxy.desktop` 允许桌面环境识别该代理并在必要时将其作为会话组件启动。
**日志、错误处理与调试**
- 使用 Qt 的日志系统(`qDebug()`, `qWarning()`, `qCritical()`),并在必要时提供命令行开关提高日志级别。
- 在 `build/` 中包含生成的 moc 文件与中间产物,便于调试源码。
- 为了重现 XEmbed 行为,可使用 `xtestsender` 模拟客户端事件。测试时建议在独立的 X 会话或使用 Xvfb 来避免影响主会话。
**线程模型与并发注意事项**
- 代理主要运行在 Qt 的主线程事件循环。X11 与 Qt 的事件处理必须在同一线程内(通常是主线程),因此避免在后台线程直接访问 X11 资源。
- 如果需要异步操作(如图标解码、网络等),请使用 Qt 的信号/槽机制与线程安全的队列,避免直接跨线程更新 GUI/DBus 导出对象。
**测试策略**
- 单元测试:对非 UI 的纯逻辑(例如 `fdoselectionmanager` 中的选择逻辑)编写单元测试。
- 集成测试:使用 `xtestsender` 配合临时的 SNI watcher 模拟完整注册/激活/更新流程。
- 手动测试:在一个受控的 X 会话中运行代理并观察与系统面板的交互;使用 `dbus-monitor` 监听 `org.kde.StatusNotifier*` 相关的消息以验证代理行为。
**安全与权限**
- 代理通常在用户会话中运行(无需 root 权限)。
- 不要在代理中执行不受信任内容(例如解析远程图标数据)而不做必要的验证与资源限制。
**扩展点与改进建议**
- Wayland 支持:当前项目以 XEmbed/X11 为中心,要支持 Wayland 需考虑客户端兼容策略(例如使用 XWayland 或转为基于 DBus 的原生实现)。
- 图标缓存层:增加一个图标缓存以减小频繁解码或网络请求对性能的影响。
- 更健壮的菜单代理:将客户端的菜单模型映射为 SNI 兼容的结构,并在需要时异步加载/延迟渲染菜单项。
- 单元与集成测试完善:将 `xtestsender` 扩展为可自动化运行的测试套件并集成到 CI。
**贡献指南(简要)**
- 代码风格:遵循现有 C++/Qt 风格;使用合理的命名和清晰的所有权语义(优先使用智能指针与 Qt 的 parent 机制)。
- 提交:每个功能分支附带描述、相关测试与打包说明。
- 运行静态检查:建议运行 clang-tidy/clang-format 并确保 `compile_commands.json` 可用以便工具检测。
**常见文件与符号对照(快速索引)**
- `main.cpp` — 启动点,初始化服务
- `snidbus.*` — DBus 层与 adaptor
- `sniproxy.*` — 代理主逻辑
- `fdoselectionmanager.*` — 名称/服务选择与策略
- `xtestsender.*` — 模拟和测试
- `org.kde.StatusNotifier*.xml` — DBus 接口定义
---
如果你希望我将文档改为英文版、生成更详细的类关系图(例如 PlantUML、或把这份文档添加到项目 README 中并更新 `CMakeLists.txt` 包含安装规则,我可以继续操作。