Files
MeowOS-SDK/docs/APP_DEV_GUIDE.md
T
AskaEth 0af6af7c7e docs: 同步主仓最新 SDK 设计/开发者指南 + README 新能力清单
MEOW_APP_SDK.md(相对主仓 MEOW_APP_SDK.md,保留公开仓化改写与免责声明):
- 公共头模块索引补 meow_ui_str.h 行
- §5.5 新增「虚拟化层」(W_LISTVIEW 固定 item 池/多行富 item/自绘钩子;去除 ncm 引用)
- §5.8 新增 TopAppBar 位图动作图标 + sb_icons[] 30 图标索引表
- §6.1 后插入 §6.4 事件分发形式化(命中/捕获/消费/层间隔离/唯一焦点)
- 新增 §12 布局/动画/滚动(meow_box/meow_anim/meow_scroll)+ §13 字符串 i18n(meow_str_*)
- 遗留/待实现 由 §12 重编号为 §14;去除 ncm/网易云/NetEase 字样

APP_DEV_GUIDE.md:新增 §7.11 字符串资源/i18n(保留既有公开化改写 §1.1/§5.2/§10 等)

README.md:更新 include/ 子头计数(20),补「SDK 能力速览」(布局/动画/滚动/事件分发/
虚拟列表/i18n/图标库/图片降采样)

.gitignore:新增本次引入的宿主测试二进制名(test_meow_ui_box/anim/dispatch/listvirt/scroll/str)
2026-09-02 09:17:20 +08:00

37 KiB
Raw Blame History

MeowOS 第三方 App 开发指南(APP_DEV_GUIDE)

受众:想在 R3 Pro II / MeowOS 上发布自己 App 的第三方开发者(非 MeowOS 系统组)。 从零到上架动线:环境 → 最小 App → 工程结构 → 构建打包 → 签名安装 → 部署调试 → SDK 能力 → 红线 → FAQ。 权威口径:本文所有 API 以 sdk/meow_ui.h 为准;规范以 MEOW_APP_SDK.md 为准。凡与 meow_ui.h 不一致,以头文件为准。 配套文档(本公开 SDK 仓内):docs/MEOW_APP_SDK.md(SDK 设计全量)。 说明:本文为 MeowOS-SDK 公开仓 的开发者指南。文中引入但未随本仓发布的主仓内部文档/组件(APPS.md、docs/MEOWOS_SPEC_REF.md、NOTIFICATION_SYSTEM.md、SYSTEM_SETTINGS.md、FRAMEWORK_DESIGN.md、devdocs/、qemu_env/,以及签名工具 patch_v2/meow_sign/meow_sign、patch_v2/meow_verify/meow_verify_host 等)均位于 MeowOS 主仓 MeowOS-For-R3P2(内部)或设备固件内,仅用于说明系统行为与约定;本公开仓不含这些文件,也不含任何密钥。签名请走你自己生成的密钥对(见 §5)。


0. 一图看懂:App 是什么

一个 MeowOS App = 一个静态 MIPS32r2 ELF 二进制 + 一份 app.json 描述 + 若干资源,打包成 .mpk(= tar.gz),装到 TF 卡 MeowAPPs/<package>/ 下。二进制链接 meow_ui.a 静态库,通过 meow_ui.h 声明的一套 API 拿屏幕、输入、控件、主题、字体、通知、音频——绝不自己 open("/dev/fb0") / mmap / ioctl / open("/dev/input/event*")。

┌──────────────────────────────┐
│  MeowAPP(你的业务,只调 meow_ui API)│
├──────────────────────────────┤
│  meow_ui.a(静态库,含 meow_font)   │
│  绘制/控件/图片/输入/主题/字体/通知/音频│
├──────────────────────────────┤
│  MeowOS 系统(launcher/amsd/audiod)│
└──────────────────────────────┘

设备:480×720、RGB565(代码里颜色统一 24-bit 0xRRGGBB,SDK 内部转 RGB565);文本字号约定 28(标题/状态栏)/ 20(正文/按钮)。


1. 开发环境准备

1.1 交叉工具链

需要一台 MeowOS 开发机(装好 mipsel musl 静态交叉工具链)。工具链不随本公开仓分发;以下为 MeowOS 开发机上现成的路径(可经 MEOW_CC / MEOW_SYSROOT 覆盖,或用你自己装好的 zig 交叉工具链):

$HOME/Project/R3ProII/.tools/zig-mipsel-cc                          # zig cc 包装(--target=mipsel-linux-musleabi, soft-float)
$HOME/Project/R3ProII/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/   # sysroot(含 libz.a 等)

编译一个静态 MIPS 二进制(参考 examples/hello/build_hello.sh;examples/hello/ 即本仓的 SDK 示例工程):

MEOW_CC="$HOME/Project/R3ProII/.tools/zig-mipsel-cc"          # 可覆盖;用你装好的 zig 交叉 cc
MEOW_SYSROOT="$HOME/Project/R3ProII/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf"
"$MEOW_CC" -Os -static -Wall -Wextra -I"<本仓>/sdk" -I. \
    app.c ui/*.c logic/*.c \
    "<本仓>/sdk/meow_ui.a" "$MEOW_SYSROOT/lib/libz.a" -lpthread -o myapp

要点:

  • 必须 -static(加载器只跑静态 MIPS32r2 ELF;软/硬浮点均可,见 APPS.md §2.1)。
  • 链 meow_ui.a(sdk/build_sdk.sh 已把 meow_font.o 打进库里)+ libz.a(meow_font 的 inflate 依赖)+ -lpthread。
  • -I"<本仓>/sdk" 引 meow_ui.h/meow_ui_view.h 等公共头(伞头 + include/ 子头已在 sdk/ 内);-I. 引你自己工程的头。
  • 期望 -Wall -Wextra 零警告(参照 hello)。

1.2 宿主编译(离屏逻辑测试)

SDK 依赖板载 /dev/fb0 与字库,宿主机没有——但你可以链真实 SDK 目标文件 + 打桩来跑逻辑单测(参考 sdk/test_host_stub.c):

  • test_host_stub.c 提供了 meow_font_* / meow_ui_global_render 等宿主桩(无 fb → 5×7 点阵回退;无字库 → meow_font_load 返回 -1)。
  • 宿主可 gcc 直接编译 SDK 各 .c + 你的逻辑,跑纯 C 断言,不碰硬件(hello 的 test_p1p2p3_repro / test_meow_ui_widgets / test_pinyin 等都是这么跑的,宿主宿主验证全 PASS)。
  • 注意:logic/ 层要写纯 C(不依赖 meow_ui.h),才能裸宿主单测;ui/ 层才碰 SDK。

1.3 qemu-mipsel 全系统仿真

需要跑全链路(含内核/fb)时可用 qemu_env/scripts/run_halley6.sh(Ingenic X1600E 全系统仿真):

qemu_env/scripts/run_halley6.sh               # 引导内核到串口控制台(默认)
qemu_env/scripts/run_halley6.sh -usbrootfs    # 附加 USB rootfs 镜像

注意:必须带 mem=64M(脚本已内置,去掉会 early hang);-usbrootfs 的 USB 数据通路在 QEMU 里仍不完整(设备未枚举),rootfs 引导会失败——多数情况下它在宿主编译 + 真机验证之间用不上,不是常规提交流水线。

1.4 真机前置

  • 出站靠 TF 卡;确保卡上至少有 MeowAPPs/(放 App)与 MeowData/(App 数据,首启自动建)。
  • 设备跑 MeowOS 模式(默认)。看 MEOWOS_SPEC_REF.md §5.4 确认固件版本单点 /etc/meowos_version。

2. 五分钟最小 App(以 hello 为模板)

最快路径:拷贝 examples/hello/ 改包名与文案。逐文件讲。

2.1 app.json 全字段表

app.json 必填,放 MeowAPPs/<package>/ 根目录。示例(examples/hello/ 示例工程的 app.json 在 MeowOS 主仓 apps_assets/ 下,本公开仓未附带;下面给出可直接照抄的完整示例):

{"name":"Hello App","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.hello/hello",
 "package":"os.meow.hello","version":"0.1","framework":1,"immersive":1,"depends":"curl:optional"}
字段 必填 说明
name ✓ 列表显示名。当前仅 ASCII(UTF-8 中文留待 v3,需完整 UTF-8→UTF-16 转换)
exec ✓ 可执行文件绝对路径,必须是静态 MIPS32r2 ELF(指向 MeowAPPs/<package>/ 内二进制)
package — 反域名权威标识(= app_id = 目录名 = .mpk 文件名茎,三者必须一致)。os.meow.* 为系统保留前缀(见 §5),第三方用自有前缀(如 dev.*)。缺失回退目录名;两者存在且不同 → WARN
version — 显示字符串(versionName 角色,如 1.2.3-beta,不参与比较)
version_code — 纯数字,安装/升级比较的唯一依据;缺失 → 拒装;新版 < 已装 → 拒装(防降级),相等 → 允许重装。见 §5
author — 保留字段
framework — 1=SDK 框架 App(用 meow_ui.a;推荐)。0/不写=裸自绘(不推荐,见红线)
immersive — 1=沉浸。注意(M2.89+):看门狗 SIGUSR1 只发给「裸沉浸」App(非 framework=1);SDK 框架 App 的返回走 on_swipe,别把 SIGUSR1 当返回键
background — M4:无头后台服务注册权限(service.<svc> 服务二进制 = App 同目录 <svc> 文件)
background_audio — M4:后台播放保活(进程不冻结 + UI 冻结复用 §10.5 自抑);播放类 App 声明。如 dev.player.music 可声明 background_audio=1
fullscreen — app 级全屏:状态栏收合至提示线 + 保留左缘返回/顶部提示线/下滑通知中心。activity 级用 route.<id>.fullscreen
fonts — 字库档位声明:"full" / "gbk" / "common"(默认 common≈4k;需大字库声明 "full")。注意与 "fonts":["<handle>"](应用自带字库句柄)同名二义,见 MEOWOS_SPEC_REF §7 A-14
depends — 二进制依赖声明:"curl,jq:optional,ffmpeg:required"(JSON 值串)或行式 depends=curl,jq:optional,ffmpeg:required。逗号分项,每项 <name>[:<mod>];无标注默认 required,也接受 :opt/:req;未知修饰符按 required 并 WARN。启动前查板载 /usr/bin/<name> 或卡上 /usr/data/mnt/sd_0/MeowOS/opt/bin/<name>(X_OK,命一即可)。required 缺失 → 不启动(toast「缺依赖: 」);optional 缺失 → 仅 WARN 放行
icon — 预留:应用列表图标

⚠️ 解析器极简(launcher/nyai 内实现):只匹配 "name":/"exec":/"package":/framework/immersive/route.*/awake.*/service.*/background/background_audio/fullscreen/fonts/depends,不做 JSON 校验(多行/单行均可),但字段名和引号写法要规范。

2.2 main 入口:SDK 初始化 + 事件循环 + 退出约定

看 examples/hello/app.c,核心骨架如下:

#include "meow_ui.h"
#include "meow_ui_nav.h"
#include "meow_ui_media.h"
#include <signal.h>

static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); }   /* 干净退出:不画 BYE 直接退 */

int main(void)
{
    meow_log(MEOW_LOG_INFO, "start");

    /* ① SDk 初始化:开 fb / 读几何 / 载字库 / 建 back_buf。opts 宽高 0=自动读,宿主导跑用 480x720 */
    meow_ui_opts o = { 480, 720 };
    if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; }

    /* ② 主题监听 + M3 导航上报 + 媒体会话(可选,但推荐接线) */
    meow_theme_add_listener(NULL);
    meow_ui_nav_init();                        /* 读 MEOW_ACTIVITY / MEOW_CHAIN 初始化导航栈 */
    meow_ui_media_init(NULL);                  /* app_id = env MEOW_LOG_APP */
    meow_ui_media_on_playctl(demo_media_playctl);

    signal(SIGTERM, on_sigterm);               /* 退出兜底:SIGTERM → request_exit */

    /* ③ 建各页 view(on_create 建控件/状态)+ 弹层实例(dialog/sheet 建一次,复用)*/
    meow_view *home = home_view_create();
    meow_view *full = full_view_create();
    meow_view *demo = demo_view_create();

    /* ④ 进入根页:activity_push + home on_enter → home_redraw */
    meow_view_page_push(ACT_HOME);

    /* ⑤ 事件循环回调表(on_key 物理键是系统语义,SDK 不下发业务确认/exit)*/
    meow_ui_events ev = { app_on_key, app_on_tap, app_on_swipe, app_on_tick,
                          app_on_swipe_progress, app_on_touch_down, app_on_touch_move };
    meow_ui_run(&ev);                          /* 阻塞,直到 meow_ui_request_exit() */

    /* ⑥ 清理:释放 view(触发各页 on_destroy)+ 弹层 + shutdown */
    meow_view_free(full); meow_view_free(demo); meow_view_free(home);
    meow_dialog_free(g_dlg); meow_sheet_free(g_sheet);
    meow_ui_shutdown();
    meow_log(MEOW_LOG_INFO, "exit");
    return 0;
}

退出约定(APPS.md §3.3):

  • 任意退出码均可,exit(0/1) 即可,不要 abort() 留僵尸——守护进程 waitpid 立即返回并恢复播放器。
  • 用 meow_ui_request_exit() 或根页左缘左滑退出;绝不监听物理键映射 exit(见 §8 红线)。
  • 不需要「恢复/清屏」——应用退出后系统恢复原界面,你清屏反而闪一下。

2.3 事件分发

meow_ui_run 给的平铺回调,在 app.c 里路由到当前页(见 app.c 的 app_on_tap/app_on_swipe/app_on_tick/app_on_touch_*)。典型分法:

  • on_tap → 命中当前页控件(meow_widget_hit / meow_listview_hit / meow_topappbar_hit 等)。
  • on_swipe → LEFT=返回(meow_view_page_pop,根页则 meow_ui_request_exit);UP/DOWN=滚动。
  • on_touch_down/on_touch_move → 跟手拖动(slider / 列表 / 页面滚动)。
  • on_tick → 动画/时钟(如进度条步进)。
  • on_key 仅供 SDK 内部状态,不含电源/音量/播放的确认/exit 语义——别把它当输入。
  • 左缘返回手势:边沿上报 on_swipe_progress,SDK 自动画顶部进度条(meow_ui_swipe_hint)。

3. 工程结构约定

hello 的分层(hello_internal.h 里写得很清楚),建议照抄:

myapp/
├── app.c                 # main + 装配 + 事件分发(把 meow_ui_run 回调路由到各页)
├── myapp_internal.h      # 跨 TU 共享声明(只 extern 声明,不定义)
├── ui/
│   ├── home/home.c       # 每页一个 view 文件(持本页视图态)
│   ├── home/home.h
│   ├── demo/demo.c
│   ├── list/list.c       # 页面内 CONTAINER 子视图(列表区块)
│   ├── full.c
│   └── common/chrome.c   # 共享 chrome(TopAppBar / NavigationBar / FAB)
└── logic/
    ├── logic.h           # 纯 C 接口(不依赖 meow_ui.h)
    ├── state.c           # 计数/状态业务 + getter/setter
    └── notify.c          # 通知业务(此处才 include meow_ui.h 用 SDK 通知 API)

分层契约(logic/logic.h):

  • ① UI→业务:只调 logic_* / notify_* 纯函数命令/getter,业务不回调 UI。
  • ② 业务→UI:业务态变化经 logic_set_listener(cb, ctx) 的可选 push 监听器通知 UI 刷新(home_state_listener 示例)。
  • ③ 每页 meow_view 的 ctx = 视图控制器(biz-UI 绑定点),页面后续可把这层状态吸进控制器。

视图框架 meow_view(sdk/meow_ui_view.h):

  • 生命周期钩子:on_create → on_enter → (事件循环) → on_leave → on_destroy,另有 on_layout/on_draw/on_event。均可 NULL。
  • 页面即文件:每页一个 meow_view_create(id, MEOW_VIEW_ACTIVITY, &hooks, &ctrl),id 是你命名的稳定值(ACT_HOME=0/ACT_FULL=1/ACT_DEMO=2)。
  • 页面切换统一走 meow_view_page_push/pop/replace(包装 activity 栈,on_enter/on_leave 唯一入口),App 不再分别调 activity_push/pop。
  • 子视图嵌套:meow_view_add_child 挂 CONTAINER 子 view(如 demo 页的列表区块 list.c),嵌套生命周期由 SDK 递归驱动(随父页进出),无需 App 显式接管。
  • on_draw 为回调、view 不缓存像素(非保留);重绘由 meow_ui_invalidate 触发。
  • meow_view_dispatch_event 是「把语义事件注入当前页谓词子树路由」的入口(先子>父按 z 降序,命中即消费);未接线于 meow_ui_run,你可在 on_tap/on_swipe 里改用它做 SDK 原生路由。

4. 构建与打包

4.1 链接 meow_ui.a

见 §1.1 命令。meow_ui.a 由 sdk/build_sdk.sh 构建(内含 meow_font.o),改 SDK 源码后重编 meow_ui.a 必须同步重编你的 App(结构变化即 ABI 变化,见 MEOWOS_SPEC_REF §3.4)。

4.2 .mpk = tar.gz

.mpk 内容 = app.json + 二进制 + 资源(不含 data/cache,运行时按设备生成)。包名 = .mpk 文件名茎 = app.json.package(如 os.meow.hello → os.meow.hello.mpk)。

nyai 无独立 pack 命令。两种出包:

  1. 手工打包(从 staging 目录,meow_sign/README §2.2 的口径):
    mkdir -p build/os.meow.xxx
    # 放 app.json + meow 二进制 + 资源;app.json.package == 目录名
    tar czf os.meow.xxx.mpk os.meow.xxx      # .mpk 须在 tar 根(无 <pkg>/ 包裹 → 换包时注意)
    
  2. 导出已装应用:设备上 nyai export <pkg> [out.mpk](打 MeowAPPs/<pkg>/,排掉 *.pre_*/*.bak_*/logs/ 等运行残留)。

.mpk 内部结构:

os.meow.xxx.mpk   (tar.gz)
├── app.json       # package/exec/version(+version_code)/framework/immersive/background_audio/...
├── meow           # 静态 MIPS32r2 ELF(exec 指向)
├── <资源文件>      # 只读程序资源
└── <signature footer>   # 仅签名过的包有(Ed25519 v2 184B;尾部追加)

5. 签名与安装分级

5.1 分级规则(APPS.md 附录 §3 / 签名设计)

包 规则
os.meow.*(系统保留前缀) 必须有有效签名;无签 / 验签失败 → 一律拒装(防伪造系统包名)
第三方(自有前缀如 dev.*) 无签可直接装(安装时告警);有签必须验签(失败拒装)
所有包 验签 + 三重绑定 + 防降级(见下)

防降级:只看 version_code(纯数字)。新版 code < 已装 → 拒装;相等 → 允许重装;code 缺失 → 拒装(提示缺 version_code)。version 仅显示,不比较。

三重绑定(三方各司其职):

  1. 签名 footer 的 package == .mpk 文件名茎(meow_verify 校验)——防改名。
  2. 签名 footer 的 package == 解包 app.json.package(nyai / launcher 安装流校验)——防改 app.json。
  3. os.meow.* 前缀 + 必须验签通过(nyai / launcher 安装流)——防无签冒充系统包。

5.2 开发者自签名流程(不泄露任何密钥材料)

工具:签名用 meow_sign(MeowOS 打包工具链成员,宿主端、离线维护机运行,不落设备、设备只持公钥;随 MeowOS 主仓打包工具分发,本公开 SDK 仓不含)。私钥永不入库、永不落设备。设备侧把关的是固件 /usr/bin/meow_verify(内嵌公钥数组,任一公钥通过即放行)。

# ① 首次:生成/备份你自己的密钥对(仅一次;重复生成会令旧包全部失效)
./meow_sign keygen myapp.key            # myapp.key 为你的私钥(0600,离线备份);myapp.key.pub 公钥
# 私钥离线备份、永不入库/不落设备;公钥 myapp.key.pub 可提交设备厂商固化进 meow_verify 公钥数组

# ② 打包(不含签名,纯 tar.gz;app.json.package 必须 == 目录名 == .mpk 文件名茎;version_code 单调递增)
tar czf os.meow.xxx.mpk os.meow.xxx

# ③ 签名(默认 v2 含被签时间戳)
./meow_sign sign os.meow.xxx.mpk myapp.key

# ④ 产签后自测(推荐)
./meow_sign verify os.meow.xxx.mpk myapp.key.pub
# 设备侧用 meow_verify_host 复验:与固件内嵌公钥同口径(主仓打包工具提供,未随本仓分发)

# ⑤ 装到设备:把 .mpk 放卡上 → nyai install

要点:

  • sign 的 .mpk 文件名茎必须等于包名(否则 meow_verify 二次校验拒)。
  • v2 块(184B,含时间戳)需 2B+ 固件;--v1 产物(180B)兼容旧 2A 固件——固件与工具链同版本发布即可避免兼容坑。
  • 签名是防误装 / 防伪造包名 / 防沿途篡改,不是安全边界(见 meow_sign/README §0,诚实标注)。
  • MeowOS 系统主钥不随本公开 SDK 仓分发。第三方开发者请用自己的密钥对(否则无法区分来源;用自己的私钥离线备份,公钥按需提交给设备厂商固化进 meow_verify 公钥数组)。泄露私钥即等于放弃对应包名的信任。

5.3 安装

  • 设备端:nyai install <pkg或文件.mpk>(固件内置 /usr/bin/nyai 优先,或卡上 MeowOS/opt/bin/nyai)。同名 = 更新(旧目录备份 .bak_<pid>,MeowData/<pkg>/{data,cache} 保留,成功自清 .bak)。
  • nyai list / nyai info <pkg> / nyai remove <pkg>(数据默认保留,--purge 连 MeowData 删)。

6. 部署与调试

6.1 TF 卡目录

/usr/data/mnt/sd_0/
├── MeowAPPs/<package>/   # app.json + meow 二进制 + 资源(目录名 = app.json.package)
├── MeowData/<package>/{data,cache}   # App 数据(首启自动建;exFAT 无软链,用 env 注入)
└── MeowOS/logs/<package>/            # App 日志(每次启动新文件 <ts>_<pid>.log)

启动时注入 env(SDK meow_config_*/meow_ui_nav_init/日志都读它): MEOW_DATA_DIR / MEOW_CACHE_DIR / MEOW_LOG_DIR / MEOW_LOG_APP(=package) / MEOW_LOG_LEVEL / MEOW_ACTIVITY / MEOW_CHAIN(+ MEOW_FONT_PNG/MEOW_FONT_IDX 当前生效字体、MEOW_FULLSCREEN)。

6.2 面板上传

MeowPanel(外置 TF 卡 Web 面板,8080)可 nyai install/export/remove。⚠️ 面板鉴权默认关(内网信任模型)——reboot/shutdown/ota/apps/删除/上传 危险端点勿暴露公网。

6.3 am CLI(活动管理器)

am 是 amsd 的用户侧 CLI(固件 /usr/bin/am),连 /usr/data/meow_ams.sock(--sock / env MEOW_AMS_SOCK 覆盖):

am status                          # 在跑 App 表(pkg/状态/pid/运行时长/activity)
am list                            # 注册表 + 状态(pkg/name/state/pid/launches/crashes)
am history [pkg]                   # 事件历史(每 App 最近 8 条)
am start <pkg>[/<act>]             # 拉起(root)
am kill  <pkg>                     # 终止(SIGTERM 语义,root)
am rescan                          # 手动触发注册表重扫(卡刚挂载/热装后立即可见)
am resolve <pkg> <activity>        # 路由决策(返回 CHAIN)

写操作(start/kill)仅 root。已知坑(agent-250):开机早于卡挂载 → amsd 注册表扫空 + 永不重扫 → am start 报 not registered——用 am rescan 或等 3s 自愈/30s 合并重扫。

6.4 日志

  • App 日志:meow_log(lvl, fmt, ...) 一行;SDK 写 MeowOS/logs/<package>/<ts>_<pid>.log(每次启动新文件,单文件 ≥2MB 自动续新,每 App 总量上限,无卡降级 stderr 不崩)。级别:MEOW_LOG_DEBUG/INFO/WARN/ERROR;meow_log_set_level 可选,默认 INFO。
  • 崩溃日志:系统级 meow_crash 捕获 + App 崩溃直报。真机看 /usr/data/meow_* 与 MeowOS/logs/。
  • 离线跑:meow_launcherd --scan 列应用;宿主跑逻辑单测看 stdout。

7. SDK 能力速查表

(完整签名以 sdk/meow_ui.h 为准——下面每行都可交叉印证。)

7.1 控件库清单(一句话)

控件 一句
meow_btn_create(Filled)/ meow_btn_create_variant(五型:FILLED/TONAL/OUTLINED/TEXT/ELEVATED) 按钮;命中用 meow_widget_hit,选中/禁用走状态机
meow_slider_create / meow_switch_create 滑条/开关;set/get 取值
meow_list_create(label-only)/ meow_lazycolumn_create/meow_lazyrow_create 数据源回调 + 可见区虚拟化;on_swipe 里 meow_list_scroll_by 驱动滚动
meow_listview_create(W_LISTVIEW 结构化列表) 行=图标+标题+副标题+右侧操作+选中;meow_listview_hit/meow_listview_tap_at
meow_textfield_create(接 IME)+ meow_textfield_activate 单行文本输入(阻塞 IME)
meow_card_create + meow_card_set_content / meow_divider_create / meow_badge_create 卡片/分隔线/角标
meow_checkbox_create / meow_radio_create 勾选/单选(走 set_selected)
meow_progress_create(线性)/ meow_progress_circular_create(圆形) 进度条(0-100,-1=不定)
meow_fab_create 悬浮按钮
meow_topappbar_create(标题+返回+动作)/ meow_navigationbar_create(底部导航)/ meow_tabbar_create(顶部分页) 标题栏/导航栏/页签
meow_dialog_create(CONFIRM/SELECT/INPUT/INFO)/ meow_sheet_create(BottomSheet) 阻塞模态;点外/左缘返回=取消
meow_row_create/meow_column_create/meow_box_create + meow_layout_add/arrange + meow_layout_spacer 组合布局(固定尺寸子项)
meow_widget_custom_create 自定义画布:App 给 render/hit 回调(同 meow_widget)
通用 meow_widget_set_opts(Modifier 式 Options)、状态机 meow_widget_set_state(ENABLED/SELECTED/FOCUSED/PRESSED)、meow_widget_set_z(999) 置顶、meow_draw_custom_register 注册非控件直绘

7.2 主题(MD3 / MIUIx × 明暗——适配义务)

  • 主题 = 顶级风格族 ui.style(MEOW_STYLE_MD3/MEOW_STYLE_MIUIx)× 明暗 display.theme(light/dark/auto)。
  • 适配义务:用 meow_theme_get() 取的语义色(primary/surface/surfaceContainer/outline/bg/fg/accent/dim…),不要硬编码颜色;明暗都要能看(AUTO 会来回切)。当前 MIUIx 仅换配色(状态栏图标未 per-style 换表,是已知缺口)。
  • 字号约定:font_title=28 / font_body=20(meow_theme 给默认)。
  • 界面底 meow_ui_set_page_bg(th->bg);状态栏高度 meow_ui_statusbar_height()(非全屏=28,全屏=0)用于内容 inset。

7.3 字体档位声明 + 应用字库接口

  • 档位:MEOW_FONT_TIER_COMMON(默认 ≈4k)/ GBK(≈20k+)/ FULL(全字符集)。App 需大字库时 app.json "fonts":"full",launcher 为你加载大字库。
  • SDK:meow_font_set_tier / meow_font_tier;meow_font_get_available_count / meow_font_enumerate(读 MeowOS/config/fonts/index)/ meow_font_select_by_id(App 会话内固定用某套;丢失降级默认 + WARN)。
  • 判缺字:meow_font_has_glyph(cp) 预判当前字库是否含该码位——缺字自行回退/提示。
  • 注意:App 未自带字库则跟随系统 font.file(launcher 以 MEOW_FONT_PNG/MEOW_FONT_IDX env 注入);应用自带字库(load_handle)为草案/未实现。

7.4 IME(输入法)

  • 模式:MEOW_IME_ASCII / MEOW_IME_NUM / MEOW_IME_PINYIN(中文拼音)。
  • 阻塞:meow_ui_text_input(ctx)(回车=提交+关闭、ESC/点背景=取消);meow_textfield_activate 聚焦并打开阻塞 IME。
  • 非阻塞:meow_ui_text_input_async + meow_ime_active()/meow_ime_text()/meow_ime_composition()(App 在 on_tick 里轮询)。
  • 坑:阻塞 IME 的内层轮询必须喂 meow_ui_activity_ping(SDK 已做)否则进度条/息屏卡死;键盘区背景快照/恢复(防残影);回显条是实时输入回显非候选,候选框留中文。

7.5 音频(meow_audio_*,独占 audiod)

  • 链路:meow_audio_open(rate, ch, fmt) → 返回 stream_id > 0 → meow_audio_write(pcm, frames)(阻塞,socket 背压)→ meow_audio_pause(sid, paused) → meow_audio_close(sid)。
  • audiod 独占 hw:0,0;后开流赢,旧流收 MSG_AUDIO_PREEMPTED → 你的 on_preempt 回调 → 之后 write 返回负值,应重新 open。
  • audiod 不在 → open 返回 MEOW_ERR_NOENT(负值),不做隐式直开回退;MEOW_AUDIO_DIRECT=1 仅调试用别依赖。
  • meow_audio_query(&state) 读快照(活跃流/采样率/underrun/路由口/音量);meow_audio_on_preempt(cb, ctx) 注册被抢占回调。
  • DAC 原生率跟随、无 SRC;格式归一(S16 直通 / S32 升位 / mono 复制);4.4/3.5 路由随插孔。
  • 播放类 App 记得 background_audio=1 + 媒体会话上报(见 §7.7)。

7.6 通知(meow_notify_* / MSG_NOTIFY)

  • 声明:meow_notify_register_type(cat)(每 App ≤4 类型);发:meow_notify_send(&n)(返回 id);消:meow_notify_cancel(id);进度:meow_notify_update_progress(id, p)。
  • 配额:每 App ≤4 类型;每类型活动 ≤4(满丢最旧);媒体单例(每 App 1 条,重发替换)。
  • timeout_ms:>0=过期毫秒数;0=不过期(直到 cancel);-1(ONGOING)=常驻。
  • 负载 meow_notify{n}:category/urgency/title/body/icon/timeout_ms/target_activity(点击跳转 Activity 名,M3 导航预留)/progress。字段对齐 freedesktop Notify()。
  • 走 IPC MSG_NOTIFY;10s 后自动转储到 TF MeowOS/notifications/(ring 10MB)。当前 SDK 内部维护活动表 + 日志落点;真实投递到通知中心 UI(launcher 渲染)为已落地链路。

7.7 媒体会话(meow_ui_media_*)

  • meow_ui_media_init(app_id) / meow_ui_media_on_playctl(cb)(注册播放控制并启动后台接收线程)/ meow_ui_media_meta(state, pos_ms, dur_ms, title, artist, album, artwork, lyric)(上报元数据)/ meow_ui_media_stop()。
  • 控制回调 playctl(cmd, seek_ms):cmd 1=播放 2=暂停 3=上一首 4=下一首 5=seek 6=播放/暂停 toggle(物理播放键 164 恒发 6,由你对当前态翻转——源是状态权威)。物理上下曲:meow_ui_media_sys_key(code)。
  • 声明 MF_PLAYBACK 的 App 首次上报 → 注册为媒体源;通知中心媒体卡片 / 主界面 Now Playing 据此显示。
  • best-effort:无 launcher/socket 静默降级,绝不崩。

7.8 后台播放声明

  • app.json "background_audio":1(M4 已落地):进程不解冻 + UI 冻结复用 §10.5 自抑(比任务书 SIGSTOP 方案优)。launcher 有 meow_bgplay 保活表(退避 1/2/4/10s + 熔断 + 广播);应用列表标注「·后台」。依赖 background 字段 + service.<svc>。
  • 声明后台播放后:无头时仍喂媒体会话 + 可继续 meow_audio_* 出声;退出时停线程/关流。

7.9 应用配置(meow_config_*)

  • App 自有键值偏好,落 TF 数据目录(MEOW_DATA_DIR,无卡回退 /tmp/meowcfg_<pid>),不占板载、无 schema(与系统 meow_settings_* 区分)。
  • meow_config_set/get/int/bool/remove;写 <data>/<sanitized_key>.cfg(key 只允许 [A-Za-z0-9._-],上限 64)。返回码 MEOW_CFG_OK(0) / NOENT(-2) 等。

7.10 网络注意事项

  • App 一般不需要联网;确需联网时克制请求(见 §8 风控式节制)。
  • 系统层面(netmgrd 期 0/1):无 iptables 用户态 / 无 cgroup / 无 conntrack → 内核级每应用管控不可行;主杠杆 = DNS 代理(域名级)+ 连接普查(inet_diag)+ SDK 自愿通道。App 走 SDK 自愿通道配合管控是后续方向;当前 App 如联网,请自律、尊重断网降级。

7.11 字符串资源 / i18n(meow_str_*,2026-09-02 追加)

  • 任何 UI 文案都不要硬编码,用 meow_str("域.名") 按当前语言取串(示例:meow_str("common.ok"))。meow_str 永不返回 NULL:未知 id 原样返回 id,方便你打点/兜底展示。
  • meow_strf(out, outsz, id, ...):取串后按 snprintf 语义格式化进调用方缓冲(自带 NUL;out==NULL/outsz<=0 返回 -1)。
  • 切换语言:meow_str_set_locale("zh_cn"|"en_us") 即时生效;locale 存系统设置 settings.conf 的 ui.locale(默认 zh_cn,SDK 启动惰性读取)。App 里做语言切换页时调用它即可。
  • 覆盖/追加:可不改二进制在 TF 卡放 MeowData/<pkg>/strings/<locale>.txt(每行 id=text,UTF-8)覆盖内嵌或新增自己的串(如 player.pause=暂停);无卡/无文件/无该 id → 回落内嵌双语默认表(common.* + ime.* 内置,见 include/meow_ui_str.h)。
  • id 规范:域.名;SDK 自带 common.*/ime.*,你自建 域(如 player.*)避免撞名。
  • 边界:当前为 i18n 雏形,仅 zh_cn/en_us 双语整串取用;不做复数规则 / BiDi / RTL / 动态字体回退(详见 MEOW_APP_SDK.md §13.7)。

8. 规范与红线

详见 APPS.md §1.3 / §3.3 / §3.4.1、MEOWOS_SPEC_REF §2.4 / §5.1。以下为最要紧的违约原因。

  1. 物理键禁映射返回/确认/OK/翻页。物理键是系统资源:电源=息屏/亮屏/长按菜单/8s 强关,音量=调音量,播放/上下曲=媒体控制,返回=触屏左滑(MEOW_SWIPE_LEFT)/虚拟返回(非物理键)。绝不用 on_key(POWER)→exit 这类映射;on_key 仅供 SDK 内部状态。界面导航一律走触摸(返回钮/左缘左滑)。
  2. 局部脏区刷新,别整屏重画。控件/区域变化用 meow_ui_invalidate(x,y,w,h)(清底 + 按 z 重画相交绘制物 + 原子上屏),别 meow_ui_flush/整帧重画。非控件直绘内容(meow_ui_text/meow_ui_fill_rect 等)必须 meow_draw_custom_register 注册(声明 bbox + 绘制回调 + z),否则区域重画清底后会漏画(hello demo 的分区标题/slider 值文本就是典型注册示例)。滚动列表的视口裁剪由 SDK 全局负责(W_LIST 只画视口内行),别在 App 侧再做整屏。
  3. 内存预算。设备内存很紧(QEMU mem=64M 参考,rootfs ≤45MB)。避免大块分配、避免大字库双驻留(大字库按档装载、卸载旧 atlas)、别把卡上大文件 mmap 当运行必需——运行期别依赖卡上资源(见 §9 卡移除)。
  4. 板载存储别乱写。板载 /usr/data 只剩 ~10MB,是稳定框架 + KB 级设置。App 数据一律写 MeowData/<pkg>/(TF,经 MEOW_DATA_DIR/MEOW_CACHE_DIR env);绝不碰 NAND 分区 / mtd / /data/recovery_all(恢复出厂,红线)。
  5. 系统动作经专项服务。App/组件不直连 sys_do_reboot、不写 NAND/mtd5;关机/重启/升级/回 HiBy 一律经 meow_svc_power(IPC)。App 一般无需做,但别绕过。
  6. 风控式网络请求节制。避免高频轮询/无限重试/后台自灌;尊重断网/弱网(优雅降级而非死循环/崩溃);与系统 netmgrd 管控共存(域名级,非内核 per-app)。
  7. 别 destroy 别占用播放器/声卡。不要 killall hiby_player(触发看门狗整机重启);不要 fork 长驻后台进程(退出要「真正退出」,否则 SIGCONT 后仍占屏抢输入)。
  8. app.json 别缺失 version_code(拒装);别用 os.meow.* 前缀(除非你是系统签名 App)。

9. FAQ / 常见坑(真踩过的)

  1. 脏区透传:局部刷新抹掉上层 chrome。症状:拖动/点某个控件后,上层 FAB/标题栏被抹掉。根因:App 侧旧的「清底 + 只画单个控件」刷新。修法:改走 SDK meow_ui_invalidate(z 栈重画下层+上层),非控件直绘注册 CUSTOM(meow_draw_custom_register),否则清底后漏画。meow_ui_paint_full() 整帧按 z 重画兜底。
  2. IME 阻塞不喂 on_tick → 进度条/息屏卡死。阻塞 meow_ui_text_input/meow_textfield_activate 的内层轮询必须喂 meow_ui_activity_ping(SDK 已做);键盘区用背景快照/恢复防残影;回显条是实时输入回显非候选。
  3. 字体声明丢失 → 降级小字库。app.json "fonts":"full" 丢了 / 大写不符 / 该档没生成 → 按 common 装载,生僻字缺字(meow_font_has_glyph 预判,回退或提示)。字体三件套(font.png+font.idx+font.json)缺一件 → 该套不进列表。
  4. 热更新 .new + mv / 覆盖运行中程序的坑。程序主体在卡上、运行中被覆盖/替换 → 访问旧代码页可能 SIGBUS。修法:升级用 nyai install(同包名 = 更新:旧目录备份 .bak_<pid> → 重启后替换 → 数据保留 → 成功自清 .bak);别自己 rm/mv 运行中的可执行文件。导出时 *.pre_*(热更备份)、*.bak_* 会被过滤。
  5. 开机早于卡挂载 → amsd 注册表扫空。am start/list 报 not registered(agent-250)。修法:am rescan 手动触发,或等 3s 快重试 / 30s 合并重扫自愈。
  6. SIGUSR1 误当返回键。M2.89 起看门狗只对「裸沉浸」App(非 framework=1)发 SIGUSR1;SDK 框架 App 收到会被 SDK 的 sigusr1_guard 护栏记日志+吞掉。别为「返回」装 SIGUSR1 handler;SDK 框架 App 返回走 on_swipe。
  7. goodix 幽灵触摸。真机启动触摸尾部会发一段幽灵触摸(启动后 1.2–2.25s,见 APPS.md §3.5)。框架 App 走 on_tap/on_swipe 语义事件,launcher/SDK 已做 drain + 冷却;裸自绘 App 要自己加启动冷却(如 COOLDOWN_MS),否则会把幽灵当下按立即退出。
  8. 音频:audiod 不在 / 被抢占。meow_audio_open 返回负值(MEOW_ERR_NOENT)→ audiod 不在,别隐式直开(MEOW_AUDIO_DIRECT=1 仅调试)。被抢占后 write 返回负值 → 重新 open。长流卡死(~12s 停摆)是早期驱动/ring 问题,已由 audiod 加固(period=1024×4 + 长流护栏)修掉;App 侧 write 阻塞背压,别一 flood 灌。
  9. 物理键映射 exit。旧 hello 曾 on_key(KEY_POWER)→request_exit(),违反红线——已要求删除。规范:电力键=息屏/亮屏,播放键=媒体控制,返回=左缘左滑。你的 App 别重蹈。
  10. meow_ui.a ABI 变化。改 SDK(meow_ui_theme/meow_widget 结构)即 ABI 变化 → 必须同步重编 App。本公开 SDK 仓把共享协议头 meow_ipc.h、meow_font.h、meow_notif.h 与 meow_font.c 一并在 sdk/ 内 vendored(不依赖主仓 meow_launcherd/),build_sdk.sh 已是自包含独立库;运行时 App 只链 meow_ui.a,不含任何 launcher 符号。

10. 上架检查单

  • app.json:package(自有前缀)/exec(静态路径)/version+version_code(单调递增)/framework:1;需大字库加 fonts;播放类加 background_audio;依赖外部二进制加 depends。
  • 二进制是静态 MIPS32r2 ELF;链 meow_ui.a + libz.a + -lpthread;-Wall -Wextra 零警告。
  • 颜色用主题语义色不硬编码;明暗都能看。
  • 控件/区域刷新走 meow_ui_invalidate;直绘内容已 meow_draw_custom_register。
  • 数据写 MeowData/<pkg>/,不写板载/NAND;退出干净 exit(0);根页左滑 meow_ui_request_exit。
  • os.meow.*(系统签名)vs 第三方(自有前缀,建议也签名防篡改)。打包 tar czf(或 nyai export,文件名茎=包名)→ meow_sign sign → 自测 verify/meow_verify_host。
  • 真机冒烟:am start/am status/日志 MeowOS/logs/<pkg>/,卡移除/无网/断流降级不崩。