docs: 新增模块分层与系统托盘设计文档
This commit is contained in:
116
docs/module-layering.org
Normal file
116
docs/module-layering.org
Normal file
@@ -0,0 +1,116 @@
|
||||
#+title: ZDWM 模块分层与依赖方向
|
||||
#+OPTIONS: toc:2
|
||||
|
||||
* 动机
|
||||
|
||||
随着模块增加,跨模块的"契约"和"共享数据结构"开始散落:有的进了 =core/=(和窗口管理逻辑混在一起),有的不得不靠依赖倒置塞进消费方目录(如 tray 契约放 =bar/= 导致 backend 反向依赖 bar)。结果是 core 职责不清、=bar ↔ backend= 出现交叉依赖、依赖方向不再单向。
|
||||
|
||||
本文档定义一套分层约定,把"跨模块共享的东西"从具体实现里抽出来,集中到两个目录,使依赖方向回到干净的单向。
|
||||
|
||||
* 核心标准:按"谁直接消费类型"归类
|
||||
|
||||
判断一个跨模块的东西进哪个目录,不看它"属于哪个模块",也不只看"是声明还是实现",而是看**它的类型被哪些层直接消费**:
|
||||
|
||||
- 类型被**两个或以上对等的实现层**直接消费 → 类型放 =interface/=(跨模块声明)
|
||||
- 只有**操作函数**被多个实现层共用、且这些层不该互相依赖 → 操作放 =common/=
|
||||
- 类型 + 操作都只被一个实现层(及其依赖者)用 → 整个 ADT 放 =common/=,或就近放该实现层
|
||||
|
||||
关键推论:**只需消费"类型"的层,不该被迫背上"操作函数"的依赖**。这是把类型和操作分到两个目录的根本理由。
|
||||
|
||||
* 两个目录的定位
|
||||
|
||||
** =src/interface/= :跨模块共享的【声明】,零 .c
|
||||
|
||||
只放类型定义、多态接口的函数声明。绝对不放假装、零实现代码。被所有实现层依赖,自身不依赖任何实现层。
|
||||
|
||||
** =src/common/= :被多实现层共用的【操作函数】,.h + .c
|
||||
|
||||
放自包含的 ADT 操作(构造/清理/查询)。操作只依赖 =interface/= 里的类型(和 =base/= 工具),不依赖任何实现层的内部状态。被"需要这些操作的层"依赖。
|
||||
|
||||
注意:=common/= 不是"什么都往里塞的杂物间"。只有当操作被**多个不该互相依赖的实现层**共用时,才进 =common/=;只被单一实现层用的操作,就近放该层。
|
||||
|
||||
* 依赖方向
|
||||
|
||||
#+begin_example
|
||||
interface/ ← 零依赖(纯声明),所有实现层都依赖它
|
||||
↑
|
||||
common/ ← 依赖 interface(用类型)+ base/(工具),被需要操作的层依赖
|
||||
↑
|
||||
core/ runtime/ (依赖 common + interface)
|
||||
backend/ bar/ (只依赖 interface —— 不碰 common 的操作)
|
||||
#+end_example
|
||||
|
||||
不变量:
|
||||
- =interface/= 不依赖任何实现层。
|
||||
- =common/= 只依赖 =interface/= 和 =base/=,不依赖任何实现层。
|
||||
- 实现层之间(core/backend/bar/runtime)不直接相互依赖跨模块契约,都经由 =interface/= 或 =common/=。
|
||||
- 因此 =bar ↔ backend= 这类对等层之间不再有交叉依赖:都通过 =interface/tray.h= 通信。
|
||||
|
||||
* 判定示例:effect / plan / event
|
||||
|
||||
用上面的标准,逐个判定现有跨模块类型:
|
||||
|
||||
** =effect_t= → interface(类型),无 .c
|
||||
|
||||
消费方:core(policy/plan 产生 =effect_t=)+ backend(执行 =effect_t=)。两个对等层直接消费类型,所以类型进 =interface/effect.h=。=effect_t= 是纯数据,构造它的活(=plan_push_*=)属于 plan 的操作,所以 effect 在 interface 里只有类型、没有 .c。
|
||||
|
||||
#+begin_src c
|
||||
/* backend 只消费展开后的 effect 数组,从不碰 plan_t */
|
||||
bool backend_apply_effect(backend_t *backend, const effect_t *effects, size_t effect_count);
|
||||
#+end_src
|
||||
|
||||
** =plan_t= → common(完整 ADT:类型 + 操作)
|
||||
|
||||
消费方:只 core(policy 产生、收集 effect)和 runtime(提交后 reset/cleanup)。backend 完全不消费 =plan_t=——runtime 提交时把 plan 拆成 =effect_t= 数组喂给 backend。所以 =plan_t= 非跨模块,类型和操作整个 ADT 都落 =common/plan.h= + =common/plan.c=。
|
||||
|
||||
** =event_t= → interface(类型)+ common(操作)
|
||||
|
||||
=event_t= 类型被 backend(产生/填充)和 core/runtime(路由/清理)直接消费 → 类型进 =interface/event.h=。
|
||||
但 =event_reset= / =event_cleanup= 这些操作被 backend 和 runtime 共用,而两者对等、不该互相依赖,操作不能归属任一层 → 操作落 =common/event.h= + =common/event.c=。
|
||||
|
||||
这是"类型和操作分两个目录"的典型:类型跨模块必须在 interface(backend 要产 =event_t=),操作对等共用必须在 common。
|
||||
|
||||
* 汇总表
|
||||
|
||||
| 东西 | 类型在哪 | 操作在哪 | 判定理由 |
|
||||
|------------------------------+-------------------------+-------------------------------------+--------------------------------------------------------------------------|
|
||||
| =effect_t= | =interface/effect.h= | (无,构造归 plan 操作) | 类型跨模块(core 产 + backend 执行),纯数据 |
|
||||
| =plan_t= | =common/plan.h= | =common/plan.c= | 非跨模块,只 core/runtime 用,整个 ADT 在 common |
|
||||
| =event_t= | =interface/event.h= | =common/event.h= + =common/event.c= | 类型跨模块;操作对等共用 |
|
||||
| 后端接口(=backend_t= 等) | =interface/backend.h= | =backend/x11/*.c= | 多态接口:声明在 interface,实现在具体后端 |
|
||||
| tray 契约(=tray_t= 等) | =interface/tray.h= | =backend/x11/tray.c= | 多态接口:bar 用、backend 实现,声明在 interface 消除 =bar↔backend= 交叉 |
|
||||
| 通知协议(listeners) | =interface/listeners.h= | =core/listeners.c= | 多态接口:core 发布、bar 订阅,声明在 interface |
|
||||
| 基础类型(=window_id_t= 等) | =interface/types.h= | (按需) | 所有层共用 |
|
||||
|
||||
* 多态接口 vs 共享 ADT:.c 归属的不同
|
||||
|
||||
=interface/= 里有两类声明,它们的 .c 归属不同,务必区分:
|
||||
|
||||
- **多态接口**(=backend.h=、=tray.h=、=listeners.h=):声明一组"由谁来实现"的函数,实现依赖具体实现层(xcb / 未来 wayland)。这类 .c **必须在实现层**(=backend/x11/=、=core/=),不能在 interface——否则 interface 就"知道"了具体实现。
|
||||
- **共享 ADT 的操作**(plan、event 的操作):实现自包含、不依赖任何实现层。这类 .c 进 =common/=。
|
||||
|
||||
换句话说:=interface/= 永远零 .c。多态接口的 .c 在实现层,共享操作的 .c 在 =common/=。
|
||||
|
||||
* 和 include/zdwm/ 的区别
|
||||
|
||||
- =include/zdwm/= 是**对外公共 API**(给用户配置、插件使用)。
|
||||
- =src/interface/= 是**内部跨模块契约**(给各子系统互相通信)。
|
||||
|
||||
两者职责不同,不合并。像 =backend.h=、=event.h=、=effect.h= 是内部的,不该进 =include/zdwm/= 暴露给外部。
|
||||
|
||||
* 迁移指引
|
||||
|
||||
把现有代码按本分层整理时,建议顺序:
|
||||
|
||||
1. 建 =src/interface/= 和 =src/common/= 目录,加入 CMakeLists。
|
||||
2. 先迁移"纯类型、零依赖"的声明进 =interface/=:=types.h=、=effect.h=(从 =core/plan.h= 抽出 =effect_t=)、=event.h= 的类型部分。
|
||||
3. 多态接口声明迁移:=backend.h=、=listeners.h= 进 =interface/=(=.c= 留原处)。
|
||||
4. 共享 ADT 迁移:=plan.h= + =plan.c= 整体进 =common/=;=event.h= 操作部分 + =event.c= 进 =common/=。
|
||||
5. 更新全项目 include 路径(="core/plan.h"= → ="common/plan.h"= 等)。
|
||||
6. 每步后编译验证,确保依赖方向单向、无循环。
|
||||
|
||||
注意边界:遇到"看起来该进 interface、但其类型依赖还在 core"的情况(如某个 event 子结构引用了 =core/window.h= 的类型),要么把那个被引用的类型也提到 =interface/=,要么承认它暂不进 interface——**进 interface 的前提是它的所有依赖都在 interface**,不能硬塞。
|
||||
|
||||
* tray 的落点
|
||||
|
||||
tray 的契约(=tray_t= / =tray_api_t=)是 bar 与 backend 之间的多态接口,按本分层落 =interface/tray.h=(声明),实现在 =backend/x11/tray.c=。这样 bar 和 backend 都只依赖 =interface/tray.h=,彻底消除 =bar ↔ backend= 交叉依赖——这正是引入这套分层的最初动机之一。详见 [[file:tray-design.org][tray-design.org]]。
|
||||
256
docs/tray-design.org
Normal file
256
docs/tray-design.org
Normal file
@@ -0,0 +1,256 @@
|
||||
#+title: ZDWM Tray 系统托盘设计
|
||||
#+OPTIONS: toc:2
|
||||
|
||||
* 背景与动机
|
||||
|
||||
ZDWM 当前没有系统托盘(system tray / systray)功能。本文档描述如何在 bar 中实现 X11 系统托盘,让外部应用(输入法、音量、蓝牙、网络等)的托盘图标显示在状态栏右侧并正常交互,同时保持现有前后端分离架构,为未来 Wayland 后端预留扩展空间。
|
||||
|
||||
* 设计目标与约束
|
||||
|
||||
** 前后端分离
|
||||
|
||||
tray 协议(=_NET_SYSTEM_TRAY= + XEMBED)必须*闭环在后端内部*。core 的 =event_t= / =policy= / =plan= 完全不感知 tray——tray 不产生 =event_t=、不进 policy 路由、不进 plan/effect。
|
||||
|
||||
这与现有架构的三条解耦通道一致:
|
||||
- =backend -> core=:后端把平台原始事件归一化为 =event_t=
|
||||
- =core -> backend=:core 产出 =effect_t=,后端执行
|
||||
- =runtime= 作为唯一桥接点:bar 模块本身拿不到 backend,所有平台访问经 runtime 中转
|
||||
|
||||
** Wayland 预留
|
||||
|
||||
bar 与 runtime 层必须后端无关、零 xcb。未来加 Wayland 后端时,只需新增一个后端实现——Wayland 用 StatusNotifierItem(SNI)over D-Bus,icon 是像素数据而非真实窗口,由前端 cairo 绘制。只要后端接口形状不变,上层(bar/runtime)零改动。
|
||||
|
||||
** tray 是 bar 的附属组件
|
||||
|
||||
没有 bar 就没有 tray。tray 的生命周期随 bar 窗口,*不*作为独立公共接口(不进 =include/zdwm/=)。tray 能力内嵌在 bar 窗口的创建/销毁里。
|
||||
|
||||
* 方案选型:嵌入式容器子窗口(A')
|
||||
|
||||
X11 系统托盘协议只要求:tray 创建一个 window 成为 =_NET_SYSTEM_TRAY_S<n>= 的 selection owner,客户端向 owner 发 =_NET_SYSTEM_TRAY_REQUEST_DOCK= 请求,tray 用 XEMBED reparent icon。协议本身*不*规定 icon reparent 到 bar 窗口还是独立窗口。
|
||||
|
||||
** 为何不用独立 top-level 窗口
|
||||
|
||||
完全独立的 top-level tray 窗口(如 stalonetray)是为*没有状态栏*的环境设计的。ZDWM 已有 bar,用独立窗口会引入"两个窗口的几何/对齐同步"问题(tray 窗口要贴 bar 右端、随 icon 数动态 resize、跟随 bar 移动),且无功能收益。
|
||||
|
||||
** 为何用专门的容器子窗口
|
||||
|
||||
虽然直接 reparent 到 bar 的 cairo 绘制窗口技术上可行(bar 已是 ARGB,子窗口叠加在 cairo 内容之上),但用一个*专门的 tray 容器子窗口*做 selection owner 更正统、更干净:
|
||||
|
||||
1. *职责分离*:bar 窗口专注 cairo 绘制,tray 容器专注 XEMBED / icon 管理。dock 请求和 =_XEMBED= 消息定向到容器,不进 bar 的 =handle_client_message=。
|
||||
2. *事件隔离*:tray 协议事件在容器窗口闭环,bar 的窗口管理事件流零污染。
|
||||
3. *视觉不变*:容器是 bar 窗口的子窗口,位于 tray region 内,icon 跟随 bar 移动/resize(X 子窗口语义),不占额外屏幕空间。
|
||||
|
||||
* 后端接口设计
|
||||
|
||||
tray 契约独立成头 =src/bar/tray.h= —— *不放 core*,因为 tray 与 core 无关(core 不感知 tray)。由 bar 定义、后端实现(消费方定义接口、提供方实现的依赖倒置;契约是纯抽象头)。它只依赖 =core/types.h=(=window_id_t=),不含 =backend_t=,所以 bar include 它而不碰 =core/backend.h=,保持"bar 不知道后端内部"。=core/backend.h= 反过来 include =bar/tray.h=,让 =backend_bar_window_t= 内嵌 =tray_t=。
|
||||
|
||||
#+begin_src c
|
||||
typedef void (*tray_icons_change_cb_t)(void *user_data);
|
||||
|
||||
typedef struct tray_api_t {
|
||||
/* 返回 tray 当前挂载的 bar window_id;invalid 表示无 tray 挂载 */
|
||||
window_id_t (*host_window)(void *handle);
|
||||
/* 当前 icon 数量 */
|
||||
size_t (*icon_count)(void *handle);
|
||||
/* icon 边长(= bar height),bar 据此算 cell 宽度 */
|
||||
int32_t (*icon_size)(void *handle);
|
||||
/* 把容器 + icon 摆到 region 起点 x */
|
||||
void (*place)(void *handle, int32_t x);
|
||||
/* 注册 icon 数量变化回调 */
|
||||
void (*set_listener)(void *handle,
|
||||
tray_icons_change_cb_t cb,
|
||||
void *user_data);
|
||||
} tray_api_t;
|
||||
|
||||
typedef struct tray_t {
|
||||
tray_api_t api; /* 值类型,后端总填充,一直可用 */
|
||||
void *handle; /* 指向 backend(bar 不知其类型) */
|
||||
} tray_t;
|
||||
|
||||
typedef struct backend_bar_window_t {
|
||||
window_id_t window_id;
|
||||
cairo_t *cr;
|
||||
tray_t tray;
|
||||
} backend_bar_window_t;
|
||||
|
||||
/* 扩展:加 enable_tray;icon_size 直接用 geometry.height */
|
||||
backend_bar_window_t backend_create_bar_window(
|
||||
backend_t *backend, rect_t geometry, uint32_t bg_pixel, bool enable_tray);
|
||||
#+end_src
|
||||
|
||||
** 关键设计点
|
||||
|
||||
- *api 是值类型*:后端用一个全局 =static const= vtable 填充,保证一直可用,bar 不需判 NULL。这与项目现有 =zdwm_bar_item_type_t=(纯 vtable)+ =zdwm_bar_item_t=(实例持有 api + state)的模式一致。
|
||||
- *handle 总有效*:实际指向 =backend_t*=(tray api 函数内用 =backend->conn=、=backend->tray= host),对 bar 透明(=void*=)。有无 tray 统一由 =host_window()= 返回值判断(invalid = 无),不靠 api 是否 NULL,避免"先判 api、再判 host_window"两个判断点。
|
||||
- *icon_size 来自 handle*(单一数据源):后端 configure icon 用的尺寸 = bar 查布局用的尺寸,杜绝两边各算导致 icon 错位/溢出。这比在 =tray_t= 冗余存一份快照更纯粹。
|
||||
- *生命周期随 bar/backend*:=backend_create_bar_window(enable_tray=true)= 时创建/迁移 host;=backend_destroy= 释放。无 =tray_host_create/destroy= 独立入口,体现"tray 是 bar 附属"。
|
||||
|
||||
** 挂载语义
|
||||
|
||||
X11 的 =_NET_SYSTEM_TRAY_S0= selection owner *整个 display 只能有一个*,但 =backend_create_bar_window= 是 per-output 调用的。处理方式:
|
||||
|
||||
- tray host 是后端单例(内联在 =backend_t=)。
|
||||
- 每次传 =enable_tray=true= 的 =create_bar_window= 把容器 reparent 到本次 bar 窗口,最终挂在*最后一个* true 的 output 上。
|
||||
- =host_window()= 返回当前挂载的 bar window_id,bar 据此决定哪个 output 渲染 tray。
|
||||
|
||||
** 为何 set_listener 不配 remove_listener
|
||||
|
||||
tray icon 变化的唯一消费者是 bar 的 tray item,且其生命周期 = host 生命周期(=backend_destroy= 时自然消失)。与 core 的 =listeners= 模块一致(listener 跟宿主容器走,靠容器销毁回收),不引入 =remove_listener=。
|
||||
|
||||
* bar item 框架扩展
|
||||
|
||||
tray 作为 right-side 的普通 item,复用 =zdwm_bar_item_type_t= 框架。为支持 tray 的两个本质差异(cell 无文本 + 需要在 layout 后把结果同步到后端真实窗口),对框架做最小*通用*扩展——不为 tray 特例化。
|
||||
|
||||
** cell_api 全包 cell 相关(不引入 item_api)
|
||||
|
||||
=set_cell_count= / =get_cell_count= 本质是 *cell 集合的管理*(类比 =std::vector= 的 =size()/resize()= 与 =operator[]= 同属 vector),留在 =zdwm_bar_cell_api_t=。
|
||||
|
||||
新增两个 cell 级接口:
|
||||
|
||||
#+begin_src c
|
||||
/* 固定宽度:>0 = 固定宽度,0 = 走文本测量(零值默认),<0 = 隐藏 */
|
||||
void cell_set_fixed_width(zdwm_bar_item_t *, size_t index, int32_t width);
|
||||
/* 单个 cell 的 region(layout 后的坐标) */
|
||||
bar_x_region_t cell_get_region(zdwm_bar_item_t *, size_t index);
|
||||
#+end_src
|
||||
|
||||
=bar_cell_t= 加 =int32_t fixed_width=,默认 =0=(走文本测量)。把零值留给最常见的默认行为(测量),=p_clear= 清零后新 cell 天然正确,=set_cell_count= 无需补初始化;=-1= 当"隐藏"哨兵(负数宽度本就无意义),与 =0=(测量)明确区分,没有歧义。
|
||||
|
||||
=bar_output_layout= 改一处:
|
||||
|
||||
#+begin_src c
|
||||
int32_t width;
|
||||
if (cell->fixed_width > 0) width = cell->fixed_width; /* 固定 */
|
||||
else if (cell->fixed_width == 0) width = 文本测量; /* 测量(默认)*/
|
||||
else width = 0; /* 隐藏 */
|
||||
#+end_src
|
||||
|
||||
** after_layout / after_draw 钩子
|
||||
|
||||
普通 item 是 cairo 绘制;tray 是"layout 后把 region 应用到后端真实窗口"。给 =zdwm_bar_item_type_t= 加两个*可空*通用钩子,参数打包(仿现有 =zdwm_bar_click_params_t= 先例):
|
||||
|
||||
#+begin_src c
|
||||
typedef struct zdwm_bar_item_layout_params_t {
|
||||
zdwm_bar_item_t *item;
|
||||
const zdwm_bar_cell_api_t *cell_api;
|
||||
bar_x_region_t region;
|
||||
void *state;
|
||||
} zdwm_bar_item_layout_params_t;
|
||||
|
||||
void (*after_layout)(const zdwm_bar_item_layout_params_t *); /* 可空 */
|
||||
void (*after_draw)(const zdwm_bar_item_layout_params_t *); /* 可空 */
|
||||
#+end_src
|
||||
|
||||
设计要点:
|
||||
- 钩子为 NULL 则跳过,普通 item 零影响。
|
||||
- =after_layout= / =after_draw= 比 =update= 多一个 =region=(它们在 layout 之后调用),且通过 =cell_api= 可调 =cell_get_region= 取 per-cell 位置。
|
||||
- =update= 不打包参数(3 个参数不多,保持现有 =workspaces= / =binding= / =windows= 三个 item 零改动)。
|
||||
|
||||
=bar_draw= 流程:
|
||||
|
||||
#+begin_src c
|
||||
bar_output_layout(bo, ctx);
|
||||
for each item: if (after_layout) after_layout(params); /* params 含 region */
|
||||
bar_output_draw(bo, ctx, bg);
|
||||
for each item: if (after_draw) after_draw(params);
|
||||
#+end_src
|
||||
|
||||
* bar tray item
|
||||
|
||||
新增 =src/bar/tray.c=,作为普通 right-side item(参考 [[file:../src/bar/workspaces.c][workspaces.c]] 的实现模式):
|
||||
|
||||
#+begin_src c
|
||||
zdwm_bar_item_type_t bar_tray = {
|
||||
.create_state = bar_tray_create_state,
|
||||
.update = bar_tray_update,
|
||||
.on_click = bar_tray_on_click, /* 返回 NONE:icon 真实子窗口自收点击 */
|
||||
.after_layout = bar_tray_after_layout,
|
||||
.destroy_state = bar_tray_destroy_state,
|
||||
};
|
||||
#+end_src
|
||||
|
||||
- =create_state=:拷贝 =tray_t=,调 =set_listener= 注册回调(回调置 =state->dirty = true=)。
|
||||
- =update=:=dirty= 时 =set_cell_count(icon_count)= + 循环 =cell_set_fixed_width(i, icon_size)=。
|
||||
- =after_layout=:=place(handle, params->region.start)=。
|
||||
|
||||
tray cell 不画 cairo 内容(text 为空,=bar_item_draw= 只画融入 bar 的背景色块,被真实 icon 窗口盖住)。
|
||||
|
||||
* runtime 桥接
|
||||
|
||||
=runtime_init_bar=(=src/runtime/runtime.c=)的 create 循环:
|
||||
|
||||
#+begin_src c
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
bool enable_tray = ((int32_t)i == runtime->bar.config.tray_output_index);
|
||||
auto bar_window = backend_create_bar_window(backend, bar_rect, color, enable_tray);
|
||||
...
|
||||
runtime->bar.tray = bar_window.tray; /* 每个返回都一样,存一份 */
|
||||
}
|
||||
bar_init(&runtime->bar, &runtime->listeners);
|
||||
#+end_src
|
||||
|
||||
=bar_init= 遍历 =bar_output=,对 =host_window()= 匹配自身 =window_id= 的那个 output 调 =bar_output_add_tray=(right side),其余不加。tray 生命周期随 =backend_destroy=,runtime 无特殊清理。
|
||||
|
||||
* 配置
|
||||
|
||||
=zdwm_bar_config_t=(=include/zdwm/config.h=)加:
|
||||
|
||||
#+begin_src c
|
||||
int32_t tray_output_index; /* 默认 0 = 第一个 output */
|
||||
#+end_src
|
||||
|
||||
配置 setup 中可通过 output 信息确定具体 index。
|
||||
|
||||
* 数据流(触发链)
|
||||
|
||||
#+begin_example
|
||||
icon 增删
|
||||
-> 后端 set_listener 回调 -> state->dirty = true
|
||||
-> 下个 timerfd tick -> bar_update -> bar_tray_update
|
||||
-> set_cell_count + cell_set_fixed_width -> item 标 dirty
|
||||
-> bar_draw -> bar_output_layout 重算 region(cell 数变了)
|
||||
-> tray 的 after_layout -> backend place(handle, region.start)
|
||||
-> 后端 move 容器 + configure 每个 icon 到 (region.start + i*icon_size, 0)
|
||||
#+end_example
|
||||
|
||||
bar 重绘是 timerfd(fps)驱动,非事件驱动。tray icon 是真实窗口,X server 自管绘制,不依赖 cairo 重绘。
|
||||
|
||||
* 实现范围(第一版)
|
||||
|
||||
第一版做*基础可用*:
|
||||
|
||||
- =_NET_SYSTEM_TRAY_S0= selection owner + MANAGER 广播
|
||||
- 基础 XEMBED(reparent 容器、=_XEMBED_EMBEDDED_NOTIFY=)
|
||||
- icon 显示与点击
|
||||
- icon 尺寸 = bar 高度(统一,不缩放)
|
||||
|
||||
放后续迭代:
|
||||
- 完整 XEMBED 焦点 / 激活转发
|
||||
- icon 尺寸协商与缩放
|
||||
- 客户端异常断开的清理
|
||||
|
||||
*透明背景*第一版基本白送——bar 窗口已是 ARGB visual(见 =backend_create_bar_window= 调用 =window_get_visual(backend, true)=),tray 容器作为子窗口继承。
|
||||
|
||||
* 关键文件清单
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------------------------------+----------------------------------------------------------------------------------------------------------|
|
||||
| =src/bar/tray.h= | **新增** tray 契约:=tray_t= / =tray_api_t= / =tray_icons_change_cb_t=(bar 定义、后端实现;不进 core) |
|
||||
| =src/core/backend.h= | include =bar/tray.h=;=backend_bar_window_t= 内嵌 =tray_t=;扩展 =backend_create_bar_window=(加 =enable_tray=) |
|
||||
| =src/backend/x11/tray.c= | *新增*:selection / MANAGER / 基础 XEMBED / icon 管理 / =place= |
|
||||
| =src/backend/x11/internal.h= | =ATOM_LIST= 追加 tray/XEMBED atoms;=backend_t= 加 =tray= 字段 |
|
||||
| =src/backend/x11/event.c= | 消化 tray 相关 client message(不产 =event_t=) |
|
||||
| =src/backend/x11/backend.c= | =backend_create_bar_window= 实现 enable_tray 分支;=backend_destroy= 释放 tray |
|
||||
| =src/bar/types.h= | =bar_cell_t= 加 =fixed_width=;=bar_t= 加 =tray= |
|
||||
| =src/bar/cell.h= / =cell.c= | =cell_api= 加 =cell_set_fixed_width= / =cell_get_region= |
|
||||
| =src/bar/bar.c= | layout 支持 =fixed_width=;=bar_draw= 调 after 钩子;=bar_init= 加 tray item;新增 layout params 类型 |
|
||||
| =src/bar/tray.c= | *新增*:tray item 实现 |
|
||||
| =src/runtime/runtime.c= | =runtime_init_bar= 传 =enable_tray=、存 =bar->tray= |
|
||||
| =include/zdwm/config.h= | =zdwm_bar_config_t= 加 =tray_output_index= |
|
||||
|
||||
* 复用现有
|
||||
|
||||
- =backend_create_bar_window=(=src/backend/x11/backend.c=):bar 窗口已是 ARGB,tray 容器继承透明。
|
||||
- =zdwm_bar_item_type_t= vtable 框架 + [[file:../src/bar/workspaces.c][workspaces.c]]:tray item 同模式落地。
|
||||
- =zdwm_bar_click_params_t=:=after_layout= / =after_draw= 的 params 打包仿此先例。
|
||||
- =cell_api= 机制:tray =update= 通过它操作 cell(与 workspaces 一致)。
|
||||
Reference in New Issue
Block a user