Files
MeowOS-For-R3P2/MEOW_APP_SDK.md
T
MeowOS 2b17f69f24 阶段B 通知中心UI(agent-123) + 进程模型/文档完整(agent-124) + 对外README(agent-125)
- 通知中心=launcher侧系统级叠加层(§6.1③ 仅内部): meow_notif_panel.h(纯逻辑) + meow_launcherd.c(内联 draw/run_notif_center + run_menu顶部热区下滑接入) + test_notif_panel.c(56单测)
- 通知中心零旁路走 meow_settings_*; 通知列表读 meow_notif; 左缘返回复用 meow_edge_back; 局部脏区; 系统级控件无公共API(App无法自建悬浮层)
- meow_ui.a 零改动(d249c6e6); meow_launcherd=4652901a(0warn)
- FRAMEWORK_DESIGN §10 进程模型/Supervisor树(svcd服务子进程化隔离防级联崩) ; MEOW_APP_SDK §6.1 系统UI归launcher+SDK/APP进程隔离; NOTIFICATION_SYSTEM §9 归属; SYSTEM_SETTINGS/SPEC_REF/DEVELOPMENT_PLAN 补齐
- README 重写为面向第三方(进度/架构/构建/部署/SDK开发; 固件打包标注本地工具不入库, 无敏感)
- 遗留: SDK(App进程)侧通知中心/activity跳转 依赖 M3 导航栈(独立里程碑); 真机待验证
2026-08-30 15:59:23 +08:00

61 KiB
Raw Blame History

MeowOS 应用开发框架 SDK(meow_ui)设计

状态:设计稿(2026-08-29)。路线 B:框架承载——MeowAPPs 不再裸 mmap("/dev/fb0") 自绘、自读输入,而是链接统一静态库 meow_ui.a,用 API 开发业务;渲染/控件/图片/输入/导航由框架收口。 目标:应用只写业务,不重复造轮子;观感、交互、生命周期统一;避免每个应用踩一遍 fb 双缓冲 / ioctl 号 / 返回键 / 息屏交互这些坑。


1. 问题与定案

现状(APPS.md §3.3):应用必须"独占屏幕自绘"——自行 open("/dev/fb0")+mmap 绘制;自行 open("/dev/input/event*") 读输入;自行管返回。全部 480×720、RGB565、双缓冲 yoffset、ioctl 号 0x4600/0x4602,每个应用都要重写一遍,观感必然乱。

定案:路线 B(框架承载)。理由:

  • 现状只有 1 个示范应用能凑合;要开放多应用/第三方,裸自绘 = 爆炸。
  • 渲染基建已就绪(back_buf + meow_present 无闪烁、meow_font 字库、blend565 混叠、脏区追踪),把它开放成 app 可链接的 API,是低成本高收益。

分层决策(分两步落地,不一次做完):

  • B1 渲染/控件/图片 SDK(本文档主体,优先):应用链接 meow_ui.a。依赖现成 meow_font.a + 内建 stb_image(单头文件、静态编入、解 PNG/JPEG/BMP)。不依赖阶段 D,可独立先行。
  • B2 输入/生命周期/导航(衔接阶段 D):把应用从"自读 event / 自管返回"解放,走 IPC + M3 导航栈。本文档 §5 给衔接设计,B2 实现归阶段 D。

2. 架构分层

┌─────────────────────────────────────────────┐
│  MeowAPP(业务逻辑,只调 meow_ui API)        │
├─────────────────────────────────────────────┤
│  meow_ui 静态库(meow_ui.a)                 │
│    ├─ 绘制层   meow_draw_*                   │
│    ├─ 控件层   meow_widget_*(按钮/列表/滑条…)│
│    ├─ 图片层   meow_img_*(stb_image 解码)   │
│    ├─ 输入层   meow_input_*(语义事件)        │  ← B1:只收事件;B2:接 M3 导航
│    └─ 主题/配置 meow_theme_*(颜色/间距/字体)  │
├─────────────────────────────────────────────┤
│  渲染后端 meow_ui_gfx(复用 launcher 机制)    │
│    back_buf(RGB565) + meow_present(dirty)    │
│    + meow_font(位图字库) + blend565           │
└─────────────────────────────────────────────┘

关键约定:

  • 应用只调 API,绝不 open fb、mmap、ioctl、open event。屏幕/输入由 meow_ui 在应用 meow_app_start() 时建立、退出时释放。
  • 渲染统一走 back_buf + meow_present:应用画到离屏,只在 meow_ui_flush() 时单次原子 blit 到 fb0(无撕裂无频闪,复用 M2.16 地基)。
  • 颜色统一 24-bit RGB(0xRRGGBB),meow_ui 内部做 rgb565() 转换;字体绘制用 meow_font,字号约定 28(标题/状态栏)/20(正文/按钮)。

3. 绘制层 API(B1)

所有绘制先画进离屏 back_buf;调用 meow_ui_flush() 才上屏。坐标 0,0 = 左上。

/* 初始化/销毁(应用 main 里 meow_app_start 的懒加载) */
int  meow_ui_init(const meow_ui_opts *opts);      /* 开 fb、读几何、载字库、建 back_buf */
void meow_ui_shutdown(void);

/* 帧缓冲几何(应用需自适应时读取) */
int  meow_ui_width(void);                          /* 480 */
int  meow_ui_height(void);                         /* 720 */

/* 基本绘制(全部画进 back_buf) */
void meow_ui_clear(uint32_t color);                /* 整屏清(填背景色) */
void meow_ui_put_px(int x, int y, uint32_t color);
void meow_ui_fill_rect(int x, int y, int w, int h, uint32_t color);
void meow_ui_draw_rect(int x, int y, int w, int h, uint32_t color);        /* 空心框 */
void meow_ui_draw_rect_round(int x, int y, int w, int h, int r, uint32_t color); /* 圆角框 */

/* 文本 */
void meow_ui_text(int x, int y, const char *utf8, int size, uint32_t color);  /* size=20/28 */
void meow_ui_text_right(int right_x, int y, const char *utf8, int size, uint32_t color);
void meow_ui_text_center(int cx, int y, const char *utf8, int size, uint32_t color);
int  meow_ui_text_width(const char *utf8, int size);
int  meow_ui_text_height(int size);                /* = meow_font_line_height */

/* 上屏:vsync + 原子 blit 脏区(内建脏区追踪,应用无需关心) */
void meow_ui_flush(void);
void meow_ui_flush_region(int x, int y, int w, int h);   /* 只刷新指定区 */

/* 半透明(已有 blend565:前景按 alpha 与背景混叠) */
void meow_ui_fill_rect_alpha(int x, int y, int w, int h, uint32_t color, int alpha);  /* 0-255 */

说明:meow_ui_text_* 底层即 meow_font_drawtext + meow_font_width_text;meow_ui_fill_rect_alpha 复用 blend565。模糊/毛玻璃不在 B1 承诺(硬件无 GPU、CPU 软渲;仅在静态小区域可考虑,见 §7)。

3.1 全局状态栏(系统级组件,A+C 方案并入)

定位:状态栏是系统级全局组件,由 SDK 统一绘制——所有界面(launcher 菜单 + MeowAPP 画面)默认都显示,仅由 SDK 运行时 meow_ui_set_fullscreen(1) 控制隐藏(fullscreen 字段未在 app.json 解析,已定未做,见 §3.1 注 [源:审计 2026-08-30])。数据由 SDK 内置状态读取器抓系统快照(与 launcher 同源单点一致),非各画各的。

/* 状态栏显隐(默认显示;APP 声明全屏则隐藏——沉浸) */
void meow_ui_set_statusbar(int visible);   /* 1=显示(默认) 0=隐藏(全屏) */
int  meow_ui_statusbar_visible(void);

/* 全屏:由 SDK 运行时 meow_ui_set_fullscreen(1) 控制(app.json `fullscreen` 未解析,已定未做;外观=隐藏状态栏+隐藏返回键)。[源:审计 2026-08-30] */
void meow_ui_set_fullscreen(int on);

/* 状态栏高度(px,供 APP 布局上移内容,避免被盖——status bar inset) */
int  meow_ui_statusbar_height(void);

绘制规则:

  • meow_ui_flush() 上屏前,SDK 自动在顶部画状态栏(28px,meow_ui_statusbar_height() 即此值;用户定 40→28,栏内字号 20),内容:时钟 / 电量 / WiFi / 蓝牙 / 音量(与 launcher 一致)。
  • 状态栏数据由 SDK 内置读取器周期刷新(~1s 轻刷,读 /sys/class/power_supply/*、WiFi 状态、时钟、音量寄存器——与 launcher 同一批 source)。
  • z 顺序:状态栏是最顶层。APP 内容需 meow_ui_statusbar_height() 下移(inset),否则被状态栏盖住(有状态栏 OS 的通用做法)。
  • 全屏:运行时 meow_ui_set_fullscreen(1) 控制 → 隐藏状态栏 + 隐藏返回键(沉浸);退出恢复。注:app.json fullscreen 字段未在解析器读取(已定未做 [源:审计 2026-08-30]),故不能经 app.json 声明全屏。

与 launcher 统一(A+C):launcher 迁移到 SDK 后,其状态栏(现私有 draw_statusbar())改走 SDK 这一套,全局统一、单点一致。

注意(诚实):这是 SDK 的较大增强——需引入系统状态读取器 + 顶部留白(inset)协调 + 与 APP 内容 z 顺序。非全屏 APP 必须给顶部 inset,否则内容被盖。API 名实现时以 meow_ui.h 为准。


4. 图片层 API(B1)

内建 stb_image(单头文件,静态编入 meow_ui.a),解 PNG/JPEG/BMP,统一转 RGBA。这是对"图片渲染"的直接答案(背景/封面/图标)。

/* 加载图片(从 RAM 缓冲或 TF 卡文件)。返回句柄,失败 NULL。
 * mem: 应用可先读文件到 RAM 再喂给解码器;meow_ui 不解读自身以外的 IO。 */
meow_img *meow_img_load_file(const char *path);    /* 内部读文件 + stb 解码 */
meow_img *meow_img_load_mem(const void *buf, int len);
void       meow_img_free(meow_img *img);

/* 查询 */
int meow_img_w(meow_img *img);
int meow_img_h(meow_img *img);

/* 绘制:整图 / 缩放 / 带 alpha */
void meow_ui_img(int x, int y, meow_img *img);                          /* 原始尺寸 */
void meow_ui_img_scaled(int x, int y, int w, int h, meow_img *img);     /* 最近邻缩放 */
void meow_ui_img_alpha(int x, int y, int w, int h, meow_img *img, int alpha);

/* 最常用:封面/图标九宫格或圆形裁剪(封面/专辑图等) */
void meow_ui_img_cover(int x, int y, int size, meow_img *img);          /* 居中正方形裁剪成 size×size */

裁剪/缩放:软渲用最近邻(快),不做高质量插值(省 CPU)。封面用 meow_ui_img_cover 居中裁方 + 缩放到目标尺寸即可。


5. 控件层 API(B1)

内置常用控件,观感统一(MD3 优先 / MIUIx 备选,见 §6 主题)。控件状态由 meow_ui 维护,绘制走 back_buf。

typedef struct meow_widget meow_widget;

/* 创建/销毁;控件 layout 由应用每次 flush 前调用 meow_widget_draw 绘制 */
meow_widget *meow_btn_create(int x, int y, int w, int h, const char *label);
meow_widget *meow_slider_create(int x, int y, int w, int min, int max, int val);
meow_widget *meow_switch_create(int x, int y, int on);
meow_widget *meow_list_create(/* 数据源回调 */);
void meow_widget_free(meow_widget *w);

/* 绘制 */
void meow_widget_draw(meow_widget *w);

/* 状态/取值 */
void meow_slider_set(meow_widget *s, int val);
int  meow_slider_get(meow_widget *s);
void meow_switch_set(meow_widget *s, int on);

控件命中(触摸点是否落在控件内)由输入层语义事件驱动,应用在 meow_ui_on_tap(x,y) 回调里做分发——见下节。 B1 先做最小控件集(按钮/滑条/开关/列表四件套),其余(下拉、进度圈、通知卡片)随阶段 B 通知中心 / 网易云需要用再加。

5.1 输入法(IME)—— 内置触屏软键盘

背景:R3 Pro II 无独立键盘设备(仅触摸 goodix-ts + 物理按键 md-gpio-keys / jz adc keyboard),无 USB/蓝牙 HID 键盘栈。任何需要文本输入的应用(输入框/搜索/设置项)都依赖 IME,且必须由 SDK 内置——应用绝不能自己实现键盘,否则观感/行为不一、重复造轮子。

形态:系统级单例软键盘,作为控件层的一个特例(meow_ime),应用调 meow_ui_text_input() 弹出,键盘覆盖在应用之上,由 SDK 绘制与分发(复用 back_buf + meow_present 无闪烁地基,不干扰业务渲染)。

B1 范围:英文 QWERTY 键盘(数字+字母+符号),覆盖字符输入、退格 Backspace、空格、回车/完成。 不在 B1:中文拼音/词库选字——复杂度高、依赖词库,留到后期独立 IME(网易云/中文搜索需要时再上)。B1 的 IME 只保证"能输入 ASCII 文本"。

/* 弹出软键盘,输入到应用提供的缓冲。返回 1=确认(fn=enter),0=取消(fn=esc/背景点)。
 * 回调驱动:输入变化由 SDK 内部循环处理,应用只需在 done 时读 out。 */
typedef enum { MEOW_IME_ASCII, MEOW_IME_NUM } meow_ime_mode;   /* 全键盘/数字键盘 */
typedef struct {
    meow_ime_mode mode;
    char   buf[256];        /* 应用提供的输入缓冲(初始值可预填,如 Wi-Fi 密码) */
    int    maxlen;
    /* 回调:每次字符变化回调一次(用于实时预览/校验) */
    void (*on_change)(const char *text, void *ctx);
    void *ctx;
} meow_ime_ctx;

/* 阻塞:弹出键盘直到确认/取消。返回 1=确认(buf 已回填),0=取消。 */
int  meow_ui_text_input(meow_ime_ctx *ctx);

/* 非阻塞变体:返回后应用在 on_tick 里轮询 meow_ime_active()/meow_ime_text() */
void meow_ui_text_input_async(meow_ime_ctx *ctx);
int  meow_ime_active(void);
const char *meow_ime_text(void);

布局(480×720 竖屏):键盘占底部约 40% 高(~290px),上方为输入预览行 + 候选区。键位 40×40(够 touch target),触屏点按输入。 物理键辅助(兼容未来硬件键/可访问性):方向键移动光标、播放/确认键=选择、音量=翻页。B1 以触摸为主,物理键作为可选加分项(SDK 内实现)。

[B1 后续 / 不阻塞]:中文拼音 IME、词库、候选词联想、剪贴板、长按重复——作为独立里程碑,等首个中文输入场景(网易云搜索)出现再做。


5.2 横向分页(横向滑动切换页面)设计模板 [源:2026-08-30]

性质:SDK 设计模板,供后续 APP 主页/子页按需选用;参照 HibyOS 播放器主页横划进播放页的实现思路。B1 不实现,本小节只列设计要点与接口草案(标注「草案/待定案」),供后续落地时对齐,避免与既有手势/控件约定冲突。

模板目标:多页横向滑动切换,流畅不卡;供 APP 主页/子页复用。

1) 手势分流(关键,与既有手势不冲突):按起始位置分流,三者互斥——

  • 触点落在屏幕左缘(≤24px)热区 → SDK 左滑返回(不动画,on_swipe(LEFT),见 §6 与 spec-ref §5.1)。
  • 触点落在中部左/右滑 → 页切换(页视图接管)。
  • 顶部下滑 → 通知中心(不夺走既有顶部热区)。

命中判定:先判断按下 x 落在左缘/顶部热区还是中部,再定手势语义;互斥,不与既有「左缘返回 + 顶部下滑通知中心」冲突。

2) 预渲染快照缓存(抗卡顿核心):

  • 相邻页各预渲染一张 back_buf 快照(页小数据 + 内容缓存)。切换时只 blit 快照,不重跑布局/控件绘制(页多不卡的关键)。
  • 缓存上限 = 当前页前后各 1 页(共 3 张 ≈ 3 × 屏宽 × 高 × 2B;480×720 RGB565 每张 ≈ 0.66MB → 3 张约 2MB,远低于 14MB 内存预算;按更保守 2MB/张 × 3 ≈ 6MB 口径亦可接受)。
  • LRU 淘汰远页;回滚重建时显示「加载中」轻提示。

3) 拖动 / 吸附:

  • 手指跟手平移当前页 + 露出下一页边缘:单帧内多次 blit 位移 + 一次 meow_present(vsync 门控 ≤60 帧,不做逐帧重排版)。
  • 松手过阈值 → 补 2-4 帧平移动画吸附相邻页;未过阈值 → 回弹当前页;首/末页不滑(或橡皮筋回弹)。

4) 性能要点:切换 = 快照 blit + 位移平移(内存拷贝远快于重绘) + 节流 present;拖动时零 UI 重算(不触发布局/控件重绘)→ 页多不卡。

5) SDK 建议 API 草案(「草案/待定案」,不实现):

typedef struct meow_page_view meow_page_view;

/* 数据源回调:页数 / 每页渲染回调 */
typedef struct {
    int  (*page_count)(void *ctx);                          /* 总页数 */
    void (*draw_page)(meow_page_view *pv, int page,
                      uint16_t *snapshot, void *ctx);       /* 渲染该页到快照 buf */
    void *ctx;
} meow_page_view_source;

meow_page_view *meow_page_view_create(const meow_page_view_source *src,
                                      int x, int y, int w, int h);
void meow_page_view_free(meow_page_view *pv);
int  meow_page_view_set(meow_page_view *pv, int page);      /* 跳页(快照重建 + 吸附动画) */
int  meow_page_view_current(const meow_page_view *pv);      /* 当前页 */

SDK 内部处理:手势分流(左缘返回除外,仍归 SDK 手势层 on_swipe(LEFT))、快照缓存、平移吸附、LRU 淘汰、加载中提示。实现时以 meow_ui.h 为准。

6) 关联:与既有「全局左缘返回 + 顶部滑条 + 状态栏收合/通知中心」手势约定一致(spec-ref §5.1/§3.1)——左缘返回仍为全局手势(on_swipe(LEFT)/on_swipe_progress);页视图只接管中部横滑;顶部下滑仍归通知中心。三者按起始位置分流,互斥不冲突。

5.3 SDK 可复用设计模板清单 [源:2026-08-30]

性质:把 MeowOS 应用开发里反复出现的界面结构抽象为可复用的设计模板(设计要点 + 交互约定 + 绘制/数据模型 + 性能注意 + 复用组合),供各 App 与设置页按需选用、观感统一。本小节是目录:列出全部模板及优先级/状态;已展开为设计模板者见对应小节。所有模板与既有手势/控件约定一致(见 §5.2/§6/spec-ref §5.1/§3.1),且相互可组合。

# 类别 模板 一句用途 优先级/状态
① A 分组设置表单 分组标题 + 行(开关/滑条/下拉/子菜单/信息),数据源 = setting 键 ✅ A+B 先行(已展开 §5.4)
② A 列表+选择 行数据源回调(图标+标题+副标题+右侧操作),滚动/单选/多选/搜索 ✅ A+B 先行(已展开 §5.5)
③ B 弹窗/对话框 确认/选择/输入/信息,模态/非模态,返回/电源键关闭 ✅ A+B 先行(已展开 §5.6)
④ B 顶/底部下拉面板 快捷设置/通知中心/侧边栏,收合动画、内容懒载 ✅ A+B 先行(已展开 §5.7)
⑤ C 媒体控制/播放页 进度/控制/封面/歌词滚动 🔵 随播放器 app 细化
⑥ C 沉浸内容页 全屏/左滑返回/图片缩放/歌词随乐 🔵 随播放器 app 细化
⑦ C 进度/加载 进度条/转圈/骨架屏/下拉刷新 🔵 随对应 app 细化
⑧ D 卡片/磁贴 圆角卡片/图标宫格/应用网格 🔵 随 MD3 细化
⑨ D 导航栏/工具栏 顶标题+返回/底栏 tabs/面包屑 🔵 随导航细化
⑩ D 列表项布局 图标+文本+右侧操作,点击/长按/滑删 🔵 随 MD3 细化
⑪ E 拖拽排序/长按菜单/时间轴 拖拽排序/长按菜单/时间轴 ⚪ E 后置(可选)

优先级 / 状态说明:

  • A+B 先行(近期展开,§5.4-§5.7):设置表单/列表/弹窗/面板是现阶段 MeowOS 应用(设置、通知、App 列表、WiFi)最常用的四类结构,先定下来沉淀范型,落地时可复用 launcher 已调通的 s_row_*/s_dd_modal 逻辑。
  • C-D 随对应 app/MD3 细化:媒体/沉浸/加载随播放器类 app 落地时细化;卡片/导航/列表项随 MD3 观感统一时细化——避免过早抽象、先让 app 跑通。
  • E 后置:拖拽排序/长按菜单/时间轴为可选增强,非首批通用结构,后续需要时再立项。
  • 已含横向分页:多页横向滑动已在 §5.2 单独展开,不重复列入本清单。
  • 可扩展:本清单按需增项(新模板补一行;若展开则新增一节),编号后续连续。

手势一致性(此处只述结论,不重复展开):全部模板与既有「全局左缘返回 + 顶部滑条 + 状态栏收合 + 通知中心热区」手势约定一致(spec-ref §5.1/§3.1、本文件 §5.2/§6)——左缘 ≤24px 内划≥90px 抬手=返回(on_swipe(LEFT)/on_swipe_progress);顶部下滑=通知中心;中部横滑/竖滑才归内容。各模板不重定义这些全局手势,只在其上做内部命中分流。


5.4 设计模板 A① 分组设置表单 [源:2026-08-30]

参照:patch_v2/meow_launcherd/meow_launcherd.c 设置页(SMENU 表 + srow_t + draw_settings_page/run_settings_page)。性质:SDK 设计模板,供 App/设置页按需选用;meow_ui.h 现状控件最小集(按钮/滑条/开关/列表,§5)只提供单控件,本模板是把它们组织成「分组设置表单」的结构范型。API 为「草案/待定案」,不实现。

摘要:一页设置表单 = 若干分组标题行 + 若干设置行。每行 label + 控件,控件类型决定取值方式——开关 / 滑条 / 下拉(枚举·预设)/ 子菜单 / 信息 / 列表。数据源 = setting 键(经 meow_settings_* 读写,见 spec-ref §4),UI 只绑定键、不直接触存储文件。

关键交互:

  • 分组标题行(key=NULL)仅展示分组名(如「显示/网络/电源/字体主题」),不可选中。
  • 行命中分发:触摸命中某行后按行控件类型分发——
    • 开关:meow_settings_set_switch(key, !on)(或 set_bool),行右侧画开关。
    • 滑条:按下/拖动沿 track 取值 → meow_settings_set_int(key, v)(后台 clamp 到 min..max + 同步缓存),行内画 track+thumb+当前值。
    • 下拉(枚举/预设):点行 → 弹单选弹窗(复用 B③ §5.6)→ meow_settings_set_enum/set_str(key, idx)。
    • 子菜单(如字体 family→weight 两级):点行 → 二级列表(复用 A② §5.5 容器)→ 选中写回串值(如卡根相对路径 MeowOS/fonts/<family>/<weight>/font.png)。
    • 信息/列表:点行 → 打开只读详情或子列表(如「关于本机」「WiFi 连接状态」「附近WiFi」)。
  • 滚动:整行步进(SROW_H),选中行钳制在视口内;列表拖动滚动(纵向位移超阈值判滚动、非 tap)。
  • 返回/取消:左缘返回手势回上级(全局手势,不由本模板重定义)。

绘制/数据模型:

  • 行描述结构(参照 srow_t):{label, key, ctrl(min,max,step), presets/preset_labels/preset_n, net_list};ctrl ∈ {HEADER, SWITCH, SLIDER, ENUM, PRESET, INFO, LIST};key=NULL 表示分组标题行。
  • 单卡片 + 固定行高:SET_CARD_X/W/Y/H + SROW_H(行高统一,便于整行滚动对位与裁剪;视口高取行高的整数倍)。
  • 可见性:s_row_visible(i)(如多字体 6 个 class 行仅在开关开时可见)→ 压缩索引 s_vis_of/vis_at;标题行恒显。
  • 值格式化:s_fmt_*(秒→「×分×秒」、分钟→「HH:MM」、枚举→中文标签、预设项显示名)。
  • 命中/滚动换算:s_row_at_content_y(cy)(content 坐标→行号);滑条单独记录 track 纵坐标供拖动命中。

性能注意:

  • 局部刷新:静态展示不主动刷新,仅在交互时经 meow_dirty 区标脏 + meow_ui_flush_region 只刷变化区(滑条拖动/开关切换/选中行高亮)。
  • 滑条拖动实时 set_int(后台 clamp、同步缓存,不逐 1% 落盘),松手才落盘;避免拖动时每帧写文件。
  • 滚动用整行步进 + 区间钳制,不做逐像素重排。

复用组合:A① 是容器;行内容用 A②(列表项布局)/ B③(下拉弹窗)+ SDK 控件(meow_btn/slider/switch);设置页顶部「快捷开关」组可再被 B④ 面板复用。

API 草案(「草案/待定案」,不实现;实现以 meow_ui.h 为准):

typedef enum { MEOW_FROW_HDR, MEOW_FROW_SWITCH, MEOW_FROW_SLIDER,
               MEOW_FROW_ENUM, MEOW_FROW_PRESET, MEOW_FROW_INFO,
               MEOW_FROW_LIST } meow_frow_type_t;
typedef struct {
    const char        *label;
    const char        *key;               /* setting 键,经 meow_settings_* 读写;NULL=分组标题行 */
    meow_frow_type_t   ctrl;              /* 控件类型 */
    int min, max, step;                  /* SLIDER 取值域/步进 */
} meow_frow_t;
meow_widget *meow_ui_setform_create(const meow_frow_t *rows, int n, int x, int y, int w, int h);
int  meow_ui_setform_sel(meow_widget *f);                  /* 当前选中行 */
void meow_ui_setform_scroll(meow_widget *f, int offset);   /* 整行步进滚动 */
int  meow_ui_setform_hit_row(meow_widget *f, int x, int y);/* 命中行号(-1=分组标题/卡片外) */

说明:meow_ui_setform_* 与 launcher 的 srow_t/SMENU/run_settings_page 同构(一行=label+key+控件),实现期可直接借已调通逻辑。区分「行描述表」与「每行当前值」——值一律经 meow_settings_get_* 读取,不缓存在行结构里(避免双写漂移)。


5.5 设计模板 A② 列表+选择 [源:2026-08-30]

性质:SDK 设计模板,供 App 主页/子页按需选用;对既有 meow_list_create(B1 最小集,§5,仅 label 文本)做结构化扩展(行=图标+标题+副标题+右侧操作 + 选中态)。API「草案/待定案」,不实现。

摘要:可滚动选择容器。行数据源回调逐行给出(图标 / 标题 / 副标题 / 右侧操作 / 是否可选),容器负责绘制/滚动/选中/命中;支持单选、多选、长按(上下文操作)。适合「已保存网络 / 附近WiFi / 蓝牙设备 / 歌曲列表 / 设置子菜单」等。

关键交互:

  • 行内容:图标(可选 meow_img* 或内置符号)+ 标题 + 副标题(次要信息,如 SSID/设备名/时长)+ 右侧操作(如箭头/开关/详情钮)。点整行=选中/进入;点右侧操作=该行动作。
  • 单选/多选:mode 决定——单选(高亮当前 + 确认回填);多选(复选标记);「无选择」= 纯入口列表。
  • 长按:长按行 → 上下文菜单(如删除/重命名),或不弹菜单仅标记长按态(复用 B③ 小弹窗)。
  • 搜索/过滤:顶部搜索框(IME 输入,§5.1)→ 数据源回调按关键字过滤重算行数(B1 IME 仅英文/数字)。
  • 滚动:局部 dirty;整行步进或像素滚动均可;首/末行钳制(橡皮筋或截断)。

绘制/数据模型:

  • 行数据源回调(meow_list_source 的结构化扩展):
    typedef struct {
        const meow_img *icon;       /* 可选 */
        const char     *title;      /* 主文本 */
        const char     *subtitle;   /* 副文本,可空 */
        int             right;      /* 右侧操作类型(无/箭头/开关/详情钮) */
        int             enabled;    /* 可选中性 */
    } meow_list_item;
    typedef int (*meow_list2_source)(int idx, meow_list_item *out, void *ctx); /* 返回 1=有该项,0=越界 */
    
  • 容器:视口(x,y,w,h) + 总行数(count,可再查询更新) + 滚动偏移;count 由数据源动态变化(搜索过滤/懒加载)。
  • 命中:行 = y/row_h;右侧操作细分命中(行右侧 1/3 区域判操作,其余判整行)。命中判定避免 goodix 时序问题——按下不锁原点、抬手时用最新有效坐标(参照 §5.6 与 meow_touch 语义)。
  • 选中态:单选高亮条 / 多选复选标记,用主题 accent 色。

性能注意:

  • 局部 dirty:滚动/翻页只刷变化行(meow_ui_flush_region 行块或视口带),不整屏重绘;数据源回调每行轻量填充,不在回调里做 IO(大图标用缓存/延迟加载)。
  • 懒加载:行多(如几百首歌曲)时数据源按需返回,仅落入视口 ± 预取若干行才真正加载。
  • 内存:行数据瞬时(回调填充到栈上 meow_list_item),不长期持有全部行对象;图标句柄归属调用方。

复用组合:A② 是 A① 的子菜单/列表行、B③ 的「选择」数据源、播放页歌单的通用底座;选中结果回填到 A① 的下拉项。

API 草案(「草案/待定案」,不实现;实现以 meow_ui.h 为准):

typedef enum { MEOW_LIST_SEL_NONE, MEOW_LIST_SEL_SINGLE, MEOW_LIST_SEL_MULTI } meow_list_sel;
typedef int (*meow_list2_source)(int idx, meow_list_item *out, void *ctx);
meow_widget *meow_ui_listview_create(int x, int y, int w, int h, int count,
                                     meow_list2_source src, void *ctx, meow_list_sel mode);
void meow_ui_listview_set_sel(meow_widget *lv, int idx);        /* 单选/多选当前选中集 */
int  meow_ui_listview_get_sel(meow_widget *lv, int *out, int max);
void meow_ui_listview_scroll(meow_widget *lv, int offset);
int  meow_ui_listview_hit(meow_widget *lv, int x, int y);       /* 命中行;右侧操作另判 */

说明:既有 meow_list_create(label-only)保留;meow_ui_listview_* 为结构化扩展(图标/副标题/右侧/选中),可作为真则替换裸 label 列表。


5.6 设计模板 B③ 弹窗/对话框 [源:2026-08-30]

参照:meow_launcherd.c s_dd_modal()(WiFi 列表 / 关于本机 / 字体子菜单共用同一模态)。性质:SDK 设计模板,覆盖 确认/选择/输入/信息 四种;模态/非模态;返回/电源键关闭;遮罩。meow_ui.h 无通用弹窗,本模板为「草案/待定案」。

摘要:在应用之上弹出居中卡片(遮罩压暗 + 圆角卡片 + 标题 + 内容 + 操作),用于打断当前流程的一次性决策。按用途分四型:确认(是/否)、选择(单选列表,可复用 A② 数据源)、输入(复用 IME §5.1)、信息(只读展示,如「关于本机」)。

关键交互:

  • 模态 vs 非模态:模态=遮罩吞掉背景输入、打开期间应用主界面不可交互(返回/电源关闭);非模态=不遮罩、可同时操作背景(如播放页浮层)。默认模态。
  • 关闭途径:① 点操作钮确认/取消;② 点遮罩外=取消;③ 返回键(左缘返回手势 / 虚拟返回键)→ 取消;④ 电源键单击 → 确认当前高亮项(launcher s_dd_modal 的电源抬起=确认语义)。音量键上下移动高亮。
  • 选择弹窗:选项列表高亮 + 预勾选(cur 非 -1 时);cur=-1 = 只读展示不预选(「关于本机」)。
  • 物理键可访问:音量=移动高亮、电源=确认、返回=取消(借鉴 s_dd_modal 的 kp_* 键轮询)。

绘制/数据模型:

  • 绘制(参照 s_dd_modal,即画即显一次成型):
    1. 全屏遮罩 meow_ui_fill_rect_alpha(0,0,W,H, 0x000000, 150)(压暗)。
    2. 居中卡片 meow_gfx_fill_round(d_x,d_y,d_w,d_h, 16, surface_container) + meow_ui_draw_rect_round(..., 16, outline) 描边;圆角 16。
    3. 标题(theme on_surface,20 号)+ 选项行(行高 ~48;选中行 primary α36 高亮 + 白字;预勾选项右侧画小方块)。
    4. 不压状态栏:卡片 y 钳制 d_y ≥ meow_ui_statusbar_height();底部留 ≥20px。
  • 尺寸:宽 ~348、居中;高 = 标题 + n×行高 + 内边距;内容过多滚动(或扩容)。
  • 数据模型:{title, item[], n, cur, type};选择型=选项数组;信息型=多行文本;输入型=携 IME ctx。
  • 命中:抬手时用最新有效坐标(不锁按下原点,goodix 免疫);卡片内命中→选项;卡片外→取消。打开前双排空触摸残留(s_dd_modal 的 open 后 read 排空→40ms→再排空,吸收 goodix 抬起长尾)。

性能注意:

  • 即画即显:弹窗打开时一次性画好遮罩+卡片+选项,再进事件循环(s_dd_modal 初始绘制一次,仅高亮变化才重绘);不每帧重画静态内容。
  • 重绘只刷变化区(切换选中高亮时,仅重画遮罩+卡片或行块 flush_region)。
  • 无动画(B1 无 tween);如需淡入淡出,用 fill_rect_alpha 单次叠加以免每帧重算。

复用组合:B③ 是 A① 下拉(enum/preset 选中)、A② 长按上下文菜单、B④ 面板关闭确认、电源菜单的通用弹窗底座;输入型接 IME §5.1;选择型数据源同 A②。

API 草案(「草案/待定案」,不实现;实现以 meow_ui.h 为准):

typedef enum { MEOW_DLG_CONFIRM, MEOW_DLG_SELECT, MEOW_DLG_INPUT, MEOW_DLG_INFO } meow_dlg_t;
typedef struct meow_dialog meow_dialog;
meow_dialog *meow_ui_dialog_create(meow_dlg_t t, const char *title);
void  meow_ui_dialog_set_items(meow_dialog *d, const char *const *item, int n, int cur);
void  meow_ui_dialog_set_ime(meow_dialog *d, meow_ime_ctx *ime);   /* INPUT 型 */
void  meow_ui_dialog_show(meow_dialog *d);                        /* 阻塞到确认/取消 */
int   meow_ui_dialog_result(meow_dialog *d);                      /* 选中=选项下标;输入=IME buf;确认=1/0 */
void  meow_ui_dialog_close(meow_dialog *d);

说明:模态=默认;非模态=show 非阻塞变体(启动后由 on_tick 驱动)。返回/电源关闭语义沿用全局约定(返回=取消;电源=确认当前高亮),本模板不重定义。


5.7 设计模板 B④ 顶/底部下拉面板 [源:2026-08-30]

性质:SDK 设计模板,覆盖顶部下拉(快捷设置/通知中心)与底部面板(操作抽屉/侧边栏),带收合动画与内容懒载。关键前提:与既有「全局左缘返回 + 顶部滑条 + 状态栏收合 + 通知中心热区」手势一致(spec-ref §5.1/§3.1、A-13)——本模板不夺走「顶部下滑=通知中心」,而是按起始位置/触达途径与既有热区分流。API「草案/待定案」。

摘要:沿某种边缘滑出/推入的非全屏浮层。顶/底部下拉面板 = 手指从对应边缘向内滑 → 面板从边缘滑出(带收合动画),内容按需加载。典型:快捷设置(亮度/音量/开关)、通知中心(列表)、底部操作抽屉;可再收合。

关键交互:

  • 手势分流(互斥,与既有约定一致):
    • 左缘 ≤24px 向内划 → 全局左缘返回(on_swipe(LEFT),SDK 兜底;本面板不接管)。
    • 顶部边缘下滑 → 通知中心(既有热区,on_swipe(DOWN)/顶部热区;全屏下热区缩小但仍保留,见 spec-ref §5.1/A-13)。
    • 底部边缘上滑 → 底部面板(本模板新增;底部边缘无既有冲突)。
    • 快捷设置:为不与通知中心冲突,待定案——建议从通知中心二级拉出、或屏幕右上角/右侧缘单独热区触达、或「通知中心内点箭头展开」。不新占顶部下滑(避免与 §5.1 冲突)。
    • 侧边栏(可选):右缘向左滑出(右侧无既有手势,安全)。
  • 收合动画:跟手平移面板(手指拖多长露多长),松手过半阈值 → 补 2-4 帧短插值吸附到位;未过 → 回弹。动画只在松手时做一次,不做逐帧几何重算(B1 无 tween;短插值=有限帧序列)。
  • 内容懒载:面板首次拉出才构建内容(或预建空容器),避免打开即全量绘制;面板内滚动局部 dirty。
  • 关闭:点面板外关闭 / 再划回边缘收合 / 返回键关闭(模态时)。

绘制/数据模型:

  • 面板 = 一个矩形区域(顶/底边缘滑出),内容区 + 头部(把手/标题/关闭钮)。绘制走 back_buf,meow_ui_flush_region 只刷面板交汇区。
  • 内容:快捷设置=一组开关/滑条(复用 A① 控件);通知中心=列表(复用 A②);操作抽屉=一组按钮(复用 meow_btn)。
  • 数据模型:{edge, x, y, w, h, content_widget, collapsed, anim_t};内容视图句柄(可为 A①② 或自定义);懒载=首次展开时创建内容视图。
  • 布局 inset:顶部面板 y 钳制 ≥ meow_ui_statusbar_height()(非全屏不压状态栏);全屏时顶部热区缩小。

性能注意:

  • 内容懒载:展开才构建;收合后保留视图(缓存)或销毁(按需)。
  • 收合动画:短插值(≤4 帧),只 blit 面板+交汇带,零内容重排(内容已预渲染到 offscreen 或复用快照,与 §5.2 快照思想同源)。
  • 局部 dirty:面板滑入滑出用 meow_ui_flush_region 刷边界带;内容行变化刷行块。

复用组合:B④ 顶栏=快捷设置(A① 控件组)+ 通知中心(A② 列表);底部面板=操作抽屉(meow_btn 组 / A② 动作行);侧边栏=A② + 导航。

API 草案(「草案/待定案」,不实现;实现以 meow_ui.h 为准):

typedef enum { MEOW_PANEL_TOP, MEOW_PANEL_BOTTOM, MEOW_PANEL_EDGE_R } meow_panel_edge;
typedef struct meow_panel meow_panel;
meow_panel *meow_ui_panel_create(meow_panel_edge e, int x, int y, int w, int h);
void meow_ui_panel_set_content(meow_panel *p, meow_widget *content); /* 懒载:首展创建 */
int  meow_ui_panel_expand(meow_panel *p);      /* 1=展开(短插值动画) 0=收合;返回位移 */
int  meow_ui_panel_is_open(const meow_panel *p);
void meow_ui_panel_set_collapsed(meow_panel *p, int collapsed);

说明:顶部面板默认不占用「顶部下滑=通知中心」(§5.1)——顶部热区归属通知中心;快捷设置在通知中心内二段展开为待定案(见关键交互),实现前须列出可与左缘返回/通知中心分流的具体触达途径,勿凭空抢占。


6. 输入层 / 生命周期(B1 部分 + B2 衔接)

B1(本阶段):应用不再自读 /dev/input/event*,meow_ui 建立输入线程,把按键/触摸翻译成语义事件回调:

/* 应用注册事件回调(meow_app_start 时传入) */
typedef struct {
    void (*on_key)(int keycode, int is_down);      /* KEY_VOLUP/VOLDOWN/PLAYPAUSE/POWER…(SDK 已消化系统语义) */
    void (*on_tap)(int x, int y);                  /* 触摸抬起落点 */
    void (*on_swipe)(int dir, int dx, int dy);     /* 左缘返回手势=LEFT;中部滑动=RIGHT/UP/DOWN */
    void (*on_tick)(void);                         /* 空闲周期回调(动画/时钟用,见 §7) */
    void (*on_swipe_progress)(int x_start, int dx);/* 左缘返回手势过程实时回调;x_start=按下X, dx=当前X-按下X */
} meow_ui_events;

void meow_ui_run(const meow_ui_events *ev);        /* 阻塞:事件循环,直到 meow_app 请求退出 */
void meow_ui_request_exit(void);                   /* 应用调它请求退出(相当于触返回/按退出键) */

/* 左缘返回手势的顶部视觉条(SDK 已自动绘制,应用一般无需自画)。
 * 画屏幕顶部 5px、从 x=0 到 x 的横条(主题 accent 色);x<=0 清条。应用可在
 * on_swipe_progress 里用它自画(长度=当前 X=x_start+dx),或留空全靠 SDK。 */
void meow_ui_swipe_hint(int x);
  • 返回语义:B1 下按"返回"(左缘返回手势/虚拟返回键)→ 由应用决定:on_swipe(LEFT) 是返回手势,应用自行 meow_ui_request_exit() 回应用列表;若想在页内返回,可在 on_swipe(LEFT) 里处理并 return 不退出。
  • 左缘返回手势(抬手触发):手指从屏幕左缘(≤24px)向内划 ≥90px,抬手(release 边沿)后才触发 on_swipe(LEFT);过程以 on_swipe_progress 实时上报并自动画顶部进度条(meow_ui_swipe_hint,长度=当前 X)。中部左/右滑不触发返回(中部右滑→on_swipe(RIGHT) 内容导航;中部左滑→落 TAP)。与设计稿 §2.2 的偏差:§2.2 写「左縁 + 向左 mdx<-90」物理不可达(左縁起点再向左最多走 ~24px),故取「左縁向内(右)划 ≥90px」实现,事件仍报 MEOW_SWIPE_LEFT(保持「左滑=返回」契约),方向与 watchdog immersive 兜底一致。
  • 沉浸模式:应用声明(app.json immersive=1)→ launcher 写 /tmp/meowapp_state=1 → watchdog 隐藏虚拟返回键,返回手势归 SDK。这是 B1 可做、B2 转正的一部分。

B2(衔接阶段 D):把 on_key/on_swipe 换成走 IPC 的语义导航(MSG_NAV 上报 + 启动链路),应用不再自己决定"返回回哪层",而是上报当前 Activity,由框架导航栈统一回退。届时:

  • meow_ui_run 改为从 IPC 收启动链路(MEOW_CHAIN/MEOW_ACTIVITY 环境变量)初始化导航栈;
  • 返回 = 栈回退,到根才 meow_ui_request_exit()。本文档 §5 的控件与 §3/§4 绘制在 B2 不变,只换输入/导航后端。

6.1 控件生命周期与归属(Activity 栈 + 三层控件模型)[源:2026-08-30]

性质:SDK 控件生命周期的权威约定(B1/B2 均遵守)。根治「跨页面控件残留」——控件命中/绘制必须严格绑定其所属页面,否则会出现"按钮已不可见但仍被触摸命中"(如全屏页触摸到主页按钮位置闪现)。

核心:控件严格归属创建它的 Activity,不跨页面共享。

三层职责(从 App 到系统,风险从低到高)

层 谁负责 谁能用 生命周期
① 页面控件 归属唯一 owner Activity App 随 Activity 栈;owner 在栈顶(active)才绘制+响应触摸
② App 跨 Activity 控件 SDK 托管(逻辑+渲染,框架职责) App 可声明使用 钉在 App 的 Activity 栈之上,跨 Activity 常驻
③ 系统级控件 MeowOS/SDK 框架内部 仅系统组件,用户 App 禁调用 随系统,不开放
  • ① 页面控件:meow_btn_create 等创建时绑定当前 Activity;命中(live_btn_hit)只在当前 active Activity 的控件集里找;对象不销毁(Activity 内对象池复用),弹栈回来自动恢复 → 回页面零重建。
  • ② App 跨 Activity 控件:因为 App 页面本身依赖 SDK,跨 Activity UI 也由 SDK 负责逻辑与渲染。App 用 meow_ui_global_*(如下)声明,SDK 托管(钉在 App 栈之上、切 Activity 仍存在、逻辑/命中统一调度)。例:App 内部底部播放栏(多 Activity 共享)。
  • ③ 系统级控件:状态栏、通知中心入口、左缘返回/全屏滑条、电源菜单、全局返回手势指示等全局叠加层——只留 SDK 内部接口位,用户侧 App 一律不可调用(开放成「悬浮窗/全局叠加层」会引入遮挡、冲突、性能与安全风险)。App 这类需求只能走 SDK 标准能力,不得自行叠加全局层。

交互分层(命中分流)

触摸命中顺序:③ 系统全局 → ② App 全局 → ① 当前 Activity 页面控件;某层命中即消费,否则落到下一层。

与 agent-119「即时重绘注册表」的关系

g_live_btns[] 从进程级全局,改为按 Activity 分区的控件注册集;非 active Activity 的控件不参与命中。这直接根治 agent-119 遗留的"全屏页触摸到主界面按钮位置 → 短暂闪现 accent 底"(页面控件残留,跨页不复用)。

API 定案(本轮已在 B1 实现)

/* ① Activity 栈 */
void meow_ui_activity_push(int id);
void meow_ui_activity_pop(void);
int  meow_ui_activity_active(void);            /* 当前 active activity id */

/* ② App 跨 Activity 控件(SDK 托管) */
typedef void (*meow_global_render_fn)(void *ctx);  /* 每次渲染帧回调(SDK 调用) */
typedef int  (*meow_global_hit_fn)(void *ctx, int x, int y); /* 命中返回1=消费 */
void meow_ui_global_add(int id, meow_global_render_fn r, meow_global_hit_fn h, void *ctx);
void meow_ui_global_remove(int id);

/* ③ 系统级控件:仅框架内部,无公共 API(不开放) */

实现期定案 / 与上文的偏差(2026-08-30 落地,保持文档实时一致)

  • 控件绑定 owner:meow_btn_create 等所有控件(btn/slider/switch/list)创建时即取 meow_ui_activity_active() 作为唯一 owner(meow_widget 新增 int owner 字段)。栈空时 active() 返回 0(根/default Activity)——未显式管理栈的 App 所有控件默认归属根 0。
  • 命中双闸门:除 live_btn_hit(实时按压重绘)外,公共 meow_widget_hit 同样仅限 owner==active 才返回命中(页面控件只对自身 Activity 负责,不跨页复用)。SDK 侧 meow_widget_touch_refresh 另加 stale 保护:按压期间若发生 Activity 切换(如点 FULLSCREEN 进全屏),静默清除其按住引用、不绘制,杜绝在新页底上画出旧按钮 accent 残留。
  • 对象复用(非销毁):§6.1 的「Activity 内对象池复用」本轮从简落地——控件对象不随 pop 销毁,SDK 不维护独立对象池;App 自行持有控件指针(hello 用静态全局),弹栈回来自动恢复命中(零重建)。注册表按指针去重(live_btn_register 查重),故反复 push/pop 不重复注册、不泄漏。
  • ② 为可用骨架:SDK 维护全局控件列表(meow_ui_global.c),渲染帧 meow_ui_global_render() 在 active activity 内容之上、全局状态栏之下调用 render_cb;触摸 TAP 先 meow_ui_global_hit()(返回 1=消费),否则落 on_tap。无 z 序细分/无按压态管理/无命中区排布(待 B2 细化);同 id global_add 幂等。
  • ③ 系统级(状态栏/通知中心/返回滑条/电源菜单):未开放任何公共悬浮/叠加层 API,既有状态栏/返回滑条逻辑原样保留。

系统 UI 归属与进程隔离(源:2026-08-30 定案,跨引用 FRAMEWORK_DESIGN §10.2)

把「系统组件归谁渲染」在 SDK 规范层面钉死,明确 APP 进程不渲染系统 UI、SDK 负责系统组件的进程与负责 APP 的进程相互独立。

  • 系统组件渲染归系统进程(launcher):状态栏 / 通知中心(下拉)/ 电源菜单 / 全局左缘返回与滑条等全局叠加层,由系统进程(meow_launcherd)负责;APP 进程不渲染系统 UI。
  • SDK 提供系统组件 API 仅供系统进程链接调用:SDK 保留系统级控件内部接口位(上面 ③);用户 App 拿不到、不可自建悬浮层/全局叠加——App 这类需求只能走 SDK 标准能力。
  • 进程隔离(核心):SDK 负责系统组件渲染的进程(launcher)与负责 APP 渲染的进程相互独立——APP 卡死/崩溃时,系统 UI(launcher)不崩,始终可息屏/返回/下拉通知中心(对应 APPS §3.4.1 物理键全局接管、APP 禁映射)。
  • APP 侧职责:只画自己的内容;经 IPC 上报全屏声明(fullscreen/route.<id>.fullscreen)、内容区域(inset)与前台 activity;系统 UI 据此做叠画 + 命中分流(§6.1 交互分层:③ 系统全局 → ② App 全局 → ① 当前 Activity 页面控件)。

7. 动画/时间(B1 最小可用)

当前无动画框架。B1 提供帧驱动回调 + 内置时间,应用据此做低成本的帧序列/淡入淡出:

/* on_tick(ev) 里用 meow_ui_time_ms() 做时间轴动画 */
uint64_t meow_ui_time_ms(void);                     /* 单调毫秒时间戳 */
/* 动画 = 应用在 on_tick 里根据时间更新状态 → 调绘制 → meow_ui_flush_region 只刷变化区 */

约束:

  • 只做状态时间轴动画(如滑条拖动回馈、列表项滑动、淡入淡出用 fill_rect_alpha 叠加),不做实时 tween 每帧重算几何(CPU 软渲扛不住)。
  • 动图/视频:B1 不提供(无解码器/无 GPU)。若确需"动画封面",用预渲染位图帧序列(应用持有若干帧,on_tick 按时间切帧 + flush_region),这是唯一低成本的动图方案。
  • 毛玻璃/模糊背景:支持廉价版(HibyOS 同款降采样模糊,见 §11)——用 meow_ui_img_scaled 缩 1/8 铺底 + fill_rect_alpha 遮罩;仅在切歌/换封面时算一次,非每帧。真高斯卷积/全屏动态模糊不做。

8. 主题/配置(B1)

观感统一的核心,也是回应"控件样式效果"。内置日间 / 夜间两套主题,跟随 MeowOS 系统设置的主题模式自动切换。

typedef enum { MEOW_THEME_LIGHT, MEOW_THEME_DARK, MEOW_THEME_AUTO } meow_theme_mode;
typedef struct {
    uint32_t bg;            /* 背景色 */
    uint32_t fg;            /* 前景/正文 */
    uint32_t accent;        /* 强调色(按钮/选中) */
    uint32_t dim;           /* 次要文字 */
    int      font_title;    /* 28 */
    int      font_body;     /* 20 */
    /* MD3 优先 / MIUIx 备选 */
} meow_theme;

/* 当前生效主题(应用只读)。AUTO 时按系统主题设置(display.theme_mode)解析,
 * 日间取 light 表、夜间取 dark 表;由框架在主题模式变化时刷新。 */
const meow_theme *meow_theme_get(void);
/* 设置主题模式(AUTO 时联动系统设置键 display.theme_mode)。 */
void meow_theme_set_mode(meow_theme_mode m);
meow_theme_mode meow_theme_get_mode(void);

/* 主题变化通知:应用注册后,AUTO 切换日/夜时收到回调(据此重绘)。 */
typedef void (*meow_theme_listener)(meow_theme_mode m);
void meow_theme_add_listener(meow_theme_listener cb);
void meow_theme_remove_listener(meow_theme_listener cb);

双主题 + 跟随系统设置:

  • 日间/夜间:SDK 内置两套标准色板(meow_theme_light / meow_theme_dark),日间高亮(浅底深字)、夜间低亮度(深底浅字,护眼省电)。框架所有控件/绘制默认读当前生效主题,应用 meow_theme_get() 只读、不自配色。
  • 跟随系统设置:主题模式 display.theme_mode(light/dark/auto)是 meow_settings 的一个设置键。auto 时按系统时间(如 6:00-18:00 日间,其余夜间)或系统亮度/环境判定切换(B1 先用固定时段,后续可接入环境光,见遗留)。框架监听该键,变时对注册的 listener 广播,实体 UI / Web / 应用统一重绘。
  • 主题切换即时生效:meow_theme_set_mode 非"只改设置",会立即广播 listener 并让当前绘制的控件用新主题重绘(应用在 on_tick 里据 listener 触发 meow_ui_flush_region)。

主题随 meow_settings 的 display.theme_mode 键分发;框架统一,应用不必自配色——这正是"实体 UI / Web 面板 / 应用全程一致观感"的保证。


9. 目录/构建约定

patch_v2/apps_assets/apps/<pkg>/app.json      # 应用描述(新增 SDK/immersive 字段,见下)
patch_v2/hello_app/hello.c                    # 应用源码(改造为调 meow_ui 的示例)
patch_v2/sdk/                                 # SDK 源码
  ├── meow_ui.h                              # 对外 API(本文档 §3-§8)
  ├── meow_ui_gfx.c                          # 渲染后端(back_buf+present+font)
  ├── meow_ui_widget.c                       # 控件
  ├── meow_ui_activity.c                     # §6.1 ① Activity 栈(控件归属)
  ├── meow_ui_global.c                       # §6.1 ② App 跨 Activity 控件(SDK 托管骨架)
  ├── meow_ui_img.c                          # stb_image 解码
  ├── meow_ui_input.c                        # 输入线程+语义事件
  ├── meow_ui_theme.c
  ├── stb_image.h / stb_image.c              # 单头文件(vendor)
patch_v2/meow_launcherd/meow_font.a          # 复用(静态链接)

构建出 patch_v2/sdk/meow_ui.a;应用编译链(zig-mipsel-cc)额外链 meow_ui.a meow_font.a libz.a -lpthread。

构建耦合(已知设计,非缺陷):build_sdk.sh 用 -I../meow_launcherd 引 meow_ipc.h(meow_notify_ipc.c 复用共享协议头,同一份、杜绝两端漂移),故 SDK 编译期依赖 launcher 头、非独立库;运行时无碍 —— App 只链 meow_ui.a(含 meow_font.o),不链入 launcher 符号,故 app 侧无 launcher 依赖。

app.json 扩展字段(向后兼容,缺省即旧行为):

{
  "name": "Hello App",
  "exec": "/usr/data/mnt/sd_0/MeowAPPs/os.meow.hello/hello",
  "package": "os.meow.hello",  // 反域名权威 app_id;目录名=package,launcher 取之为 app_id
  "version": "0.1",
  "framework": 1,            // 1=走 meow_ui(框架承载);缺省/0=裸自绘(旧)
  "immersive": 0,            // 1=沉浸模式(隐藏虚拟返回键,左缘滑动返回)
  "icon": "apps/os.meow.hello/icon.png"   // 可选:列表图标(供图片层)
}

上例 <pkg>=os.meow.hello(= apps/<pkg>/ 目录名 = package 字段值);exec 即 <挂载点>/MeowAPPs/<pkg>/<binary>。

app 字体来源(M2.x 已落地):app 若自带字库(load_handle,见 §5.7.3,未实现)则用它;否则 SDK meow_ui_gfx.c load_font() 优先读 launcher 注入的环境变量 MEOW_FONT_PNG/MEOW_FONT_IDX(=系统 font.file 当前生效字体,绝对路径)/MEOW_FONT_APP(family),无 env 则回退卡默认 MeowOS/fonts/default/regular/font.png(M2.6 字重结构)→ 固件。因此app 未自带字库时跟随系统 font.file,不再硬编码 MeowAPPs/fonts。

app 多字体(M2.5x,display.multi_font):launcher 注入 MEOW_MULTI_FONT=0/1 与 6 类 MEOW_FONT_CLS_<类>_PNG/IDX(命中则覆盖全局;类值为空=跟随全局不注入)。SDK meow_ui_gfx 的 meow_gfx_load_multifont() 读 env → 调 meow_font_config_multifont()(同 png 去重、≤4 套上限、缺类回退全局)构建逐字符分类路由:meow_font_drawtext/width_text/line_height 按 meow_font_class_of(cp)(MEOW_FCLS_CJK/_CJK_SYM/_NUM/_LATIN/_LATIN_SYM/_OTHER)用对应字库取字形与度量;meow_font_has_glyph(cp) 可预判缺字。display.multi_font=0/无 env → 单字体(现状,零额外开销)。底层接口见 meow_font.h:meow_font_load_slot/meow_font_unload_slot/meow_font_enable_multifont/meow_font_set_class_slot/meow_font_config_multifont;MEOW_MAX_SLOTS=5。

app 字体档位(M2.5x,app.json "fonts" 声明 + font.load_full):launcher 按 app.json "fonts":"common|gbk|full" 与系统「加载大字库」开关(font.load_full,默认 0;开=强制 ≥gbk 档)算出生效档,注入 MEOW_FONT_TIER=<0|1|2>(0=common≈4k 常用 1=gbk≈20k+ 大 2=full 全/可选),并把 MEOW_FONT_PNG/IDX 指向该档字库绝对路径——meow_font_effective_paths_tiered() 在卡内 MeowOS/fonts/<family>/<weight>/ 按 font.json 的 charset(common|gbk|full)选库(gbk 请求接受 gbk/full;full 请求优先 full 再降级 gbk;M2.6 优先同 family(其次同 weight);无匹配回退全局 font.file)。SDK meow_ui_gfx.c load_font() 载入后,应用可经 meow_font_tier() 查询当前档、meow_font_set_tier() 切档(触发 meow_font_reload_tier() 真重载:大字库切换先 unload 旧再 load 新,释放旧 atlas,防 GBK/Full 大库双驻留)。判缺字:meow_font_has_glyph(cp) 预判当前字库是否含该码位(common 小库下缺字→app 回退/提示,避免静默豆腐)。底层接口见 meow_font.h meow_font_reload_tier;font/gen/gen_font.py --charset common|gbk|full [--weight <name>] 按档生成 font.png/font.idx/font.json(font.json 写 weight+charset+glyph_count)。


10. 与阶段关系 / 落地顺序

  • B1(本文档,先行):meow_ui.h + gfx/widget/img/input/theme 最小实现 + meow_ui.a;改造 hello.c 为 SDK 版作为示例;出包 r3proii_m2_20.upt 或独立应用包验证。不依赖阶段 D。
  • B2(衔接阶段 D):输入/导航后端换成 IPC + M3 导航栈,应用框架字段 framework=1 的应用自动升级为"导航栈承载",裸应用保持兼容。
  • 依赖:meow_present/back_buf/dirty(M2.16 地基,✅)、meow_font(✅)、blend565(✅)——B1 全部基于现有能力,无需新的硬件依赖;仅新增 stb_image(单头文件)与输入线程。

11. 硬件约束(务必让应用作者知道)

能力 可行性 说明
图片(PNG/JPEG/BMP 静态图) ✅ 支持 stb_image 解码 + 最近邻缩放;封面居中裁方
半透明叠加 ✅ 支持 fill_rect_alpha(blend565),面板/遮罩可用
圆角/描边 ✅ 支持 圆角框 + 字体描边
毛玻璃/模糊背景 ✅ 支持(廉价版) HibyOS 同款「降采样模糊」:封面缩到 1/8 再放大铺底(天然低通滤波抹掉高频),叠一层 fill_rect_alpha 半透明遮罩,清晰小封面叠其上。背景只在切歌/换封面时算一次,非每帧;不是真高斯卷积、不需 GPU。用 meow_ui_img_blur_bg() 生成背景 cache。已实测依据:设备 playing_plane/ 无模糊背景图 + 无 GPU/无 SIMD/14MB 内存 → 运行时模糊只能走降采样近似,成本微秒级
控件动画(时间轴/帧序列) ⚠️ 有限 只做状态式动画;实时 tween 不做;动图=预渲染帧序列
动图(GIF/WebP 动画) ⚠️ 弱支持 用预渲染位图帧序列模拟;不承诺实时 GIF 解码
视频 ❌ 不做 无解码器/无 GPU/内存 14MB 会 OOM;如需"视频"用预渲染帧序列
刷新率 上限 60(默认)/30/15 应用动画别贪高帧率,软渲耗 CPU

12. 遗留 / 待实现

  1. meow_ui 渲染后端需从 launcher 里抽公共绘制实现(back_buf/meow_dirty_*/meow_present/meow_font_bind 目前是 launcher 静态函数——需提取为可链接的 meow_ui_gfx 或改为共享 obj,注意 launcher 与 app 是不同进程,不能共享同一 back_buf;meow_ui.a 是独立库,内部自建 fb 上下文,不复用 launcher 的全局 g_dirty)。
  2. 输入线程优先级:vs 主循环 10ms 热键轮询的竞态,需在 B1 实现时定义清楚(应用进程内独立事件循环,不与 launcher 抢)。
  3. 网易云跑通后迭代控件集(播放页/列表/歌词),非一次性补齐。
  4. meow_ui_img_blur_bg(降采样模糊背景,HibyOS 同款)API 待加:已在 §11 确认可行性(缩 1/8 放大铺底 + α 遮罩,切歌算一次);函数签名与实现留到 B1 之后(或并入网易云播放页时)。当前 SDK B1 的图片层先用 meow_ui_img_scaled + fill_rect_alpha 手动组合即可达到同等观感。
  5. 视频已定不做:无解码器/无 GPU/内存 14MB 会 OOM;"视频"一律用预渲染帧序列模拟。若确需播放具体小视频,宿主机离线转帧序列 PNG → 打包到 TF 卡 → SDK 按帧播放(规避设备一切解码负担)。

B1 落地状态(2026-08-29)

  • B1.0 渲染后端 ✅ meow_ui_gfx.c:自建 fb 上下文(自 mmap /dev/fb0、自建 back_buf、独立脏区集 + vsync+原子 blit);无 fb 时(宿主/qemu)离屏 480×720,MEOW_UI_DUMP=<path> 可把 back_buf 导出 PPM 截图。
  • B1.1 绘制层 ✅ §3 全量:meow_ui_clear/put_px/fill_rect/draw_rect/draw_rect_round/fill_rect_alpha/blend565 + text/text_right/text_center/text_width/text_height。字库(真机)优先,回退内建 5×7 点阵。
  • B1.2 图片层 ✅ meow_ui_img.c + 内建 stb_image(PNG/JPEG/BMP→RGBA):meow_img_load_file/mem/free/w/h、meow_ui_img/scaled/alpha/cover(最近邻 + 居中裁方)。宿主已用 test.png 验证预览/裁剪/缩放。
  • B1.3 控件层 ✅ meow_ui_widget.c:meow_btn/slider/switch/list 最小集(绘制 + 命中 + 取值)。
  • B1.4 输入层 ✅ meow_ui_input.c:输入线程读 event*(触摸 goodix-ts / md-gpio-keys / ADC 键),翻译成 on_key/on_tap/on_swipe/on_tick 语义事件;meow_ui_run/request_exit。手势专项(GESTURE_BACK):左缘返回手势=抬手触发 + MEOW_EV_SWIPE_PROGRESS 进度上报 + on_swipe_progress 回调;SDK 自动画顶部进度条 meow_ui_swipe_hint。
  • 配套:Launcher 统一触摸接口 meow_touch(M2.25,launcher 内部,应用不依赖)✅ patch_v2/meow_launcherd/meow_touch.{h,c}:Launcher 自身 UI(应用列表 / 电源菜单 / 确认框)的低性能触摸解析收敛为一份,goodix 时序免疫(BTN_TOUCH=1 不锁原点,等按下后首个有效 ABS 才锁;抬手位移 ≥65px 判滑动不命中)。应用侧不用它——应用触摸由本 SDK 输入层(meow_ui_input.c)翻译成 on_tap/on_swipe 回调,两者互不干扰;与 SDK 触摸保持一致的 goodix 时序免疫语义(详见 meow_touch.h 注释)。
  • B1.5 主题 ✅ meow_ui_theme.c:日间/夜间两套色板 + get/set_mode/get_mode/add/remove_listener;跟随 display.theme_mode(/usr/data/meow/settings.conf)。AUTO 按本地 6:00-18:00 日间。
  • B1.6 IME ✅ meow_ui_ime.c:英文 QWERTY / 数字软键盘(ASCII),meow_ui_text_input(阻塞)+ meow_ui_text_input_async/meow_ime_active/meow_ime_text;覆盖底部约 300px,背景点=取消、OK=确认。
  • 构建 ✅ patch_v2/sdk/meow_ui.a(含 meow_font.o 内联,应用只需链 meow_ui.a libz.a -lpthread);build_sdk.sh 一键重编。宿主 + qemu-mipsel 均跑通;示例 hello 改造为 SDK 版(见 §9 目录与 patch_v2/hello_app/build_hello.sh)。
  • 验证:meow_ui.a mipsel 静态编译链接 ✅(宿主 demo + qemu 均导出可见画面到 device_test/);真机 /dev/fb0 几何与输入触屏未实测(设备本次不可达,逻辑参照 launcher 既有实现)。

实现期 API 微调(相对本文档):

  • [D1] meow_ui_opts 补充 width/height(0=自动读 fb;离屏缺省 480×720)。
  • [D2] meow_list_create 数据源回调定案为 int (*)(int idx, char *label, int len, void *ctx)。
  • [D3] 新增内部 meow_gfx_fill_round/blit_rgba/mark_dirty(控件/图片用,非公共 API)。
  • [D4] 新增 meow_theme_defaults(mode)(脱机取内置色板)。
  • [D5] 新增 on_swipe_progress 回调 + meow_ui_swipe_hint(x)(左缘返回手势视觉条)+ 内部 MEOW_EV_SWIPE_PROGRESS / meow_input_inject(宿主验证用)。
  • meow_ui.h 为对外权威签名;本文件 §3-§8 作为设计蓝本。

文档基于 2026-08-29 现有渲染基建(M2.16 无闪烁地基 + meow_font + blend565)编写;API 名会随 B1 实现微调,实现时以 meow_ui.h 为准。