207 lines
9.6 KiB
Markdown
207 lines
9.6 KiB
Markdown
# 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/Qt6(QtCore, 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` 包含安装规则,我可以继续操作。 |