- reader: app.c/shelf.c/reader.c 全套UI; 新增 reader_conf(reader.conf)/settings(App设置页)/reader_ipc(音量翻页IPC响应); app.json加volume_page - launcher: keys.c meow_volpage_*门 + util.c app_id→fd映射(HELLO记fd/断开清) + app_launcher.c 前台三处接线(进入sink/按键路径volpage_key/离开clear) → 前台声明volume_page的App音量键→MSG_KEY_PAGEUP/PAGEDOWN翻页(非阅读器前台音量照旧) - sdk: meow_select.c 底部面板28 + meow_ui_dialog.c 对话框按钮胶囊(h/2) + test_meow_ui_select.c修采样 - meowpanel: /api/net/policy·/api/power/policy 加防自锁+policy默认关两条护栏(panel.policy_enabled默认0) - 文档: MEOW_APP_SDK.md §12.6 + meowpanel README端点表 0warning;宿主测试全过;二进制留批量批次未入库
574 lines
62 KiB
Markdown
574 lines
62 KiB
Markdown
# MeowPanel 后端(`meowpanel.c`)
|
||
|
||
MeowOS(HiBy R3 Pro II 第三方系统,MIPS32 静态交叉编译)的**外置 Web 面板后端**:单进程、零第三方依赖的
|
||
C 静态二进制,内嵌 HTTP/JSON 服务器 + 前端静态托管 + 框架专项服务 IPC 客户端 + `settings.conf` 设置镜像。
|
||
|
||
- **定位**(`MEOWPANEL_DESIGN.md`):外置于 TF 卡 `MeowOS/opt/MeowPanel/` 的系统组件,由 `meow_svcd`
|
||
读 TF 卡 `MeowOS/services.d/meowpanel.conf` 启动、`meow_sysui` 监管。**不进固件**;无卡时系统核心
|
||
(launcher/sdk/设置/通知)绝不依赖它。
|
||
- **协议**:`devdocs/plan/MEOWPANEL_PROTOCOL.md`(HTTP/JSON 端点;本实现以「实现为准,协议/README 记录出入」)。
|
||
- **数据路径**(`SYSTEM_FRAMEWORK_SERVICES.md §2/§4`):读侧(`/api/{status,perf,system}`)经 `meow_svc_perf`
|
||
IPC(`MSG_PERF_QUERY→RESULT`)聚合,**绝不各自爬 /proc**(M2.73 起 perf 聚合含 `network` 段);动作侧(`/api/system/{reboot,shutdown,ota}`)经
|
||
`meow_svc_power` IPC(`MSG_POWER_ACTION→STATUS`),不再直连 `poweroff`/写 mtd5。两专项服务为**板载 svcd 托管**
|
||
(`trigger=boot`),拔卡保留,与无卡依赖最小化自洽。
|
||
- **⚠️ OTA 检查升级 ≠ 升级**:前端「检查升级」只读 `GET /api/system` 的 `ota{current,available,latest}`,**勿 `POST /api/system/ota`**(那是触发升级进升级模式)。
|
||
|
||
---
|
||
|
||
## 1. 编译
|
||
|
||
工具链:交叉编译器 `/home/Aska/Project/R3ProII/.tools/zig-mipsel-cc`(mipsel 32 位、musl 静态,可用)。
|
||
|
||
```bash
|
||
cd /home/Aska/Project/R3ProII/patch_v2/meowpanel
|
||
./build_meowpanel.sh # 交叉编译 → meowpanel(mipsel 静态,firmware-ready)
|
||
./build_meowpanel.sh host # 宿主编译 → meowpanel_host(本机 ld,仅供调试)
|
||
```
|
||
|
||
- **后端源码已按模块拆分**(`src/*.c` + 共享内部头 `src/mp.h`):`mp_server`(HTTP/路由/main) + `mp_util`/`mp_json` + `mp_store`(设置/主题) + `mp_ipc`(IPC 帧/单一收发) + 各 api_* 域 `mp_perf`/`mp_power`/`mp_audio`/`mp_net`/`mp_ams`/`mp_svcd`/`mp_files`/`mp_fonts`/`mp_apps`/`mp_logs`/`mp_notif`/`mp_auth`/`mp_key`/`mp_screen`。
|
||
`mp.h` 承载全部共享类型/常量/外部全局 + 跨模块函数原型;每个 `.c` 以 `#include "mp.h"` 起步。
|
||
拆分保持后端接口/行为**完全不变**(宿主 `-Wall -Wextra` 0 warning;重编产物与拆分前**逐字节一致**——同一 mock 环境全端点 diff 无差异)。
|
||
原单体 `meowpanel.c` 保留在根目录作参考,**不在构建源列表**(构建以 `src/*.c` 为准)。
|
||
- 两分支均以 `-Os -Wall -Wextra` 复核,**0 warning**(`-Werror` 亦通过)。
|
||
- 产出(本次构建实测):
|
||
- `meowpanel`:mipsel 静态,`ELF 32-bit LSB executable, MIPS, MIPS32 rel2, statically linked, stripped`,
|
||
体积约 **256 KB**(固件阈值内)。md5:`820b24541ed69f2e27e4a301aff10c98`(UTF-8 加固:`sA_str` 统一嵌 JSON 字符串做 UTF-8 消毒,非法/截断字节逐字节回退 `?`——amsd 曾把「流媒体客户端」截成 16B 腰斩「端」字导致浏览器拒收整页,面板侧防任一上游再产坏字节;`--selftest` 宿主回归;原始 IPC JSON(perf/power/net 含 ssid)先 `utf8_valid_n` 校验,坏字节降级不拖垮 /api/overview)。
|
||
**重防火墙注意**:运行中二进制先 `.new` 再 `mv`,切勿直接覆盖运行中的 `meowpanel`)。
|
||
- `meowpanel_host`:aarch64 动态链接(宿主编译,仅供调试),体积约 **148 KB**。
|
||
- 依赖:仅 libc + Linux 内核头(`linux/input.h`),零第三方库(设备无 python3/lua)。鉴权用**内联 SHA-256**(拷贝自
|
||
`meow_verify/sha256.c` 公有领域参考写法,前缀 `mp_sha256_*` 避免符号冲突;**未改 `meow_verify/` 目录**)。
|
||
阶段 C 实时画面用**自研最小 baseline JPEG 编码器** `mp_jpeg.c`(顺序 DCT + 4:2:0 + 标准 Huffman 表 Annex K;
|
||
无 libm、零依赖,仅 malloc/realloc),质量 70 下 480×720 帧约 41 KB。
|
||
**字体库导入/导出(用户 2026-08-31 定案)**:与 `.mpk` 同构、**放弃 zip,改走 busybox `gzip -dc | tar` 管道**
|
||
(设备必带 tar/gzip,零新增依赖、不引入 zlib/miniz/自研 zip,见 §5.11)。
|
||
**性能**:设备为 mipsel muslsf **无 FPU**,初版逐块 `double` 矩阵 DCT 编码 480×720 一帧约 **3.96 s**(掉帧到 5-6 s/帧的根因)。
|
||
已改为**整数定点 DCT + 整数 YCbCr**(FSCALE=13,BT.601 256 系数,纯整型乘加),
|
||
实测编码降至约 **0.19 s(≈20×)**,单请求端到端(本机 localhost)约 0.20 s、LAN 约 0.22-0.31 s,
|
||
质量/体积与 double 版 PSNR 等价(主机 480×720 高频测试图两者差 <0.1 dB)。
|
||
- 头文件引用:`-I patch_v2/meow_launcherd`(`meow_ipc.h` 帧格式 / `m_perf_*`/`m_power_*`/`POWER_*`,
|
||
**header 为权威源**,结构体字段以它为准)。
|
||
|
||
## 2. 部署到 TF 卡
|
||
|
||
```bash
|
||
# 1) 后端 + 前端(web/index.html + meowpanel.css + meowpanel.js)放在同一目录
|
||
sudo mkdir -p /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel
|
||
sudo cp patch_v2/meowpanel/meowpanel /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
|
||
sudo cp patch_v2/meowpanel/web/index.html /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
|
||
sudo cp patch_v2/meowpanel/web/meowpanel.css /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
|
||
sudo cp patch_v2/meowpanel/web/meowpanel.js /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
|
||
|
||
# 2) 服务配置(svcd 读 TF 卡 services.d 拉起)
|
||
sudo mkdir -p /usr/data/mnt/sd_0/MeowOS/services.d
|
||
sudo cp patch_v2/meowpanel/services.d/meowpanel.conf /usr/data/mnt/sd_0/MeowOS/services.d/
|
||
```
|
||
|
||
说明:
|
||
- 前端资源与后端**同目录**(后端 `GET /` → `index.html`,`GET /meowpanel.css`/`meowpanel.js` 同目录取)。
|
||
后端也支持 `GET /assets/<file>`(映射到同目录文件),前端当前用根相对路径。
|
||
- 服务启动后整卡由 `meow_svcd` 按 `trigger=boot` 拉起;`meow_sysui` 监管(restart/backoff/max_restart)。
|
||
- **无卡**:`MeowOS/services.d/` 不可读 → svcd 跳过本服务;系统核心照常。
|
||
|
||
## 3. 访问
|
||
|
||
浏览器(同局域网):
|
||
```
|
||
http://<host>.local:8080/
|
||
```
|
||
- mDNS 由板载 `meow_mdns` 应答 `<host>.local`。
|
||
- 端口默认 **8080**,可用 `-p PORT` / 环境变量 `MEOWPORT` / 与后端同目录 `meowpanel.conf`(`port=N`)覆盖。
|
||
(优先级:`-p` > `MEOWPORT` > `meowpanel.conf` > 默认 8080。)
|
||
- **访问级 PIN 鉴权(用户 2026-09-01 定案)**:访问面板即需登录(4 位 PIN,`panel.pin`,见 §5.9);未认证请求一律 401。
|
||
除 `/api/auth/{login,setup,logout,status}` 外全部 `/api/` 需有效会话(token 存面板进程内存,重启失效,3 天固定不滑动续期)。
|
||
- 上传/下载/管理类请求统一带 `X-MeowToken: <token>`(或 `Authorization: Bearer <token>` / `Cookie: meow_token=<token>`);
|
||
缺/错/过期 → 401 `UNAUTHORIZED`。**HTTP 明文(无 TLS),内网使用;请勿把 8080 暴露公网**。
|
||
|
||
---
|
||
|
||
## 4. 端点实现清单(对照 MEOWPANEL_PROTOCOL §2 端点总览)
|
||
|
||
### 已实现(返回 200/2xx 或语义错误码)
|
||
|
||
| 方法 | 路径 | 说明 | 实现 |
|
||
|---|---|---|---|
|
||
| GET | `/`、`/meowpanel.css`、`/meowpanel.js`、`/assets/<f>` | 静态托管 | ✅ |
|
||
| GET | `/api/screen.jpeg` | 实时画面:fb0 当前帧 → RGB565→JPEG(`?q=1-100` 质量,默认 70;`?delta=1` 开启增量帧,见 §5.10) | ✅(阶段 C) |
|
||
| POST | `/api/input/tap` | 触摸注入:`{x,y}`(480×720 原生坐标;write 进 goodix event1,本机实测可投递);本机触摸后 5s 内忽略注入 | ✅(阶段 C) |
|
||
| GET | `/api/auth/status` | 访问鉴权状态(auth_enabled/session/pin_set/legacy/locked/lock_remaining_ms) | ✅(公开) |
|
||
| POST | `/api/auth/setup` | 首次设置 4 位 PIN(仅未设 PIN 时;`{pin}` 4 位数字;已设→409) | ✅(公开;非法→422) |
|
||
| POST | `/api/auth/login` | PIN 登录发 token(`{pin}`;未设 PIN 时接受旧密码 `{password}` 迁移;错→401,连错 5 次→429 LOCKED) | ✅(公开) |
|
||
| POST | `/api/auth/logout` | 登出失效 token | ✅(公开;幂等) |
|
||
| GET | `/api/status` | 性能+系统(`meow_svc_perf` IPC subset=all 透传) | ✅ |
|
||
| GET | `/api/perf` | 仅性能(subset=perf 透传) | ✅ |
|
||
| GET | `/api/system` | perf IPC + boot/network/ota 局部补全(network 含蓝牙,无网口;`ota` 只读检查,勿误触升级) | ✅ |
|
||
| POST | `/api/system/reboot`、`/shutdown`、`/ota` | `meow_svc_power` IPC | ✅ |
|
||
| GET | `/api/settings` | 全量+元数据(items/count/groups) | ✅ |
|
||
| GET | `/api/settings/:key` | 单项 | ✅ |
|
||
| PUT | `/api/settings/:key` | 写单项(clamp/枚举校验) | ✅(越界/非法枚举→422) |
|
||
| POST | `/api/settings` | 批量写 | ✅ |
|
||
| POST | `/api/settings/restore` | 恢复默认 | ✅ |
|
||
| GET | `/api/theme` | 读主题 | ✅ |
|
||
| PUT | `/api/theme` | 写主题(style/theme/background 子集) | ✅(非法→422) |
|
||
| GET | `/api/files?path=` | 目录列表(白名单) | ✅(无卡→503) |
|
||
| GET | `/api/files/read?path=` | 读文本(<1MB;二进制→415) | ✅ |
|
||
| GET | `/api/files/download?path=` | 下载流 | ✅ |
|
||
| POST | `/api/files/upload` | multipart 上传 | ✅ |
|
||
| POST | `/api/files/mkdir` | 新建目录(已存在→409) | ✅ |
|
||
| POST | `/api/files/rename` | 重命名 | ✅ |
|
||
| DELETE | `/api/files?path=` | 删除(可 recursive=1;非空未 recursive→409) | ✅ |
|
||
| GET | `/api/fonts` | 字体库列表(扫描 `MeowOS/fonts/<family>/<weight>/` 三件套齐) | ✅(无卡→503) |
|
||
| POST | `/api/fonts/import` | 上传字体库 `.tar.gz`(multipart `file`,可带 `?force=1` 覆盖) | ✅(缺件/坏json/穿越→400,冲突→409,超限→413) |
|
||
| GET | `/api/fonts/export` | 导出 `<family>/<weight>/` 三件套为 `.tar.gz`(`?family=&weight=`) | ✅(缺件→404) |
|
||
| GET | `/api/audio` | 音频状态快照:meow_audiod IPC `MSG_AUDIO_QUERY→STATE` 透传(当前流/路由/音量/underrun + **`rms/peak/lvlseq` 电平 + `vol_max`**,见 §5.12) | ✅(audiod 不在→`available:false`,不报错) |
|
||
| POST | `/api/audio/route` | 手动切输出路由(**写级**,PIN 已护):`MSG_AUDIO_ROUTE`,仅接受 `{port: 0\|2\|3}`,**绝不接受 1/4/5**(4=SPDIF、5=audiod 内部强制重烧值);前端切换前确认弹窗(选错口会无声) | ✅(非法端口→422;audiod 不在→503) |
|
||
| POST | `/api/audio/volume` | 写音量(**写级**,PIN 已护):`MSG_AUDIO_VOLUME`,**clamp 到系统 vol_max**(读 settings.conf `audio.volume_max`,缺省 100;设备当前 50);audiod 仅 clamp 0-100 无 vol_max 防护,面板是把守方(防炸耳红线) | ✅(clamp 后写;audiod 不在→503) |
|
||
| GET | `/api/power` | 功耗管理:meow_powermgr IPC `MSG_PM_QUERY→RESULT`(battery/estimate/breakdown,confidence 原样透传) | ✅(powermgr 不在→503 `POWER_UNAVAILABLE`) |
|
||
| GET | `/api/ams` | App 运行表:`am list`(pkg/name/state/pid/launches/crashes/last_kind/last_code) | ✅(amsd 不在→`available:false`) |
|
||
| GET | `/api/ams/bg` | **后台活动列表**(M6.38,批④):`am list` 仅取 `state=="bg"`,逐进程 `/proc` 采样 `VmRSS`/`cpu_pct`(首采样/不可得→`null` 前端显 `—`,绝不编造);同源于 amsd FG/BG 元数据,杀/清走既有 `/api/ams/:pkg/kill` | ✅(只读,需 PIN 会话;amsd 不在→`available:false`) |
|
||
| GET | `/api/svcd` | 服务表:`meow_svcd status all`(name/state/pid/restart/backoff_ms/trigger/src/exit/exec) | ✅(svcd 不在→`available:false`) |
|
||
| GET | `/api/overview` | **首页聚合**(单线程后端只暴露聚合):后端顺序 fan-out perf/power+events/audio/net+union dns/ams+fg/svcd 六源合成一份 JSON;任一源失败→该子块 `available:false`,绝不假数据 | ✅(**读级,需 PIN 会话**) |
|
||
| GET | `/api/net/ifaces` | 接口列表详情(kind/ip/gw/rx/tx/rssi/rfkill/dns;netmgrd 0xE2+0xE0 合成) | ✅(只读,需 PIN 会话) |
|
||
| GET | `/api/net/policy` | 管控策略只读(0xEA SHOW;`enforce=dns_only` 如实)+ **`policy_write_enabled`**(guard③ 门禁状态)| ✅(只读,需 PIN 会话;上游坏字节→`available:false`) |
|
||
| POST | `/api/net/policy` | 写管控策略(`set_net`/`set_domain`/`del`):**guard① 防自锁**(`SELF_LOCK` 422)+ **guard③ 默认关**(`POLICY_DISABLED` 403)+ 二次确认 | ✅(**写级**,PIN 会话;自锁→422,门禁→403,IPC 权限→403 FORBIDDEN) |
|
||
| GET | `/api/power/policy` | 电源策略快照(profile 四档/阈值/`enacted`)+ **`policy_write_enabled`**(guard③;注入后 jv_parse 自校验) | ✅(只读,需 PIN 会话;坏字节→503) |
|
||
| POST | `/api/power/policy` | 写电源策略(**只写 settings.conf `power.*` 键**):**guard③ 默认关**(`POLICY_DISABLED` 403)+ 二次确认 | ✅(**写级**,PIN 会话;非法值→422,权限→403) |
|
||
| GET | `/api/power/events` | 电源异常事件(充放电/低电/亮灭屏/背光跳变;overview 摘要 top2 的来源) | ✅(只读,需 PIN 会话;powermgr 不在→503) |
|
||
| GET | `/api/power/history` | 功耗采样历史(30s 环形 4h,不落盘) | ✅(只读,需 PIN 会话;powermgr 不在→503) |
|
||
| GET | `/api/ams/:pkg` | 单 App incident 历史(`am history`,崩溃根因 detail;坏 detail→降级 `available:false`) | ✅(只读,需 PIN 会话) |
|
||
| POST | `/api/ams/:pkg/start` | 启动 App(`am start <pkg>`;**写级**,PIN 会话) | ✅(包名非法→400,launcher 离线→503) |
|
||
| POST | `/api/ams/:pkg/kill` | 结束 App(`am kill <pkg>`;**写级**,PIN 会话,前台前端二次确认) | ✅(包名非法→400,not registered→404,busy→409) |
|
||
| GET | `/api/apps` | 扫描 `MeowAPPs/` 读 `app.json` | ✅ |
|
||
| GET | `/api/apps/:pkg` | 应用详情 | ✅ |
|
||
| POST | `/api/apps/install` | 经 `nyai install`(multipart 或 path) | ✅ |
|
||
| GET | `/api/apps/:pkg/export` | `nyai export` 下载流 | ✅ |
|
||
| DELETE | `/api/apps/:pkg` | `nyai remove [--purge]` | ✅ |
|
||
| POST | `/api/key/:code` | 按键注入(tap/long) | ✅(物理键红线内) |
|
||
| POST | `/api/key` | 批量/顺序注入 | ✅ |
|
||
| GET | `/api/logs` | `MeowOS/logs` 文件列表(每文件含 `group` 来源分组) | ✅(无卡→503) |
|
||
| GET | `/api/logs/crash` | **崩溃汇总**:解析最新 `crash_<bootts>.log`(meow_crash 落盘格式)→ 每实体崩溃数 + 最近崩溃时间/信号 + dmesg 计数 | ✅(无卡→503;无 crash 日志→空汇总) |
|
||
| GET | `/api/logs/read?file=&lines=` | 读日志(上限 1000 行;`..`/绝对→403;敏感名→403) | ✅ |
|
||
| GET | `/api/notifications` | 通知列表(**优先 launcher IPC 内存活跃集** `source:"ipc"`;IPC 不可达回退 TF 转储日志 `source:"tf"`;见 §5.8) | ✅(无源→空态 `source:none`,不 503) |
|
||
| DELETE | `/api/notifications/:id` | 单条删除(**优先 IPC 撤下 launcher 内存**;IPC 不可达回退重写日志移除该 id) | ✅(不存在→404) |
|
||
| POST | `/api/notifications/dismiss_all`(别名 `/api/notifications/clear`) | 一键清空:**优先 IPC 联动 launcher 内存**,保留活动中的媒体/进度通知;IPC 不可达回退重写日志 | ✅ |
|
||
| GET | `/api/files/read?path=` | 读文本:预览上限 1MB,超限 `truncated:true`(不再 404);二进制→415 | ✅ |
|
||
|
||
### 返回 501 `NOT_IMPLEMENTED` 的端点(诚实标注)
|
||
|
||
| 端点 | 原因 |
|
||
|---|---|
|
||
| `/api/key/:code` 的 `press:"hold"` | `hold` 需常驻持续注入通道(阶段 D),后端未实现;`tap`/`long` 已实现。 |
|
||
| OTA 升级模式(`/api/system/ota` 带 `url`) | 升级为草案(MEOWPANEL_PROTOCOL §6 待定案);当前 `POST /api/system/ota` 空 body 触发 `meow_svc_power` 的 `POWER_UPGRADE`(写 mtd5 进升级模式),带 `url` 的下载+升级未实现。 |
|
||
|
||
> 通知端点已从 501 落地,且现为**实时**:优先经 launcher IPC `MSG_NOTIFY_QUERY(0x25)→QUERY_RESULT(0x27)` 取
|
||
> **内存活跃集**(`source:"ipc"`);IPC 不可达/超时回退读 TF 卡通知转储日志(`source:"tf"`,会话级归档);两者皆无
|
||
> `source:"none"`。撤/清(`DELETE` / `dismiss_all`)同样**优先走 IPC**联动 launcher 内存(launcher 撤下时自动归档留档)。
|
||
> 详见 §5.8。
|
||
|
||
> 前端对 `501` 显示「该功能后端尚未实现」(`ok:false,error.code=NOT_IMPLEMENTED`)。
|
||
|
||
---
|
||
|
||
## 5. 关键实现说明
|
||
|
||
### 5.1 HTTP 路由 / JSON 序列化
|
||
- **单线程 + poll 串行**(面板访问量极小)。`accept` → `read_request`(先读头到 `\r\n\r\n`,**在缓冲区内搜索**头边界,
|
||
处理「头+body 同段到达」,再按 `Content-Length` 精确读 body)→ `handle` → 写响应 → 关闭连接(`Connection: close`)。
|
||
- 读/写均有 `poll` 超时(读头部 5s、body 分段 5s,IPC 2s)。`SIGPIPE` 忽略。
|
||
- JSON 响应信封:成功 `{"ok":true,"data":<payload>}`;失败 `{"ok":false,"error":{"code","message"}}`
|
||
(MEOWPANEL_PROTOCOL §1.1/§1.2,HTTP 状态码映射照抄)。
|
||
- **无 JSON 库**:手写最小递归下降解析器(对象/数组/字符串转义/数字/bool/null),仅覆盖实际用到的收发。
|
||
- 下载/上传用流式(`multipart/form-data` 极简解析:按 boundary 切,取 `path`+`file` 字段)。
|
||
|
||
### 5.2 IPC 对接(meow_svc_perf / meow_svc_power)
|
||
- **帧**:复用 `meow_ipc.h`——16B 头(magic `0x4D4F5701`/version 1/type/req_id/len,全小端)+ 变长负载,
|
||
`MEOW_IPC_MAX=4096`。帧读写 `x_read_full`/`x_write_full` + `send_frame`/`recv_frame`(与 meow_svc_perf.c 对称)。
|
||
- **握手**:连接后先 `MSG_HELLO`(`app_id="os.meow.panel"`,系统面板身份,§5 二期拟名)→ 读 `MSG_ACK(code=0)`。
|
||
- **读侧**:`MSG_PERF_QUERY`(`m_perf_query_t.subset`)→ `MSG_PERF_RESULT`(`m_perf_result_t`,`code`/`json_len`/`json`),
|
||
**直接透传**服务端聚合 JSON(`/api/status`=全部、`/api/perf`=perf 子集、`/api/system`=全量取 uptime/storage/tfcard/system)。
|
||
读取防溢出:`json_len` 以 `sizeof(m_perf_result_t.json)-1` 与 `cap` 双钳位。
|
||
- **动作侧**:`MSG_POWER_ACTION`(`m_power_action_t.cmd=POWER_REBOOT/SHUTDOWN/UPGRADE`)→ `MSG_POWER_STATUS`(`code`/`msg`)。
|
||
收到 `MEOW_OK` → `{"accepted":true}`;否则按 `code`(PERM→403 / NOENT/IO→503 / 其余→500)返回错误信封。
|
||
- **失败语义**:socket 不存在/连接拒/超时/应答非 OK → 返回 500 `INTERNAL`(或 503 `NO_CARD` 当缺升级文件)
|
||
并带中文 message。**绝不静默失败、绝不降级直连 /proc 或 poweroff**(单一权威源)。
|
||
- 连接超时:非阻塞 connect + `poll(POLLOUT)` 2s,`SO_RCVTIMEO`/`SO_SNDTIMEO` 兜底。
|
||
|
||
### 5.3 设置镜像(读写板载 `settings.conf`)
|
||
- **不旁路**:只经 `settings_get_value`(`=` 切分 + key 去首尾空白精确匹配,防 `audio.volume` 误配 `audio.volume_max`,
|
||
照 `meow_ui_vol_read`/`meow_svc_power settings_get_int_trim` 同款解析)与 `settings_write_one`(`"key = value\n"`,
|
||
保留 `# meow-settings 1` 头、逐行重写、缺键追加;目录缺失自动 `mkdir`)。**不绕过此函数直接改文件**。
|
||
- **键表/默认值来源**:`patch_v2/meow_launcherd/meow_settings.c` 的 `g_defs[]`(**权威来源**,逐键一致),
|
||
含 type/def/min/max/enum_names/enum_labels;`GET /api/settings` 返回 `items/count/groups`(分组按键前缀,
|
||
如 `display.*`→显示、`font.*|ui.*`→字体主题…)。
|
||
- 类型:`int`→number(带 min/max/def 字符串)、`bool`→原生布尔、`enum`→`options(name/label)+default_name+def`、
|
||
`str`→string。**越界/非法枚举 → 422 `UNSUPPORTED`**。
|
||
- 语义 clamp:`display.brightness` 读/写都 clamp 到 `[display.brightness_min(默认1), 100]`(防 101 闪屏,
|
||
与 `meow_settings_brightness_clamped`/`clamp_brightness` 同口径);`boot.default` 写后同步 `/data/meow_boot_default`
|
||
(meow→0/hiby→1,同 `meow_settings_save`)。
|
||
- 注:后端为独立进程,无法调用 launcher 进程内的 `meow_settings_*`(那是其内存表),故**直接镜像同一份
|
||
`settings.conf`**,保持与实体设置页/CLI 同一存储契约(协议 §3.2 说「经 meow_settings_*」,此处以
|
||
「镜像同一文件 + 同款 parse/save 契约」落地,见 §8 出入记录)。
|
||
|
||
### 5.4 文件白名单
|
||
- 根 `/usr/data/mnt/sd_0`;白名单子目录 `MeowOS/Music/MeowData/MeowAPPs/book`(`allowed` 数组)。
|
||
- 路径经 `normalize_path` 规范化(去 `.`/`..`,拒绝逃逸出根)后 `path_in_whitelist` 校验(== 根或属于某一白名单
|
||
一级子目录)。**`..`/绝对路径逃逸 → 403 `FORBIDDEN`**;无卡 → 503 `NO_CARD`。
|
||
- 读取拒绝敏感名(`settings.conf`/`setting.bak`/`.env`/`passwd`/`shadow`/`meow.conf`/`user.ini`/`config.json`)。
|
||
- 列表/上传/下载/读文本(1MB+二进制→415)/mkdir(已存在→409)/rename(new_name 不含 `/`)/delete(递归,非空未 recursive→409)。
|
||
|
||
### 5.5 nyai 对接
|
||
- 包管理器 CLI 绝对路径 `/usr/data/mnt/sd_0/MeowOS/opt/bin/nyai`(M2.12+ 固件经 `meow_card_path.sh` 前插 PATH,可直接调;
|
||
本后端用绝对路径,兼容更早固件)。包名 = `app.json` 的 `package` = `.mpk` 文件名茎。
|
||
- `install`:multipart 上传 `.mpk` 落临时文件(`MeowOS/tmp/`)或 JSON `{path}` → `nyai install <file>`;用 `popen` 捕获输出+退出码,
|
||
非 0 → 422。`export`:`nyai export <pkg> <tmp>` → 以 `Content-Disposition: attachment` 流式下发。`remove`:`nyai remove <pkg> [--purge]`。
|
||
- `GET /api/apps` / `:pkg` 详情:直接扫描 `MeowAPPs/<dir>/app.json`(手写 JSON 解析提取 name/version/exec/framework/immersive/fullscreen),
|
||
不依赖 nyai(nyai 未部署时列表/详情仍可用)。解析失败项 `invalid:true`。
|
||
- `nyai` 未部署 → install/export/remove 返回 500(带 nyai 输出)。
|
||
|
||
### 5.6 按键注入通道
|
||
- **复用现有先例**:`patch_v2/uinput_tap/uinput_tap.c` 的 `write`-到-evdev 注入机制。launcher 实际轮询
|
||
`meow_launcher_internal.h`:`/dev/input/event0`(md-gpio-keys: POWER/PREVIOUS)、`/dev/input/event2`
|
||
(jz adc keyboard: VOL/PLAY/NEXT)。后端 open `O_WRONLY` 写 `EV_KEY press/release + SYN_REPORT` 即可到达 launcher 按键处理。
|
||
- **物理键红线**(APPS.md / 任务约束):只注入音量(114/115)/电源(116)/播放(164)/上下曲(163/165);
|
||
**禁止注入 返回(158)/主页(172)/确认(28)/退出(96)** → 403 `FORBIDDEN`。
|
||
- `tap`(默认,抬起间隔 80ms)、`long`(按住 ms,缺省 3000);`hold` → 501。
|
||
- 局限:若 `/dev/input/event0/event2` 在设备上不可写(权限/路径差异),注入失败 → 500;注入为**即时性注入**,
|
||
不在 launcher 内维护长按状态机(与实体按键的 `longpressed` 语义近似,见 README 已知边界)。
|
||
|
||
### 5.7 日志
|
||
- `GET /api/logs`:`MeowOS/logs` 下常规文件列表(跳过敏感名)。
|
||
- `GET /api/logs/read?file=&lines=`:`file` 必须相对 `MeowOS/logs/`,**禁 `..`/绝对路径 → 403**,敏感名 → 403;
|
||
缺省 `lines=200`,上限 `1000`;返回 `{file,lines[],total,truncated}`。
|
||
|
||
### 5.8 通知(数据源 = 优先 launcher 内存活跃集,回退 TF 归档)
|
||
- **数据源优先级**:① launcher IPC 内存活跃集(实时,`source:"ipc"`)→② TF 转储文件(`source:"tf"`)→③ 空态(`source:"none"`)。
|
||
- ① **launcher IPC**:后端连 `/usr/data/meow_ipc.sock`,`MSG_HELLO` 握手(`app_id=os.meow.panel`)→ `MSG_NOTIFY_QUERY(0x25)`
|
||
(空 payload=全部 app)→ 期望 `MSG_NOTIFY_QUERY_RESULT(0x27)`(`m_notify_query_result_t`,JSON 数组文本,字段同 TF 归档,
|
||
`ts` 为 epoch **毫秒**)。launcher 在**主循环单写者**序列化(绝不把通知存储读到 IPC 线程)。后端按 `ms` 升序重排
|
||
(oldest..newest,与 TF 一致),emit 时倒序= newest first;`ts` 除以 1000 出秒(与 TF 同口径)。
|
||
- ② **TF 转储文件**(回退):`<卡根>/MeowOS/notifications/notifications.log`(launcher `meow_notif_dump_tf` 一行一条 CSV,
|
||
字段 `timestamp,id,app_id,app_icon,category,urgency,progress,title,body,target_activity,ongoing,media,dismissed`;
|
||
`meow_notif_clear_tf` 开机清空 → 会话级归档)。后端逐行流式读取(环形保留最新 `NOTIF_PREVIEW_MAX=200` 条,不全量进内存)。
|
||
注意 TF 是**归档快照**(归档时 `dismissed` 尚未置 1,故该列恒 ≈0 不可靠,且非活跃快照)。
|
||
- **`GET /api/notifications`**:返回 `{count, items:[...], notifications:[...], source}`;`items`= `notifications` 别名
|
||
(兼容前端读 `.notifications` 与任务约定空态 `items:[]`)。字段含 `id/app_id/app_name/app_icon/category/summary/body/
|
||
urgency/ts/progress/ongoing/media/target_activity/dismissed`。`progress<0`→`null`;`app_name` 对 `system`→`系统`,其余用 `app_id` 兜底。
|
||
无源(无卡/文件不存在)→ `{count:0,items:[],notifications:[],source:"none"}`(**不 503、不伪造数据**)。
|
||
- **`DELETE /api/notifications/:id`**:**优先 IPC** `MSG_NOTIFY_DISMISS(0x22)`(4B id → `MSG_ACK`;launcher 撤下时自动归档留档);
|
||
IPC 不可达/未受理 → 回退从日志移除该行(写临时 + rename)。IPC 未受理且日志也无该 id → 404。
|
||
- **`POST /api/notifications/dismiss_all`**:**优先 IPC** `MSG_NOTIFY_DISMISS_ALL(0x26)`(→ `MSG_ACK.status`=清除数;
|
||
launcher 保留 ongoing/media/progress 红线项);IPC 不可达 → 回退重写日志(`ongoing||media||progress>=0||category=="media"` 保留)。
|
||
- **实时性**:IPC 路径直接反映 launcher **内存活跃集**——新通知(含未到期/未归档)**立即**出现在面板(实测:post 一条
|
||
通知后面板同时刻即见,`source:"ipc"`);撤/清经 IPC 联动 launcher 内存(实测:面板 DELETE 后 launcher 活跃集即无该 id)。
|
||
TF 路径仅为 launcher 不可达时的诚实降级(会显示自动过期的历史通知,直到 clear)。
|
||
- **并发**:IPC 可用时面板不写 TF(撤清由 launcher 自行归档),避免与 launcher `O_APPEND` 竞写;仅 IPC 回退到「写临时 + rename」。
|
||
|
||
### 5.9 访问级 PIN 鉴权(panel.pin;用户定案 2026-09-01)
|
||
- **模型(覆盖 282 报告「默认无鉴权」前提)**:**访问面板即需登录**。未认证请求一律 401(静态登录页自身 + css/js 放行——
|
||
`serve_static` 先于 `require_auth`);未设 PIN 前无法建立会话,故**读端点也全拦**(登录/设置流程除外),写端点自然一律拒绝。
|
||
- **设置键**:`panel.pin`(str,存 `salt:hex` = sha256(salt||pin) 带盐哈希,绝不存明文),写板载 `settings.conf`(与实体设置页/CLI
|
||
同一存储契约,面板独立进程直接镜像文件);**与未来 MeowOS 锁屏 PIN 同源**:设置键 `system.pin` 预留(本后端只读面板键,见 `g_sets` 注记)。
|
||
`panel.pin` 的 `GET /api/settings` 恒回 `"value":"" + "set":<bool>`,**绝不回传哈希/明文**。
|
||
- **PIN**:4 位数字(`pin_validate`);校验时重算 sha256(salt||pin) 并恒定比较(`memcmp`)。
|
||
- **门禁**:`require_auth` 在路由分发前执行——除 `/api/auth/{login,setup,logout,status}` 外全部 `/api/` 需有效会话;
|
||
缺/错/过期 → 401 `UNAUTHORIZED`。危险端点(reboot/shutdown/ota、key 注入、settings 写等)一律纳入会话校验(不再有「未开启」旁路)。
|
||
- **会话 token**:`/dev/urandom` 16B → **32 hex(128bit)**,**只存面板进程内存**(`g_sess[]`,`AUTH_MAX_SESS`=8,满淘汰最早过期),
|
||
**无卡上持久化**——设备重启自然清(会话即失效)。有效期 **3 天**(`AUTH_TOKEN_TTL`,**固定不滑动续期**,逐 token 校验到期戳);
|
||
多会话并存。`logout` 失效当前 token。
|
||
- **锁定(防爆破)**:连续 `PIN_MAX_FAILS=5` 次错 PIN → 锁 `PIN_LOCK_SEC=300`s(`429 LOCKED`,响应附 `lock_remaining_ms`;
|
||
`status` 透出 `locked/lock_remaining_ms`);内存态,重启清。
|
||
- **旧密码机制兼容迁移**(`panel.auth_enabled`/`panel.auth_pass` 保留):旧密码存在且未设 PIN 时,视为「已设访问屏障」的同等物——
|
||
登录可用旧密码(`{"password":…}`),`status` 透出 `legacy:true` 并提示改设 4 位 PIN;**一旦设置 `panel.pin`,新代码以 PIN 为准**
|
||
(登录只认 PIN,旧密码不再作为凭据)。`PUT /api/settings/panel.auth_pass` 仍可写(引导迁移),`panel.auth_enabled` 已不作门禁(保留兼容)。
|
||
- **前端**:api 客户端统一带 `X-MeowToken`(token 存 `localStorage`);收到 401 → 自动切登录页;未设 PIN → 首次设置流程
|
||
(`#login` overlay 双态:设置=输入+确认,登录=单输入);429 `LOCKED` → 显示锁定提示。设置页「面板鉴权」卡片沿用旧密码项。
|
||
- **静态屏蔽**:`serve_static` 额外拒绝后端可执行文件(403),防可执行被 HTTP 读取。
|
||
|
||
### 5.10 实时画面(`/api/screen.jpeg`,含增量帧 `?delta=1`)+ 触摸注入(`/api/input/tap`)— 阶段 C
|
||
- **实时画面(整帧,默认)**:`GET /api/screen.jpeg` 读 `/dev/fb0` **当前显示帧**(`FBIOGET_VSCREENINFO` 取 `yoffset`/`xres`/`yres`/`bpp`,
|
||
`FBIOGET_FSCREENINFO` 取 `line_length`,`pread` 活动缓冲,**只读,不影响渲染**;16bpp RGB565)→
|
||
`mp_jpeg_encode_rgb565()` 编码为内存 JPEG(自研最小 baseline 编码器,见 §1),返回 `Content-Type: image/jpeg`。
|
||
`?q=1-100` 控制质量(默认 70)。恢复 `line_length != xres*2`(行尾填充)时先压缩为紧凑 RGB565。
|
||
- **增量帧(`?delta=1`,阶段 C 增强)**:服务端**缓存上一帧紧凑 RGB565**(单线程 accept 循环跨请求持有),按 **8×8 块**逐行
|
||
`memcmp` 比对(1 块=8×8 像素×2B),只编码**变化块的外接矩形子图**(8 对齐、钳到屏内)为独立 JPEG,并以**响应头**回传偏移/几何
|
||
(`X-Meow-Delta:1`/`X-Meow-Full:0|1`/`X-Meow-W/H`(整屏)/`X-Meow-Sub-X/Y/W/H`(子图原点与尺寸)/`X-Meow-Changed`/`X-Meow-Blocks`)。
|
||
- 冷启动/几何变化/变化块覆盖全屏 → 发**整帧**(`X-Meow-Full:1`);帧无变化 → `Content-Length:0` + `X-Meow-Changed:0`(前端跳过叠加)。
|
||
- 体积:近静态帧(只刷新状态栏时钟等)增量子图可低至数百 B(实测 6 变化块=**804 B**,对比整帧 ~27 KB @q70)。
|
||
- **不破坏默认**:未带 `?delta=1` 仍为整帧 `image/jpeg`(零回归)。
|
||
- 前端「屏幕」页新增「增量传输(省流量)」开关:开启后走 `?delta=1`,用 `<canvas>` 叠加(整帧播种/替换,子图 `drawImage(x,y)`);
|
||
关闭走原 `<img>`+objectURL 整帧。默认关。
|
||
- **触摸注入**:`POST /api/input/tap`,body `{x,y}`(0..479 × 0..719 原生像素坐标,前端按 480×720 映射,后端再 clamp)。
|
||
**机制 = write 进 goodix evdev(`/dev/input/event1`)**——**本机实测 write 注入可投递**(见 §7 修正历史结论),
|
||
事件序列与真实 goodix 一致:先 `BTN_TOUCH=1`(按下边沿)**后** `ABS_MT_*`/`ABS_X/Y`(坐标),`SYN` → 按住 100ms →
|
||
`BTN_TOUCH=0` + `ABS_MT_TRACKING_ID=-1` + `SYN`。**关键**:launcher/`meow_touch` 在按下边沿会**重置坐标 -1**,
|
||
只在 down 之后到来的首个有效 ABS 才锁原点(goodix 时序免疫)——故坐标必须**在 BTN_TOUCH=1 之后**,否则原点永不
|
||
锁定 → 抬手被判无效 tap(只高亮不启动)。注入前不写 uinput(本设备厂商内核 write 进 goodix 即可投递,逐 UB 少)。
|
||
- **仲裁(本机优先打断)**:注入前 `touch_last_local_ms()` 排空 event1,取「最近一次本机真实触摸」的单调毫秒时间
|
||
(剔除本进程自注入事件:事件时间 ≤ 上次注入时刻+200ms),若 `now-last < 5000` → **忽略注入**,返回
|
||
`{"dispatched":false,"reason":"local_touch_active","cooldown_ms":N}`;否则注入并返回 `{"dispatched":true,"x","y"}`。
|
||
MIPS 的 `struct input_event` 无 `time` 成员(ABI),按 launcher 同款读裸 16 字节取偏移 0/4 的 `tv_sec/tv_usec`(单调时钟)。
|
||
- **前端「屏幕」页**(`web/#/screen`,导航「屏幕」):`<img>` 轮询 `GET /api/screen.jpeg?t=…&q=70`(`fetch`→blob→objectURL,
|
||
防重叠 + 旧 URL 回收;后端不可达提示「后端未连接」);默认 **1fps**,滑条 **0.2–2fps**(存 `screen.fps`,页面隐藏/切走即停刷,
|
||
`visibilitychange` 回前台续刷);点击画面按 480×720 比例换算坐标 → `POST /api/input/tap`,`dispatched:false` 时 toast「本机触摸中」。
|
||
|
||
### 5.11 字体库 tar.gz 导入 / 导出(`/api/fonts*`,新增)
|
||
- **定位**(MEOWOS_SPEC_REF §5.7.6 / FONT_SYSTEM.md,**2026-08-31 用户定案:放弃 zip,与 .mpk 同构走 tar.gz**):
|
||
系统字库按 `MeowOS/fonts/<family>/<weight>/` 三件套存放(schema 见 SPEC_REF §5.2:family/weight/name/
|
||
glyph_count/charset/tier/size_list/bit_depth/sha256_png/sha256_idx…)。**设备 busybox tar/gzip 必定存在**,
|
||
后端全部走 `gzip -dc | tar` 管道(nyai .mpk 同款),**零新增依赖、不引入 zlib/miniz/自研 zip**。
|
||
- **导入 `POST /api/fonts/import`**(multipart `file`=<.tar.gz>,上限 **`MAX_FONT_UPLOAD`=32MB**,可带 `?force=1`):
|
||
1. multipart `file` 落临时 `<TF_CARD>/tmp/mp_font_...tar.gz`(`multipart_extract_file`,二进制安全 `mem_find`)。
|
||
2. **成员预校验**:`gzip -dc '<tmp>' | tar -t`,每成员经 `sanitize_member`(去前导 `/`/`./`,拒 `..` 组件与
|
||
shell 危险字符)且**恰为三件套平铺**(多出条目/子目录/绝对/`..` → 400)。
|
||
3. **解压到临时目录**:`gzip -dc | tar -x -C stg` → opendir/lstat 锁:**恰为 3 个普通文件**
|
||
(拒软链/目录/设备;总大小 ≤ `FONT_EXTRACT_MAX`=32MB 防 gzip 炸弹)→ 缺件/坏 json 400。
|
||
4. `font.json` 手写 JSON 解析 → `family`(缺省回退 `name`)/`weight`(缺省 `regular`),`safe_dir_segment`
|
||
消毒(拒 `/`、`\`、控制符、`.`/`..`)→ 非法 422。
|
||
5. **覆盖冲突**:目标目录任一文件已存在且非 `force=1` → **409 `CONFLICT`**(前端 confirm 后带 `force=1` 重试)。
|
||
6. **sha256 仅 WARN**(对齐 font.json 约定):流式重算 `sha256_png`/`sha256_idx` 与声明比对,不符写 `warning`
|
||
字段、仍导入成功(**不硬失败**);响应含 `family`/`weight`/`imported`/`hint`(提示进设备「设置→字体」页
|
||
列表自动重扫生效——**后端不触发 launcher 重扫**,扫描时机=启动/进设置页,勿加热路径)。
|
||
7. `mkdir`(系统调用,**不用 `system()`**,防注入)→ `rename` 三件套到目标目录(原子替换)。
|
||
- **导出 `GET /api/fonts/export?family=&weight=`**:校验 family/weight 段 → 目标目录三件套须齐(缺件 404)→
|
||
`tar -cf - -C <font_dir> font.png font.idx font.json | gzip -c > <tmp>` 落临时文件(求 `Content-Length`)→
|
||
`Content-Disposition: attachment; filename="<family>-<weight>.font.tar.gz"` + `application/gzip` → 流式下发。
|
||
- **列表 `GET /api/fonts`**:扫描 `MeowOS/fonts/<family>/<weight>/`,三件套齐才列出,读 `font.json` 取
|
||
name/tier/glyph_count(容错)。无卡 → 503。
|
||
- **往返验证**(宿主):Python tarfile 生成的平铺三件套 `.tar.gz` 导入→导出→逐字节比对源文件一致;本后端导出
|
||
再导入→逐字节一致;GNU tar/`tarfile` 均能读;目录穿越 `../`/绝对路径/软链成员/缺件/坏 font.json/多出条目/
|
||
子目录包裹/非 gzip——全部拒绝;打包文件名 `<family>-<weight>.font.tar.gz`。见 §7 宿主测试备注。
|
||
|
||
### 5.12 音频状态(`/api/audio`,新增,AUDIOD_DESIGN §8 面板对接)
|
||
- **数据源**:`meow_audiod` 独占音频守护(板载进固件,svcd 托管;HibyOS 模式按门控不启动)。
|
||
后端连 `/usr/data/meow_audio.sock`(可被 `--sock-audio` / env `MEOW_AUDIO_SOCK` 覆盖,宿主/qemu 测试用,
|
||
与 audiod CLI 同 env),发 `MSG_AUDIO_QUERY`(0x00C7) 空负载,收 `MSG_AUDIO_STATE`(0x00C8) 文本帧
|
||
(`active=.. app=.. sid=.. rate=.. ch=.. fmt=.. written=.. played=.. underrun=.. paused=.. route_mode=.. port=.. vol=..`,
|
||
空格分隔 k=v、NUL 结尾)。**audiod 无 MSG_HELLO 握手**(见 meow_audio.c CLI),与 perf/power/launcher 路径不同。
|
||
- **响应**:解析 k=v 为 JSON——`{available:true, active, app, sid, rate, ch, fmt, written_frames, played_frames,
|
||
underrun, paused, route_mode("auto"/"manual"), port, vol, **rms, peak, lvlseq**}`;`port` 为 amixer numid=9 路由口
|
||
(0=空闲 1=lineout 2=3.5 3=4.4 4=Type-C SPDIF;宿主无 amixer 时为 -1)。
|
||
- **电平(追加键,ABI/协议兼容)**:`rms`/`peak` = deci-dB(`-990`=‑inf;如 `-139`=‑13.9dBFS),`lvlseq` = 快照代际
|
||
(每次结算/开流复位递增,前端可用其判「新刻度」驱动 sparkline)。仅追加键,不破坏既有字段。
|
||
- **audiod 不在**(socket 缺失/连接拒/超时/非预期应答)→ HTTP 200 `{"available":false}`,**不报错**(面板音频页显示
|
||
「audiod 不可用」)。只读端点;不放测试音(真机乱响风险,tone 留给 CLI `meow_audio tone`)。
|
||
- **前端**:「音频」页(`web/#/audio`)5s 轮询展示——当前流(app/rate/ch/fmt/已写帧/已播放帧/underrun/暂停态)+
|
||
路由口 + 音量 + audiod 可用性;切页即停轮询。
|
||
|
||
### 5.13 字体管理页(前端 `web/#/fonts`,新增)
|
||
- 后端 `/api/fonts*` 三端点(§5.11,agent-243 落地)的专属页面:表格列出 family/weight/name/tier/glyph_count,
|
||
行内「导出」(busy 防连点「打包中…」)+ 顶部「上传字体库 .tar.gz」(导入中禁用按钮=进度态)+「刷新」。
|
||
- 上传遇 **409 CONFLICT**(同 family/weight 已存在)→ 弹确认框,确认后带 `?force=1` 重试;成功 toast 带
|
||
`family/weight` 与 `warning`(sha256 不符仅提示)/`hint`。
|
||
- 设置页原「字体库」卡保留(同一后端,互不影响)。
|
||
|
||
### 5.14 批1-A 新增只读端点(供批1-B 前端消费;JSON 形状)
|
||
> 本批只做**后端端点 + 最小前端支撑(登录)**,首页/电源/进程页归批1-B。前端调用统一带 PIN 会话 token(见 §5.9)。
|
||
|
||
- **`GET /api/overview`**(首页生命线,单线程后端只暴露聚合):
|
||
```json
|
||
{ "ts": 1788268891,
|
||
"perf": { "...": "meow_svc_perf 聚合 JSON(cpu/memory/storage/tfcard/temp/system/network,透传)" },
|
||
"power": { "mode":"meow","battery":{"state","pct","mv","health","technology","temp_c10","charger_temp_c10","cycle_count","nominal_mah_note"},
|
||
"estimate":{"est_mw","confidence":"estimated|derived|measured|none","calibrated","degraded"},
|
||
"breakdown":{"baseline_mw","cpu_mw","cpu_util_pct","backlight_mw","panel_mw","brightness_pct","wifi_mw","wifi_state","bt_mw","bt_state","audio_mw","audio_state","degraded"},
|
||
"policy": null },
|
||
"audio": { "available": true, "active","app","sid","rate","ch","fmt","written_frames","played_frames","underrun","paused",
|
||
"route_mode","port","vol","rms","peak","lvlseq" },
|
||
"net": { "...": "netmgrd 概览 JSON(mode/ifaces[]/wifi/bt/rfkill/hostname_local,透传)" },
|
||
"ams": { "available": true, "count": 3, "apps":[ {"pkg","name","state":"fg|bg|stop","pid","launches","crashes","last_kind":"clean|exit|sig","last_code"} ] },
|
||
"svcd": { "available": true, "count": 3, "services":[ {"name","state":"running|stopped|stopping|disabled|failed","pid","restart","restart_str","backoff_ms","trigger","src":"board|tf","exit","exit_time","exec"} ] }
|
||
}
|
||
```
|
||
- **任一源失败 → 该子块 `{"available":false}`**(`perf`/`power`/`net` 失败也是 `{"available":false}`,绝不假数据);
|
||
`audio`/`ams`/`svcd` 成功时自带 `available:true`。`perf`/`power`/`net` 成功时为对应源**原始 JSON**(无 `available` 字段)。
|
||
- **`GET /api/power`**:即 overview 的 `power` 子块(可 503 `POWER_UNAVAILABLE`,非 `available:false`)。
|
||
- **`GET /api/ams`** / **`GET /api/svcd`**:即 overview 的 `ams`/`svcd` 子块;源不在 → `{"available":false}`。
|
||
- **`GET /api/audio`**:overview 的 `audio` 子块 + rms/peak/lvlseq 消费说明见 §5.12。
|
||
|
||
### 5.15 批2:崩溃/日志中心 + 音频写 + 后端补缺口(PANEL_ENHANCE_PLAN 批2)
|
||
- **崩溃/日志中心(升级 `#/logs`)**:
|
||
- **`GET /api/logs/crash`**:读最新 `crash_<bootts>.log`(meow_crash 落盘格式:`<epoch> <uptime> <svc|dmesg> <detail>`;
|
||
svc 的 `detail=<name> exit=<code>`/`<name> signal=<sig>`)。聚合出 `{file,count,dmesg_count,dmesg_tail,entities:[
|
||
{name,source:"svc",count,last_epoch,last_kind,last_code,last_time,last_detail}]}`;只读、无副作用。
|
||
读取有界 `CRASH_TAIL_MAX`=256KB、实体 `CRASH_ENT_MAX`=64、dmesg 保留最近 `CRASH_DMESG_TAIL`=5 行(行数上限保护)。
|
||
- **`/api/logs` 文件列表**每项带 `group`(`meow_*→system`/`crash_*→crash`/`ams_*→ams`/`audio_monitor_*→audio_monitor`/
|
||
`net_*→net`/`power_*→power`/其余→`app`),供前端按来源分组 + 前缀过滤;`group` 为追加键,向后兼容。
|
||
- **崩溃尾巴前置**:崩溃汇总实体行「stdout 尾部」按钮复用批1-B 读取路径(`/api/files?path=/MeowOS/logs/<name>/`
|
||
找最新 `stdout_*.log` → `/api/logs/read?file=<name>/<file>&lines=200` 读尾部)。
|
||
- **音频写(写级,PIN 已护;前端切换/调高前确认弹窗)**:
|
||
- **`POST /api/audio/route`**:`body {port}`。**仅 0=auto/2=3.5/3=4.4**,其余(含 1=lineout/4=SPDIF、5=audiod 内部
|
||
强制重烧 FPGA 值)→ 422。audiod 对非法 port 也回 MEOW_OK(内部仅记录不应用),故面板在**发出前**自行校验。
|
||
应答 `{accepted,port}`;audiod 不在 → 503 `AUDIO_UNAVAILABLE`。
|
||
- **`POST /api/audio/volume`**:`body {value}`。**clamp 到系统 vol_max**(`settings.audio.volume_max`,缺省
|
||
`PANEL_VOL_MAX_DEFAULT`=100;设备当前 50)。audiod 内部仅 clamp 0-100、无 vol_max 防护——面板是唯一把守方
|
||
(防炸耳红线:平方锥度 `dac=(100-pct)²×192/10000`,接近 100 极响)。应答 `{accepted,value(clamped),applied,vol_max}`。
|
||
- **`/api/audio` 补 `vol_max`**(读 settings `audio.volume_max`),前端滑条上限同步 + 「平方锥度非线性」标注 +
|
||
「当前值只读显示锥度后百分比」。
|
||
- **后端补缺口**:
|
||
- **`/api/system` 补 `reboot_reason`/`upgrade_status`**:svc_power 读级查询 `POWER_REBOOT_REASON(8)`/
|
||
`POWER_UPGRADE_STATUS(9)`(`st.msg` 分别为 `coldboot=reboot|shutdown|unknown`、`upgrade=yes|no`);查询失败回空串不谎报。
|
||
前端批1-B 已留位(读 `d.reboot_reason`/`d.upgrade_status`),此处点亮。
|
||
- **`/api/overview` net 块并入 DNS 统计**:netmgrd 状态 JSON 剥外层括号拼入 `{"net":{…,"dns":{…}}}`(复用 `/api/net/dns`
|
||
同款统计);DNS 不可用则省略 `dns` 键仅保留网络状态,**绝不假数据**。首页 netCard 改读 `net.dns`,去掉单独 `/api/net/dns`
|
||
(首页严格单请求)。
|
||
|
||
### 5.16 批 P1 写端点(power/net/ams 写)+ 三条护栏(MEOWPANEL_ENHANCE_PLAN P1)
|
||
> 三个写端点全部落在 `POST` 分支 + `require_auth` 之后(无一漏到无鉴权路径);响应**如实回错误码**,绝不掩盖。
|
||
|
||
- **三条护栏**:
|
||
1. **guard① 防自锁**(仅 `/api/net/policy`):对系统关键网络/服务组件禁设「阻断」(`set_net`+`arg=block`、
|
||
或任何 `set_domain` 域名限制)。命中即 `422 {"code":"SELF_LOCK"}`。自锁实体(取 `svc:|app:|proc:` 后裸名),
|
||
`net_locked_entity()` 精确比对 `dropbear/wpa_supplicant/udhcpc/netmgrd/svcd/sysui/init`(netmgrd 加载时也剔除,面板侧抢先硬拒)。
|
||
「放行 / 删除规则」不阻断(恢复连通)仍放行。
|
||
2. **guard② PIN 会话鉴权**:复用全局 `require_auth`(§5.9);无会话 → `401 UNAUTHORIZED`。
|
||
3. **guard③ 策略写默认关**:`panel.policy_enabled`(新设置项,`settings.conf`「面板」组,默认 `0`)。
|
||
`0`(缺省)时 `POST /api/net/policy`、`POST /api/power/policy` 在解析 body **前**即拒 `403 POLICY_DISABLED`
|
||
(只读 GET 不受此限);置 `1`(设置页 / 电源·网络策略卡的「开启策略写」)放行。与 powermgr `enacted` **独立**。
|
||
|
||
- **`POST /api/net/policy`**:`{op:"set_net|set_domain|del", entity:"app:…|svc:…|proc:…", arg:"…"}`(`del` 免 `arg`)。
|
||
成功 `{"accepted":true,"message":…}`;权限→`403 FORBIDDEN`、非法→`422 UNSUPPORTED`、自锁→`422 SELF_LOCK`、门禁→`403 POLICY_DISABLED`、
|
||
IPC 不可达→`503 NET_UNAVAILABLE`。`enforce=dns_only` 如实显示「仅 DNS 层拒绝」,不谎称断连。
|
||
- **`GET /api/net/policy`**:`net_policy_parse` 输出 `{"available":true,"policy_write_enabled":<bool>,"enforce","log_level","rules":[…]}`;
|
||
坏字节(实体名/域名)降级 `{"available":false}`。前端据此渲染「默认关」横幅 + 「开启策略写」(`enablePolicyWrite`)。
|
||
- **`POST /api/power/policy`**:`{item:"profile|low-pct|crit-pct|bri-cap|scr-cap", value:"…"}`,**只写 settings.conf `power.*` 键**;
|
||
成功后透传 powermgr 应答。非法值→`422`(服务端 `ok:false` 按 `why` 映射)、权限→`403`、门禁→`403 POLICY_DISABLED`、
|
||
IPC 不可达→`503 POWER_UNAVAILABLE`。
|
||
- **`GET /api/power/policy`**:透传 powermgr 策略快照(含 `enacted`)后经 `power_policy_append_flag()` **追加顶层 `policy_write_enabled`**
|
||
(剥尾拼键 + jv_parse 自校验,绝不发非法 JSON);非对象答原样透传。
|
||
- **`POST /api/ams/:pkg/{start,kill}`**:`am start/kill <pkg>`;包名经 `ams_pkg_ok`(仅 ASCII 可见、禁 `/ : 空白`)。成功
|
||
`{"accepted":true,"op","pkg","message"}`;`not registered`→404、`busy`/`not running`→409、`launcher offline`→503、其余→500。
|
||
**kill 前台 App 前端二次确认**。
|
||
|
||
---
|
||
|
||
## 6. 与协议的出入(「实现为准 + 原因」,供回改 MEOWPANEL_PROTOCOL.md)
|
||
|
||
1. **`ui.style` 值域**:`meow_settings.c g_defs` 源码为 `"md3,miui"`(label "Material You,MIUI"),但
|
||
SYSTEM_SETTINGS.md 定案 + MEOWPANEL_PROTOCOL §5 一致行为 **`miuix`**(对应 `MEOW_STYLE_MIUIX`),前端 `data-style` 也用 `miuix`。
|
||
→ 本表对齐为 `"md3,miuix"` / "Material You,MIUIx"。**建议**同步改回 `meow_settings.c g_defs`(否则实体设置页与面板值域漂移)。
|
||
2. **设置项映射**:协议 §3.2 称后端经 `meow_settings_*` 读写;因后端为独立进程,无法链接 launcher 内 API,
|
||
**改为直接镜像同一份 `settings.conf`**(同款 parse/save 契约)。改哪边都会落同一文件,实体/网页/CLI 仍同步(listener/落盘)。
|
||
3. **`display.brightness` 元数据**:`g_defs` 为 `min=0 max=255 def=101`;但语义 clamp 到 `[brightness_min(1),100]`
|
||
(防 101 闪屏)。后端给前端 `min=1 max=100`(协议 §3.2 期望),读值经 clamp 后返回(默认→100)。若回改协议可注明「原始表 0-255,面板展示语义 1-100」。
|
||
4. **`/api/system` 的 `network`/`ota`**:**M2.73** 起 perf 服务已聚合 `network` 段(真实 WiFi `wpa_cli`/`ip` + 蓝牙 `hci0`/sysfs + `hostname_local`,SYSTEM 子集),后端直接透传;只补 `boot.default`(读 `/data/meow_boot_default`)与 `ota{current=version,available:false,url:null}`。**网络栏以「蓝牙」为准**(设备无网口,无 `以太网` 一行,`bluetooth.available=false` 显示「无蓝牙」)。若回改协议可注明「network 已并入 perf 服务,ota 为面板占位」。
|
||
5. **`/api/files` 上传/删除/重命名等 path**:前端对这些操作传**绝对路径**(`d.path/子项`),列表传**相对 root**;
|
||
后端 `normalize_path` 对两者均容错(相对则拼 root,绝对则直用),符合协议「白名单根 + 规范化」。
|
||
6. **`GET /api/files` 返回值**:`files_list` 返回 `500` 语义时以 500 封装(协议未定义 `root` 之外错误枚举,用通用错误信封)。
|
||
7. **通知清单数据源**:协议 §3.8 称后端「经通知后端读取」。launcher 现已有**客户端查询/撤/清 IPC**(`MSG_NOTIFY_QUERY(0x25)`/
|
||
`MSG_NOTIFY_DISMISS(0x22)`/`MSG_NOTIFY_DISMISS_ALL(0x26)`,agent-199 落地),后端改为**优先经这些 IPC** 取 launcher **内存活跃集**
|
||
(实时,`source:"ipc"`);IPC 不可达/超时回退读 TF 卡通知转储日志(`source:"tf"`,会话级归档)。响应额外带 `source` 与 `items` 别名
|
||
(与 `notifications` 同内容);`items` 为任务约定空态形状,`notifications` 为前端实际读取字段,二者并存。撤/清同样**优先走 IPC**联动
|
||
launcher 内存;`clear` 语义为「保留活动中的媒体/进度通知」(`ongoing||media||progress>=0`),对应通知中心既定语义。
|
||
8. **`/api/files/read` 文本截断**:协议 §3.4 称「仅文本(<1MB)→ 二进制 415」;现实现为「预览上限 1MB,超限 `truncated:true`,
|
||
不再整体 404」,二进制仍 415(比协议更友好,返回文件真实 `size` + `truncated`)。
|
||
9. **`/api/files/upload`/`rename` 修正**:上传文件名经 `safe_filename` 取 basename、拒绝 `..`/分隔符(防 `../../etc/passwd` 逃逸),
|
||
落盘前对 dir 与拼接后路径双重 `normalize_path`+白名单校验;rename 的新路径 = **原路径父目录** + `new_name`(原实现误拼到文件尾部,
|
||
恒 ENOTDIR 失败)。上传体上限 `MAX_UPLOAD_BODY`=8MB,超限 → 413 `PAYLOAD_TOO_LARGE`。
|
||
10. **TF 卡根可覆盖(宿主测试)**:`env MEOW_TF_ROOT` 覆盖卡根(设备上不设,恒用 `/usr/data/mnt/sd_0`);`env MEOW_NOTIF_LOG`
|
||
覆盖通知转储日志路径。设 `MEOW_TF_ROOT` 时 `tf_present()` 恒真(测试用)。
|
||
11. **实时画面/触摸注入(阶段 C)**:协议 §6 待定案项称「实时画面(`/api/frame` 等,本文件暂未定义)」。本面板落地为
|
||
**`GET /api/screen.jpeg` + `POST /api/input/tap`**(命名自定:`.jpeg` 表达二进制帧 + `input/tap` 表达触摸注入),
|
||
见 §5.10。回改协议时可补这两端点(以及 `frames` 语义、`/api/input/tap` 的 `dispatched:false/reason` 软失败约定)。
|
||
12. **multipart 二进制查找修正(strstr→memmem,本次修复,属 bug 修复非出入变更)**:`parse_multipart`/`multipart_extract_file`
|
||
均用 `strstr(data_start, boundary)` 定位 part 数据边界——**strstr 遇到数据中的 NUL 字节即提前停止**(含 NUL 的二进制
|
||
.tar.gz/.mpk/图会误把后续 boundary 与拖尾算进数据 → 解压出错)。已改**二进制安全**的 `mem_find`(memmem 语义,手写字节扫描,
|
||
不依赖 glibc/musl 扩展)。这修复了字体库 tar.gz 导入,也顺带修复了二进制 `.mpk`/文件上传的潜在同病。**实现优先**,供回改协议/审计。
|
||
13. **`/api/fonts*`(新增,§5.11)**:协议 §2 端点总览无此组;按 SPEC_REF §5.7.6「MeowPanel 预留 .zip 导入/导出」落地——**但用户
|
||
2026-08-31 定案:改 tar.gz、放弃 zip**(与 .mpk 同构)。落地为 `/api/fonts`(列表)/`/api/fonts/import`(导入,POST multipart,
|
||
上限 32MB,`?force=1` 覆盖)/`/api/fonts/export`(导出,GET,`<family>-<weight>.font.tar.gz`)。前两者由前端设置页「字体库」卡接入。
|
||
还修正了 `/api/files` 排序:**目录优先 + 名称自然排序**(数字感知 track2<track10,中文按无符号字节序,见 §5.4)。
|
||
**导入不触发 launcher 重扫**(扫描时机=启动/进设置页),响应用 `hint` 提示。
|
||
14. **`/api/audio`(新增,§5.12)**:协议 §2 无此端点。按 AUDIOD_DESIGN §8「MSG_AUDIO_STATE 供 MeowPanel 系统/音频页展示」落地——
|
||
连 audiod sock 发 `MSG_AUDIO_QUERY`(0x00C7)、收 `MSG_AUDIO_STATE`(0x00C8) k=v 文本解析为 JSON 透传;**audiod 无握手**、
|
||
不在时回 `{"available":false}`(200,非错误)。前端新增「音频」页(5s 轮询只读)。同批新增前端「字体」页(§5.13,
|
||
`/api/fonts*` 的专属页;设置页字体库卡保留)。
|
||
|
||
---
|
||
|
||
## 7. 已知边界 / 风险
|
||
|
||
- **单线程串行**:面板访问量极小,但并发请求会被排队(长上传/下载期间其它请求等待)。对面板用途可接受。
|
||
- **字体导入内存/体积上限**:`POST /api/fonts/import` 上传体上限 **32MB**(`MAX_FONT_UPLOAD`,仅此端点放宽;其余
|
||
multipart 仍 8MB);tar 解压到临时目录的总大小 ≤ `FONT_EXTRACT_MAX`=32MB(防 gzip 炸弹)。**全程不做整读进内存**
|
||
(写临时文件 + `gzip -dc | tar` 管道 + rename),峰值内存很小,不依赖 dma/font.png 大件缓冲——相比内存解压方案的 14MB 约束更稳健。
|
||
⚠️ 依赖设备 busybox `gzip`/`tar`(必带);若某内核裁剪过二者,导入/导出会失败(400/500),列表不受影响。
|
||
- **无鉴权(默认)**:设备内网信任模型(协议 §1.3)。**高危端点**(`reboot/shutdown/ota/apps/删除/上传)在默认关鉴权时无鉴权,
|
||
请勿把 8080 暴露到不可信网络。`meow_svc_power` 服务端做 `SO_PEERCRED uid==0` 基础校验(root 才受理动作),后端以 root 运行可过;launcher 电源菜单/后端同链路。
|
||
**可选鉴权**(§5.9)开启后可对内网作加固(登录密码 + 会话),但密码经**局域网明文 HTTP** 传输,不提供 TLS。
|
||
- **IPC 服务未起**:`meow_svc_perf`/`meow_svc_power` 为板载 `trigger=boot` 服务,正常恒在;若异常退出,读侧返回 500 `INTERNAL`,
|
||
动作侧返回 500/503,绝不静默。请勿拔除这两服务。
|
||
- **升级保护**:`POWER_UPGRADE` 由 `meow_svc_power` 在写 mtd5 前校验卡根 `r3proii.upt`(存在/可读/非空/size>0),失败不写 mtd5;期望 md5/hash 校验留二期。
|
||
- **文件/日志/应用均依赖 TF 卡**:无卡统一 503 `NO_CARD`(不阻塞系统核心)。`settings`/`system`/`perf`/`key` 无卡也可用(板载/rootfs)。
|
||
- **`/api/files/upload` 上传路径**:multipart 需要 `path` 字段(目标目录);缺失时上传文件会被忽略(前端总是带着 `path`)。
|
||
- **数据一致性**:设置直接写 `settings.conf`;`display.brightness`/`boot.default` 的 clamp/同步与 launcher `meow_settings_*` 同口径。
|
||
若 launcher 未来改 clamp 逻辑,需同步本表。
|
||
- **通知 IPC 路径为 launcher 内存活跃集(实时)**:优先 `MSG_NOTIFY_QUERY` 取活跃集;TF 归档日志仅在 launcher 不可达时作为
|
||
回退(此时才出现「会话级归档」语义——非 activity 通知 10s TTL 自动归档后仍留日志,会显示已过期历史直到 clear;媒体/进度/ongoing 常驻)。
|
||
IPC 响应为 launcher 的 `m_notify_query_result_t` 单帧文本(≤4KB),活跃通知超限时 launcher 侧 `meow_notif_serialize_json` 保前缀截断
|
||
(理论上最多 ~30 条,实际通知数远小,可接受)。
|
||
- **通知撤/清联动 launcher 内存**:面板 delete/dismiss_all **优先走 IPC**(launcher 撤下时自动归档留档);仅 IPC 不可达时回退重写 TF 日志。
|
||
- **增量帧依赖单线程基线**:`/api/screen.jpeg?delta=1` 用上一帧缓存比对,**面板为单线程 accept 循环**(跨请求持久);对**单面板用户**直接有效。
|
||
多客户端混用(一全帧+一增量、跨浏览器)时增量基线可能被另一客户端发的全帧刷新,前端「canvas 未播种则整帧取图」兜底自愈;极端并发下增量图
|
||
可能基于过期基线(可容忍,仅多发一帧整帧即校准)。子图为独立 JPEG(4:2:0 在子图内自采样),与整帧同源同质量,无拼接色差。
|
||
- **上传为「限长缓冲」非全流式**:multipart 请求体整体读入(上限 8MB,超限 413),下载为流式(8KB 分块)。严格「socket 逐字节流式上传
|
||
解析」未实现(需增量 multipart 解析器),列为后续;14MB 内存约束下 8MB 上限 + 流式下载已覆盖大文件读侧。
|
||
- **存储为 `meowpanel` 编译时刻写死**的 socket/路径会因 `--sock-perf/--sock-power`(qemu sim)可覆盖,但默认走 `/usr/data/meow_*.sock`。
|
||
- **触摸注入在设备息屏时不生效**:launcher 主界面在 `meow_screen_on==0` 时**不处理触摸**(main_menu.c,息屏防误触);设备自动息屏
|
||
(30s 空闲)后注入的 tap 会被 launcher 忽略——面板无法用触摸唤醒(需先电源键点亮)。前端「屏幕」页已提示「设备息屏请先点亮」;
|
||
对「正在看」设备的正常使用场景(屏幕亮着)注入即有效。
|
||
- **实时画面读取 fb0 当前帧(只读)**:双层缓冲下 `pread` 活动缓冲,存在翻页瞬间撕裂一行的可能(1fps 预览可接受);
|
||
`/dev/fb0` 不可读/非 16bpp → 500 `INTERNAL`(绝不静默)。
|
||
- **JPEG 编码器为 baseline 4:2:0**:质量 70 下 480×720 ≈ 24KB;质量越高体积越大(`q=100` 约 60-80KB)。编码为单线程
|
||
串行(面板本就单线程),一帧编码约几十 ms(MIPS 单核),1fps 轮询无压力。
|
||
- **触摸注入为即时性注入**:不维护长按/多指手势状态机(与按键注入一致);`dispatched:false`(本机触摸仲裁)属软失败,
|
||
前端可按 `reason` 提示,不视为错误。
|
||
|
||
---
|
||
|
||
## 8. 文件清单(本目录)
|
||
|
||
```
|
||
patch_v2/meowpanel/
|
||
├── meowpanel.c # 拆分前单体源码(约 6372 行,保留作参考,**不在构建源列表**;构建以 src/*.c 为准)
|
||
├── src/ # 后端模块化源码(模块化拆分,接口/行为不变,-Wall -Wextra 0 warning)
|
||
│ ├── mp.h # 共享内部头:全部共享类型/常量/外部全局 + 跨模块函数原型(含鉴权/实时画面等)
|
||
│ ├── mp_server.c # HTTP 服务器/路由分发 + 静态托管 + main(单线程 + poll 串行)
|
||
│ ├── mp_util.c # sbuf/UTF-8 消毒/内存搜索/命令管道/路径与 token 工具
|
||
│ ├── mp_json.c # 最简 JSON 解析/序列化(jv_t 树)
|
||
│ ├── mp_store.c # 设置镜像(settings.conf)+ 主题 + /api/settings*,/api/theme* + g_sets 表
|
||
│ ├── mp_ipc.c # IPC 帧读写 + meow_svc_perf/power/netmgr 客户端(单点收发)+ socket 路径
|
||
│ ├── mp_perf.c # /api/perf,/api/system,action,pin-unlock(perf 聚合)
|
||
│ ├── mp_power.c # meow_powermgr IPC(/api/power*,policy,window,events)+ ipc_pm_send
|
||
│ ├── mp_audio.c # meow_audiod IPC(状态快照 + 写命令 route/volume)
|
||
│ ├── mp_net.c # meow_netmgrd IPC(状态/连接/DNS/ifaces/policy,期2/期3)
|
||
│ ├── mp_ams.c # App 运行表 + amd 历史 + start/kill 控制
|
||
│ ├── mp_svcd.c # svcd 服务表 + 首页聚合 /api/overview
|
||
│ ├── mp_files.c # 路径安全/白名单 + /api/files* + multipart 上传 + 卡根路径
|
||
│ ├── mp_fonts.c # 字体库 tar.gz 导入/导出 + 列表(busybox gzip|tar 管道)
|
||
│ ├── mp_apps.c # MeowAPPs 扫描 + nyai 安装/导出/移除
|
||
│ ├── mp_logs.c # /api/logs 列表 + 读 + 崩溃汇总
|
||
│ ├── mp_notif.c # 通知(优先 launcher IPC 活跃集,回退 TF 归档)
|
||
│ ├── mp_auth.c # 访问级 PIN 鉴权(panel.pin)+ 内联 SHA-256 + 内存会话
|
||
│ ├── mp_key.c # 按键注入(写 /dev/input/event* EV_KEY,物理键红线内)
|
||
│ └── mp_screen.c # 实时画面(fb0→JPEG,增量帧)+ 触摸注入 + delta 缓存
|
||
├── mp_jpeg.c # 最小 baseline JPEG 编码器(顺序 DCT + 4:2:0 + 标准 Huffman,整数定点,无 libm)
|
||
├── mp_jpeg.h # mp_jpeg.c 接口(RGB565 → 内存 JPEG)
|
||
├── build_meowpanel.sh # 交叉编译 + host 分支(-Wall -Wextra 0 warning;src/*.c + mp_jpeg.c + -I.)
|
||
├── meowpanel # 交叉产物(mipsel 静态,本次 md5 fead4852…)
|
||
├── meowpanel_host # 宿主编译(本机 ld,调试用)
|
||
├── services.d/
|
||
│ └── meowpanel.conf # 部署到 TF 卡 MeowOS/services.d/meowpanel.conf
|
||
└── web/ # 前端(模块化拆分;部署时拷到后端同目录)
|
||
├── index.html # 含登录页 overlay + 设置密码对话框 + 音频/字体导航;脚本按序加载
|
||
├── meowpanel.css # 含登录页样式 + 「屏幕·遥控」合并页样式(screen-remote/remote-btn)
|
||
├── mp_shared.js # 共享框架:全局状态 + API 客户端(token/401) + 公共工具 + 主题/弹窗/登录/滑条(window.MP)
|
||
├── mp_pages.js # 各页渲染/绑定:home/perf/power/audio/net/files/apps/fonts/settings/system/logs/notif/proc + 合并的屏幕·遥控
|
||
└── meowpanel.js # 前端入口:ROUTES/路由/启动(读取 window.MP 装配)
|
||
```
|
||
**前端拆分说明**:`meowpanel.js`(单体 IIFE)按「公共 util+api(client) / 各页渲染 / 应用入口」拆为
|
||
`mp_shared.js` + `mp_pages.js` + `meowpanel.js` 三文件(经典脚本按序加载,函数体保持原样,仅顶部从
|
||
`window.MP` 解构共享框架,**行为不变**)。
|
||
「屏幕」+「控制」两页**合并为一页**(route `screen`,导航「屏幕·遥控」):内容区左侧=屏幕实时画面
|
||
(复用 /api/screen.jpeg + 触摸注入 + fps 滑条 + 增量省流开关),右侧=MD3 矢量图标遥控板(音量± / 亮度± /
|
||
指定亮度滑条 / 播放·暂停 / 下一曲 / 上一曲 / 电源单击 / 电源长按),**全矢量内联 SVG,无 emoji**。
|
||
`返回`/`主页` 属物理键红线(后端 /api/key 禁注入 158/172),未提供注入按钮(遥控板附说明)。
|