diff --git a/README.md b/README.md index 1b87617..d3c4206 100644 --- a/README.md +++ b/README.md @@ -23,22 +23,24 @@ ## 当前架构与组件(2026-08-30 快照) -- **SDK 统一日志 + 通知(M2.52/53 已落地)**:`meow_log()` 按 app 分目录、每次启动新文件 `MeowOS/logs/apps//logs/_.log`,单文件 2MB 轮转、每 App 总量 50MB 删最旧;通知 API `meow_notify_*`(4 类型×每类型 4、媒体单例);**#5 SDK↔IPC 桥**(`meow_notify_ipc.c/.h`:app→launcher 中央存储,send/cancel 接入、回填 launcher `nid`,无 socket 降级不崩)。通知中心 UI / D-Bus 桥 / TF 转储为后续任务。 -- **TF 卡多字体库(M2.6 字重结构)**:`MeowOS/fonts///`(`font.png`+`font.idx`+`font.json` 三件套),默认 `MeowOS/fonts/default/regular`;`font.file` 设置键选默认套(family→字重两级子菜单);SDK `load_font` **优先读 env `MEOW_FONT_PNG/IDX`**(launcher 每次 `launch_app` 注入),app 未自带字库时**跟随系统 `font.file`**;≤16 套卡内(防爆);§5.7 字体管理 + 应用字库(多句柄,**草案未实现**)。已部署 6 套(思源宋体 / MiSans / GoogleSans / Meslo / 思源黑体 / Ubuntu Mono,`/regular`,位深 4)。 +- **SDK 统一日志 + 通知(M2.52/53 已落地 + 模型补全)**:`meow_log()` 按 app 分目录、每次启动新文件 `MeowOS/logs/apps//logs/_.log`,单文件 2MB 轮转、每 App 总量 50MB 删最旧;通知 API `meow_notify_*`(4 类型×每类型 4、媒体单例);**#5 SDK↔IPC 桥**(`meow_notify_ipc.c/.h`:app→launcher 中央存储,send/cancel 接入、回填 launcher `nid`,无 socket 降级不崩)。**通知模型补全(agent-107)**:SDK 补 `icon`+`meow_notify_update_progress`,IPC `m_notify_t` 补 `urgency`(复用 pad)+`MSG_NOTIFY_PROGRESS`,存储补 urgency + icon 回退 + progress(内存实时/里程碑落盘),dump CSV 加 3 列;**#6 TF 转储**已落地(`MeowOS/notifications/` appended CSV + 10MB 滚动 + 开机清板载/内存表)。通知中心 UI / D-Bus 桥为后续任务。 +- **TF 卡多字体库 + 字重结构(M2.5x/M2.58)**:`MeowOS/fonts///`(`font.png`+`font.idx`+`font.json` 三件套),默认 `MeowOS/fonts/default/regular`;`font.file` 设置键选默认套(**family 点开→字重子菜单**,两级扫描 fam/wt/name 数组);`gen_font.py --weight` 写 `font.json.weight` + `--charset` 档位(common/gbk/full);SDK `load_font` **优先读 env `MEOW_FONT_PNG/IDX`**(launcher 每次 `launch_app` 注入),app 未自带字库时**跟随系统 `font.file`**;多字体 6 类(按字符类别路由) + `font.load_full` 大字库开关 + 档位按需加载;≤16 套卡内(防爆);§5.7 字体管理 + 应用字库(多句柄,**草案未实现/**字体索引 agent-113 进行中)。已迁移卡上 8 套到 `/regular/`(default / google_sans / meslo / misans / source_han_sans / source_han_serif / ubuntu_mono / ztest,位深 4)。 +- **全屏机制(M2.5x)**:`app.json fullscreen`(app 级)+`route..fullscreen`(activity 级)+env `MEOW_FULLSCREEN` 注入,launcher 启动 app/切 activity 前预设 SDK 全屏;进入/退出全屏=**状态栏上收出屏顶只剩提示线(4px)**+自然过渡;全屏下保留左缘返回手势+顶部伸缩滑条+顶部下滑开通知中心(热区缩小)。⚠️ 全屏滑条仍有已知问题在跟进(agent-112)。 - **包名/目录安卓化**:`app.json` `package`(反域名**权威 app_id**,==目录名,缺省回退目录名)→ 示例 `os.meow.hello`;目录 `MeowAPPs/os.meow.hello`;数据 `MeowData//{data,cache}`。 -- **meow_svcd 服务管理器**(`patch_v2/meow_svcd/`,mipsel 静态 ~99KB,进固件不入卡):类 systemd/systemctl,由 supervisor(bootmenu/launcher)`fork/exec` 托管;读 TF `MeowOS/services.d/.conf`(目录式 `key=value`,未知键忽略/前向兼容);服务=svcd **独立子进程**(崩溃隔离:服务挂不影响 svcd/supervisor);事件触发 `boot`/`card-mount`/`wifi-up`(1s 轮询);`-f` 前台便于 supervisor `waitpid`;状态机 DISABLED/STOPPED/RUNNING/STOPPING/FAILED + CLI。**接入 supervisor 启动链未做**。 -- **mDNS 方案A(已拍板)**:完整 **`mdnsd`**(994KB,mipsel 静态)放 TF `MeowOS/opt/mdnsd/mdnsd`,由 meow_svcd 托管(需 root/setcap `cap_net_bind_service,cap_net_raw` 绑 UDP5353+组播 224.0.0.251);SAResponder(885KB)因"单服务注册型"无法按名访问设备(`ingenic.local`)被排除。⚠️ 手机热点组播常在客户端间丢失 → mDNS 有风险,主机 DNS 兜底。**`MeowOpt` 已废弃并入 `MeowOS/opt`**(二进制归 MeowOS/opt,拔卡降级不崩)。 +- **meow_svcd 服务管理器**(`patch_v2/meow_svcd/`,mipsel 静态 ~99KB,进固件不入卡):类 systemd/systemctl;**已接入固件开机链**(`etc/init.d/S89meow_svcd` 开机自启,`-f` 前台 + 重挂循环,置于 S84ssh 后、S90 前;bootmenu/launcher `fork/exec`+`waitpid` 托管仍为备选);**双源 services.d**(板载 `/usr/data/meow/services.d/` + TF `MeowOS/services.d/.conf`,同名板载优先+WARN;目录式 `key=value`,未知键忽略/前向兼容);服务=svcd **独立子进程**(崩溃隔离:服务挂不影响 svcd/supervisor);事件触发 `boot`/`card-mount`/`wifi-up`(1s 轮询);`-f` 前台便于 supervisor `waitpid`;状态机 DISABLED/STOPPED/RUNNING/STOPPING/FAILED + CLI。 +- **mDNS(自研 `meow_mdns`,IPv4-only)+ `meow_svcd` 双源服务**:完整 mDNSResponder/SAResponder 均因内核无 IPv6(`socket(AF_INET6)` 失败)被否,改**自研 `meow_mdns`**(mipsel 静态 ~79KB)→ **进固件 `/usr/bin/meow_mdns`**(不入卡),由 `meow_svcd` 托管。`meow_svcd` **双源 services.d**(板载 `/usr/data/meow/services.d/` + TF `MeowOS/services.d/`,同名板载优先+WARN),`etc/init.d/S89meow_svcd` 开机自启并预置 `mdnsd.conf`(trigger=boot);hostname=**meowos**(`system.hostname`,S89 应用)。⚠️ **手机热点组播隔离**:设备→主机组播被热点丢弃 → 该热点下 `avahi-resolve .local` 超时(网络层限制,非缺陷),主机需 DNS 兜底。**`MeowOpt` 已废弃并入 `MeowOS/opt`**。 - **boot 默认→板载**:**`/data/meow_boot_default`**(O_TRUNC 单值、不递增、权威);bootmenu 读它定默认;**meow.conf 不再承担 boot_default**(其 O_APPEND 累积 bug 已修,设备已清)。 - **状态栏 40→28px**:SDK `MUI_SBAR_H=28`(字号 20)+ launcher `SB_BAR_H=28`/`SB_FONT=20`;状态栏两条实现(SDK `meow_ui_sys_statusbar_render` / launcher `draw_statusbar`)**待 MD3 收敛为单一 SDK 全局状态栏**(launcher 退役 draw_statusbar 复用 SDK)。 +- **系统修复(agent-104/106,M2.56)**:亮度开机被播放器覆盖→launcher 接管压回用户值;SDK 状态栏音量读取修复(空格匹配 + clamp `volume_max`);设置开关 `meow_settings_get_switch/set_switch` 修复(SET_INT{0,1});**「关于本机」**(设置最底,只读弹窗:hostname=meowos/版本/内存/存储/WiFi);bootmenu 版本显示读 `/etc/meowos_version`(替换硬编码 0.7.9);boot 默认板载 `/data/meow_boot_default`;SDK 拆分触发线(>1200 行才拆,现状 meow_ui_gfx.c=839 行保持扁平)。 - **键码/阈值修正**:`MEOW_KEY_PREVIOUS=165`(19 是另一码,[A-6] 定案);左缘返回阈值 **65px**([A-4] 90vs65 定案 65);亮度下限 1([A-3]);热键窗口下限 60ms([A-1]);日志路径统一 `MeowOS/logs`([A-7])。 - **TF 卡资产**:包管理 CLI `nyai`(部署 `MeowOS/opt/bin/nyai`,同 launcher 包语义);应用日志 `MeowOS/logs/apps//logs/_.log`;服务定义 `MeowOS/services.d/.conf`。 -- **mips-repo/**(宿主侧 mipsel 软件仓库):交叉编译产物 + `build.sh` + `manifest.md`,供后续固件/TF 复用(见「目录速览」)。 -- **仓库公开净化**:本地 main 单提交,HEAD 敏感 0 命中;`devdocs/`(本地开发文档 + WORK_STATE/DEV_NOTES)、`scripts/build/`(固件打包脚本)**gitignore 不入库**;`mips-repo/`(mipsel 软件仓库)入库。 +- **mips-repo/**(宿主侧 mipsel 软件仓库):`bins/`(二进制工具:交叉编译产物 + `build.sh` + `manifest.md`)+ `mpk/`(`.mpk` 软件包),供后续固件/TF 复用(见「目录速览」)。 +- **仓库公开净化**:净化后基线单提交 `245bee2` + 本开发批次 8 提交(领先 origin/main 8),HEAD 敏感 0 命中;`devdocs/`(本地开发文档 + WORK_STATE/DEV_NOTES)、`scripts/build/`(固件打包脚本)**gitignore 不入库**;`mips-repo/`(mipsel 软件仓库)入库。 ## 目录速览 - `patch_v2/` — meow_launcherd(MeowOS 核心)、bootmenu(引导选单)、watchdog、meow_svcd(服务管理器)、sdk(meow_ui SDK)、meowpkg(包管理);源码与构建脚本 -- `mips-repo/` — mipsel 交叉编译软件仓库(mdnsd 等:`README.md`+`bin/`),供后续固件/TF 复用 +- `mips-repo/` — mipsel 交叉编译软件仓库(`bins/`=二进制工具=`meow_mdns`、`mdnsd` 等;`mpk/`=`.mpk` 软件包),供后续固件/TF 复用 - `font/` — 预渲染字形系统(gen 生成器、meow_font 渲染库、atlas 产物) - `fw_apps/` — 固件组装产物(`.upt` 不入库,由 `scripts/build/build_fw_apps*.sh` 重新打包) - `bootlogos/` — 用户定制启动图/关机 logo @@ -48,7 +50,7 @@ ## 构建/刷机 -- 固件打包:`scripts/build/build_fw_apps_m253.sh`(**现 M2.53 / 0.7.48**;需交叉工具链 `zig-mipsel-cc` + musl.cc,见 `devdocs/ANALYSIS.md` 第 10 节) +- 固件打包:`scripts/build/build_fw_apps_m258.sh`(**现 M2.58 / 0.7.53**;需交叉工具链 `zig-mipsel-cc` + musl.cc,见 `devdocs/ANALYSIS.md` 第 10 节) - 升级锁根:SD 卡根 `r3proii.upt`(固件由 `build_fw_apps*.sh` 打包成 `.upt` 再刷) - 刷机:failsafe(关机后按住 音量加+电源)从 SD 卡根 `r3proii.upt` 升级;勿用播放器 UI 卡死的升级按钮 - 组件归属:**launcher / bootmenu / watchdog 为固件组件**(`/usr/bin`,只读 squashfs);`/usr/data` 下的同名文件仅 dev 手动 exec 快速下发,开机不用。**真机验证字体/包名/通知须重打固件 + 刷机 + reboot**。 diff --git a/docs/MEOWOS_SPEC_REF.md b/docs/MEOWOS_SPEC_REF.md index 1fabc8f..04a7342 100644 --- a/docs/MEOWOS_SPEC_REF.md +++ b/docs/MEOWOS_SPEC_REF.md @@ -9,7 +9,7 @@ ### 1.1 板载(NAND rootfs / /data) - 设置持久化:**板载** `/usr/data/meow/settings.conf`(UBIFS,小文件 <10KB,跨换卡保留)`[SYSTEM_SETTINGS §2][MEOWOS_API §3]` - **板载服务目录**:`/usr/data/meow/services.d/.conf`(M2.54,Data 区,恒定存在、权威优先;首启由 `S89meow_svcd` 预置 `mdnsd.conf`)。与 TF `MeowOS/services.d/` 双源,同名板载优先 + WARN;板载 `trigger=boot` 服务不依赖卡(拔卡保留)`[meow_svcd.md][meow_svcd.c]` -- **主机名**:设置键 `system.hostname`(SET_STR,默认 `meowos`;RFC1035 首标签),`S89meow_svcd` 开机读 settings `sethostname`;供自研 mDNS 应答器公布 `.local` `[SYSTEM_SETTINGS §1][mips-repo/meow_mdns]` +- **主机名**:设置键 `system.hostname`(SET_STR,默认 `meowos`;RFC1035 首标签),`S89meow_svcd` 开机读 settings `sethostname`;供自研 mDNS 应答器公布 `.local` `[SYSTEM_SETTINGS §1][mips-repo/bins/meow_mdns]` - `/etc/meowos_version`:版本唯一来源(launcher 运行时 `load_meowos_version` 读;`MEOWOS_VERSION` 宏仅作 dev 占位兜底)`[DEV_NOTES #4]` - `/tmp`:tmpfs,开机清空;开机模式标记 `/tmp/meow_boot_mode`、电源菜单 `/tmp/meow_power_menu{,_active}`、app 沉浸 `/tmp/meowapp_state` `[BOOT_SELECTOR §1][KEY_CONTROL §9][APPS §3.5]` - 日志启动缓冲 `/tmp/meow_boot.log`(256KB 滚动,卡出现后自动迁卡)`[APPS §4.5]` @@ -22,17 +22,17 @@ ├── Music/ # 播放器标准索引目录 ├── MeowData//{data,cache} # 应用专属数据(exFAT 无软链,用 env 注入) ├── MeowAPPs/ # 应用(=app.json.package=目录名;app.json+二进制+资源, fonts/, backgrounds/;日志已入 MeowOS/logs[源:审计 2026-08-30]) -├── MeowOpt/ # 板载外部工具/二进制(如 mdnsd/,卡上加载,拔卡降级不崩)`[DEV_NOTES][meow_svcd.md]` -└── MeowOS/ # MeowOS 专属(一切非必要文件) +└── MeowOS/ # MeowOS 专属(一切非必要文件;**MeowOpt 已废弃并入此处的 opt/**) ├── backups/settings/setting.bak # 设置导出默认路径 ├── services.d/ # 服务配置(meow_svcd 的 .conf;TF 源,与板载 /usr/data/meow/services.d 双源,同名板载优先)`[meow_svcd.md]` ├── fonts/// # 多字体库(每 / 一套三件套;见 §5.2) │ ├── font.png # 预渲染灰度 PNG(bit depth 4|8,gen/gen_font.py 产物) │ ├── font.idx # 索引(MFNI v2,gen/gen_font.py 产物) - │ └── font.json # 元数据 + sha256 完整性校验(schema 见 §5.2) + │ └── font.json # 元数据 + sha256 完整性校验(schema 见 §5.2;含 weight+charset+glyph_count) + ├── config/fonts/index # 字体列表索引(M2.58:CSV family,weight,path,label,tier,glyph_count;见 §5.7) ├── logs/ # 系统日志 meow_.log + apps/...(见 §3 日志) - ├── notifications/(转储区,保留最新 10MB,不被电源事件清) - ├── opt/{bin,nyai,sources.list}, lib/, tools/ + ├── notifications/(转储区,保留最新 10MB,不被电源事件清;文件 = notifications.log,appended CSV 行) + ├── opt/{bin,nyai,sources.list}, lib/, tools/ # 外围工具/二进制(MeowOpt 已废弃并入;mDNS 自研 meow_mdns 在固件,不入此) └── themes/charging// # 充电屏主题(theme.json manifest) ``` `[TF_CARD_LAYOUT §1][APPS 附录][NOTIFICATION_SYSTEM §5]` @@ -129,10 +129,11 @@ - 类 systemd/systemctl。**已接入系统启动链(M2.54)**:固件 `etc/init.d/S89meow_svcd` 开机自启(`-f` 前台 + 重挂循环,失败不死;置于 S84ssh 之后、S90meow_bootmenu 之前——因 S90 exec 引导选单阻塞 rcS)。由 bootmenu/launcher `fork/exec` + `waitpid` 托管仍为备选。 - **双源 services.d**(板载优先):板载 `/usr/data/meow/services.d/.conf` + TF **`MeowOS/services.d/.conf`**,两处都读;同名服务板载优先(TF 同名跳过 + WARN「 板载与 TF 都存在,采用板载」)。目录式 `key=value`,未知键忽略/前向兼容:`exec`(必填)/`args`/`env`/`enabled`(0=永不自动启动,默认0)/`restart`(`always`/`on-fail`/`never`,默认on-fail)/`backoff_ms`(默认1000)/`max_restart`(默认5)/`cap`(本阶段仅解析不实装)/`trigger`(`boot`/`card-mount`/`wifi-up`,默认card-mount)。 - **板载-trigger=boot 语义**:板载服务恒定存在,boot 触发不依赖卡(拔卡保留);TF 服务 boot 仍需卡。 + - **服务目录规则**:**固件工具**→板载 `/usr/data/meow/services.d/.conf`(Data 区,拔卡保留);**TF 工具/第三方**→TF `MeowOS/services.d/.conf`(随卡存在/消失);**双源板载优先**(TF 同名被板载屏蔽 + WARN「 板载与 TF 都存在,采用板载」)。 - 崩溃隔离:服务=svcd **独立子进程**(`fork+execv`),svcd 只 `waitpid`/`SIGCHLD` 感知;服务死不影响 svcd/supervisor;重启受 `backoff_ms` 退避防热循环。 - - 状态机:`DISABLED/STOPPED/RUNNING/STOPPING/FAILED`;稳定运行 30s 清零 restart_count。CLI:`status/reload/start|stop|restart /pid`(AF_UNIX 单行文本 `/tmp/meow_svcd.sock`)。 -- **卡上外部工具 = `MeowOpt/`**(TF 根下平级目录,如 `MeowOpt/mdnsd/mdnsd`):MeowOS 启动时从卡加载,拔卡降级不崩 `[DEV_NOTES][meow_svcd.md]`。 -- **mDNS 应答器 = `meow_mdns`(自研,已拍板替代完整 mdnsd)**:`mips-repo/meow_mdns/`,**IPv4-only**(内核无 IPv6,完整 mDNSResponder/SAResponder 均 `socket(AF_INET6)` 失败),mipsel 静态 ~79KB(md5 b23de7ce)。**进固件 `/usr/bin/meow_mdns`**(不入卡),由 meow_svcd 托管(板载 `/usr/data/meow/services.d/mdnsd.conf`,`trigger=boot`、root 即可绑 5353/组播,`cap=` 空)。功能:join `224.0.0.251:5353`,应答 `.local` A(wlan0 IPv4)/`_ssh._tcp` PTR/SRV/TXT/`_services._dns-sd` PTR;支持 `MEOW_MDNS_HOST`/`_ADDR`/`_PORT`/`_TTL`/`_CF`/`_QD`/`_MCAST`/`_MIF`/`_DBG`。⚠️ **手机热点隔离**:设备→主机组播被热点丢弃,avahi 只收组播应答 → 该热点下 `avahi-resolve .local` 超时(网络层限制,非应答器缺陷),主机需 DNS 兜底 `[mips-repo/meow_mdns/manifest.md][meow_svcd.md]`。 + - 状态机:`DISABLED/STOPPED/RUNNING/STOPPING/FAILED`;稳定运行 30s 清零 restart_count。CLI(systemctl 式,SSH 直呼):`status/reload/start|stop|restart /pid`,示例 `meow_svcd status`/`meow_svcd restart mdnsd`/`meow_svcd stop all`/`meow_svcd reload`;控制走 AF_UNIX 单行文本 `/tmp/meow_svcd.sock`;守护默认后台,`-f/--foreground` 前台供 supervisor 托管。 +- **卡上外部工具 = `MeowOS/opt/`**(`{bin,nyai,sources.list}, lib/, tools/`):MeowOS 启动时从卡加载,拔卡降级不崩。**`MeowOpt` 已废弃并入此处**;mDNS 已改为自研 `meow_mdns`(固件 `/usr/bin`),不再放卡。`[DEV_NOTES][meow_svcd.md]`。 +- **mDNS 应答器 = `meow_mdns`(自研,已拍板替代完整 mdnsd)**:`mips-repo/bins/meow_mdns/`,**IPv4-only**(内核无 IPv6,完整 mDNSResponder/SAResponder 均 `socket(AF_INET6)` 失败),mipsel 静态 ~79KB(md5 b23de7ce)。**进固件 `/usr/bin/meow_mdns`**(不入卡),由 meow_svcd 托管(板载 `/usr/data/meow/services.d/mdnsd.conf`,`trigger=boot`、root 即可绑 5353/组播,`cap=` 空)。功能:join `224.0.0.251:5353`,应答 `.local` A(wlan0 IPv4)/`_ssh._tcp` PTR/SRV/TXT/`_services._dns-sd` PTR;支持 `MEOW_MDNS_HOST`/`_ADDR`/`_PORT`/`_TTL`/`_CF`/`_QD`/`_MCAST`/`_MIF`/`_DBG`。⚠️ **手机热点隔离**:设备→主机组播被热点丢弃,avahi 只收组播应答 → 该热点下 `avahi-resolve .local` 超时(网络层限制,非应答器缺陷),主机需 DNS 兜底 `[mips-repo/bins/meow_mdns/manifest.md][meow_svcd.md]`。 - `meow_settings_*`(设置后端,已定)/`meowcli`/`meow_sys_devattr`/`meow_devattr_set` `[MEOWOS_API]` - 后台权限:`MSG_HELLO/MSG_REGISTER.flags` 扩展 `MF_BACKGROUND`/`MF_PLAYBACK`;`PERF_BACKGROUND`/`PERF_AUDIO_BACKGROUND`(规划)`[FRAMEWORK_DESIGN 附录 A]` @@ -184,7 +185,7 @@ - 刷新率:`display.refresh_rate` enum 60/30/15(默认 60);`display.refresh_always_max` bool false(关=允许 60→30→15 降档)。governor:连续 12 帧超预算降一档,连续 4 批≈48 帧远低预算升一档 `[SYSTEM_SETTINGS §1][PERF_SCHEDULER][MIGRATION_AC §12.1]` - 息屏:`display.screen_off_timeout` int 秒 默认 **30**(0=常亮);`display.screen_off_mode` enum `panel`(面板下电+背光0+FBIOPBLANK)/`black`(背光0全黑+双击亮屏) `[SYSTEM_SETTINGS §1]` - 亮度 clamp:`display.brightness` 0-255 默认 101(clamp ≥ `display.brightness_min`);`display.brightness_min` 默认 **1**(M2.39 由 25 改 1)——**与旧文档 25 冲突见 §A** `[SYSTEM_SETTINGS §1]` -- **主机名(M2.54)**:`system.hostname`(SET_STR 默认 `meowos`,RFC1035 首标签);`S89meow_svcd` 开机读板载 `settings.conf` 应用 `sethostname`,缺省 `meowos`/系统名;供 `meow_mdns` 公布 `.local`。settings 无该键 → 默认 `meowos`。`[SYSTEM_SETTINGS §1][mips-repo/meow_mdns/manifest.md]` +- **主机名(M2.54)**:`system.hostname`(SET_STR 默认 `meowos`,RFC1035 首标签);`S89meow_svcd` 开机读板载 `settings.conf` 应用 `sethostname`,缺省 `meowos`/系统名;供 `meow_mdns` 公布 `.local`。settings 无该键 → 默认 `meowos`。`[SYSTEM_SETTINGS §1][mips-repo/bins/meow_mdns/manifest.md]` ### 5.5 MeowAPPs 包结构 / .mpk / 数据 - `/` = 程序主体+app.json+资源;`MeowData//{data,cache}` = 数据;首次启动自动建 data/cache 并经 env 注入,exFAT 无软链 `[APPS 附录 §1/§2]` @@ -199,13 +200,14 @@ ### 5.7 字体管理 + 应用字库接口(扩展 → SDK 统一接口 / 应用自带字库 / 多句柄) > 在 §5.2「TF 卡多字体库(M2.5x)」基础上**扩展**:把 MeowOS 字体管理器定义为可用字库的**唯一权威来源**,向 SDK 提供统一查询/选用接口,并支持**应用自带字库**与**多句柄(多字体)**。 -> ⚠️ **凡 `meow_font_*_h` / `meow_ui_*_h` / 列表枚举 / 句柄加载类接口均为「草案/待定案」**:当前 SDK 只有单字体 `meow_font_*` 与 `meow_ui_text*`(用当前默认字体),本小节为新功能、**未实现**,勿当作已落地。 +> ⚠️ **状态分级(2026-08-30)**:launcher 侧字体管理器(§5.7.1)与 **两级扫描/字重子菜单(agent-111,M2.58)**、`meow_font_has_glyph` 已实现;**列表枚举(§5.7.1/§5.7.2)** 落地(**agent-113,M2.58**):「扫描结果落盘 `MeowOS/config/fonts/index`(family/weight/path/label/tier/glyph_count),SDK `meow_font_get_available_count/enumerate` 从该文件读、不依赖 launcher 内存数组;字库丢失→降级默认+日志;render 走 env `MEOW_FONT_PNG/IDX`,list 枚举走 index——两路明确」。**应用自带字库(§5.7.3)/ 多句柄(§5.7.4)/ 凡 `_h` 接口仍为「草案/未实现」**,勿当作已落地;当前 SDK 只有单字体 `meow_font_*` 与 `meow_ui_text*`(用当前默认字体)。 > `[源:meow_font.h][源:agent-79 实现][源:meow_ui.h]` > **app 未自带字库 → 跟随系统 `font.file`(M2.x 已落地)**:launcher 每次 `launch_app` 前把「当前生效字体」注入为环境变量 `MEOW_FONT_PNG`/`MEOW_FONT_IDX`(绝对路径,可选 `MEOW_FONT_APP=`);SDK `meow_ui_gfx.c load_font()` **优先读 env**,无 env 则回退卡默认 `MeowOS/fonts/default` → 固件。§5.7.3 的 `load_handle`(app 自带字库)**未实现**,故当前 app 只能跟随系统字体;一旦实现自带字库,其加载优先级应高于 env。 - **5.7.1 字体管理器(launcher 侧)= 可用字库唯一权威来源** - - 唯一维护并扫描 `MeowOS/fonts///`(+默认),产出「字体列表」(family slug / weight slug / 显示名 / glyph_count / size_list / bit_depth)。**SDK 不自行发现字库**,一律经本接口取 launcher 扫描结果。 + - 唯一维护并扫描 `MeowOS/fonts///`(+默认),产出「字体列表」(family slug / weight slug / 显示名 / glyph_count / size_list / bit_depth)。**SDK 不自行发现字库**,一律经本接口取 launcher 扫描结果。**扫描结果落盘 `MeowOS/config/fonts/index`**(family/weight/path/label/tier/glyph_count);SDK 读该文件(不依赖 launcher 内存数组)。 + - **释放语义(agent-113 落地结论)**:持久来源 = index 文件;`font_presets*`/`*_buf` 为「静态、有界(`FONT_PRESETS_MAX=17`)、非堆」数组 → 无内存泄漏、无运行期 free;「释放」= 两个扫描点(启动/进设置页)重建 + 固定上限防膨胀,离开字体列表视图无需显式 free(下次进设置/重扫自动重建)。⚠️ **未做「启动后即清空数组」**:`meow_font_effective_paths_tiered`(launch_app 大字库档位选库)与 `meow_font_reload_multifont` 运行期持续读 `font_presets_n`,清空会造成大字库档位退化(red line「保持档位/多字体行为」);如需「零驻留」,须把这几个核心路径改为直接读 index(较大重构,留待后续)。 - 扫描与校验规则沿用 §5.2:三件套 `font.png/font.idx/font.json` **缺任一件 → 该套跳过 + 日志**;`font.json` 解析 `name`(缺回退目录名)/`weight`(缺省 `regular`)/`glyph_count`/`size_list`/`bit_depth`;`sha256_png/sha256_idx` 仅 **WARN** 不硬失败(该套仍可选)。 - 列表上限沿用 `FONT_PRESETS_MAX = 17`(1 默认 + ≤16 卡内套装,防爆);顺序 = 默认(0) + 扫描顺序。扫描时机不变(启动 / 进设置页),**勿放热路径** `[源:agent-79 实现]`。 - 权威列表数据模型(SDK 侧快照,枚举时填充): @@ -221,17 +223,20 @@ char path_png[320]; /* 系统库:相对卡根 MeowOS/fonts///font.png */ } meow_font_info; ``` -- **5.7.2 应用可读 TF 卡字体列表(统一规范接口,app 面向)** `[源:agent-79 实现][源:meow_font.h]` +- **5.7.2 应用可读 TF 卡字体列表(统一规范接口,app 面向;已落地,M2.58)** `[源:agent-79 实现][源:agent-113 实现][源:meow_font.h]` + - **读取来源 = `MeowOS/config/fonts/index`**(launcher 扫 `MeowOS/fonts///` 后落盘;CSV 每套一条 `"family","weight","path","label","tier",glyph_count`,path=卡根相对 `font.png`,label=显示名+字重+尺寸标记,tier=common|gbk|full;默认行恒为 index0)。`meow_font_get_available_count/enumerate` **只读该文件**,不再依赖 launcher 内存数组;无卡/无 index → 仅板载默认项(family=default,count=1)。index 解析在 `font/meow_font.c`(`meow_font_index_load`),SDK 经 `meow_ui.h §5.7` 暴露。 ```c - int meow_font_get_available_count(void); /* 可用字库数(含默认) */ + int meow_font_get_available_count(void); /* 可用字库数(含默认;无卡→1) */ int meow_font_enumerate(int idx, meow_font_info *out); /* 按序号取,越界返回 0 */ typedef int (*meow_font_enum_cb)(const meow_font_info *info, void *ctx); int meow_font_enumerate_cb(meow_font_enum_cb cb, void *ctx); /* 回调式,返回套数 */ int meow_font_select_by_id(int id); /* 选该套为 app 会话默认字体 */ ``` - - 语义:`id` = 列表序号(§5.7.1 快照);`0` = **跟随系统默认**(用户 `font.file` 所选,app 会话不覆盖);`>0` = **app 会话内固定用该套**(覆盖 `font.file` 至 app 结束)。成功 0,越界/加载失败返回负值。 + - 语义:`id` = 列表序号(index 行号);`0` = **跟随系统默认**(用户 `font.file` 所选,经 env `MEOW_FONT_PNG/IDX`,app 会话不覆盖);`>0` = **app 会话内固定用该套**(覆盖 `font.file` 至 app 结束)。成功 0,越界返回负值。 + - **字库丢失降级默认(不丢请求)+ 日志**:`select_by_id(id>0)` 加载失败(png 缺失/坏)→ **降级**到系统默认(env→卡默认→固件),并 `meow_log(MEOW_LOG_WARN, "font missing, fallback default")`(落 app 日志 = SDK meow_log);请求不丢(仍按"选过"处理)。渲染仍走 env `MEOW_FONT_PNG/IDX`(选中的那一套),**列表枚举走 index 文件**——两路明确匹配。 - **列表快照是临时的**:launcher 仅启动/进设置页重扫,期间拔卡或改卡内字库不刷新;app 应**每次会话重新枚举**、按 `family` 持久化,勿缓存数值 id。 - 覆盖范围:系统库 `MeowOS/fonts///` 与默认(含固件 `/usr/share/meowos/font` 回退)都反映在列表里;app 侧「选用已有字库」**只用本接口,不直接拼路径**。 + - **launcher 侧系统设置同样降级 + 系统日志**:`font.file` 选中字体缺失 → `meow_font_ensure`/`meow_font_reload_multifont` 兜底链(font.file→卡默认→固件)回退默认,并 `logmsg("meow_font: selected font %s missing, fallback default")`(系统日志 = launcher logmsg;多字体类别缺失 → `multi_font: class %s font %s missing, fallback global`)。app 日志(SDK meow_log)与系统日志(launcher logmsg)分流明确。 - **5.7.3 应用自带字库(按 handle 加载)** `[源:meow_font.h]` - 目录:`MeowAPPs//fonts//{font.png,font.idx,font.json}`;`` = `app.json.package`(反域名 app_id,见 §5.5),`` = 应用自设名字(如 `lyrics`/`reader`)。 - `app.json` 声明(**可选**):`"fonts": ["lyrics","reader"]`。作用 = MeowPanel 打包校验 / launcher 装包校验 / SDK 提供取证;**不声明也可运行时按 handle 加载**。 @@ -329,10 +334,10 @@ - **A-6 KEY_PREVIOUS(code)**:KEY_CONTROL §3 明注「KEY_PREVIOUSSONG 是 **165**;19 是另一码」;meow_ui.h `#define MEOW_KEY_PREVIOUS 19 /* KEY_PREVIOUSSONG */` 注释标错。→ **19 的标签应改**(值 19 非 prevsong;165 才是),属 meow_ui.h 注释误导。 - **A-7 app 日志路径**:APP_LOG_MANAGER §4 写 `apps/.log`(单文件);meow_ui.h §9 / DEV_NOTES #9 为 **`apps/<包名>/logs/<启动时间>_.log`**(每次启动新文件)——用户已纠正,勿用单文件。→ 以 meow_ui.h/DEV_NOTES(M2.51)为准,APP_LOG_MANAGER 目录小节过时。 - **A-8 状态栏高**:MEOW_APP_SDK §3.1「slim ~20-40px」;实际定案 **40px**([D6] MIGRATION_AC §11.1),后被用户改为 **28px**(MUI_SBAR_H=28,字号 20,图标 ink 16/中线 14,见 M2.x 状态栏压缩)。→ 现取 28。 -- **A-9 SDK 通知转储**:NOTIFICATION_SYSTEM §8「创建 10s 后自动转储到 TF」;meow_ui.h 现仅「活动表+meow_log 打日志,真实投递 UI/D-Bus 为后续」。→ 10s 转储/TF 尚未实现,属"已定未做"。 +- **A-9 SDK 通知转储**:NOTIFICATION_SYSTEM §8「创建 10s 后自动转储到 TF」;meow_ui.h 现仅「活动表+meow_log 打日志,真实投递 UI/D-Bus 为后续」。→ **10s 转储/TF 已实现(M2.5x,#6)**:launcher 侧带 TTL 10s 转储钩子,dump 到 `MeowOS/notifications/notifications.log`(appended CSV + 10MB 滚动 + 开机清板载/内存表);meow_ui.h 注释「真实投递 UI/D-Bus 为后续」仍指**通知中心 UI / D-Bus 桥**未做。 - **A-10 版本来源细解**:DEV_NOTES #4「/etc/meowos_version 唯一来源;MEOWOS_VERSION 宏仅 dev 占位永不动」;APPS.md §4.6「MEOWOS_VERSION 宏=单一来源,build 时 sed 写入 etc/meowos_version」。→ 两者都强调"写入同源",但"谁是源头"措辞矛盾;实践上 build 脚本 echo 生成 /etc/meowos_version,宏作屏显 dev 占位。 -- **A-11 `MeowOS/opt/` vs `MeowOpt/`**:§1.2 树中 `MeowOS/opt/{bin,nyai,sources.list},lib/,tools/`(nyai 包管 + 工具)与新增顶层 `MeowOpt/`(板载外部工具/二进制,如 mdnsd)并存,命名仅大小写之差、语义可能重叠。→ **两者是否为同一用途/是否需归并待确认**;当前按各自文档记录,勿混用。 -- **A-12 `fullscreen` 字段(已定 → 本次实现)**:§5.5 与 MEOW_APP_SDK §9 将 `fullscreen` 列为 app.json 字段,但 `meow_parse_app_json()` **此前并未读取该键**(实现只解析 name/exec/icon/package/framework/immersive/route./awake./service.),属"已定未做"。→ **本次实现**:launcher 启动 app/切 activity 据 `app.json fullscreen`/`route..fullscreen` 预设 SDK 全屏(env `MEOW_FULLSCREEN`),见 §5.1/§3.1。注意全屏语义由「隐藏状态栏+隐藏返回键」细化为「状态栏收合至提示线 + 保留左缘返回+顶部提示线+下滑通知中心(热区缩小)」(见 A-13)。 -- **A-13 全屏语义细化(状态栏「隐藏」→「收合至提示线」)**:既有 §3.1/§5.1 描述全屏=隐藏状态栏(`meow_ui_statusbar_height()`=0、隐藏返回键=沉浸);本次定案细化为**进入/退出全屏:状态栏向上收出屏顶、只留一条提示线高度**,**全屏下保留**左缘返回手势 + 顶部动态伸缩提示线,且**顶部下滑仍可开通知中心(顶部热区缩小)**。→ 需在 SDK 端明确「收合(collapsed)」状态(新增 `meow_ui_statusbar_collapsed()` 等)与「隐藏」的差异,UI 依此绘制提示线。 +- **A-11 `MeowOS/opt/` vs `MeowOpt/`(已归并)**:曾有顶层 `MeowOpt/`(板载外部工具/二进制,如 mdnsd)与 `MeowOS/opt/{bin,nyai,sources.list},lib/,tools/`(nyai 包管 + 工具)并存、命名仅大小写之差。→ **已归并**:`MeowOpt` 废弃并入 `MeowOS/opt/`;mDNS 由完整 mdnsd(TF MeowOS/opt/mdnsd)改为**自研 `meow_mdns`(固件 `/usr/bin`,IPv4-only)**,不再放卡。全仓库统一用 `MeowOS/opt/`。 +- **A-12 `fullscreen` 字段(已定 → 已实现,M2.5x)**:§5.5 与 MEOW_APP_SDK §9 将 `fullscreen` 列为 app.json 字段,但 `meow_parse_app_json()` **此前并未读取该键**(实现只解析 name/exec/icon/package/framework/immersive/route./awake./service.),属"已定未做"。→ **已实现**:launcher 启动 app/切 activity 据 `app.json fullscreen`/`route..fullscreen` 预设 SDK 全屏(env `MEOW_FULLSCREEN`),见 §5.1/§3.1。全屏语义由「隐藏状态栏+隐藏返回键」细化为「状态栏收合至提示线 + 保留左缘返回+顶部提示线+下滑通知中心(热区缩小)」(见 A-13)。 +- **A-13 全屏语义细化(状态栏「隐藏」→「收合至提示线」)**:既有 §3.1/§5.1 描述全屏=隐藏状态栏(`meow_ui_statusbar_height()`=0、隐藏返回键=沉浸);本次定案细化为**进入/退出全屏:状态栏向上收出屏顶、只留一条提示线高度**,**全屏下保留**左缘返回手势 + 顶部动态伸缩提示线,且**顶部下滑仍可开通知中心(顶部热区缩小)**。→ **已实现(M2.5x)**:SDK 端明确「收合(collapsed)=状态栏缩为提示线(4px)」vs「隐藏=高度0」;全屏下保留左缘返回+顶部伸缩滑条+顶部下滑通知中心(热区缩小)。⚠️ **全屏顶部动态伸缩滑条仍有已知问题在跟进(agent-112)**。 - **A-14 `fonts` 键二义**:§5.7.3 `"fonts":[…]`=应用自带字库句柄数组;§5.2/§5.7.8 `"fonts":"full"|"gbk"|"common"`=字库档位字符串。→ 同名键两种语义,解析器需区分(字符串→档位 / 数组→句柄);若避免歧义可改用专键如 `"font_tier"`(**未拍板**)。 `[各源标注见上]` diff --git a/font/meow_font.c b/font/meow_font.c index 05f0a34..9283978 100644 --- a/font/meow_font.c +++ b/font/meow_font.c @@ -22,6 +22,7 @@ #include #include #include +#include /* access():字库缺失判定用 */ /* ---------------- 最小 zlib ABI 声明(与 libz.a 匹配,mipsel 32-bit unsigned long=4B) ------- */ typedef unsigned char mzf_Bytef; @@ -721,3 +722,196 @@ int meow_font_config_multifont(int on, if (distinct) *distinct = d_n; return 0; } + +/* ================================================================== */ +/* M2.58 字体索引 + 应用字库查询(规格 §5.7:索引文件 / 查列表 / 丢失降级默认) */ +/* ================================================================== */ +/* SDK 可选日志:SDK 链 meow_log 强符号(→app 日志);launcher 无 meow_log(弱符号 → NULL, + * 静默跳过)——避免 meow_font.o 依赖 SDK,launcher 链接不至于为未定义 meow_log 而失败。 */ +extern void meow_log(int lvl, const char *fmt, ...) __attribute__((weak)); +#define MEOW_FONT_INDEX_MAX 32 /* index 条数上限(实际 ≤17,留余量) */ + +#define MEOW_FONT_DEF_PNG "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.png" +#define MEOW_FONT_DEF_IDX "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.idx" +#define MEOW_FONT_SYS_PNG "/usr/share/meowos/font/font.png" +#define MEOW_FONT_SYS_IDX "/usr/share/meowos/font/font.idx" + +const char *meow_font_card_root(void) +{ + const char *e = getenv("MEOW_CARD_ROOT"); + return (e && e[0]) ? e : "/usr/data/mnt/sd_0"; +} + +int meow_font_index_path(char *out, int olen) +{ + if (!out || olen <= 0) return -1; + snprintf(out, (size_t)olen, "%s/%s", meow_font_card_root(), MEOW_FONT_INDEX_REL); + return 0; +} + +/* 合成「板载默认」条目(index0;无卡/无 index 时唯一项)。 */ +static void meow_font_default_entry(meow_font_info *out) +{ + if (!out) return; + memset(out, 0, sizeof *out); + snprintf(out->family, sizeof out->family, "default"); + snprintf(out->name, sizeof out->name, "默认字体"); + snprintf(out->weight, sizeof out->weight, "regular"); + snprintf(out->tier, sizeof out->tier, "common"); + out->glyph_count = 0; + snprintf(out->label, sizeof out->label, "默认字体 Regular(common)"); + snprintf(out->path_png, sizeof out->path_png, "MeowOS/fonts/default/regular/font.png"); +} + +/* CSV 单字段读取(支持双引号包裹 + 内嵌双引号转义)。p 走到下一字段/行尾。 */ +static int csv_field(const char **pp, char *out, int olen) +{ + const char *p = *pp; + if (!out || olen <= 0) return 0; + int o = 0; + if (*p == '"') { + p++; + while (*p && o < olen - 1) { + if (*p == '"') { + if (p[1] == '"') { out[o++] = '"'; p += 2; continue; } + p++; /* 结束引号 */ + break; + } + out[o++] = *p++; + } + if (*p == ',') p++; + else if (*p == '\r' || *p == '\n') { /* 行尾,由外层清理 */ } + } else { + while (*p && *p != ',' && *p != '\r' && *p != '\n' && o < olen - 1) + out[o++] = *p++; + if (*p == ',') p++; + } + out[o] = 0; + *pp = p; + return 1; +} + +/* 有界复制(字符串入 dst≤dlen 且保证 NUL;避免 -Wformat-truncation 之类告警)。 */ +static void fcpy(char *dst, int dlen, const char *src) +{ + if (dlen <= 0) return; + size_t n = src ? strlen(src) : 0; + if (n >= (size_t)dlen) n = (size_t)dlen - 1; + memcpy(dst, src, n); + dst[n] = 0; +} + +/* 解析一行 index(CSV 6 字段)。成功填 info 并返回 1;path 为空 → 0(跳过)。 */ +static int meow_font_parse_index_line(char *line, meow_font_info *info) +{ + if (!line || !info) return 0; + const char *p = line; + char f[6][360]; + for (int i = 0; i < 6; i++) { + if (!csv_field(&p, f[i], (int)sizeof f[i])) return 0; + if (!f[i][0]) return 0; + } + memset(info, 0, sizeof *info); + fcpy(info->family, sizeof info->family, f[0]); + fcpy(info->name, sizeof info->name, f[0]); /* index 无单列 name → 回退 family */ + fcpy(info->weight, sizeof info->weight, f[1]); + fcpy(info->path_png, sizeof info->path_png, f[2]); + fcpy(info->label, sizeof info->label, f[3]); + fcpy(info->tier, sizeof info->tier, f[4]); + info->glyph_count = atoi(f[5]); + return info->path_png[0] ? 1 : 0; +} + +int meow_font_index_load(meow_font_info *out, int max) +{ + if (!out || max <= 0) return 0; + char path[450]; + if (meow_font_index_path(path, sizeof path) != 0) return 0; + FILE *f = fopen(path, "r"); + if (!f) { meow_font_default_entry(&out[0]); return 1; } /* 无卡/无 index → 仅板载默认 */ + int n = 0, total = 0; + char line[1024]; + while (fgets(line, sizeof line, f)) { + char *s = line; + while (*s == ' ' || *s == '\t') s++; + if (!*s || *s == '\n' || *s == '\r' || *s == '#') continue; /* 忽略空行/注释 */ + meow_font_info tmp; + if (!meow_font_parse_index_line(s, &tmp)) continue; + total++; + if (n < max) { out[n] = tmp; n++; } + } + fclose(f); + if (total == 0) { meow_font_default_entry(&out[0]); return 1; } + return total; +} + +int meow_font_get_available_count(void) +{ + meow_font_info buf[1]; + return meow_font_index_load(buf, 1); /* max=1:只统计总数,不要求全填 */ +} + +int meow_font_enumerate(int idx, meow_font_info *out) +{ + if (!out || idx < 0) return 0; + meow_font_info buf[MEOW_FONT_INDEX_MAX]; + int n = meow_font_index_load(buf, MEOW_FONT_INDEX_MAX); + if (idx >= n) return 0; + memcpy(out, &buf[idx], sizeof *out); + return 1; +} + +/* 相对路径按卡根解析为绝对路径(绝对路径原样返回)。 */ +static void font_abs_path(const char *rel, char *out, int olen) +{ + if (!rel || !rel[0]) { if (olen > 0) out[0] = 0; return; } + if (rel[0] == '/') { snprintf(out, (size_t)olen, "%s", rel); return; } + snprintf(out, (size_t)olen, "%s/%s", meow_font_card_root(), rel); +} + +/* 由 png 绝对路径派生同目录 font.idx(.png → .idx)。0=成功。 */ +static int font_idx_from_png(const char *png, char *out, int olen) +{ + if (!png || olen <= 0) return -1; + snprintf(out, (size_t)olen, "%s", png); + size_t n = strlen(out); + if (n > 4 && !strcmp(out + n - 4, ".png")) { + out[n-4]='.'; out[n-3]='i'; out[n-2]='d'; out[n-1]='x'; out[n]=0; + return 0; + } + return -1; +} + +/* 装载系统默认字体(跟随 font.file,经 env MEOW_FONT_PNG/IDX → 卡默认 → 固件)。 + * 与 SDK meow_ui_gfx load_font 的优先级一致;app 经 select_by_id(0) 或任何降级回退时走此路径。 */ +static int meow_font_load_system_default(void) +{ + const char *ep = getenv("MEOW_FONT_PNG"); + const char *ei = getenv("MEOW_FONT_IDX"); + char idxbuf[400]; + if (ep && ep[0]) { + if (!ei || !ei[0]) { + if (font_idx_from_png(ep, idxbuf, sizeof idxbuf) != 0) ei = NULL; + else ei = idxbuf; + } + if (ei && meow_font_load(ep, ei) == 0) return 0; + } + if (meow_font_load(MEOW_FONT_DEF_PNG, MEOW_FONT_DEF_IDX) == 0) return 0; + if (meow_font_load(MEOW_FONT_SYS_PNG, MEOW_FONT_SYS_IDX) == 0) return 0; + return -1; +} + +int meow_font_select_by_id(int id) +{ + if (id == 0) return meow_font_load_system_default(); /* 跟随系统默认(font.file) */ + meow_font_info info; + if (!meow_font_enumerate(id, &info)) return -1; /* 越界 → 负值 */ + if (!info.path_png[0]) return -1; + char png[400], idx[400]; + font_abs_path(info.path_png, png, sizeof png); + if (font_idx_from_png(png, idx, sizeof idx) != 0) return -1; + if (access(png, R_OK) == 0 && meow_font_load(png, idx) == 0) return 0; + /* 字库缺失 → 降级默认(不丢请求)+ WARN(app 日志;meow_log 为 SDK 统一日志) */ + if (meow_log) meow_log(2 /* MEOW_LOG_WARN */, "font %s missing, fallback default", png); + return meow_font_load_system_default(); +} diff --git a/font/meow_font.h b/font/meow_font.h index eb6b8ff..7860d54 100644 --- a/font/meow_font.h +++ b/font/meow_font.h @@ -117,6 +117,54 @@ int meow_font_config_multifont(int on, int meow_font_import_zip(const char *zip_path); /* TODO:MeowPanel */ int meow_font_export_zip(const char *family, const char *out_zip); /* TODO:MeowPanel */ +/* ================================================================== */ +/* M2.58 字体索引 + 应用字库查询(规格 §5.7:索引文件 / 查列表 / 丢失降级默认) */ +/* ================================================================== */ +/* 系统字体列表的持久化快照 = TF 卡 /MeowOS/config/fonts/index(CSV,每套一条: + * "family","weight","path","label","tier",glyph_count + * path = 卡根相对 font.png(如 MeowOS/fonts/misans/regular/font.png) + * label = 显示名(含字重 + 尺寸标记,如 `MiSans Regular(common≈4k)`) + * tier = common|gbk|full(档位,信息用 + 驱动按需加载) + * launcher 扫描 MeowOS/fonts/// 后写该文件(覆盖,含默认行 index0); + * SDK 侧 meow_font_get_available_count()/enumerate() 只从这个文件读,不依赖 launcher 内存数组; + * 无卡/无 index → 仅 1 项板载默认(family=default)。 */ +#define MEOW_FONT_INDEX_REL "MeowOS/config/fonts/index" + +/* 权威列表数据模型(SDK 侧快照,枚举时填充;规格 §5.7.1)。 */ +#ifndef MEOW_FONT_INFO_DEFINED +#define MEOW_FONT_INFO_DEFINED +typedef struct meow_font_info { + char family[64]; /* family slug = MeowOS/fonts// 一级目录名 */ + char name[64]; /* 显示名(index 无单列 → 回退 family) */ + char weight[32]; /* weight slug(regular|bold|…,缺省 regular) */ + char tier[8]; /* 档位 common|gbk|full(info 用) */ + int glyph_count; /* font.json glyph_count(info 用) */ + char label[128]; /* index 显示名(含字重 + 尺寸标记) */ + int size_list[16]; /* 原生字号表(index 无 → 空) */ + int size_count; /* 有效字号数 */ + int bit_depth; /* 4|8(index 无 → 0) */ + char path_png[320]; /* 卡根相对 MeowOS/fonts///font.png */ +} meow_font_info; +#endif + +/* 卡根:env MEOW_CARD_ROOT 优先,否则默认 /usr/data/mnt/sd_0。 */ +const char *meow_font_card_root(void); +/* /MeowOS/config/fonts/index 绝对路径写入 out;0=成功。 */ +int meow_font_index_path(char *out, int olen); +/* 读 index → 填充 out 数组(最多 max 条),返回「总条数」(无 index/不可读/空 → 1=合成默认行)。 + * 注意 out[i].path_png 为「卡根相对」路径,需经卡根解析为绝对路径再使用。 */ +int meow_font_index_load(meow_font_info *out, int max); + +/* 应用可读 TF 卡字体列表(规格 §5.7.2): + * - get_available_count():可用字库数(含默认;无卡 → 1)。 + * - enumerate(idx, out):按序号取,越界返回 0;idx=0=默认行(family=default)。 + * - select_by_id(id):id=0 跟随系统默认(font.file,经 env MEOW_FONT_PNG/IDX); + * id>0 会话内固定用该套;该套字库缺失 → 降级默认(不丢请求)+ WARN 日志 + * (meow_log→app 日志)。成功 0,越界负值。 */ +int meow_font_get_available_count(void); +int meow_font_enumerate(int idx, meow_font_info *out); +int meow_font_select_by_id(int id); + #ifdef __cplusplus } #endif diff --git a/mips-repo/README.md b/mips-repo/README.md index c6b2c88..1974e72 100644 --- a/mips-repo/README.md +++ b/mips-repo/README.md @@ -2,24 +2,42 @@ 目标设备:R3 Pro II / MeowOS 固件(**mipsel-linux-muslsf**,静态,无 glibc 依赖)。 用途:存放针对该设备的交叉编译二进制与构建信息,便于**复用**到后续固件 / TF 卡 -(MeowOpt 目录;`MeowLib` 未定义/未用——`MeowOpt` 与 `MeowOS/opt` 命名需归一:代码用 `MeowOS/opt`,文档 `MeowOpt` 指卡上外部工具目录。[源:审计 2026-08-30])。 +(`MeowOS/opt` 目录;`MeowLib` 未定义/未用——`MeowOpt` 与 `MeowOS/opt` 命名需归一:代码用 `MeowOS/opt`,文档 `MeowOpt` 指卡上外部工具目录。[源:审计 2026-08-30])。 + +## 仓库结构(**两半**:`bins/` 普通二进制工具 + `mpk/` `.mpk` 软件包) -## 仓库结构(每工具一个目录) ``` mips-repo/ README.md # 本文件 - / - build.sh # 交叉编译脚本(可重复执行) - manifest.md # 元信息:来源/版本/arch/校验/产物路径/安装目标 - bin/ # 交叉编译产物(静态 mipsel,如存在) + bins/ # 「普通二进制/工具」仓库:交叉编译产物 + 构建/元信息 + / + build.sh # 交叉编译脚本(可重复执行;可搬,路径相对本目录) + manifest.md # 元信息:来源/版本/arch/校验/产物路径/安装目标 + bin/ # 交叉编译产物(静态 mipsel,如存在) + (.c 源码如自研也放此) + mpk/ # 「.mpk 文件」仓库:MeowAPPs 软件包(.mpk = tar.gz) + README.md # .mpk 规范说明(格式/字段/安装) + .mpk # 成品包(app.json + meow 二进制 + 资源;不含 data/cache) ``` +> 两者定位不同:`bins/` 是**宿主侧工具**(服务/二进制,如 mDNS 应答器、netd),由 +> **meow_svcd** 托管、打进固件或按需从卡 `MeowOS/opt//` 部署;`mpk/` 是**应用层 +> 软件包**(MeowAPP 分发单元),用 **`nyai`** 安装到 `MeowAPPs/<包名>/`。 + ## 当前已入库工具 -- **mdnsd/**:Apple mDNSResponder-2881.0.25 交叉编译产物(`mipsel-linux-muslsf` 静态)。两种变体同在 `bin/`: + +### bins/(普通二进制 / 工具仓库) + +- **bins/mdnsd/**:Apple mDNSResponder-2881.0.25 交叉编译产物(`mipsel-linux-muslsf` 静态)。两种变体同在 `bin/`: - `bin/mdnsd` = **SAResponder**(mDNSResponderPosix,单服务注册型,`-n/-f` 只发布/回指定服务)strip 后 **885.6 KB**; - `bin/mdnsd.bin` = **完整 mdnsd** 守护进程(通用宿主响应器,回任意 `.local` 查询)strip 后 **994 KB**。 - ⚠️ 实测(内核无 IPv6)**两者均失败**:`socket AF_INET6: Address family not supported by protocol`;且即便 `make HAVE_IPV6=0` 编译通过、daemon 能跑,**也不发布主机名 A 记录**(IPC 服务型守护进程,等客户端注册)。故 mDNS 方案A 判为**不达标**,由 `meow_mdns/` 替代(见下)。 -- **meow_mdns/**:自研极简 IPv4-only mDNS 应答器(~77.6 KB,`mipsel-linux-muslsf` 静态)。join `224.0.0.251:5353`,应答 `.local` A + `_ssh._tcp.local` PTR/SRV/TXT;零 IPv6 依赖。部署到 TF `MeowOS/opt/mdnsd/mdnsd`(95 KB < 阈值,可考虑进固件)。详见 `meow_mdns/manifest.md`。⚠️ 手机热点**丢弃客户端间组播**→ 设备能应答、主机收不到(avahi 只认组播应答),`avahi-resolve *.local` 仍超时(网络层热点隔离,非应答器缺陷),需主机 DNS 兜底。 + ⚠️ 实测(内核无 IPv6)**两者均失败**:`socket AF_INET6: Address family not supported by protocol`;且即便 `make HAVE_IPV6=0` 编译通过、daemon 能跑,**也不发布主机名 A 记录**(IPC 服务型守护进程,等客户端注册)。故 mDNS 方案A 判为**不达标**,由 `bins/meow_mdns/` 替代(见下)。 +- **bins/meow_mdns/**:自研极简 IPv4-only mDNS 应答器(~77.6 KB,`mipsel-linux-muslsf` 静态)。join `224.0.0.251:5353`,应答 `.local` A + `_ssh._tcp.local` PTR/SRV/TXT;零 IPv6 依赖。部署到 TF `MeowOS/opt/mdnsd/mdnsd`(95 KB < 阈值,可考虑进固件,实际已改板载 `/usr/bin/meow_mdns`,见下)。详见 `bins/meow_mdns/manifest.md`。⚠️ 手机热点**丢弃客户端间组播**→ 设备能应答、主机收不到(avahi 只认组播应答),`avahi-resolve *.local` 仍超时(网络层热点隔离,非应答器缺陷),需主机 DNS 兜底。 + +### mpk/(`.mpk` 软件包仓库) + +- 目前**为空**(仅 `README.md` 说明规范)。MeowAPPs 成品包 `.mpk` 集中放此,供 `nyai` 经 + `sources.list`(本地源 = `MeowOS/opt/repo`)或 `nyai install ` 直接安装。 ## 目标架构与工具链 - 架构:`mipsel`(MIPS32r2, little-endian) @@ -27,13 +45,54 @@ mips-repo/ - 交叉编译器:`~/.tools/zig-mipsel-cc`(zig 目标 mips-linux-musl) - 目标静态库 sysroot:`~/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf` -## 新增一个工具的流程 -1. `mkdir -p /bin`,写 `/build.sh`(用 zig-mipsel-cc,`-Os -static`)。 -2. 跑 `build.sh`,产出放 `/bin/`。 -3. 写 `/manifest.md`:来源(如 Apple-OSS/mDNSResponder 版本)、ARCH、`file` - 输出摘要、`ls -l` 大小、构建命令、安装路径(固件 or TF MeowOpt)。 +## 服务管理:meow_svcd CLI(托管 `bins/` 下的服务) + +`bins/` 里的常驻服务(如 `meow_mdns`)由 **meow_svcd**(类 systemd/systemctl,固件 +`/usr/bin/meow_svcd`,板载 `etc/init.d/S89meow_svcd` 开机自启)托管,SSH 到设备后可直接用: + +``` +meow_svcd status # 列出所有服务与状态(card_present/wifi_up/services=N) +meow_svcd restart meow_mdns # 重启单服务(如板载 mDNS 应答器) +meow_svcd stop all # 停止全部服务 +meow_svcd reload # 重读板载+TF 双源 services.d +meow_svcd start meow_mdns # 启动单服务 +meow_svcd pid # 打印守护进程 pid +``` + +- 控制走 AF_UNIX 单行文本,默认 socket `/tmp/meow_svcd.sock`;应答 `OK` / `ERR no-such-service` / + 多行 `svc= state=… pid=… restart=… exit=…`。守护默认**后台**;`-f/--foreground` 前台(供 + supervisor `fork/exec`+`waitpid` 托管)。 +- **服务目录规则(双源板载优先)**:固件工具 → 板载 `/usr/data/meow/services.d/.conf`(Data 区, + 拔卡保留);TF 工具/第三方 → TF `MeowOS/services.d/.conf`(随卡存在/消失);同名板载优先(TF 被屏蔽 + WARN)。 +- 详见 `patch_v2/meow_svcd/meow_svcd.md` / `docs/MEOWOS_SPEC_REF.md §4`。 + +## 应用包:`.mpk` 规范 & `nyai` + +`.mpk` 是 MeowOS 的应用分发单元(`MeowAPPs`),规范与 `nyai` 用法见 `APPS.md 附录 §3(.mpk) / §5(nyai)`。 +要点: + +- **包名 = 文件名茎 = `app.json` 的 `package`**(如 `os.meow.hello` → `os.meow.hello.mpk`)。 +- **格式**:`.mpk` = tar.gz,含 `app.json` + `meow` 二进制 + 资源,**不含 data/cache**(运行时按设备生成)。 +- **安装**:`nyai install `;`nyai index ` 为目录生成 `Packages` 索引 + (Package/Version/Filename/Size/SHA256/Depends/Description);`nyai export ` 打包导出。 +- **部署 `nyai`**:`MeowOS/opt/bin/nyai`(卡,随 TF_CARD_LAYOUT 外围工具目录)。M2.12+ 固件 + `meow_card_path.sh` 已前插 `MeowOS/opt/bin` 可直呼;更早固件上 SSH 需绝对路径 + `/usr/data/mnt/sd_0/MeowOS/opt/bin/nyai` 或 `export PATH=/usr/data/mnt/sd_0/MeowOS/opt/bin:$PATH`。 + +## 新增一个工具/包的流程 + +- **工具(进 `bins/`)**: + 1. `mkdir -p bins//bin`,写 `bins//build.sh`(用 zig-mipsel-cc,`-Os -static`)。 + 2. 跑 `build.sh`,产出放 `bins//bin/`。 + 3. 写 `bins//manifest.md`:来源(如 Apple-OSS/mDNSResponder 版本)、ARCH、`file` + 输出摘要、`ls -l` 大小、构建命令、安装路径(固件 or TF MeowOpt)。 +- **应用包(进 `mpk/`)**: + 1. 打包不含 data/cache 的 `MeowAPPs/<包名>/` → `mpk/<包名>.mpk`(或 `nyai export`)。 + 2. `nyai index mpk/` 生成/刷新 `Packages` 索引(供经源安装)。 + 3. `.mpk` 字段与安装语义见 `mpk/README.md` / `APPS.md 附录 §3`。 ## 部署到哪里 -- 体积小(约 <200KB)→ 可直接打进固件(/usr/bin 或由 supervisor 启动)。 -- 体积大 → 放 TF 卡 `MeowOpt//`,由 MeowOS 启动时从卡加载(拔卡降级不崩)。 -- **按需取用**:从 `MeowOpt//` 按需部署,不一定要全部入库;若某工具体积仍偏重(如 mdnsd 的 885~994KB),可评估更小方案(OpenWrt linkd / libmdns,或自研极简响应器——仅回 A 记录 + `_ssh` 服务)以塞进固件 <200KB。 +- 体积小(约 <200KB)→ 可直接打进固件(/usr/bin 或由 supervisor/meow_svcd 启动)。 +- 体积大 → 放 TF 卡 `MeowOS/opt//`,由 MeowOS 启动时从卡加载(拔卡降级不崩)。 +- **按需取用**:从 `MeowOS/opt//` 按需部署,不一定要全部入库;若某工具体积仍偏重(如 mdnsd 的 885~994KB),可评估更小方案(OpenWrt linkd / libmdns,或自研极简响应器——仅回 A 记录 + `_ssh` 服务)以塞进固件 <200KB。 +- `.mpk` → 经 `nyai` 装到 `MeowAPPs/<包名>/`(数据建 `MeowData/<包名>/{data,cache}`)。 diff --git a/mips-repo/mdnsd/bin/mdnsd b/mips-repo/bins/mdnsd/bin/mdnsd similarity index 100% rename from mips-repo/mdnsd/bin/mdnsd rename to mips-repo/bins/mdnsd/bin/mdnsd diff --git a/mips-repo/mdnsd/bin/mdnsd.bin b/mips-repo/bins/mdnsd/bin/mdnsd.bin similarity index 100% rename from mips-repo/mdnsd/bin/mdnsd.bin rename to mips-repo/bins/mdnsd/bin/mdnsd.bin diff --git a/mips-repo/mdnsd/build.sh b/mips-repo/bins/mdnsd/build.sh similarity index 100% rename from mips-repo/mdnsd/build.sh rename to mips-repo/bins/mdnsd/build.sh diff --git a/mips-repo/mdnsd/manifest.md b/mips-repo/bins/mdnsd/manifest.md similarity index 100% rename from mips-repo/mdnsd/manifest.md rename to mips-repo/bins/mdnsd/manifest.md diff --git a/mips-repo/meow_mdns/bin/meow_mdns b/mips-repo/bins/meow_mdns/bin/meow_mdns similarity index 100% rename from mips-repo/meow_mdns/bin/meow_mdns rename to mips-repo/bins/meow_mdns/bin/meow_mdns diff --git a/mips-repo/meow_mdns/build.sh b/mips-repo/bins/meow_mdns/build.sh similarity index 100% rename from mips-repo/meow_mdns/build.sh rename to mips-repo/bins/meow_mdns/build.sh diff --git a/mips-repo/meow_mdns/manifest.md b/mips-repo/bins/meow_mdns/manifest.md similarity index 100% rename from mips-repo/meow_mdns/manifest.md rename to mips-repo/bins/meow_mdns/manifest.md diff --git a/mips-repo/meow_mdns/meow_mdns.c b/mips-repo/bins/meow_mdns/meow_mdns.c similarity index 100% rename from mips-repo/meow_mdns/meow_mdns.c rename to mips-repo/bins/meow_mdns/meow_mdns.c diff --git a/mips-repo/mpk/README.md b/mips-repo/mpk/README.md new file mode 100644 index 0000000..7bec691 --- /dev/null +++ b/mips-repo/mpk/README.md @@ -0,0 +1,49 @@ +# mips-repo/mpk/ — MeowOS `.mpk` 软件包仓库 + +本目录存放 MeowAPPs 的成品**`.mpk`** 软件包(应用分发单元),供 `nyai` 安装。规范见 +`APPS.md 附录 §3(.mpk) / §5(nyai)`,此处为速览与示例。 + +目前仓库为**空**(仅说明);首个包入仓后形如: + +``` +mpk/ + README.md # 本文件 + .mpk # 成品包(tar.gz) + Packages # 可选:nyai index mpk/ 生成的索引 +``` + +## `.mpk` 规范 + +- **包名 `` = 文件名茎 = `app.json` 的 `package`**(如 `os.meow.hello` → `os.meow.hello.mpk`)。 +- **格式**:`.mpk` = **tar.gz**,含 `app.json` + `meow` 二进制 + 资源,**不含 `data`/`cache`** + (运行时按设备生成)。打包目录为 `MeowAPPs/<包名>/`(剔除 data/cache 后打包)。 +- **关键字段**(在 `app.json` 内,`package` 必填):`package`/`name`/`version`/`exec`/ + `routes`/`services`/`depends`/`description` 等;`size`/`sha256` 由索引生成。 +- **安装**:`nyai install <包或文件.mpk>`(读 `.mpk` 内 `app.json`,校验包名/哈希,解压到 + `MeowAPPs/<包名>/`,同名先备份 `.bak_`,触发数据 init 建 `MeowData/<包名>/{data,cache}`)。 +- **导出**:`nyai export [out.mpk]`(打包 `MeowAPPs//`,不含 data/cache)。 +- **索引**:`nyai index ` 为目录生成 `Packages`(每行 `Package/Version/Filename/Size/SHA256/ + Depends/Description`,空行分隔记录),供经源(`sources.list`)安装解析依赖。 + +## 示例(结构示意) + +以 `os.meow.hello` 为例,打包自 `MeowAPPs/os.meow.hello/`: + +```bash +# 打包导出 +nyai export os.meow.hello # → os.meow.hello.mpk + +# 或手动:剔除 data/cache 后 tar.gz(与 launcher 同 gzip -dc | tar -x 管道语义) +cd MeowAPPs/os.meow.hello && tar czf os.meow.hello.mpk app.json meow assets/ + +# 本地源目录 + 索引生成 +mkdir -p /usr/data/mnt/sd_0/MeowOS/opt/repo +cp os.meow.hello.mpk /usr/data/mnt/sd_0/MeowOS/opt/repo/ +nyai index /usr/data/mnt/sd_0/MeowOS/opt/repo # 生成 Packages + +# 在设备上安装(M2.12+ 可直呼 nyai;更早固件用绝对路径) +nyai install os.meow.hello # 或 nyai install os.meow.hello.mpk +``` + +> 注:`nyai` 部署于卡 `MeowOS/opt/bin/nyai`(M2.12+ 由 `meow_card_path.sh` 前插 PATH 可直呼)。 +> 卡上源配置 `MeowOS/opt/nyai/sources.list`(无此文件默认本地源 `MeowOS/opt/repo`)。 diff --git a/patch_v2/hello_app/hello b/patch_v2/hello_app/hello index 4ed5387..1ffb089 100755 Binary files a/patch_v2/hello_app/hello and b/patch_v2/hello_app/hello differ diff --git a/patch_v2/meow_launcherd/meow_font.h b/patch_v2/meow_launcherd/meow_font.h index eb6b8ff..7860d54 100644 --- a/patch_v2/meow_launcherd/meow_font.h +++ b/patch_v2/meow_launcherd/meow_font.h @@ -117,6 +117,54 @@ int meow_font_config_multifont(int on, int meow_font_import_zip(const char *zip_path); /* TODO:MeowPanel */ int meow_font_export_zip(const char *family, const char *out_zip); /* TODO:MeowPanel */ +/* ================================================================== */ +/* M2.58 字体索引 + 应用字库查询(规格 §5.7:索引文件 / 查列表 / 丢失降级默认) */ +/* ================================================================== */ +/* 系统字体列表的持久化快照 = TF 卡 /MeowOS/config/fonts/index(CSV,每套一条: + * "family","weight","path","label","tier",glyph_count + * path = 卡根相对 font.png(如 MeowOS/fonts/misans/regular/font.png) + * label = 显示名(含字重 + 尺寸标记,如 `MiSans Regular(common≈4k)`) + * tier = common|gbk|full(档位,信息用 + 驱动按需加载) + * launcher 扫描 MeowOS/fonts/// 后写该文件(覆盖,含默认行 index0); + * SDK 侧 meow_font_get_available_count()/enumerate() 只从这个文件读,不依赖 launcher 内存数组; + * 无卡/无 index → 仅 1 项板载默认(family=default)。 */ +#define MEOW_FONT_INDEX_REL "MeowOS/config/fonts/index" + +/* 权威列表数据模型(SDK 侧快照,枚举时填充;规格 §5.7.1)。 */ +#ifndef MEOW_FONT_INFO_DEFINED +#define MEOW_FONT_INFO_DEFINED +typedef struct meow_font_info { + char family[64]; /* family slug = MeowOS/fonts// 一级目录名 */ + char name[64]; /* 显示名(index 无单列 → 回退 family) */ + char weight[32]; /* weight slug(regular|bold|…,缺省 regular) */ + char tier[8]; /* 档位 common|gbk|full(info 用) */ + int glyph_count; /* font.json glyph_count(info 用) */ + char label[128]; /* index 显示名(含字重 + 尺寸标记) */ + int size_list[16]; /* 原生字号表(index 无 → 空) */ + int size_count; /* 有效字号数 */ + int bit_depth; /* 4|8(index 无 → 0) */ + char path_png[320]; /* 卡根相对 MeowOS/fonts///font.png */ +} meow_font_info; +#endif + +/* 卡根:env MEOW_CARD_ROOT 优先,否则默认 /usr/data/mnt/sd_0。 */ +const char *meow_font_card_root(void); +/* /MeowOS/config/fonts/index 绝对路径写入 out;0=成功。 */ +int meow_font_index_path(char *out, int olen); +/* 读 index → 填充 out 数组(最多 max 条),返回「总条数」(无 index/不可读/空 → 1=合成默认行)。 + * 注意 out[i].path_png 为「卡根相对」路径,需经卡根解析为绝对路径再使用。 */ +int meow_font_index_load(meow_font_info *out, int max); + +/* 应用可读 TF 卡字体列表(规格 §5.7.2): + * - get_available_count():可用字库数(含默认;无卡 → 1)。 + * - enumerate(idx, out):按序号取,越界返回 0;idx=0=默认行(family=default)。 + * - select_by_id(id):id=0 跟随系统默认(font.file,经 env MEOW_FONT_PNG/IDX); + * id>0 会话内固定用该套;该套字库缺失 → 降级默认(不丢请求)+ WARN 日志 + * (meow_log→app 日志)。成功 0,越界负值。 */ +int meow_font_get_available_count(void); +int meow_font_enumerate(int idx, meow_font_info *out); +int meow_font_select_by_id(int id); + #ifdef __cplusplus } #endif diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index 3bdb672..0917e7c 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meow_launcherd/meow_launcherd.c b/patch_v2/meow_launcherd/meow_launcherd.c index a41a5bf..7019d0b 100644 --- a/patch_v2/meow_launcherd/meow_launcherd.c +++ b/patch_v2/meow_launcherd/meow_launcherd.c @@ -897,7 +897,7 @@ static int meow_font_ensure(void) return 1; } if (have_sel) - logmsg("meow_font: %s load failed, fallback", fpng); + logmsg("meow_font: selected font %s missing, fallback default", fpng); /* 系统日志 */ /* 卡默认 → 固件。 */ if (meow_font_load(UI_FONT_PNG_CARD, UI_FONT_IDX_CARD) == 0 || meow_font_load(UI_FONT_PNG_SYS, UI_FONT_IDX_SYS) == 0) { @@ -906,6 +906,7 @@ static int meow_font_ensure(void) meow_font_has_size(28), meow_font_has_size(20)); return 1; } + logmsg("meow_font: all font sources missing, using 5x7 bitmap"); /* WARN:字库不可用 */ return 0; } @@ -1082,6 +1083,9 @@ static void meow_font_reload_multifont(void) cidx[c][n-4]='.'; cidx[c][n-3]='i'; cidx[c][n-2]='d'; cidx[c][n-1]='x'; cidx[c][n]=0; } cfg[c].png = cpng[c]; cfg[c].idx = cidx[c]; + } else { + /* M2.58:类别字体配置了但缺失 → 该类别回退全局,并记系统 WARN。 */ + logmsg("multi_font: class %s font %s missing, fallback global", cls_keys[c], ref); } } } @@ -1900,10 +1904,84 @@ static int meow_font_probe_weight(const char *dirpath, const char *family, const return 1; } +/* M2.58 字体索引:CSV 字段写入(始终双引号包裹,内嵌双引号加倍)。返回写入长度。 */ +static int font_index_csvq(char *dst, int olen, const char *s) +{ + int o = 0; + if (olen <= 0) return 0; + if (o < olen) dst[o++] = '"'; + for (; *s && o < olen - 3; s++) { + if (*s == '"') dst[o++] = '"'; + if (o < olen - 2) dst[o++] = *s; + } + if (o < olen) dst[o++] = '"'; + dst[o] = 0; + return o; +} + +/* 把当前 font_presets* 扫描结果落盘为 /MeowOS/config/fonts/index(覆盖)。 + * 每套一条 `"family","weight","path","label","tier",glyph_count`;path=卡根相对 font.png。 + * 目录不存在则 mkdir -p;无卡(mkdir/fopen 失败)→ 静默放弃(仅内存默认项)。 + * 该文件是 SDK meow_font_get_available_count()/enumerate() 的持久数据源(规格 §5.7)。 */ +static void meow_font_write_index(const char *card_root) +{ + if (!card_root || !card_root[0]) return; + char t1[360], t2[380], base[400], path[420]; + snprintf(t1, sizeof t1, "%s/MeowOS", card_root); + snprintf(t2, sizeof t2, "%s/MeowOS/config", card_root); + snprintf(base, sizeof base, "%s/MeowOS/config/fonts", card_root); + /* 无卡(card_root 父目录不存在 → ENOENT)→ 静默放弃:仅内存默认项,不刷系统日志。 */ + if (mkdir(t1, 0755) != 0 && errno != EEXIST) { + if (errno != ENOENT) logmsg("fonts: index mkdir fail %s", t1); + return; + } + if (mkdir(t2, 0755) != 0 && errno != EEXIST) { logmsg("fonts: index mkdir fail %s", t2); return; } + if (mkdir(base, 0755) != 0 && errno != EEXIST) { logmsg("fonts: index mkdir fail %s", base); return; } + snprintf(path, sizeof path, "%s/index", base); + FILE *f = fopen(path, "w"); + if (!f) { logmsg("fonts: index fopen fail %s", path); return; } + char line[1024]; + for (int i = 0; i < font_presets_n; i++) { + const char *fam = font_preset_fams[i] ? font_preset_fams[i] : "default"; + const char *wt = font_preset_wts[i] ? font_preset_wts[i] : "regular"; + const char *val = font_presets[i] ? font_presets[i] : "MeowOS/fonts/default/regular/font.png"; + const char *lbl = font_preset_labels[i] ? font_preset_labels[i] : fam; + const char *tier = font_preset_tiers[i] ? font_preset_tiers[i] : "common"; + int o = 0; + o = font_index_csvq(line, sizeof line, fam); + line[o++] = ','; line[o] = 0; + o += font_index_csvq(line + o, sizeof line - (size_t)o, wt); + line[o++] = ','; line[o] = 0; + o += font_index_csvq(line + o, sizeof line - (size_t)o, val); + line[o++] = ','; line[o] = 0; + o += font_index_csvq(line + o, sizeof line - (size_t)o, lbl); + line[o++] = ','; line[o] = 0; + o += font_index_csvq(line + o, sizeof line - (size_t)o, tier); + line[o++] = ','; line[o] = 0; + snprintf(line + o, sizeof line - (size_t)o, "%d\n", font_preset_glyphs[i]); + fputs(line, f); + } + fclose(f); + logmsg("fonts: index written (%d entries) -> %s", font_presets_n, path); +} + /* 扫描 /MeowOS/fonts/ 下两级 //,重建字体 preset 列表。 * 每个 (family,weight) 组合一套(如 misans/regular、misans/bold);family 级 'default' 由 * 索引 0(默认字体)单独处理,不进循环,避免重复。上限 FONT_PRESETS_MAX - * (1 默认 + ≤16 用户字体)。无卡/无目录时仅默认。 */ + * (1 默认 + ≤16 用户字体)。无卡/无目录时仅默认。 + * + * M2.58 释放语义(规格 §5.7 需求 4「扫→写 index→释放(或按需读 index)」结论): + * - 持久来源 = /MeowOS/config/fonts/index(扫描后由 meow_font_write_index 落盘)。 + * SDK 只读该文件,从不依赖本内存数组。 + * - font_presets* / *_buf 均为「静态、有界(FONT_PRESETS_MAX=17)、非堆」数组:无内存泄漏, + * 故无运行期 free;「释放」含义 = 两个扫描点(启动 / 进设置页)重建 + 每套固定上限防膨胀。 + * - 设置字体列表(font.file / font.class_* 下拉、family→字重子菜单)在本视图内显示时用 + * 该数组;离开字体列表视图(run_settings_page 退出)无需显式 free(静态数组),下次 + * 进设置/重扫自动重建。 + * - ⚠️ 未做「启动后即清空数组」:meow_font_effective_paths_tiered(launch_app 大字库档位 + * 选库)与 meow_font_reload_multifont 在运行期持续读 font_presets_n,清空会造成大字库 + * 档位退化(red line「保持档位/多字体行为」)。如需「零驻留」,须把这几个核心路径改为 + * 直接读 index(较大重构,超出本批边界,留待后续)。 */ static void meow_font_scan_presets_root(const char *card_root) { font_presets_n = 0; @@ -1933,43 +2011,48 @@ static void meow_font_scan_presets_root(const char *card_root) char root[340]; snprintf(root, sizeof root, "%s/%s", card_root, MEOW_FONTS_CFG); DIR *d = opendir(root); - if (!d) return; /* 无卡/无字库目录:仅默认 */ - struct dirent *de; - while ((de = readdir(d)) && font_presets_n < FONT_PRESETS_MAX) { - if (de->d_name[0] == '.') continue; - if (!strcmp(de->d_name, "default")) continue; /* 默认字体由索引 0 单独处理 */ - char fampath[400]; - snprintf(fampath, sizeof fampath, "%s/%s", root, de->d_name); - DIR *w = opendir(fampath); /* 一级:/ */ - if (!w) continue; - struct dirent *we; - while ((we = readdir(w)) && font_presets_n < FONT_PRESETS_MAX) { - if (we->d_name[0] == '.') continue; - char dirpath[400], label[96], cs[8] = ""; int gc = 0; char wt[32] = "", nm[64] = ""; - snprintf(dirpath, sizeof dirpath, "%s/%s", fampath, we->d_name); - if (meow_font_probe_weight(dirpath, de->d_name, we->d_name, label, sizeof label, - cs, sizeof cs, &gc, wt, sizeof wt, nm, sizeof nm) == 0) - continue; /* 缺组件/损坏 → 跳过该套 */ - int i = font_presets_n; - snprintf(font_preset_val_buf[i], sizeof font_preset_val_buf[i], - "%s/%s/%s/font.png", MEOW_FONTS_CFG, de->d_name, we->d_name); - snprintf(font_preset_label_buf[i], sizeof font_preset_label_buf[i], "%s", label); - snprintf(font_preset_tier_buf[i], sizeof font_preset_tier_buf[i], "%s", cs[0]?cs:"common"); - snprintf(font_preset_fam_buf[i], sizeof font_preset_fam_buf[i], "%s", de->d_name); - snprintf(font_preset_wt_buf[i], sizeof font_preset_wt_buf[i], "%s", wt); - snprintf(font_preset_name_buf[i], sizeof font_preset_name_buf[i], "%s", nm); - font_presets[i] = font_preset_val_buf[i]; - font_preset_labels[i] = font_preset_label_buf[i]; - font_preset_tiers[i] = font_preset_tier_buf[i]; - font_preset_fams[i] = font_preset_fam_buf[i]; - font_preset_wts[i] = font_preset_wt_buf[i]; - font_preset_names[i] = font_preset_name_buf[i]; - font_preset_glyphs[i] = gc; - font_presets_n++; + if (d) { /* 无卡/无字库目录 → 跳过扫描,仅默认 */ + struct dirent *de; + while ((de = readdir(d)) && font_presets_n < FONT_PRESETS_MAX) { + if (de->d_name[0] == '.') continue; + if (!strcmp(de->d_name, "default")) continue; /* 默认字体由索引 0 单独处理 */ + char fampath[400]; + snprintf(fampath, sizeof fampath, "%s/%s", root, de->d_name); + DIR *w = opendir(fampath); /* 一级:/ */ + if (!w) continue; + struct dirent *we; + while ((we = readdir(w)) && font_presets_n < FONT_PRESETS_MAX) { + if (we->d_name[0] == '.') continue; + char dirpath[400], label[96], cs[8] = ""; int gc = 0; char wt[32] = "", nm[64] = ""; + snprintf(dirpath, sizeof dirpath, "%s/%s", fampath, we->d_name); + if (meow_font_probe_weight(dirpath, de->d_name, we->d_name, label, sizeof label, + cs, sizeof cs, &gc, wt, sizeof wt, nm, sizeof nm) == 0) + continue; /* 缺组件/损坏 → 跳过该套 */ + int i = font_presets_n; + snprintf(font_preset_val_buf[i], sizeof font_preset_val_buf[i], + "%s/%s/%s/font.png", MEOW_FONTS_CFG, de->d_name, we->d_name); + snprintf(font_preset_label_buf[i], sizeof font_preset_label_buf[i], "%s", label); + snprintf(font_preset_tier_buf[i], sizeof font_preset_tier_buf[i], "%s", cs[0]?cs:"common"); + snprintf(font_preset_fam_buf[i], sizeof font_preset_fam_buf[i], "%s", de->d_name); + snprintf(font_preset_wt_buf[i], sizeof font_preset_wt_buf[i], "%s", wt); + snprintf(font_preset_name_buf[i], sizeof font_preset_name_buf[i], "%s", nm); + font_presets[i] = font_preset_val_buf[i]; + font_preset_labels[i] = font_preset_label_buf[i]; + font_preset_tiers[i] = font_preset_tier_buf[i]; + font_preset_fams[i] = font_preset_fam_buf[i]; + font_preset_wts[i] = font_preset_wt_buf[i]; + font_preset_names[i] = font_preset_name_buf[i]; + font_preset_glyphs[i] = gc; + font_presets_n++; + } + closedir(w); } - closedir(w); + closedir(d); } - closedir(d); + /* M2.58:扫描结果落盘为 /MeowOS/config/fonts/index(含默认行 index0), + * 作为 SDK meow_font_get_available_count()/enumerate() 的持久数据源;无卡时仅默认 1 行, + * 由 meow_font_write_index 内部 mkdir-fail 静默放弃(此路径不刷屏)。 */ + meow_font_write_index(card_root); } /* 设备默认卡根扫描(等价现状:无卡时 opendir 失败 → 仅默认字体)。 */ diff --git a/patch_v2/meow_svcd/meow_svcd.md b/patch_v2/meow_svcd/meow_svcd.md index 71428da..a5b0e07 100644 --- a/patch_v2/meow_svcd/meow_svcd.md +++ b/patch_v2/meow_svcd/meow_svcd.md @@ -158,6 +158,32 @@ meow_svcd pid 应答:`OK` / `ERR no-such-service` / 多行 `svc=... state=... pid=... restart=... exit=...`。 +### 6.1 `systemctl` 式 CLI 用法(SSH 终端示例) + +守护已由固件 `etc/init.d/S89meow_svcd` 开机自启,SSH 登录设备后**直接敲**命令即可,无需先知道 socket: + +```bash +meow_svcd status # 列出所有服务 + 全局状态 +# # meow_svcd status +# card_present=1 wifi_up=0 services=3 +# svc=mdnsd state=running pid=1234 restart=0 exit=0 trigger=boot +# svc=meow_netd state=stopped pid=0 restart=0 exit=0 trigger=card-mount +# svc=ntpc state=stopped pid=0 restart=0 exit=0 trigger=card-mount + +meow_svcd restart mdnsd # 重启单个服务(如板载 mDNS 应答器) +meow_svcd start meow_netd # 启动单个服务 +meow_svcd stop all # 停止全部服务 +meow_svcd reload # 重读板载 + TF 双源 services.d 配置 +meow_svcd pid # 打印守护进程 pid +``` + +- **socket**:控制走 AF_UNIX 单行文本,默认 `/tmp/meow_svcd.sock`(`--sock`/`MEOW_SVCD_SOCK` 可改)。 + 守护**未运行**时命令回退打印状态文件 `/tmp/meow_svcd.status`(或 `card_present=0 services=0`)并以退出码 1 返回。 +- **守护前台**:`-f`/`--foreground` —— 守护自身跑前台,供 supervisor `fork/exec`+`waitpid` 托管; + 默认(无子命令)则为后台常驻。 +- 应答:成功 `OK`(`pid`→pid 数字);服务不存在 `ERR no-such-service`;`svc=... state=...` 行 + 含 `pid/restart(重启计数)/exit(上次退出码)/trigger`。 + ## 7. 编译 ```bash diff --git a/patch_v2/sdk/meow_ui.h b/patch_v2/sdk/meow_ui.h index 3614006..e7372c3 100644 --- a/patch_v2/sdk/meow_ui.h +++ b/patch_v2/sdk/meow_ui.h @@ -333,6 +333,41 @@ int meow_notify_has_active(void); /* 1=尚有活动通知,0=无 * const char *meow_notify_urgency_str(meow_notify_urgency u); const char *meow_notify_category_str(meow_notify_category cat); +/* ============================================================ */ +/* §5.7 应用可读 TF 卡字体列表(M2.58:字体索引 / 查列表 / 丢失降级默认) */ +/* ============================================================ */ +/* 系统字体管理器的面向应用接口(规格 §5.7.1/§5.7.2): + * - 权威列表 = launcher 扫描 MeowOS/fonts/// 后落盘的持久文件 + * MeowOS/config/fonts/index(CSV:family,weight,path,label,tier,glyph_count)。 + * app 只经本接口读该列表(不直接拼路径、不依赖 launcher 内存数组)。 + * - 渲染仍走 env MEOW_FONT_PNG/MEOW_FONT_IDX(launcher 注入的「当前生效字体」, + * 选中字体那一套);本节的 get_available_count/enumerate 只负责「列有哪些库」, + * select_by_id 负责「app 会话内固定用哪套」。两路明确。 + * - 无卡 / 无 index → 仅 1 项板载默认(family=default)。 */ +#ifndef MEOW_FONT_INFO_DEFINED +#define MEOW_FONT_INFO_DEFINED +typedef struct meow_font_info { + char family[64]; /* family slug = MeowOS/fonts// 一级目录名 */ + char name[64]; /* 显示名(index 无单列 → 回退 family) */ + char weight[32]; /* weight slug(regular|bold|…,缺省 regular) */ + char tier[8]; /* 档位 common|gbk|full(info 用) */ + int glyph_count; /* font.json glyph_count(info 用) */ + char label[128]; /* index 显示名(含字重 + 尺寸标记) */ + int size_list[16]; /* 原生字号表(index 无 → 空) */ + int size_count; /* 有效字号数 */ + int bit_depth; /* 4|8(index 无 → 0) */ + char path_png[320]; /* 卡根相对 MeowOS/fonts///font.png */ +} meow_font_info; +#endif + +/* 可用字库数(含默认;无卡 → 1)。 */ +int meow_font_get_available_count(void); +/* 按序号取:idx=0=默认行(family=default);越界返回 0。 */ +int meow_font_enumerate(int idx, meow_font_info *out); +/* 选该套为 app 会话默认字体:id=0 跟随系统默认(font.file,经 env);id>0 固定用该套。 + * 该套字库缺失 → 降级默认(不丢请求)+ WARN(meow_log→app 日志)。成功 0,越界负值。 */ +int meow_font_select_by_id(int id); + /* ============================================================ */ /* §10 字体分层 / 大字库(M2.5x:基座声明 + font 域已实现按档装载) */ /* ============================================================ */ diff --git a/patch_v2/sdk/meow_ui_gfx.c b/patch_v2/sdk/meow_ui_gfx.c index 082d937..e26f796 100644 --- a/patch_v2/sdk/meow_ui_gfx.c +++ b/patch_v2/sdk/meow_ui_gfx.c @@ -361,19 +361,22 @@ void meow_ui_swipe_hint(int x) * 有状态栏(非全屏)→ y = 状态栏高度;收合(collapsed)→ y=提示线下缘 * (MUI_SBAR_COLLAPSE_H,与提示线不重叠);无状态栏(全屏/隐藏)→ y=0。 */ int hy = meow_ui_statusbar_height(); - /* x<0 视为清空(条长 0);条长上限=屏宽。 */ - if (x < 0) x = 0; - if (x > g_xres) x = g_xres; - /* 只按「新旧长度的差」增量绘制,避免整段重涂背景色: - * - 伸长:仅补画新增段(旧段已是 accent,不再用 bg 重涂——否则 app 底色≠theme bg - * 时会在旧段留下 bg 色块,就是残留残条); - * - 回缩/清空:仅把「超出新长度」的尾部填回背景色(实时回缩)。 */ - if (x > g_hint_last) { - meow_ui_fill_rect(g_hint_last, hy, x - g_hint_last, MUI_HINT_H, th->accent); - } else if (x < g_hint_last) { - meow_ui_fill_rect(x, hy, g_hint_last - x, MUI_HINT_H, th->bg); + /* 单一实现(与非全屏 Hello 完全一致,全屏仅 hy 不同):grow 画 0..x、shrunk 先清旧长 + * 再画 0..x、x<=0 清空。x<0 视为清空;条长上限=屏宽。 */ + if (x > 0) { + if (x > g_xres) x = g_xres; + /* 往回滑(新长度 < 上次)时,先清掉「上次画到旧长度、但本次超出的部分」, + * 否则条只增不减(右滑变长、左滑回缩实时可见)。 */ + if (g_hint_last > x) meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg); + /* 条色取主题前景(fg):fg 定义为与背景对比,能同时覆盖「非全屏(底=主题bg)」与 + * 「全屏 Hello 类(底=主题accent)」两种情况——accent 在 Hello 全屏 accent 底上同色不可见。 */ + meow_ui_fill_rect(0, hy, x, MUI_HINT_H, th->fg); + g_hint_last = x; + } else if (g_hint_last > 0) { + /* 手指回原点/抬手未达标/抬手达标:清掉此前画的条(填回背景色) */ + meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg); + g_hint_last = 0; } - g_hint_last = x; /* 不发 flush:由调用方 meow_ui_flush_region(0,0,宽,6) 只刷顶部条 */ } diff --git a/patch_v2/sdk/meow_ui_input.c b/patch_v2/sdk/meow_ui_input.c index c964f8c..1f27d2d 100644 --- a/patch_v2/sdk/meow_ui_input.c +++ b/patch_v2/sdk/meow_ui_input.c @@ -125,7 +125,6 @@ static int t_press_valid = 0; /* BTN_TOUCH=1 已见(本次按下的 gate static int t_dx = 0, t_dy = 0; /* 按下原点(BTN_TOUCH 后首个有效 ABS 锁定) */ static int t_cx = 0, t_cy = 0; /* 当前坐标(-1=本次尚未见到有效坐标) */ static int t_origin_valid = 0; /* 原点是否已锁定 */ -static int t_gmax_dx = 0; /* 本手势「最大右移位移」(t_cx-t_dx 的最大值,条长上限) */ /* 按下边沿:只标记按下,绝不用传入坐标立即锁原点(避免用上一手势残留坐标当原点)。 * t_cx/t_cy 归 -1 —— 本次手势以「无坐标」起步,只接受随后到来的有效 ABS。 */ @@ -137,7 +136,6 @@ static void touch_press(int x, int y) t_origin_valid = 0; t_cx = -1; t_cy = -1; t_dx = 0; t_dy = 0; - t_gmax_dx = 0; /* 新手势:最大右移位移清零(条长从 0 计) */ } /* 由 ABS_X/ABS_Y 驱动:若本次按下且原点未锁且坐标有效 → 锁原点;再更新当前坐标。 */ static void touch_update(int x, int y) @@ -147,17 +145,13 @@ static void touch_update(int x, int y) } if (x >= 0) t_cx = x; if (y >= 0) t_cy = y; - if (t_down && t_origin_valid) { - int dx = t_cx - t_dx; - if (dx > t_gmax_dx) t_gmax_dx = dx; /* 记录本手势已达的最大右移位移(条长上限) */ - } } static void touch_release(void) { if (!t_down) return; t_down = 0; t_press_valid = 0; /* 本次按下从未锁到有效坐标(幽灵触摸/坐标未就绪):丢弃,且清空全部轨迹。 */ - if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; t_gmax_dx = 0; return; } + if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; return; } t_origin_valid = 0; int mdx = t_cx - t_dx, mdy = t_cy - t_dy; int adx = mdx < 0 ? -mdx : mdx, ady = mdy < 0 ? -mdy : mdy; @@ -169,8 +163,7 @@ static void touch_release(void) * 被手势级清空,绝不继承上一手势坐标,确保: * ① 中部右滑不会误判成左缘返回(t_dx>=EDGE_W); * ② 先点中部再侧滑不返回(原点锁定在中部,>=EDGE_W),符合「必须从左缘起划」; - * ③ 返回判定要求右移严格正向(t_cx-t_dx>=0 且 ≥SWIPE_THRESH),杜绝「向左划也误返回」; - * 左划过程仅让进度条实时回缩,不额外生长(过程见上方 MEOW_EV_SWIPE_PROGRESS)。 + * ③ 右移严格正向(t_cx-t_dx>=0),杜绝「向左划也出条/误返回」。 * SDK 统一上报为 MEOW_SWIPE_LEFT(设计"左滑=返回"契约,hello 用 on_swipe(LEFT) 退出)。 * * 屏幕中部滑动不是返回手势:中部右滑→RIGHT(内容导航),中部左滑→非返回(落 TAP, @@ -199,7 +192,7 @@ static void touch_release(void) push_ev(MEOW_EV_TAP, t_cx, t_cy); } /* 手势级干净起点:清空全部轨迹,下一手势从零开始,杜绝污染无自愈。 */ - t_cx = t_cy = -1; t_dx = t_dy = 0; t_gmax_dx = 0; + t_cx = t_cy = -1; t_dx = t_dy = 0; } /* ---- 输入线程:poll 所有 event 设备 + 解析 ---- */ @@ -231,22 +224,14 @@ static void *input_thread(void *arg) if (code == ABS_X || code == ABS_MT_POSITION_X) { if (t_down) touch_update(val, t_cy); /* 锁原点+更新当前(t_down 时才驱动) */ else t_cx = val; - /* 左滑进度实时上报(视觉条)。抬手触发仍由 touch_release 判定;这里只报过程。 - * 修复要点(左缘返回滑条 bug): - * ① 双向上报(不再要求 t_cx>t_dx):向右→条伸长、向左→条实时回缩, - * 摒弃「只在返回瞬间闪末帧 / 反向才出条」; - * ② 仅左缘起手(t_dx= 0 && t_dx < EDGE_W) { - int dx = t_cx - t_dx; - if (dx > t_gmax_dx) t_gmax_dx = dx; - int len = dx < 0 ? 0 : dx; - if (len > t_gmax_dx) len = t_gmax_dx; - push_ev(MEOW_EV_SWIPE_PROGRESS, t_dx, len); - } + /* 左滑进度实时上报(视觉条):原点确实在左缘(t_dxt_dx)——grow/回缩全程由「非全屏已验证的同一条路径」驱动,全屏仅 + * 垂直位置 hy 不同(hy=meow_ui_statusbar_height()),别无全屏特判。 + * 抬手触发仍由 touch_release 判定;这里只报过程(事件载荷 b=当前X)。 + * t_dx 是本次按下后首个有效 ABS 锁定的真实按下点(非残留),且每次抬手已 + * 清空,杜绝「中部点一下出条 / 向左划也出条」。 */ + if (t_down && t_origin_valid && t_dx >= 0 && t_dx < EDGE_W && t_cx > t_dx) + push_ev(MEOW_EV_SWIPE_PROGRESS, t_dx, t_cx); } else if (code == ABS_Y || code == ABS_MT_POSITION_Y) { if (t_down) touch_update(t_cx, val); else t_cy = val; @@ -335,17 +320,17 @@ void meow_ui_run(const meow_ui_events *ev) meow_ui_sys_notif_pull(); } else if (e.type == MEOW_EV_SWIPE_PROGRESS) { if (in_startup) continue; /* 启动窗口进度条静音 */ - /* e.a=按下X(t_dx),e.b=条长 len(输入层已 clamp 到「本手势最大右移位移」且≥0)。 - * SDK 自动画顶部条:从 x=0 画到 len(右移位移),向右伸长、向左实时回缩。 - * M2.5x/A-13:全屏/收合下「左缘返回手势保留」,顶部动态提示条仍需绘制——不再以 - * meow_ui_sys_statusbar_active()(全屏/隐藏=0)开关,否则全屏时滑条完全不显示。 - * 条位 y=meow_ui_statusbar_height():非全屏=状态栏下沿、全屏=0、收合=提示线下缘 - * (MUI_SBAR_COLLAPSE_H),与提示线不重叠加。APP 若注册 on_swipe_progress 仍可自绘 - * (系统条照常画);递交给 app 的是 (len-x_start) 使 x_start+dx==len,与系统条同长。 */ - int len = e.b; - meow_ui_swipe_hint(len); + int x0 = e.a, xc = e.b, dx = xc - x0; /* dx=当前X-按下X */ + /* SDK 自动画顶部条:长度=手指当前 X(=距左缘距离,向内划长度随位移增长)。 + * 全屏/收合下「左缘返回手势保留」,顶部动态提示条(meow_ui_swipe_hint)照常绘制 + * (与 agent-108:不再以 meow_ui_sys_statusbar_active()(全屏/隐藏=0)开关,否则 + * 全屏时滑条完全不显示)。条位 y=meow_ui_statusbar_height():非全屏=状态栏下沿 + * (28)、全屏=顶部、收合=提示线下缘(MUI_SBAR_COLLAPSE_H),与提示线不重叠加—— + * 全屏与非全屏共用同一条绘制/进度路径,只 hy 不同,无全屏特有的坐标/方向/最大值逻辑。 + * APP 若注册 on_swipe_progress 仍可自绘(系统条照常画;x_start+dx==当前X,幂等)。 */ + meow_ui_swipe_hint(xc); if (g_events_set && g_events.on_swipe_progress) - g_events.on_swipe_progress(e.a, len - e.a); + g_events.on_swipe_progress(x0, dx); meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6); } }