Compare commits

...

3 Commits

Author SHA1 Message Date
6b75cda128 feat(bar): bar item 扩展钩子功能 bar cell 添加 fixed_width 属性与操作接口
- item type 加入可为空的 after_layout 和 after_draw 钩子
- cell 加 fixed_width 属性, cell api 添加 cell_set_fixed_width 和
  cell_get_region 两个接口
- cell api 改为 const 避免外部修改
2026-06-28 02:18:58 +08:00
27aab6f0e9 feat(tray): 添加 tray 相关接口并融入现有启动流程 2026-06-27 22:52:19 +08:00
eaad539a0b docs(tray): 新增实现计划,tray 接口声明移到 interface 中 2026-06-27 22:48:02 +08:00
11 changed files with 283 additions and 50 deletions

View File

@@ -42,7 +42,7 @@ X11 系统托盘协议只要求tray 创建一个 window 成为 =_NET_SYSTEM_T
* 后端接口设计 * 后端接口设计
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= tray 契约独立成头 =interface/tray.h= —— 落 interface 层:不放 coretray 与 core 无关),也不放 barbackend 也要实现它。bar 和 backend 作为两个对等实现层,都依赖 =interface/tray.h= 来解耦——这正是分层消除 bar↔backend 交叉依赖的落点(详见 [[file:module-layering.org][module-layering.org]] 的 tray 落点)。它只依赖 =interface/types.h==window_id_t=),不含 =backend_t=,所以 bar include 它而不碰 =interface/backend.h=内部。=interface/backend.h= 反过来 include =interface/tray.h=,让 =backend_bar_window_t= 内嵌 =tray_t=
#+begin_src c #+begin_src c
typedef void (*tray_icons_change_cb_t)(void *user_data); typedef void (*tray_icons_change_cb_t)(void *user_data);
@@ -233,20 +233,20 @@ bar 重绘是 timerfdfps驱动非事件驱动。tray icon 是真实窗
* 关键文件清单 * 关键文件清单
| 文件 | 改动 | | 文件 | 改动 |
|------------------------------+----------------------------------------------------------------------------------------------------------| |------------------------------+------------------------------------------------------------------------------------------------------------------------|
| =src/bar/tray.h= | **新增** tray 契约:=tray_t= / =tray_api_t= / =tray_icons_change_cb_t=bar 定义、后端实现;不进 core | | =interface/tray.h= | **新增** tray 契约:=tray_t= / =tray_api_t= / =tray_icons_change_cb_t=bar 与 backend 共享,落 interface 解耦) |
| =src/core/backend.h= | include =bar/tray.h==backend_bar_window_t= 内嵌 =tray_t=;扩展 =backend_create_bar_window=(加 =enable_tray= | | =interface/backend.h= | include =interface/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/tray.c= | *新增*selection / MANAGER / 基础 XEMBED / icon 管理 / =place= |
| =src/backend/x11/internal.h= | =ATOM_LIST= 追加 tray/XEMBED atoms=backend_t= 加 =tray= 字段 | | =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/event.c= | 消化 tray 相关 client message不产 =event_t= |
| =src/backend/x11/backend.c= | =backend_create_bar_window= 实现 enable_tray 分支;=backend_destroy= 释放 tray | | =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/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/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/bar.c= | layout 支持 =fixed_width==bar_draw= 调 after 钩子;=bar_init= 加 tray item新增 layout params 类型 |
| =src/bar/tray.c= | *新增*tray item 实现 | | =src/bar/tray.c= | *新增*tray item 实现 |
| =src/runtime/runtime.c= | =runtime_init_bar= 传 =enable_tray=、存 =bar->tray= | | =src/runtime/runtime.c= | =runtime_init_bar= 传 =enable_tray=、存 =bar->tray= |
| =include/zdwm/config.h= | =zdwm_bar_config_t= 加 =tray_output_index= | | =include/zdwm/config.h= | =zdwm_bar_config_t= 加 =tray_output_index= |
* 复用现有 * 复用现有

View File

@@ -0,0 +1,91 @@
#+title: ZDWM Tray 系统托盘实现计划
#+OPTIONS: toc:2
* 背景
bar 当前没有系统托盘。目标是让外部应用(输入法、音量、蓝牙、网络等)的 tray icon 显示在 bar 右侧并正常交互。
设计文档 [[file:tray-design.org][tray-design.org]] 已把方案定死。刚完成的模块分层重构([[file:module-layering.org][module-layering.org]])为 tray 铺了路——=interface/= 层让 bar 与 backend 经契约解耦tray 契约落 =interface/tray.h= 消除 =bar↔backend= 交叉依赖(分层的核心动机之一)。
* 关键决策
- *契约落 =interface/tray.h=*(分层后的位置,非设计文档旧版的 =src/bar/tray.h=)。
- *tray 闭环后端*core 的 =event_t= / =policy= / =plan= 完全不感知tray 消息在 =event.c= 拦截后 =return false=,不进 policy 路由。
- *tray 是 bar 附属 + 后端单例*:生命周期随 bar 窗口;=_NET_SYSTEM_TRAY_S0= selection owner 全 display 唯一,挂到最后一个 =enable_tray=true= 的 output。
- *第一版范围*selection owner + MANAGER 广播 + 基础 XEMBEDreparent + =_XEMBED_EMBEDDED_NOTIFY=+ icon 显示与点击 + icon 尺寸 = bar 高度(不缩放)。焦点/激活转发、尺寸协商、客户端异常断开清理放后续迭代。
- *tray 存 =bar_t.tray=(一份)*:单例语义,=bar_init= 时用 =host_window()= 匹配决定哪个 output 渲染。
* 实现步骤
每步可独立编译,建议每步一个提交。
** 步骤 1契约层 + 调用点占位
签名变更与唯一调用点耦合,必须一起改才能编译。
- *新增 =src/interface/tray.h=*(纯声明,零 .c=tray_icons_change_cb_t==tray_api_t==host_window= / =icon_count= / =icon_size= / =place= / =set_listener=)、=tray_t==api= + =handle=)。只依赖 =interface/types.h=
- *=src/interface/backend.h=*=#include "interface/tray.h"==backend_bar_window_t= 内嵌 =tray_t tray==backend_create_bar_window==bool enable_tray=
- *=src/runtime/runtime.c=*:临时传 =enable_tray=false=
- *=src/backend/x11/backend.c=*:签名加参数,函数体先忽略,返回 =.tray = {0}=。
- 验证编译通过bar 行为不变。
** 步骤 2bar 框架扩展(零影响现有 3 个 item
为 tray item 铺路;钩子可空、=fixed_width= 默认 0现有 item 走原路径。
- *=include/zdwm/bar.h=*=zdwm_bar_item_type_t= 加可空钩子 =after_layout= / =after_draw=;新增 =zdwm_bar_item_layout_params_t=(仿 =zdwm_bar_click_params_t==item= / =cell_api= / =region= / =state==zdwm_bar_cell_api_t==cell_set_fixed_width==cell_get_region=
- *=src/bar/types.h=*=bar_cell_t==int32_t fixed_width=(默认 0 = 走文本测量,=p_clear= 后天然正确;<0 = 隐藏哨兵)。
- *=src/bar/cell.c=*:实现 =bar_cell_set_fixed_width= / =bar_cell_get_region=,注册进 =bar_cell_api=
- *=src/bar/bar.c=*=bar_output_layout= 三处宽度计算加 =fixed_width= 分支(>0 固定 / ==0 测量 / <0 隐藏);=bar_draw= 在 layout 后遍历调 =after_layout=、draw 后调 =after_draw=NULL 跳过)。
- 验证:编译 + 运行,*workspaces/binding/windows 三个 item 视觉与点击零变化*(关键回归点)。
** 步骤 3后端协议层核心
tray 能力全部 X11 细节,"已实现但未启用"。
- *新增 =src/backend/x11/tray.c=*tray host 状态 + 成为 =_NET_SYSTEM_TRAY_S0= owner + 广播 MANAGER + 容器子窗口reparent 到当前 bar+ 基础 XEMBEDreparent icon + =_XEMBED_EMBEDDED_NOTIFY=+ dock 请求处理 + =place(handle,x)=move 容器、configure 每个 icon 到 =x + i*icon_size=+ 填充 =static const tray_api_t==handle= 实际是 =backend_t*=)。=icon_size= 用 bar 窗口高度(单一数据源)。
- *=src/backend/x11/internal.h=*=ATOM_LIST= 追加 =_NET_SYSTEM_TRAY_S0= / =_NET_SYSTEM_TRAY_OPCODE= / =_NET_SYSTEM_TRAY_MESSAGE_DATA= / =_XEMBED= / =_XEMBED_INFO= / =Manager==backend_t= 加 tray host 字段单例host_window、icon 列表、listener、enabled
- *=src/backend/x11/event.c=*=handle_client_message=):开头拦截 tray 消息(=Manager==data.data32[1]==_NET_SYSTEM_TRAY_S0==_NET_SYSTEM_TRAY_OPCODE==REQUEST_DOCK=),处理后 =return false=(不产 =event_t=)。
- *=src/backend/x11/backend.c=*=backend_create_bar_window= 的 =enable_tray=true= 分支(创建/迁移 host 容器到此 bar 窗口,返回 =tray= 填 backend 的 api + =handle=backend==backend_destroy= 释放 trayselection、容器、icon 列表)。
- *=CMakeLists.txt=*:加 =src/backend/x11/tray.c=
- 验证编译通过。runtime 仍传 false行为不变。
** 步骤 4bar tray item
消费步骤 2 的框架 + 步骤 3 的契约tray 作为 right-side item 接入。
- *新增 =src/bar/tray.c=*(参考 =workspaces.c= 模式):=bar_tray= vtable = =create_state= / =update= / =on_click= / =after_layout= / =destroy_state=。=create_state= 拷贝 =tray_t= + =set_listener= 注册回调(置 dirty=update= 在 dirty 时 =set_cell_count(icon_count)= + 循环 =cell_set_fixed_width(i, icon_size)==on_click= 返回 =ACTION_NONE=icon 真实子窗口自收点击);=after_layout= 调 =place(handle, region.start)=。cell 不画 cairotext 空,被真实 icon 窗口盖住)。
- *=src/bar/bar.h=*=bar_t= 加 =tray_t tray=(一份,单例)。
- *=src/bar/bar.c=*=bar_init=):遍历 output对 =bar->tray.host_window()= 匹配自身 =window_id= 的 output 调 =bar_output_add_tray=right side其余不加。
- *=CMakeLists.txt=*:加 =src/bar/tray.c=。
- 验证编译通过。right side 仍空runtime 未注入 tray
** 步骤 5runtime/配置串起来(启用)
- *=include/zdwm/bar.h=*=zdwm_bar_config_t=):加 =int32_t tray_output_index=(默认 0 = 第一个 output
- *=src/runtime/runtime.c=*=runtime_init_bar==bool enable_tray = ((int32_t)i == runtime->bar.config.tray_output_index);= 传给 =backend_create_bar_window==runtime->bar.tray = bar_window.tray;=(每个返回都一样,存一份)。
- *=src/config/defaults.c=*=tray_output_index= 默认 0。
- 验证:编译 + 运行tray 启用。
* 风险点
- *after 钩子对现有 item*:现有三个 item 的 vtable 不显式初始化新钩子 → NULLC 部分初始化保证);=bar_draw= 调用前判 NULL。=fixed_width= 默认 0 走原测量路径。步骤 2 后必须确认三个 item 零变化。
- *selection owner 单例 vs per-output create*host 是 backend 单例;每次 =enable_tray=true= 把容器 reparent 到本次 bar 窗口,最终挂最后一个 true 的 output=host_window()= 返回当前挂载 window_id。
- *XEMBED 第一版只做基础*reparent + =EMBEDDED_NOTIFY=;不做焦点/激活/缩放。某些高级 applet要求焦点的输入法可能受限基础音量/网络 icon 无影响。
- *core 零感知*tray 消息在 =event.c= 拦截 =return false=,不进 policy/plan。icon 增删不触发 core 重排——icon 是真实 X 子窗口X server 自管绘制bar 重绘靠 timerfd 驱动region 变化经 =after_layout==place= 同步。
- *tray cell padding*=fixed_width = icon_size=,确保 tray item 的 cell padding 为 0避免 icon 间额外间距。
- *透明背景*bar 窗口已 ARGB=backend.c==window_get_visual(true)=tray 容器子窗口继承,第一版白送。
* 端到端验证
1. *每步编译*=cmake --build build=,零 warning项目 =-Wall=)。
2. *步骤 5 后运行*=./build/zdwm=
3. *tray icon 验证*:启动一个提供 tray icon 的 app=volumeicon= / =nm-applet= / =fcitx5= 等,不要用 stalonetray——它会抢 selection owner。验证icon 显示在 bar *右侧*、尺寸 = bar 高度无溢出、多 icon 横向无重叠、点击有响应、透明背景融入 bar。
4. *动态增删*:启动/退出 tray app观察 icon 实时增删 + 布局重排(经 =set_listener= 回调 → dirty → 下个 timerfd tick → =update= 重算 → =after_layout= 重排),无残留空位。
5. *回归*(每步都做,重点步骤 2workspaces tag 切换、binding 模式、windows 标题、bar 点击命中全部不变。
* 关键文件
- *新增*=src/interface/tray.h=(契约)、=src/backend/x11/tray.c=(协议核心)、=src/bar/tray.c=bar item
- *修改*=src/interface/backend.h==src/backend/x11/{internal.h,event.c,backend.c}==include/zdwm/bar.h==src/bar/{types.h,cell.c,bar.c,bar.h}==src/runtime/runtime.c==src/config/defaults.c==CMakeLists.txt=
- *复用*=workspaces.c=item 模板)、=zdwm_bar_click_params_t=layout params 打包先例)、=ATOM_LIST=atom 注册)、=window_get_visual(true)=ARGB=bar_cell_api= vtable 机制。

View File

@@ -48,6 +48,11 @@ typedef struct zdwm_bar_config_t {
typedef struct zdwm_bar_item_t zdwm_bar_item_t; typedef struct zdwm_bar_item_t zdwm_bar_item_t;
typedef struct zdwm_bar_x_region_t {
int32_t start;
int32_t end;
} zdwm_bar_x_region_t;
typedef struct zdwm_bar_cell_api_t { typedef struct zdwm_bar_cell_api_t {
size_t (*get_cell_count)(zdwm_bar_item_t *item); size_t (*get_cell_count)(zdwm_bar_item_t *item);
void (*set_cell_count)(zdwm_bar_item_t *item, size_t count); void (*set_cell_count)(zdwm_bar_item_t *item, size_t count);
@@ -57,6 +62,13 @@ typedef struct zdwm_bar_cell_api_t {
void (*cell_set_fg)(zdwm_bar_item_t *item, size_t index, const char *color); void (*cell_set_fg)(zdwm_bar_item_t *item, size_t index, const char *color);
void (*cell_set_icon)(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon); void (*cell_set_icon)(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon);
void (*cell_set_indicator)(zdwm_bar_item_t *item, size_t index, bool show); void (*cell_set_indicator)(zdwm_bar_item_t *item, size_t index, bool show);
void (*cell_set_fixed_width)(
zdwm_bar_item_t *item,
size_t index,
int32_t width
);
zdwm_bar_x_region_t (*cell_get_region)(zdwm_bar_item_t *item, size_t index);
} zdwm_bar_cell_api_t; } zdwm_bar_cell_api_t;
typedef struct zdwm_bar_click_params_t { typedef struct zdwm_bar_click_params_t {
@@ -68,6 +80,13 @@ typedef struct zdwm_bar_click_params_t {
void *state; void *state;
} zdwm_bar_click_params_t; } zdwm_bar_click_params_t;
typedef struct zdwm_bar_item_hook_params_t {
zdwm_bar_item_t *item;
const zdwm_bar_cell_api_t *cell_api;
zdwm_bar_x_region_t region;
void *state;
} zdwm_bar_item_hook_params_t;
typedef struct zdwm_bar_item_type_t { typedef struct zdwm_bar_item_type_t {
void *(*create_state)(zdwm_output_id_t output_id, void *config); void *(*create_state)(zdwm_output_id_t output_id, void *config);
void (*update)( void (*update)(
@@ -75,6 +94,8 @@ typedef struct zdwm_bar_item_type_t {
const zdwm_bar_cell_api_t *cells, const zdwm_bar_cell_api_t *cells,
void *state void *state
); );
void (*after_layout)(const zdwm_bar_item_hook_params_t *params);
void (*after_draw)(const zdwm_bar_item_hook_params_t *params);
zdwm_action_t (*on_click)(zdwm_bar_click_params_t *params); zdwm_action_t (*on_click)(zdwm_bar_click_params_t *params);
void (*destroy_state)(void *state); void (*destroy_state)(void *state);
zdwm_bar_item_t *instance; zdwm_bar_item_t *instance;

View File

@@ -774,7 +774,8 @@ void backend_scan_result_destroy(backend_scan_result_t *result) {
backend_bar_window_t backend_create_bar_window( backend_bar_window_t backend_create_bar_window(
backend_t *backend, backend_t *backend,
rect_t geometry, rect_t geometry,
uint32_t bg_pixel uint32_t bg_pixel,
bool enable_tray
) { ) {
auto conn = backend->conn; auto conn = backend->conn;
auto root = backend->screen->root; auto root = backend->screen->root;

View File

@@ -212,14 +212,16 @@ static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) {
item->region.start = start; item->region.start = start;
for (size_t j = 0; j < item->count; ++j) { for (size_t j = 0; j < item->count; ++j) {
auto cell = &item->cells[j]; auto cell = &item->cells[j];
auto fixed_width = cell->fixed_width;
int32_t width = 0; bar_x_region_t region = {.start = start, .end = start};
text_context_get_text_size(ctx, cell->text, &width, nullptr); if (fixed_width == 0) {
int32_t width = 0;
bar_x_region_t region = { text_context_get_text_size(ctx, cell->text, &width, nullptr);
.start = start, region.end += width + 2 * item->cell_padding;
.end = start + width + item->cell_padding * 2, } else if (fixed_width > 0) {
}; region.end += fixed_width;
}
bar_cell_set_region(item, j, region); bar_cell_set_region(item, j, region);
start = region.end; start = region.end;
@@ -240,14 +242,16 @@ static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) {
for (size_t j = item->count; j > 0; --j) { for (size_t j = item->count; j > 0; --j) {
auto cell_index = j - 1; auto cell_index = j - 1;
auto cell = &item->cells[cell_index]; auto cell = &item->cells[cell_index];
auto fixed_width = cell->fixed_width;
int32_t width = 0; bar_x_region_t region = {.start = end, .end = end};
text_context_get_text_size(ctx, cell->text, &width, nullptr); if (fixed_width == 0) {
int32_t width = 0;
bar_x_region_t region = { text_context_get_text_size(ctx, cell->text, &width, nullptr);
.start = end - width - item->cell_padding * 2, region.start -= width + item->cell_padding * 2;
.end = end, } else if (fixed_width > 0) {
}; region.start -= fixed_width;
}
bar_cell_set_region(item, cell_index, region); bar_cell_set_region(item, cell_index, region);
end = region.start; end = region.start;
@@ -263,12 +267,30 @@ static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) {
if (center->count == 0) return; if (center->count == 0) return;
center->region = (bar_x_region_t){.start = start, .end = end}; center->region = (bar_x_region_t){.start = start, .end = end};
auto width = (end - start) / (int32_t)center->count; auto center_width = end - start;
auto center_count = center->count;
for (size_t i = 0; i < center->count; ++i) { for (size_t i = 0; i < center->count; ++i) {
bar_x_region_t region = { auto cell = &center->cells[i];
.start = start, auto fixed_width = cell->fixed_width;
.end = start + width + center->cell_padding * 2, if (fixed_width > 0) {
}; center_width -= fixed_width;
} else if (fixed_width < 0) {
/* 隐藏的 cell 不占任何空间,平分宽度时不参与 */
--center_count;
}
}
auto width = center_width / (int32_t)center_count;
for (size_t i = 0; i < center->count; ++i) {
auto cell = &center->cells[i];
auto fixed_width = cell->fixed_width;
bar_x_region_t region = {.start = start, .end = start};
if (fixed_width == 0) {
region.end += width + center->cell_padding * 2;
} else if (fixed_width > 0) {
region.end += fixed_width;
}
start = region.end; start = region.end;
bar_cell_set_region(center, i, region); bar_cell_set_region(center, i, region);
} }
@@ -386,6 +408,42 @@ bar_output_draw(bar_output_t *bar_output, text_context_t *ctx, color_t *bg) {
bar_item_draw(cr, &bar_output->center, ctx, bar_output->height); bar_item_draw(cr, &bar_output->center, ctx, bar_output->height);
} }
static inline void bar_item_run_hook(
zdwm_bar_item_t *item,
void (*hook)(const zdwm_bar_item_hook_params_t *params)
) {
if (!hook) return;
zdwm_bar_item_hook_params_t params = {
.item = item,
.cell_api = &bar_cell_api,
.region = item->region,
.state = item->state
};
hook(&params);
}
static void visitor_after_layout(zdwm_bar_item_t *item) {
bar_item_run_hook(item, item->api.after_layout);
}
static void visitor_after_draw(zdwm_bar_item_t *item) {
bar_item_run_hook(item, item->api.after_draw);
}
typedef void (*bar_item_visitor_t)(zdwm_bar_item_t *item);
static void
bar_output_foreach_item(bar_output_t *bar_output, bar_item_visitor_t visit) {
for (size_t i = 0; i < bar_output->left.count; ++i) {
visit(&bar_output->left.items[i]);
}
visit(&bar_output->center);
for (size_t i = 0; i < bar_output->right.count; ++i) {
visit(&bar_output->right.items[i]);
}
}
bool bar_draw(bar_t *bar) { bool bar_draw(bar_t *bar) {
auto ctx = bar->ctx; auto ctx = bar->ctx;
@@ -396,7 +454,9 @@ bool bar_draw(bar_t *bar) {
if (!bar_output_is_dirty(bar_output)) continue; if (!bar_output_is_dirty(bar_output)) continue;
bar_output_layout(bar_output, ctx); bar_output_layout(bar_output, ctx);
bar_output_foreach_item(bar_output, visitor_after_layout);
bar_output_draw(bar_output, ctx, &bar->palette.bg); bar_output_draw(bar_output, ctx, &bar->palette.bg);
bar_output_foreach_item(bar_output, visitor_after_draw);
changed = true; changed = true;
} }

View File

@@ -2,6 +2,7 @@
#include <assert.h> #include <assert.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <zdwm/types.h> #include <zdwm/types.h>
@@ -119,15 +120,37 @@ bar_cell_set_indicator(zdwm_bar_item_t *item, size_t index, bool show) {
item->dirty = true; item->dirty = true;
} }
static void
bar_cell_set_fixed_width(zdwm_bar_item_t *item, size_t index, int32_t width) {
if (index >= item->count) return;
auto cell = &item->cells[index];
if (cell->fixed_width == width) return;
cell->fixed_width = width;
cell->dirty = true;
item->dirty = true;
}
static bar_x_region_t bar_cell_get_region(zdwm_bar_item_t *item, size_t index) {
if (index >= item->count) return (bar_x_region_t){0};
auto cell = &item->cells[index];
return cell->region;
}
/* clang-format off */ /* clang-format off */
zdwm_bar_cell_api_t bar_cell_api = { const zdwm_bar_cell_api_t bar_cell_api = {
.get_cell_count = bar_cell_get_count, .get_cell_count = bar_cell_get_count,
.set_cell_count = bar_cell_set_count, .set_cell_count = bar_cell_set_count,
.cell_set_text = bar_cell_set_text, .cell_set_text = bar_cell_set_text,
.cell_set_bg = bar_cell_set_bg, .cell_set_bg = bar_cell_set_bg,
.cell_set_fg = bar_cell_set_fg, .cell_set_fg = bar_cell_set_fg,
.cell_set_icon = bar_cell_set_icon, .cell_set_icon = bar_cell_set_icon,
.cell_set_indicator = bar_cell_set_indicator, .cell_set_indicator = bar_cell_set_indicator,
.cell_set_fixed_width = bar_cell_set_fixed_width,
.cell_get_region = bar_cell_get_region,
}; };
/* clang-format on */ /* clang-format on */

View File

@@ -12,4 +12,4 @@ void bar_cell_set_region(
bar_x_region_t region bar_x_region_t region
); );
extern zdwm_bar_cell_api_t bar_cell_api; extern const zdwm_bar_cell_api_t bar_cell_api;

View File

@@ -7,10 +7,7 @@
#include "base/color.h" #include "base/color.h"
typedef struct bar_x_region_t { typedef struct zdwm_bar_x_region_t bar_x_region_t;
int32_t start;
int32_t end;
} bar_x_region_t;
typedef struct bar_cell_t { typedef struct bar_cell_t {
zdwm_icon_t icon; zdwm_icon_t icon;
@@ -20,6 +17,7 @@ typedef struct bar_cell_t {
color_t fg; color_t fg;
color_t bg; color_t bg;
bar_x_region_t region; bar_x_region_t region;
int32_t fixed_width;
bool show_indicator; bool show_indicator;
bool dirty; bool dirty;
} bar_cell_t; } bar_cell_t;

View File

@@ -5,6 +5,7 @@
#include "interface/effect.h" #include "interface/effect.h"
#include "interface/event.h" #include "interface/event.h"
#include "interface/tray.h"
#include "interface/types.h" #include "interface/types.h"
typedef struct backend_t backend_t; typedef struct backend_t backend_t;
@@ -69,11 +70,13 @@ void backend_scan_result_destroy(backend_scan_result_t *result);
typedef struct backend_bar_window_t { typedef struct backend_bar_window_t {
window_id_t window_id; window_id_t window_id;
cairo_t *cr; cairo_t *cr;
tray_t tray;
} backend_bar_window_t; } backend_bar_window_t;
backend_bar_window_t backend_create_bar_window( backend_bar_window_t backend_create_bar_window(
backend_t *backend, backend_t *backend,
rect_t geometry, rect_t geometry,
uint32_t bg_pixel uint32_t bg_pixel,
bool enable_tray
); );
void backend_flush(backend_t *backend); void backend_flush(backend_t *backend);

35
src/interface/tray.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "interface/types.h"
typedef void (*tray_icons_change_cb_t)(void *user_data);
typedef struct tray_api_t {
/**
* @brief 获取 tray 当前挂载的 bar window id
*
* @detail 如果返回的 window_id 是 ZDWM_WINDOW_ID_INVALID 则表示 tray
* 功能未开启,无 tray 需要挂载
*/
window_id_t (*host_window)(void *handle);
/* 获取当前 icon 数量 */
size_t (*icon_count)(void *handle);
/* 获取每个 icon 的边长, icon 长宽一样,都等于 bar_height */
int32_t (*icon_size)(void *handle);
/* 将 tray 的 icon 容器窗口起点放到 bar 中 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;
} tray_t;

View File

@@ -121,7 +121,8 @@ static void runtime_init_bar(runtime_t *runtime) {
.height = bar_height, .height = bar_height,
}; };
auto color = bar->palette.bg.argb; auto color = bar->palette.bg.argb;
auto bar_window = backend_create_bar_window(backend, bar_rect, color); auto bar_window =
backend_create_bar_window(backend, bar_rect, color, false);
auto bar_output = &bar->bars[i]; auto bar_output = &bar->bars[i];
bar_output->cr = bar_window.cr; bar_output->cr = bar_window.cr;
bar_output->window_id = bar_window.window_id; bar_output->window_id = bar_window.window_id;