需求(用户定案):App 复杂功能若依赖现成 Linux 二进制(板载 /usr/bin 或卡上 MeowOS/opt/bin)能效更好,声明后系统启动 App 前检查依赖是否存在。 - depends 行式声明:depends=curl,jq:optional,ffmpeg:required(无标注默认 required;也接受 :opt/:req;未知修饰符按 required 处理并 WARN;空项/超量丢弃) - launch_app_chain 冷启动时按序查 /usr/bin/<name> 与 <卡>/MeowOS/opt/bin/<name> (X_OK,两类命中即满足);缺 required → 不启动(toast「缺依赖: <name>」+ 日志, 沿用 CANNOT START 风格);optional 缺失 → 仅 WARN 放行;只在启动时查,不做周期复查 - nyai install 对包内 app.json depends 同查并提示(不阻断安装,缺 required 标注 「安装后启动将失败」) - 新增 meow_dep.c(独立 TU,解析/检查/块原因),宿主测试 test_depends.c 解析矩阵 12 用例 + 假根检查路径 12 用例全 PASS;launcher/nyai mipsel 交叉 -Wall -Wextra 0 warning;launcher 现有宿主测试回归全过 - 文档:APPS.md §2.1 字段表 + docs/MEOWOS_SPEC_REF.md §5.5 同步;hello app.json 加 depends:curl:optional 示例 launcher md5 899ac22c10f596909d7f35ea685744de(fw_content.conf 已更新;/usr/bin 只读,留给主会话打包固件)
49 KiB
R3 Pro II 第三方应用加载器设计文档(APPS.md)
状态:v2 定稿(Plan B:零 patch 看门狗启动器),2026-08-28 对应实现:
patch_v2/meow_launcherd/(启动器守护进程)、patch_v2/meow_watchdog/(应用看门狗)、patch_v2/hello_app/(示例应用) 前置知识:ANALYSIS.md 第 9/11/12/16/17/18 节 变更记录:v1(2026-08-27)为 hiby_player 二进制 patch 路线(Qobuz 入口触发),cave 代码在真机不稳定, 已整体废弃;v2 改为零 patch 的独立守护进程 + 热键触发。
1. 目标与边界
1.1 目标
- 硬件热键(按住电源 + 单击音量减,默认;可配 meow.conf
hotkey=power_voldown|seq)→ meow_launcherd 打开应用列表(fb0 直绘,列出 TF 卡/usr/data/mnt/sd_0/MeowAPPs/下的应用) - 触摸/按键导航选择某个应用 → 守护进程 SIGSTOP 冻结 hiby_player(让位),fork/execve/waitpid 启动该应用的静态 MIPS 二进制
- 应用进程退出 → SIGCONT 恢复 hiby_player(自动回到原界面)
- 应用运行期间 meow_watchdog 提供虚拟返回键/沉浸手势(SIGTERM→1s→SIGKILL 兜底)
1.2 边界(v2 刻意不做)
- 不做应用沙箱/权限模型(应用以 root 运行,与 hiby_player 同权)
- 不做应用安装/卸载 UI(仅支持手动往 TF 卡放目录)
- 不做动态链接应用(只支持静态 MIPS32r2 ELF)
- 不做应用内音频接入(见 §5,未来经 socket 通道)
- 不做滚动列表(应用 ≤ 8 个,单屏显示)
- 菜单内虚拟返回键不做(菜单只有「退出」语义;应用运行期的返回由看门狗提供)
1.3 安全红线
- 只允许触碰:TF 卡(
/usr/data/mnt/sd_0)、/usr/data、bind-mount 覆盖点 - 绝不写 NAND 分区;调试全程 bind-mount/直接替换可写卷文件,重启即回退,变砖风险为零
- 系统动作(关机/重启/升级/回 HiBy)统一经专项服务触发:App/组件不直连
sys_do_reboot、不写 NAND、不写 mtd5 —— 改走meow_svc_power(IPC,root 免鉴权、单一命令、不覆盖系统 shutdown/reboot);升级 = 写 mtd5=ota:kernel2(引导 kernel2 自动刷,见docs/MEOWOS_SPEC_REF.md§1.2)。M2.73 升级写保护:upgrade写 mtd5 前先校验卡根r3proii.upt(存在+可读+常规文件+size>0),失败不写 mtd5/不重启——只完整性、不签名(海贝官方固件无签名,签名会挡自刷;期望 md5/hash 校验二期)。二期:关键 IPC 只接受os.meow.*包名鉴权。[devdocs/plan/SYSTEM_FRAMEWORK_SERVICES.md] - hiby_player.sh 看门狗:
hiby_player 退出 → sleep 1 → reboot。SIGSTOP 不触发(进程未退出);严禁 killall hiby_player,须先杀 hiby_player.sh 父 shell 再手动拉起 - 若 meow_launcherd 意外死亡(SIGKILL/崩溃)且播放器被冻结:手动恢复
kill -CONT $(pidof hiby_player)(守护进程自身 SIGTERM 路径会自动 SIGCONT)
2. TF 卡目录规范(MeowAPPs)
挂载点:/usr/data/mnt/sd_0(TF 卡,exfat;hiby_player 内部虚拟盘符 a:\ 映射到该目录)。
/usr/data/mnt/sd_0/ # TF 卡挂载点
└── MeowAPPs/
├── <package>/ # 目录名 = app.json 的 package(权威 app_id)
│ ├── app.json # 应用描述(必填;package 字段为权威 app_id)
│ ├── <binary> # 静态 MIPS32r2 ELF(exec 指向)
│ └── icon.png # 预留:列表图标(v2 未用)
└── ...
<package> 即 app.json 的 package 字段(反域名权威标识,如 os.meow.hello);目录名必须等于 package,launcher 以 package 为 app_id,缺失时回退目录名。
2.1 app.json 字段
{
"name": "Hello App",
"exec": "/usr/data/mnt/sd_0/MeowAPPs/os.meow.hello/hello",
"package": "os.meow.hello",
"version": "0.1",
"version_code": 1,
"author": "whoever"
}
| 字段 | 必填 | 说明 |
|---|---|---|
name |
✓ | 列表显示名。v2 仅支持 ASCII(UTF-8 中文留待 v3,需完整 UTF-8→UTF-16 转换) |
exec |
✓ | 应用可执行文件绝对路径(必须是静态 MIPS32r2 ELF,soft/hard-float 均可) |
package |
— | 反域名权威标识(如 os.meow.hello),launcher 取之为 app_id;目录名=package。缺省回退目录名 de->d_name;两者存在且不同 → WARN |
version_code |
— | 纯数字,安装/升级比较的唯一依据(nyai/launcher 全走它;缺 code 拒装、新版<已装拒装),见 §3.8 |
version |
— | 显示字符串(versionName 角色,1.2.3-beta 等任意形态,不参与比较),见 §3.8 |
author |
— | 保留字段 |
fullscreen |
— | app 级全屏:1=进入全屏(状态栏收合至提示线 + 保留左缘返回/顶部提示线/下滑通知中心,见 MEOWOS_SPEC_REF §5.1);activity 级用它 route.<id>.fullscreen。app.json fullscreen 字段此前未解析/已定未做 → 本次实现(launcher 启动/切 activity 据声明预设 SDK 全屏,env MEOW_FULLSCREEN)[源:用户 2026-08-30] |
fonts |
— | 字库档位声明:"full" / "gbk" / "common"(app 需大字库时可声明 "full",默认小字库 common≈4k;launcher 据此为该 app 加载大字库 gbk≈20k,从已扫描的 MeowOS/fonts/<family>/<weight>/ 里按同 family(其次同 weight)选大字库,M2.6)。注意:与 "fonts":["<handle>"](应用自带字库句柄数组)同名二义,见 MEOWOS_SPEC_REF §7 A-14 [源:用户 2026-08-30] |
depends |
— | 二进制依赖声明(launcher/nyai 启动前检查):"curl,jq:optional,ffmpeg:required"(JSON 值串)或行式 depends=curl,jq:optional,ffmpeg:required。逗号分隔多项,每项 <name>[:<mod>];无标注默认 required,也接受 :opt/:req 缩写(=optional/required),未知修饰符按 required 处理并 WARN。App 某些复杂功能若依赖现成 Linux 二进制(板载 /usr/bin/<name> 或卡上 /usr/data/mnt/sd_0/MeowOS/opt/bin/<name>,均按 X_OK 查,两类任命中即满足)能效更好时声明。launcher 启动前检查(仅启动时,不做周期复查):required 缺失 → 不启动(toast「缺依赖: 」+ 日志,沿用 CANNOT START 风格);optional 缺失 → 仅 WARN 放行。[源:用户 2026-09-01] |
- 目录扫描深度 1 级:只枚举
MeowAPPs/下的直接子目录 - 应用计数上限 8(超出忽略;单屏 8 行)
- app.json 解析器极简(launcher 内实现):仅匹配
"name":、"exec":、"package":后的引号字符串(及扩展 icon/framework/immersive/route./awake.<id>(keep_awake)/service./depends(二进制依赖,line 解析)),fullscreen(本次实现)与fonts(档位串) 已解析,不做 JSON 校验(多行/单行均可)[源:审计 2026-08-30][源:用户 2026-08-30] - 列表刷新时机:每次菜单打开时重新扫描(TF 卡热插拔后立即可见)
2.2 边界情况行为(launcher 实现,全部真机验证)
| 场景 | 行为 |
|---|---|
| TF 卡未挂载/未插入 | 菜单提示「NO STORAGE CARD / INSERT TF CARD」(state=1,/proc/mounts 无 sd_0) |
| 卡挂载但无 MeowAPPs 目录 | 提示「NO APPS FOUND」+ 底部示例行 MeowAPPs/<id>/app.json(state=2) |
| MeowAPPs 为空目录 | 同上(扫描计数 0 → state=2) |
| app.json 损坏/缺 name 或 exec 字段 | 跳过该应用,列表其余项正常 |
| 二进制不存在/不可执行 | 行尾标 [MISSING];点击/确认该行提示 toast「CANNOT START: 」(access X_OK 检查,不 fork;launch_app 返回 -1) |
| 二进制存在但 execve 失败 | v6.8 起 launch_app 用 CLOEXEC 管道检测 execve 失败(子进程写 errno),返回 -1 → toast;不再把 127 误当「应用跑完」。toast「CANNOT START」仅在返回 -1(真启动失败)时显示,应用干净退出(返回 0)不显示(Bug 3,旧版无条件显示) |
| 扫描读取出错(opendir/fopen/fgets 失败) | 各自跳过;最终无应用时按 state=2 提示 |
状态机:state 0=正常 / 1=未检测到存储卡 / 2=无可用应用。
3. 应用生命周期契约
3.1 启动流程(meow_launcherd 侧)
热键(按住电源 + 单击音量减;电源按下后 60ms~2.5s 内收到音量减按下边沿触发;v6.7 起窗口下限由 300ms 改为 60ms,窗口 60–2500ms)
→ meow_launcherd 检测(EVIOCGKEY 状态轮询,10ms;电源按下即 arm,窗口外或松开即 disarm)
→ SIGSTOP hiby_player(/proc/N/exe == /usr/bin/hiby_player;等待状态 T)
→ 背光仅在面板灭时拉回(brightness==0 才写 saved/60,绝不无脑拉亮度)+ unblank 面板(先 SIGSTOP 再 unblank)→ fb0 直绘应用列表
→ 菜单循环(10ms):
· 触摸(event1 事件读)点击行 → 启动;点击「BACK TO PLAYER」行 → 退出菜单
· 按键(EVIOCGKEY 状态轮询):音量减/加=移动光标,播放键=启动
· **电源 = 纯息屏/亮屏(launcher 不干预亮度/面板,不退出菜单)**;退出菜单仅「BACK TO PLAYER」触摸行(或 launcher SIGTERM)
· 应用启动:fork → execve(exec[i])(失败 exit 127);同时 fork meow_watchdog <pid>
· waitpid(app) 阻塞;应用内强杀 = 音量减×3,退出回菜单
→ 应用退出 → 菜单退出 → SIGCONT hiby_player → 播放器恢复重绘
3.2 让位机制(守护进程方案,2026-08-28 真机验证)
结论:SIGSTOP 整进程冻结 hiby_player(UI+音频+全部线程),菜单期间播放器完全不动作;SIGCONT 后原样恢复。hiby_player.sh 看门狗只盯进程退出,SIGSTOP 不触发 reboot。
依据(真机实证,见 ANALYSIS.md §18):
- SIGSTOP 不触发看门狗:进程存活(状态 T),
hiby_player.sh的sleep 1; reboot不执行。 - 恢复确定可靠:菜单每条退出路径(含 SIGTERM 处理、错误路径)都会 SIGCONT;
meow_launcherd自身被 SIGTERM 时也先 SIGCONT 再退出。 - 崩溃安全:应用崩溃/被杀 → waitpid 返回 → 自动 SIGCONT 恢复。若守护进程本身被 SIGKILL,播放器会冻结——手动
kill -CONT $(pidof hiby_player)恢复(详见 §1.3)。 - 菜单期间播放器完全不刷新(SIGSTOP),fb0 归守护进程独占绘制,无抢绘。
- 音频:SIGSTOP 连音频线程一起冻结(应用运行期无声音;如需出声见 §5 未来方案——v2 的 SIGSTOP 方案下播放器无法代播,需改 waitpid-in-callback 让位或音频线程解冻,留待 v3)。
- 应用退出 → SIGCONT → 播放器首帧整页重绘,无需应用清屏。
3.3 应用该做什么 / 不该做什么
应用必须:
| # | 规则 | 说明 |
|---|---|---|
| 1 | 干净退出 | 任意退出码均可;守护进程 waitpid 立即返回,SIGCONT 恢复。exit(0/1) 即可,不要 abort() 留下僵尸 |
| 2 | 独占屏幕自绘 | 自行 open("/dev/fb0") + mmap 后绘制;设备帧缓冲 480×720(分辨率/色深运行时用 FBIOGET_VSCREENINFO/FSCREENINFO 探测,RGB565 或 RGBA8888 自适应)。注意:本设备 ioctl 号 VSCREENINFO=0x4600、FSCREENINFO=0x4602(勿颠倒,见 §4.3) |
| 3 | 自己读输入 | open("/dev/input/event*") 逐个打开,读到任意 key/touch 事件即处理。按任意键退出是推荐约定(物理事件对所有读者多播,播放器已冻结,无需担心抢事件) |
| 4 | 及时退出 | 建议空闲超时(如 30s 无操作)自动退出,避免长期占屏(hello 60s 看门狗) |
应用不该做:
| # | 规则 | 原因 |
|---|---|---|
| 1 | 不要尝试"恢复" fb | SIGCONT 后播放器首帧即整页重绘,无需你清屏。你清屏反而闪一下 |
| 2 | 不要 kill hiby_player | 触发 hiby_player.sh 看门狗 → 整机重启 |
| 3 | 不要继承占用声卡 | SIGSTOP 期间声卡仍被冻结的播放器持有,直接打开会失败/挂起(v2 无音频) |
| 4 | 不要 fork 出长期后台进程 | 你的退出应"真正退出",否则 SIGCONT 后后台进程仍占屏/抢输入 |
| 5 | 不要碰 NAND 分区/固件 | 只写 TF 卡与 /usr/data |
3.4 退出与恢复
- 应用退出 → waitpid 返回 → 菜单关闭 → SIGCONT 播放器 → 播放器恢复原界面
- 菜单内「BACK TO PLAYER」行 → 直接 SIGCONT(不启动任何应用);电源键在菜单内不再退出菜单(v6.5 起电源=纯息屏/亮屏,仅 sys_server/内核处理背光)
- 整机重启 → 一切回退原版(守护进程由 S90 随开机自启)
3.4.1 物理按键语义 —— 强制规范(系统统一管理,APP 禁映射)
⚠️ 红线:物理按键是系统资源,语义由 MeowOS(launcher/watchdog/内核)统一管理。任何非系统组件(MeowAPP)不得将物理按键映射成任何行为,包括但不限于:电源键→OK/确认/exit/翻页,音量键→确认/选择,播放键→确认/返回。
为什么:用户在任何界面都必须能靠物理键完成系统级操作——电源键=息屏/亮屏(MeowOS 界面也必须能息屏)。若 APP 抢走电源键当"OK/exit",用户一按电源键,屏没息反而退出了 APP,无法息屏;音量键/播放键同理。物理键必须留给系统。
约定(MeowOS 界面 = launcher 主菜单/选单/电源菜单,是唯一允许映射物理键的地方):
⚠️ 适用范围:除了电源键菜单(
show_power_menu,允许"电源=确认、音量=导航")以外,所有按键信号由 MeowOS 全局接管——任何界面(含 MeowOS 自身的设置页 / 应用列表 / 选单、以及 MeowAPP)都不得私自定义按键行为。物理键的语义只有一份:电源=息屏/亮屏/长按菜单/8s强关,音量=调系统音量,播放/上下曲=媒体控制(已落地,M2.81 起路由最近活跃媒体会话;无会话时「抓住」)。任何界面(如系统设置页)不得把电源键/播放键映射成"返回/确认"这类自定义行为;界面导航一律走触摸(返回按钮/左缘左滑),按键语义统一由系统判。
| 键 | 系统语义(APP 收到的是语义事件,不是裸 keycode) |
|---|---|
| 电源 | 息屏/亮屏、长按弹电源菜单;APP 内=纯息屏/亮屏(绝不 exit/OK) |
| 音量 +/− | 调系统音量;APP 内=调音量(绝不选中/确认/翻页) |
| 播放/暂停 | 全局媒体控制(播放/暂停当前媒体会话);APP 内=媒体控制(绝不确认/打开) |
| 返回 | 触屏左滑/虚拟返回键 → SDK 语义 on_swipe(LEFT) / watchdog 信号,非物理键 |
MeowAPP 侧:
- APP 不读
/dev/input/event*、不 open/mmap fb0、不处理任何物理 keycode——一切走 SDK 的语义回调(on_tap/on_swipe/on_tick;on_key仅用于 SDk 内部状态,不含电源/音量/播放的确认/exit 语义)。 - APP 想"返回/退出"→ 用
meow_ui_request_exit()或左滑,绝不监听物理键映射 exit。 - SDK 的
on_key回调:禁止 APP 用它实现"电源=exit"这类映射。SDK 内部消化物理键的系统语义(息屏/音量/媒体),只把触摸返回/手势等语义事件给 APP。
落地:hello.c 当前 on_key(KEY_POWER)→request_exit() 违反此规范,需删除(改由 SDK/系统处理息屏);APP 退出走左滑/request_exit/系统返回。MeowOS 系统设置页(run_settings_page)曾私自定义"电源/播放键=返回",M2.45 已按本规范改为全局语义(电源=息屏/亮屏/长按菜单/8s强关,播放=抓住),退出仅靠触摸(底部返回/左缘左滑)——任何 MeowOS 界面新增时须遵守本规范,不得私自定义按键。
3.5 虚拟返回键 / 沉浸模式 / 看门狗
应用启动时,launcher 同时 fork 看门狗进程 /usr/data/meow_watchdog <app_pid>(固件 S99ssh 首启从 /usr/bin/meow_watchdog 拷到可写卷;launcher 先试 /usr/data 再试 /usr/bin):
| 特性 | 行为 |
|---|---|
| 虚拟返回键 | 已移除(M2.21 起):全局左缘返回手势取代左上角返回键,看门狗不再画按钮。历史行为:独立 mmap fb0 画 48×48 返回键 + 命中 SIGTERM(保留备查) |
| 沉浸模式 | 应用写 /tmp/meowapp_state 内容为 1 → 看门狗识别「左缘 24px 内起点向右滑 >50px」手势。M2.89 起 SIGUSR1 只对「裸沉浸 App」(非 framework=1 且 immersive)发送(236:SDK 框架 App 走 on_swipe 处理返回,发 SIGUSR1 会误杀——ncm signal=16 崩溃的根因);SDK 侧另有 SIGUSR1 护栏(sigusr1_guard,记日志+吞掉)双保险 |
| 看门狗自退出 | 应用进程消失(kill(pid,0) ESRCH)或 launcher SIGTERM → 退出 |
| 强制退出兜底 | 应用运行期(launcher 处于 waitpid):音量减连按 3 次(间隔 < hotkey_seq_gap,与主序列同阈值)→ SIGTERM → 0.5s → SIGKILL → 恢复。⚠️ 响应音量键的应用(如示例 hello)会先自行退出(exit:key),强杀序列是不响应音量键/卡死应用的兜底 |
| 长按电源 | 硬件级强制关机(设备固有),最终兜底 |
| 测试模式 | meow_watchdog <pid> --sim-tap N / --sim-swipe N,无触摸设备也可验证 |
应用协议约定:
- 默认(非沉浸)SDK 框架 App 由 SDK
on_swipe处理左缘返回;看门狗虚拟返回键已移除(M2.21),返回语义全走手势 - ⚠️
SIGUSR1仅是裸沉浸 App 的左缘滑动手势(M2.89 起看门狗不对 framework=1 的 SDK App 发),不是返回键;框架 App 另有 SDKsigusr1_guard护栏吞掉漏网 SIGUSR1。裸 App 的 SIGTERM 默认终止会不画 BYE 直接死,故示例 hello 安装SIGTERM处理器:画 BYE +exit(0)(v6.8,Bug 2)。 - ⚠️ 应用启动后须防 goodix 幽灵触摸(v6.8,Bug 2 根因):goodix 在启动触摸的尾部发出一段幽灵触摸(实测启动后 1.2-2.25s,§18.16),无防护时应用会误把幽灵当下按而立即退出画 BYE,且 launcher 菜单会把同一幽灵重放成行点击而重开应用(用户看到「bye 后又回 hello 主界面、需再点一次」)。hello v6.8 增加
COOLDOWN_MS=2500:启动后前 2.5s 忽略一切按下边沿,只有用户真正的下按才触发退出。launcher 侧同步在应用退出后 drain + 冷却(见 §4.7)。 - 沉浸应用写
/tmp/meowapp_state,处理SIGUSR1作为「返回事件」 - 应用异常卡死 → 虚拟返回键(若可见)→ SIGKILL;沉浸态卡死 → 音量减连按 3 次(强杀序列)
- 应用日志:launcher 通过环境变量
MEOW_LOG_DIR直接告知 TF 卡上该应用的日志目录MeowOS/logs/<app_id>/(无卡时不设),应用写<ts>_<pid>.log([源:M2.x 简化 logs/apps/<app_id>/logs → logs/<app_id>]);启动时先 drain 输入队列、只响应"按下边沿"退出(释放事件/轨迹事件忽略)
3.6 应用运行期卡移除(M2.11 最佳努力)
场景:前台 MeowAPP 的二进制/资源位于 TF 卡(exec 指向 /usr/data/mnt/sd_0/MeowAPPs/…/)。运行中卡被拔出 → 应用访问卡上代码页/资源可能触发 SIGBUS 崩溃。
launcher 侧(已实现,launch_app waitpid 循环内):
- 启动应用时记录
app_on_card = (exec 路径在 MEOWAPPS 下);若是,则进入「运行期卡移除监测」。 - waitpid 主循环每 ~100ms 采样
/dev/mmcblk0存在性。一旦发现卡设备节点消失(被拔)且当前应用在卡上 → 主动 SIGTERM 应用(干净退出,避免等 SIGBUS 崩),usleep(300ms)内未退则 SIGKILL 兜底,然后走正常「应用退出→回菜单」路径。菜单/日志提示「卡已移除,应用已结束」。 - 不做:把应用二进制拷到内存/板载 staging 再 exec(该方案权衡大小/内存,本阶段先记录不做)。
应用侧规范(给第三方 MeowAPP 的约定):
- 别依赖卡上资源当运行必需:字体/封面/贴图等用 RAM 缓存到内存再渲染;运行期避免持续从卡 mmap/读大文件。
- 读错误容错:
open/read/mmap返回失败(卡没了)时别abort()/崩溃,优雅退出。 - 卡掉能干净退出:收到
SIGTERM(launcher 因卡移除而发,或用户虚拟返回键)一律画bye+exit(0);mmap到卡文件失败时视为可恢复/可退出,不 SIGBUS 宿命。
4. meow_launcherd 实现细节
4.1 架构:被动式守护进程(零 patch、零 grab、零注入)
关键内核实证(2026-08-28,见 ANALYSIS.md §18.2):
- hiby_player 不持有 EVIOCGRAB:播放器运行时对 event0/event2/event1 的 EVIOCGRAB 探测全部成功(非 EBUSY)——此前"播放器独占按键"的观察不成立或仅为瞬态。
- 注入事件永不投递:
write()到任何 event 节点都成功、且更新设备键状态(EVIOCGKEY 可见),但任何 evdev 读者(含 grabber 自身)都收不到——厂商输入子系统修改了投递路径。⇒「grab + write 回写转发」方案在真机不可行(原方案 B 的前半段作废)。 - 物理事件多播:播放器自身两个 fd 同时读 event1(触摸正常),证明物理事件对所有读者多播(标准语义)。
因此 launcher 采用:
- 按键 = EVIOCGKEY 状态轮询(10ms,只读 ioctl,与 grab/投递无关,播放器抓不抓都不影响)——音量/播放(114/115/164/163, event2)、电源/上一曲(116/165, event0)。
- 触摸 = event1 事件读(未被抓、物理多播)——菜单点击。
- 不 grab、不注入、不改播放器——"按键无感"由构造保证(播放器按键路径完全没被碰)。
4.2 热键与菜单键
| 键 | 设备 | code | 用途 |
|---|---|---|---|
| 音量加 | event2 | 115 | 菜单光标上 |
| 音量减 | event2 | 114 | 热键第二击(电源按住后单击) / 菜单光标下 / 应用内强杀序列键 |
| 播放/暂停 | event2 | 164 | 菜单确认 |
| 电源 | event0 | 116 | 热键第一击(按住) / 菜单/应用内=纯息屏亮屏(不再退出菜单) |
| 下一曲 | event2 | 163 | (未用) |
| 上一曲 | event0 | 165 | (弃用——GPIO PC28 硬件链断,§18.12) |
热键判定(v6.5 定案;v6.7 起窗口下限 300ms→60ms):按住电源 → 电源按下后 60ms~2.5s 内单击音量减(在窗口内收到音量减的按下边沿且电源仍被按住)⇒ 开菜单。电源按下会触发播放器息屏流程(已接受的副作用,用 v6.2/v6.3 已验证时序保证菜单可见:SIGSTOP 播放器 → unblank 面板 → 仅灭屏时拉背光);音量减单击副作用 = 音量 -1(可接受)。电源松开或窗口超时(>2.5s)即 disarm;音量减在窗口外(<60ms 或 >2.5s)按下视为普通音量键,不触发。纯单键序列(v6 volup×3)与组合键(上一曲+音量加,GPIO 断,§18.12)均已退役,仅保留在 meow.conf hotkey=seq(历史备选,不推荐)。
热键 meow.conf 配置:
hotkey=power_voldown # 默认:电源按住 + 单击音量减(60-2500ms 窗口;v6.7 起下限 300ms→60ms)
hotkey=seq # 历史序列热键(单键连按 N 次,不推荐)
(hotkey_seq=/hotkey_seq_gap= 仅在 hotkey=seq 时生效。)
⚠️ v6.1 修复:早期版本 hotkey_seq 解析在行尾 \n 处死循环(外层 while 不消费 EOL),配了该行 daemon 即卡死——已修复并在真机复测。该解析器同样用于 hotkey= 行(无此问题,逐行 strncmp)。
音量副作用与恢复:v6.5 主热键(电源+音量减)的副作用是音量 -1(单击音量减,播放器收到,直接作为音量调节——无恢复需求,接受)。序列热键(hotkey=seq,默认已不使用)的每次按下仍会音量+1×N,launcher 在第 1 次按下快照 CS43198 音量、序列完成触发时写回(⚠️ 实测该 control 写无效,恢复为尽力而为)——因已退役,不再展开。
热键演变记录:v1 电源+音量减 → v2 电源+上一曲 → v3 双音量同按(物理不可能:event2 单 ADC 通道)→ v4 上一曲+音量加(上一曲 GPIO PC28 硬件链断,§18.12)→ v6 单键序列(volup×3,单键物理必可注册)→ v6.5 电源+单击音量减(定案):回到"跨设备组合键"能力(电源在 event0、音量减在 event2,两键不同 ADC/GPIO,物理可同按),利用电源按下必然触发播放器息屏的既有行为,以"按住电源 + 窗口内单击音量减"作热键,同时移除菜单/应用内电源=逐级返回(电源在菜单/应用内改为纯息屏/亮屏,见 §3.1/§3.4)。
4.3 fb0 绘图
- 480×720 RGB565(运行时 ioctl 探测,RGB565/RGBA8888 自适应)
- ⚠️ 本设备 fb ioctl 号(实测):
FBIOGET_VSCREENINFO=0x4600、FBIOGET_FSCREENINFO=0x4602(旧式无 size 编码号)。hello.c/meow_watchdog.c 曾沿用标准头文件常量写反(0x4602/0x4600),导致 ioctl 返回垃圾、mmap EINVAL——已全部修正并用探针实测通过。 - ⚠️ 双缓冲(v6.6):本设备 fb0 为双缓冲(实测
yres_virtual=1440=2×720、smem_len=2 帧、ypanstep=1),面板显示哪个缓冲由 fb_var.yoffset 决定,FBIOPAN_DISPLAY=0x4606可翻页。launcher 与 hello 等 app 都只画 mmap 偏移 0(buffer0),若 yoffset 停在 buffer1(播放器电源/息屏重绘后),菜单画在 buffer0 而面板显示 buffer1 → 菜单不可见、屏幕冻结(v6.5 bug)。v6.6 起fb_openmmap 整个虚拟帧缓冲(含两帧),菜单打开时fb_backup_player快照当前 yoffset 处缓冲 →fb_norm_buf0(FBIOPAN_DISPLAY 把 yoffset 归 0,菜单/app 共同绘制目标)→ 回退时fb_restore_player把备份拷回原 yoffset 缓冲并 pan 回原 yoffset(播放器事件驱动不主动重绘,必须恢复 yoffset)。面板 blank/亮度状态仍归播放器(§18.11 教训),launcher 只在 fb 缓冲/翻页层面介入。 - 重绘纪律(v6.9,修复列表 ~3fps 频闪):菜单只在内容真正变化时整屏重绘——光标移动(输入)、应用退出/进入(重写了 buffer0)、toast 起止、未来通知徽标变化时置
dirty;绝无周期整屏重绘。v6.8 的「500ms 安全重绘」会在 500ms 定时把整个菜单重写进正在显示的 buffer0(就地改写产生撕裂/脉冲,且draw_statusbar每次调用都amixerpopen 拖慢主循环),正是频闪根因。若真需周期刷新(时钟每秒),用只刷时钟数字的轻量路径draw_clock()(不弹 amixer、不碰应用列表),其余状态(电量/WIFI/BT/音量)仅在事件(dirty)时随整屏刷新。 - 布局:标题 MEOWAPPS / 高亮行(光标)/ 状态提示 /
BACK TO PLAYER行 / 底部按键提示 / toast 区(y≈584-617);右下角开发版本号(v6.9)——MeowOS v<MEOWOS_VERSION> dev,右对齐画在底部行 y=700,与BACK TO PLAYER(y≤688)和左侧按键提示(至 x≈254)均不重叠。
4.4 命令行(含测试钩子)
meow_launcherd # 守护模式
meow_launcherd --scan # 打印应用列表与状态,退出
meow_launcherd --dumpkeys N # 轮询 N 毫秒,记录所有按键状态边沿(真机抓 key code 用)
meow_launcherd --sim-hotkey N # N 毫秒后模拟热键(默认=电源按住→N+800ms 单击音量减;hotkey=seq 时=音量加 ×3 快速连按,每次 300ms 间隔)
meow_launcherd --sim-tap N # 菜单打开 N 毫秒后模拟点击第 0 行
meow_launcherd --sim-exit N # 菜单打开 N 毫秒后模拟退出
测试钩子用于无物理输入时的全链路自动化验证(注入事件不投递、无法远程按物理键)。
4.5 统一日志系统(MeowAPPs 组件共用)
- 默认位置:TF 卡
/usr/data/mnt/sd_0/MeowOS/logs/meow_<bootts>.log(如meow_20260828_020123.log;bootts= 当前墙钟 − /proc/uptime,同一 boot 会话内多次启动共用同一文件)。系统日志(launcher/watchdog 等 MeowOS 组件)写此文件;应用日志另按logs/<app_id>/<ts>_<pid>.log分文件(<app_id>即该应用目录;见本 §4.5「组件环境变量」)。meow_bootmenu用logs/meow_boot_<bootts>.log(同目录,文件名前缀区分——早期「仍用 MeowAPPs/logs」注记已过期,现统一在MeowOS/logs)。旧布局logs/apps/<app_id>/logs/已废弃:launcher 启动时一次性迁移到logs/<app_id>/后删除 - 无 TF 卡 / 卡晚挂载(S90 早于卡挂载):开不了卡日志——日志落 /tmp 启动缓冲(
/tmp/meow_boot.log,上限 256KB 滚动),绝不写板载存储(/usr/data 只剩 ~10MB);卡出现后自动迁移(主循环 ~1s 周期重试 + 输入设备重试循环内也检查;无需开菜单)——log_poll_card()打开卡日志并把 /tmp 缓冲整段搬入后删除 /tmp 文件 - 容量上限:logs/ 目录总量 1GB,开机时超限按 mtime 删最旧
- 组件环境变量:
MEOW_LOG_FILE→ meow_watchdog:追加写同一 boot 日志文件(无该变量则走 stderr)MEOW_LOG_DIR→ 应用:日志目录(每 AppMeowOS/logs/<app_id>/,应用写<ts>_<pid>.log;[源:M2.x 简化,弃logs/apps/<app_id>/logs/])
- 内容:时间戳 + 全部按键边沿/菜单/应用事件(真机按键证据)
- PID 文件:
/usr/data/meow_launcherd.pid;播放器发现:扫描/proc/N/exe==/usr/bin/hiby_player
4.6 UI 规范(应用列表与 MeowAPPs 界面)
- 纯黑背景为默认(
menu_bg = 0x000000) - 可配置:TF 卡
MeowAPPs/meow.conf支持bg_color=RRGGBB(如bg_color=10284f),菜单打开时重新加载 - 40px 状态栏(v6.8 由 20px 翻倍;v6.9 时钟加秒):第 1 行 = 左侧时钟(HH:MM:SS,每秒刷新,v6.9;用
draw_clock()只刷时钟数字,不弹 amixer、不碰应用列表——见 §4.3 重绘纪律)、右侧电量% WIFI BT(电量读/sys/class/power_supply/battery/capacity、WIFI 读/sys/class/net/wlan0/operstate、蓝牙读/sys/class/bluetooth/hci0存在性);第 2 行 = 左侧VOL nn%(音量读 CS43198 / amixer numid=1,值 0-255);下方 1px 分隔线(移至 y=40)。⚠️ 音量 amixer control 实测为死镜像(§18.13:播放器走 sa_hgl_dma 硬件直通,ALSA control 读回恒 0),故展示值可能恒为 0%,为已知设备限制。 - 开发版本号(v6.9 起,现行来自
/etc/meowos_version):右下角绘制MeowOS v<ver> dev(字体无小写,实际渲染为大写MEOWOS V<VER> DEV)。版本号单点 = 板上/etc/meowos_version(M2.47 起;launcher/bootmenu 运行时load_meowos_version读;MEOWOS_VERSION宏仅作文件缺失时 dev 占位兜底,不再当实际版本),屏显与固件文件同源,不会两处不一致。 - 示例应用 hello 同样黑底 + 40px 状态栏(应用内左上角 48×48 返回键由 watchdog 绘制,允许盖住状态栏左侧)
4.7 应用退出后的菜单触摸守卫 + 启动失败检测(v6.8,Bug 2/3/4)
Bug 2/4 根因(应用退出后菜单重放残留触摸):应用运行期间 launcher 的菜单触摸 fd(touch_fd)不轮询(被 launch_app 的 waitpid 阻塞),期间收集的触摸事件(goodix 幽灵触摸 + 真正退出应用的那次点击/按键)全部排队。应用退出回到菜单后,菜单下一轮 poll 会把这些残留事件重放成新的行点击——落在应用行则重开应用(症状:bye 后回 hello 主界面、需再点一次,Bug 2);落在「BACK TO PLAYER」行则关菜单 → SIGCONT 播放器(整屏闪回播放器,Bug 4)。
修复(run_menu + menu_after_app_exit()):
- drain:应用退出后立即读完并丢弃
touch_fd的全部残留事件。 - 冷却:应用真正跑过(
launch_app返回 0)时置menu_touch_cooldown_until = now + 2500ms,冷却窗内忽略一切触摸按下边沿(与 watchdog 的同值 COOLDOWN_MS 对齐,覆盖实测幽灵窗 1.2-2.25s);启动失败(返回 -1)不置冷却,避免无谓锁菜单。 - 菜单重绘:v6.8 曾把
last_redraw从绝对时间戳t改为相对量el(使 500ms 安全重绘生效)以覆盖应用退出后的残帧(如 BYE)。v6.9 起摒弃周期整屏重绘(它在 500ms 定时把整个菜单重写进正在显示的 buffer0,造成列表 ~3fps 频闪):改为仅 dirty 时整屏重绘,应用退出/进入路径显式置dirty=1主动覆盖应用帧;时钟每秒用轻量draw_clock()只刷数字。
Bug 3 根因与修复:旧代码在所有 launch_app 调用点无条件置「CANNOT START: 」toast,即使应用正常跑完。修复:仅当 launch_app 返回 -1(真启动失败) 才置 toast;应用干净退出(返回 0)不显示。launch_app 用 CLOEXEC 管道检测 execve 失败(子进程写 errno → 父进程读 >0 判失败并返回 -1),不再把子进程 _exit(127) 误当「应用跑完」。
副作用(已知取舍):应用退出后 2.5s 内菜单触摸不响应(返回键误触/退出后紧急再操作需等待;与 watchdog 相同值,实测幽灵窗 1.2-2.25s)。物理触摸的幽灵无法注入复现(§18.2 注入不投递),以上防抖逻辑在注入/sim 链路已验证,物理最终确认需用户。
5. 音频通路(未来应用的出声方案)
2026-09-01 更新:常驻模式(MeowOS 主控、播放器未运行)下的出声方案已由
meow_audiod独占音频守护落地(M2.90)——守护独占 hw:0,0、App 经 SDKmeow_audio_*喂 PCM(MEOW_APP_SDK §5.9 /devdocs/plan/AUDIOD_DESIGN.md)。本节下述 v3 候选为「会话覆盖模式(HibyOS 热键唤起、SIGSTOP 冻结播放器)」的历史推演,仅在该模式下仍有参考意义;常驻模式直开 hw:0,0 的旧结论见 §3.7。
参考 ANALYSIS.md 第 11 节结论:
- 声卡 = 内部唯一 card0(CS43198 双 DAC),无混音,谁打开谁独占;hiby_player 常驻持有
hw:0,0。 - v2(SIGSTOP 让位)下播放器完全冻结,无法代播。应用直接开 ALSA 会失败。v3 候选:
- 改让位机制为"仅 UI 冻结"(如让播放器走 waitpid-in-callback 或独立音频线程),应用经 unix socket 汇入通道(0x73d320 区,
AIRPLAY:CONNECT类消息 + 16-bit PCM)出声——采样率定 44.1k/16bit(shairport 路径)。 - 应用退出后由播放器继续(现状 v2)。
- 改让位机制为"仅 UI 冻结"(如让播放器走 waitpid-in-callback 或独立音频线程),应用经 unix socket 汇入通道(0x73d320 区,
- 音量/通路切换由 hiby_player 管理(硬件音量寄存器),应用只管喂 PCM。
6. 测试流程
设备已刷 SSH 固件:
ssh -i ssh_keys/<device_key> root@<IP>(登录口令见私密记录,勿入库)。 所有设备操作遵守纪律:动播放器前数实例;重启前rm -f /data/recovery_all;息屏测试后恢复背光;结束恢复设备干净。
- 轮前检查:
ls /data/recovery_all(存在即删+告警);ps | grep hiby_player | wc -l应=1。 - 推送:
meow_launcherd/meow_launcherd→/data/meow_launcherd(测试期直接跑/data/...);meow_watchdog→/usr/data/meow_watchdog;hello→/usr/data/mnt/sd_0/MeowAPPs/os.meow.hello/hello。 - 验证输入路径(无物理按键时的替代证据):
meow_launcherd --scan→ 列出应用meow_launcherd --dumpkeys 6000+ 物理按键 → 日志出现key VOL_DOWN/VOL_UP DOWN/UP- 或注入状态模拟:
evtool send /dev/input/event2 1 114 1(仅更新键状态,无投递,供 EVIOCGKEY 观察)
- 全链路(自动化):
meow_launcherd --sim-hotkey 2000 --sim-tap 4000;期间cat /proc/<pid>/stat应 T(冻结);fb0 截图验证菜单/应用画面(黑底+状态栏);注入上一曲(165)+音量加(115)按住 2s 强杀应用 → 日志 SIGCONT → 播放器回 R、实例 1、背光 101。 - 边界 ×5:无卡(umount sd_0 → state=1 → 重挂)、无目录(mv MeowAPPs → state=2)、空目录(state=2)、坏 app.json(跳过)、二进制缺失(
[MISSING]+ toast),各拍 fb0 截图存device_test/。 - 稳定性:守护进程跑 ≥3 分钟,播放器 R、实例 1、无 recovery_all;SIGTERM 干净退出。
- 固件:
build_fw_apps.sh→fw_apps/r3proii_ssh_apps.upt,回验 0 diff、rootfs ≤ 45,088,768 B。 - 回退:重启即回退原版(守护进程不装则无热键);设备侧删除
/data/meow_launcherd等测试文件。
7. 已知风险与未决项
- ⚠️
/data/recovery_all恢复触发(设备会被出厂重置,最高优先红线):hiby_player 内部存在写该标志的代码路径(0x4823d0无限循环 /0x56d1c0消息处理器)。调试期间设备已被出厂重置两次。防护规矩:每次重启播放器前rm -f /data/recovery_all;部署后确认稳定才允许设备重启;发现该文件立即删除并告警。Plan B 全程未触发(SIGSTOP 不崩溃播放器)。 - 守护进程被 SIGKILL/崩溃 → 播放器冻结:SIGTERM 路径会自动 SIGCONT,但 SIGKILL 无法拦截。恢复:
kill -CONT $(pidof hiby_player)。v3 可加双重守护(两个进程互保)。 - 热键副作用:v6.5 主热键(电源+单击音量减)会触发播放器息屏流程(菜单打开用后置时序 unblank+仅灭屏拉背光,见 §3.1)、音量 -1(可接受);不再碰电源键"逐级返回"。仅当
hotkey=seq时才有音量 +N(amixer 音量 control 写无效无法恢复,见 §4.2;可配hotkey_seq=play,play,play换取播放停顿)。 - 菜单触摸依赖 event1 物理多播:播放器不抓触摸(已实测),若未来固件抓取,菜单仍有按键导航兜底。
- UTF-8 应用名:v2 仅 ASCII;中文名需实现 UTF-8→UTF-16 转换。
- 应用挂死且虚拟返回键不可见(沉浸态):音量减连按 3 次强杀兜底;再不行长按电源硬件关机。
- SIGSTOP 冻结音频线程:应用运行期无声(v3 方向见 §5)。
- 开机时序:S90 在 S92 播放器之前启动;输入设备由 S11 加载完成,launcher 启动时重试打开(30 次 × 1s)。
附录:MeowAPPs 开发规范 v2(包结构与数据目录/软件包)
1. 应用目录结构
MeowAPPs/<package>/ # 目录名 = app.json 的 package(权威 app_id)
├── app.json # 应用配置(name/exec/package/路由/图标/框架声明等)
├── meow # 程序主体(静态 MIPS 二进制;也可多文件/脚本)
└── <资源文件/图标…> # 只读程序资源
MeowData/<package>/
├── data/ # 持久应用数据(配置、播放列表、登录态…)
└── cache/ # 缓存(可清)
<package>=app.json的package(反域名,如os.meow.hello),与目录名一致;launcher/app 数据/日志/.mpk均以它为包名。 注:TF 卡为 exFAT,不支持符号链接,故数据目录不用链接,改由 MeowOS 通过环境变量注入(见 §2)。
2. 数据目录(MeowOS 负责,用环境变量注入)
- 本文档
<包名>=app.json的package(反域名权威标识,如os.meow.hello),与目录名一致。 - 程序主体 + 必要配置文件 放
MeowAPPs/<包名>/(可替换/可升级)。 - 软件数据 放
MeowData/<包名>/{data,cache}(独立于程序,重装/升级保留数据)。 - MeowOS 每次启动应用前:验证
MeowData/<包名>/{data,cache}存在(否则建)。 - 启动时注入环境变量给应用:
MEOW_DATA_DIR=/usr/data/mnt/sd_0/MeowData/<包名>/dataMEOW_CACHE_DIR=/usr/data/mnt/sd_0/MeowData/<包名>/cache- (与已有
MEOW_LOG_DIR/MEOW_ACTIVITY/MEOW_CHAIN一致)
- 应用写数据用
$MEOW_DATA_DIR/$MEOW_CACHE_DIR,无需链接;exFAT 也能跑。 - 好处:数据与程序分离;大体积数据在 MeowData(卡上);换程序不丢数据。
3. 软件包 .mpk
- 包名 = 文件名茎 =
app.json的package(如os.meow.hello→os.meow.hello.mpk)。 - 格式:
.mpk= tar.gz(含app.json+meow二进制 + 资源),不含 data/cache(运行时按设备生成)。 - 导出:打包
MeowAPPs/<包名>/(不含 data/cache 目录)→<包名>.mpk。 - 导入/安装(MeowOS 软件包安装程序
meow_pkg_install()):- 校验
.mpk(app.json 合法、包名不冲突/是否覆盖、校验哈希); - 解压到
MeowAPPs/<包名>/(若已有同名,确认升级/覆盖); - 触发首次启动数据初始化(建
MeowData/<包名>/{data,cache}); - 登入应用列表。
- 校验
- 更新规范 [源:用户定案 2026-08-31]:
- 同包名安装 = 更新:旧程序目录整体备份为
.bak_<pid>后替换,MeowData/<pkg>/{data,cache}数据保留;安装成功后清理自身产生的.bak_<pid>。 - 不允许降级安装:新包
app.json的version必须 > 已装版本(版本比较;相等视为重装,允许)。版本缺失/不可比 → 拒绝(提示版本无法判定)。 - 签名分级:有签名的包必须验签(失败拒装);无签名的第三方包直接安装;
os.meow.*级别必须有有效签名,无签名/验签失败一律拒装(防伪造系统包名)。
- 同包名安装 = 更新:旧程序目录整体备份为
- 后续:Web 后台(阶段 C)提供上传/安装/卸载/导出
.mpk界面;本地文件管理器可调用安装程序 UI(meow_pkg_install的交互入口)。 - 导出过滤(随更新规范落地):
nyai export/meow_pkg_export打包时排除*.pre_*(热更新备份)、*.bak_*(安装备份)、logs/等运行残留,只打app.json+exec+资源(防 2MB 垃圾进包)。 - 签名(Ⅱ期,见
devdocs/plan/SYSTEM_FRAMEWORK_SERVICES.md §6与devdocs/plan/MPK_SIGNATURE_DESIGN.md):.mpk增加signature(Ed25519,签名覆盖app.json的package+payload 哈希 以防伪造os.meow.*包名);meow_pkg_install/nyai install解包验签,按上条「签名分级」执行;meow_pkg_export/nyai export保留签名(不二次签名;安装时原签包存MeowData/<pkg>/data/<pkg>.mpk.src,导出优先复制之)。 - 包名
os.meow.*约定 + 系统 APP 鉴权规划(Ⅱ期):os.meow.*为 MeowOS 系统应用保留反域名前缀(如os.meow.hello示例);非系统(第三方)App 不得使用os.meow.*前缀——.mpk签名规划强制签名覆盖package,且os.meow.*无签名即拒绝安装,从源头防伪造系统包名。二期关键 IPC 鉴权:只接受os.meow.*系统包(关键 IPC 走os.meow.*鉴权,拒绝第三方),第三方走受限能力。
4. 实现挂点(MeowOS/launcher)
- 启动应用入口统一调
meow_app_prepare(pkg):建MeowData/<包名>/{data,cache}+ 设 MEOW_DATA_DIR/CACHE_DIR,失败回退提示。 meow_pkg_install/export:包安装/导出(本期可先做 CLI/接口,UI 后续)。- 这些为阶段 C(Web)/文件管理器预留。
5. 软件包管理器 CLI nyai(静态 MIPS,apt/yum 风格,含镜像源)
nyai 是 MeowOS 的包管理 CLI(静态 MIPS32r2/musl,无外部依赖;tar.gz 用 gzip -dc | tar -x 管道,与 launcher 一致)。包名 <pkg> = app.json 的 package(= .mpk 文件名茎 = 目录名)。部署:MeowOS/opt/bin/nyai(卡,随 TF_CARD_LAYOUT 外围工具目录)。
调用:M2.12+ 固件 meow_card_path.sh 已将 MeowOS/opt/bin 前插 PATH,nyai 可直接调用;更早固件(M2.11 及以前,无 /etc/profile.d/meow_card_path.sh)SSH 需绝对路径[源:审计 2026-08-30]
/usr/data/mnt/sd_0/MeowOS/opt/bin/nyai 或 export PATH=/usr/data/mnt/sd_0/MeowOS/opt/bin:$PATH;
M2.12+ 固件该目录已由 meow_card_path.sh 前插,nyai 可直接调用。
命令:
| 命令 | 说明 |
|---|---|
nyai update |
读 sources.list 各源:本地读 <源>/Packages;http 用卡上 curl -fsSL <url>/Packages 缓存到 MeowOS/opt/nyai/cache/;无网正确提示、不硬失败 |
nyai install <包或文件.mpk> |
① 参数是 .mpk 路径→直接装;② 是包名→按缓存索引取 .mpk(本地文件或 http 拉取)装;自动解析 Depends(递归)、校验 SHA256+app.json、同名先备份 .bak_<pid>、数据 init(建 MeowData//{data,cache}) |
nyai remove <pkg> [--purge] |
卸载 MeowAPPs/(数据默认保留);--purge 连 MeowData/ 删;清理 .bak_ 残留 |
nyai list |
列出 MeowAPPs/* 已装应用(名称/版本/占用),跳过 fonts/logs/.bak |
nyai info <pkg> |
读 app.json 显示 name/version/exec/size/routes/services/depends/description |
nyai search [pattern] |
扫索引列出可装包(含版本/描述) |
nyai list-upgradable |
索引版本 > 已装版本的包 |
nyai index <dir> |
为 <dir> 生成 Packages 索引(读每个 .mpk 的 app.json 提取 version/description,算 SHA256/Size/Filename) |
nyai export <pkg> [out.mpk] |
打包 MeowAPPs//(不含 data/cache)→ <pkg>.mpk |
nyai scan [dir] / nyai upgrade <pkg> [file.mpk] |
列出 .mpk / 升级(保留数据) |
nyai --help |
帮助;退出码 0=成功 / 非0=错误 |
源配置 MeowOS/opt/nyai/sources.list(卡;无此文件则默认本地源 MeowOS/opt/repo):
# 每行一个源,支持多源与 # 注释
local /usr/data/mnt/sd_0/MeowOS/opt/repo # 本地目录
http http://10.0.0.1/nyai # http(s) 源(url 下挂 Packages)
deb [trusted=yes] /usr/data/mnt/sd_0/MeowOS/opt/repo # apt 风格别名
file:///usr/data/mnt/sd_0/MeowOS/opt/repo # file:// 等价本地
索引 Packages(nyai index <dir> 生成,行式、空行分隔记录):
Package: simtest # 包名 = .mpk 文件名茎 = app.json.package
Version: 1.2.3 # 来自该 .mpk 内 app.json
Filename: simtest.mpk
Size: 388 # 字节
SHA256: 3fea0b73… # 可选;有则 install 时校验
Depends: depapp # 可选,逗号/空格分隔,递归安装
Description: A simulated app
与 launcher 一致:install/export 复用 M2.12 meow_pkg_install/export 同套语义(tar.gz、gzip 管道、包名=文件名茎、同名先备份、校验 app.json、数据 init);exFAT 无符号链接,数据目录由 MeowOS 经 MEOW_DATA_DIR/CACHE_DIR 注入,CLI 只建目录不建链接。
3.7 音频通路与媒体键(2026-08-31 实测定案,覆盖 §5 旧推论;M2.90 起被 meow_audiod 收口,见末段)
- MeowOS 常驻模式(boot 直进,hiby_player 未运行)声卡空闲:真机实测
fuser /dev/snd/pcmC0D0p /dev/snd/controlC0无任何占用、pidof hiby_player不存在。MeowAPP 可直接 open→ M2.90 起改走hw:0,0出声meow_audiod独占音频守护:守护独占 hw:0,0,App 经 SDKmeow_audio_*客户端喂 PCM(见 MEOW_APP_SDK §5.9 /devdocs/plan/AUDIOD_DESIGN.md);独占+抢占(后开流赢,旧流收 PREEMPTED)、DAC 原生率跟随无 SRC、路由自动跟随插孔(meow_route 并入退役);App 直开仅MEOW_AUDIO_DIRECT=1调试用。媒体键路由最近活跃媒体会话不变。 - 会话覆盖模式(HibyOS 运行中热键唤起):SIGSTOP 冻结播放器,声卡仍被其持有,App 无声(§5 旧结论,仅适用该模式)。
- 物理媒体键路由:MeowOS 与 HibyOS 不同时运行,不存在「转发给 hiby」问题;MeowOS 界面下播放/上下曲 = 全局媒体控制,路由给最近活跃媒体会话(媒体会话已落地 M2.81:
MSG_META(0x72)/MSG_PLAYCTL(0x73) + meow_media 最近活跃源(active_seq,30s 超时回收)+ 通知中心媒体卡片 + Now Playing 大页;dev.player.ncm(原 os.meow.ncm,agent-221 改名)是首真实媒体源),无会话时「抓住」不动作。262 起物理播放/暂停键(164) 恒发 PLAYCTLcmd=6(toggle),由源侧按当前态翻转(修 SDK 本地态失真恒发「播放」)。 - audiod 加固批(M2.91-96,详见 MEOW_APP_SDK §5.9 / docs/MEOWOS_SPEC_REF §4):开机武装 hbc3000 FPGA(根治 PCM 启动内核死锁);长流护栏 + ALSA 几何收窄 period=1024×4(根治长流 ~12s 停摆);音量反转四路统一(CS43198 寄存器=衰减量,
dac=255-pct*255/100);dac_vol_mute=255 真静音(炸耳修复)。
3.8 版本号规则(2026-08-31 定案,对齐 Android)
app.json增加version_code(纯数字,单调递增,安装/升级比较的唯一依据);version仅作显示字符串(versionName 角色,可为1.2.3-beta等任意形态)。- 防降级比较只看
version_code:新版 code < 已装 → 拒装;相等 → 允许重装;code 缺失 → 拒装(提示缺 version_code)。 nyai index的Packages索引同步记录Version-Code。