AskaEth
|
3f09f7c137
|
Merge branch 'feat/m31-reader-font-memo-and-read-badge'
|
2026-11-10 13:48:49 +08:00 |
|
AskaEth
|
dc9bfdbdaf
|
perf(reader): M31 字体路径记忆化(开书字体查找 35 目录 walk → 1 次 stat)+ 「· 读」徽标即时刷新
字体:find_font_rec/resolve_font 从 reader.c 抽到 app/font_resolve.c 并加进程内记忆化。
只缓存成功路径;命中后仍做一次 access() 校验(字库被删即重扫);没找到不缓存,
用户之后拷入的字库下次开书即可找到。顶栏「刷新」额外调 font_cache_invalidate(),
给「新拷入字库、旧字库仍在」一条显式失效路径。宿主实测:35 目录树 walk 0.026~0.54ms
(=300+ 次系统调用)vs 命中 0.001ms(1 次 access);开书路径每次省一次完整 walk。
徽标:副题格式化抽成 shelf_fmt_sub()(扫描与「刚读完置顶」共用一份实现),
promote_just_read() 改 ts 后立即重算这本的 sub/sub_folder,「· 读」不再等下次全量刷新。
回归:test_reader_core 6846、shelf_group_smoke 63(+8)、font_resolve_smoke 14 全绿;
build_reader.sh 0 warning;扫描缓存/进度语义未改。app.json 0.1.3/code4 → 0.1.4/code5。
|
2026-11-10 13:39:32 +08:00 |
|
AskaEth
|
0b30cdbafb
|
Merge branch 'feat/m30-reader-drop-unused-next-page-slot'
|
2026-11-10 13:30:44 +08:00 |
|
AskaEth
|
a9638fa59c
|
perf(reader): M30 删掉无人消费的下一页预取槽(refill 渲染减半、常驻 −0.69MB)+ 回书架置顶刚读完的书
|
2026-11-10 13:22:35 +08:00 |
|
AskaEth
|
dc559aa1ac
|
Merge branch 'feat/m29-reader-shelf-scan-cache-and-row-draw'
|
2026-11-10 13:11:58 +08:00 |
|
AskaEth
|
bece9848e7
|
perf(reader): M29 书架扫描缓存 + 行数多时关圆角容器(回书架 3.4s→0.03s,滚动 52ms→19ms)
真机(0.8.48 / reader 0.1.1 基线,同机同字库 ztest 实测):
- 从阅读页返回书架:3435ms(3259–3531)→ 33ms(24–54)。根因是 shelf_on_enter 无条件
scan_books():exFAT 全量扫描 434 目录 = 3.1s。改为进程内缓存(首次进入才扫,顶栏
「刷新」强制重扫);truncated/depth_skipped 提示语义不变(首次扫描时算好并保留)。
行为变化:新进 Books/ 的书、刚读完那本书的「最近阅读置顶」需点一次「刷新」才更新。
- 书架滑动每步(62px,含 widget_draw + flush):51.9–56.2ms → 19.3–19.9ms。
行数 > 200 时 meow_listview_set_container(0)(扁平行),≤200 行仍用 MD3 圆角卡;
圆角卡走 meow_gfx_fill_round 逐像素 rr_inside(464×54 一张 3.8–6.4ms ×11 行)。
- 首次进书架仍需扫一次(3497ms,含 3157ms 扫描);595 本全可见、format/folder 分组不变。
另加 test/shelf_bench_ondev.c(真机耗时基准,不进 mpk)与 build_reader.sh 的编译步骤;
app.json 0.1.1/2 → 0.1.2/3。未做(按塔砍范围):字体路径记忆化、首屏只渲染当前页、
自绘行、mtime 失效检测。
|
2026-11-10 12:54:17 +08:00 |
|
AskaEth
|
288c133efc
|
Merge branch 'feat/m27-reader-shelf-folder-grouping'
|
2026-11-09 22:52:43 +08:00 |
|
AskaEth
|
e0a82a3d47
|
fix(reader): M27 r3 评审整改 — cap 边界截断/默认副题不变/提示两行且 6s 消失/版本号
对应评审 P2-1、P3-7、P3-1、P3-2、P3-10、P3-9:
- P2-1:scan_dir 在目录边界遇 cap 时探测子树(count_books_under)再置 truncated;
同目录内超限逻辑不变。新增两条断言:cap 卡边界 → truncated=1;恰好装满且后续为空目录 → 不误报
- P3-7:shelf_book 拆 sub(默认副题,与 base 逐字一致)/sub_folder(folder 模式前置子路径);
list 按模式选,宫格始终用 base;断言 format 副题逐字等于 base
- P3-1:超量+超深合并提示拆两行绘制(610 起,不再被 480px 右缘切掉)
- P3-2:提示 6s 到期自动消失(app.c 书架前台时调 shelf_on_tick)
- P3-10:app.json version 0.1.1 / version_code 2
- P3-9:shelf_scan_bench.c 头部注明 /proc/uptime 10ms 分辨率、只适合真机
|
2026-11-09 22:45:01 +08:00 |
|
AskaEth
|
63ead3ec1e
|
fix(reader): M27 r2 — 扫描深度提到 6 + 按 Books 第一级目录分组
真机真实布局暴露两处问题(评审 P1):
- Books/400+本高质量完本合集/<分类>/<作品>/<书名>.txt 在 depth 3-4,旧 depth≤2 让 413 本 3.8GB 不可见
→ SHELF_SCAN_MAX_DEPTH=2→6;report 新增 depth_skipped,超深书籍文件计数并在 g_notice 可见提示
(有 N 个文件因目录层数超过 6 层未显示),不再静默
- 按完整相对路径分组会把 413 本书炸成 410 个每条 1 书的段头
→ 分组键改为 Books/ 第一级目录(shelf_book.top);完整相对路径仍在 group[],
第一级之后的子路径(分类/作品)放进副题 sub(如 01、玄幻/01、斗破 · TXT · 2.1MB)
→ 真机书库只剩 3 组:400+本高质量完本合集 / kh / 轻小说
- 单测 50 断言:depth3/4 收录、depth7 计入 depth_skipped、四种真实路径的组名、
a/b 与 a/c 同组 a、副题含子路径、format 回归、根组未分类、path/进度不变
|
2026-11-09 22:29:46 +08:00 |
|
AskaEth
|
409fd1edf7
|
feat(reader): M27 书架按目录分组(ui.shelf_group=folder|format,默认 format 不变)
- 新增 shelf_model.c/.h:扫描/行序列从 shelf.c 拆出(纯 C,宿主可测);shelf_book 加 group[96](相对 Books/ 的目录路径,根下空串),path 语义/内容完全不变(进度键仍为 rdprog_hash(path))
- folder 模式:目录段头(根下=未分类),未分类置顶、其余按目录名排序;组内沿用最近阅读优先/其次书名;空目录不产生段头
- format 模式逐行保持旧行为;新增 reader.conf ui.shelf_group,缺键默认 format
- 上限 256→2048(SHELF_MAX_ROWS=2N=4096,段头≤书数),超限置可读提示,不再静默截断
- 顶栏加「目录/格式」切换,设置页加「书架分组」;UTF-8 安全截断长中文目录名
- 单测 test/shelf_group_smoke.c(38 断言)+ 计时 test/shelf_scan_bench.c
- build_reader.sh:链 meow_ui.a 解析冒烟 stbi_*;无 qemu 时跳过执行而非失败
|
2026-11-09 21:55:33 +08:00 |
|
AskaEth
|
db672cbe8f
|
Merge branch 'feat/m26-sdk-image-stack-into-main-repo'
|
2026-11-06 21:35:50 +08:00 |
|
AskaEth
|
281f4153ee
|
fix(sdk): M26 r1 P1 — ILP32 尺寸算术溢出(畸形 BMP 经公开入口 SIGSEGV)+ 加 S17 回归
评审在 ARM32(ILP32,与 mipsel 同数据模型)实测:54 字节 BMP 头声明 w=2^27、bpp=32
时 w*bpp 回绕成 0 → rowsize=0 → malloc(0) → 采样索引 row+sx*4 越界读 → SIGSEGV,
经 meow_img_load_file_dim 端到端可触发;同一文件在 LP64 宿主返回 NULL(不溢出),
合并前走 stb 也是 NULL。即这是相对旧路径的鲁棒性回归。
修复(只加闸,正常路径数值不变):
- meow_png_stream.c(P1 本体)
· 新增 ILP32 守卫 MZP_MAX_DIM=1<<20 与 mzp_bytes_ok()(unsigned long long 验算,
超过 SIZE_MAX 即拒)
· BMP:声明尺寸先升 int/long long 校验(w/h 单边 ≤1<<20;h=INT_MIN 用
-(long long)hi 取绝对值,不再有 -h 溢出);rowsize 用 64 位算并验;
行偏移 off / 采样下标 sx 改 long long,sx 夹在 [0,w-1];按行 fseek 前加
off > LONG_MAX 拒绝;目标 RGBA 字节数 64 位验后再 malloc
· PNG:IHDR 尺寸原已 ≤65535,补上 tw*th*4 的 64 位校验(32768²×4 = 2^32 会回绕),
emit_row 的 sy/sx 与输出行偏移改 64 位并夹紧
- meow_jpeg.c(顺手审计同类 32 位算术,评审要求「不要只修点名那处」)
· mj_layout 返回 unsigned long long(单平面/三平面之和都可能越 2^32),
越界返回 MJ_LAYOUT_BAD → mj_sof 各处重新布局后都检查
· target_bytes 与分量 plane malloc 改 64 位验算;目标图字节超 MJ_PEAK_CAP 直接拒
· mj_output:输出缓冲 64 位验;y*vh / x*vw 采样改 long long 并夹紧
· meow_img_target_dims / mj_pick_n 的 (long)h*maxdim、8*maxdim 改 long long
- test_meow_img_stream.c:新增 S17(10 组畸形 BMP 声明尺寸 + 2 个合法正例 +
PNG 回绕点),断言「后端拒绝 + 公开入口不崩(越界尺寸另断言 NULL 且不分配)」;
文件头补 ILP32 交叉编译命令(宿主 LP64 看不到 32 位回绕,测试注释里明写这一点)
验证:同一测试在 zig arm-linux-musleabi(sizeof(long)=4)下 S1-S17 全绿(206 checks,
exit 0);把后端换回 r1 提交的旧实现,同一 ARM32 二进制 exit 139(说明 S17 真能抓);
宿主 LP64 S1-S17 全绿、B1-B9 全绿;静态库 --verify 仍 78 成员 / 全 MIPS32r2。
同步提醒:这些行与公开仓 ~/Proj/MeowOS-SDK/sdk/{meow_png_stream.c,meow_jpeg.c} 逐字节
同源,同一补丁必须由控制塔同步过去,否则立刻产生双边漂移。
|
2026-11-06 21:27:03 +08:00 |
|
AskaEth
|
ee5796747d
|
feat(sdk): M26 公开仓真缩解图像栈并入主仓 — JPEG DCT/PNG 逐行/BMP 跳行后端 + RGB565 页图
主仓 SDK 的图片层原本是 220 行 stb 直通(整幅解码后重采样)。公开仓 MeowOS-SDK
的同一子系统已被真实 App(tuku 阅读器)驱动并做过峰值实测与像素比对,故以公开仓
为权威方向并入主仓 meow_ui.a,两个方向的新功能都保留(逐文件 diff 确认公开仓是
严格超集:无一处「双方互有增减」)。
- 新增 meow_jpeg.c / meow_jpeg.h:DCT 域缩解 1/8、1/4、1/2(N=1/2/4 点 IDCT),
渐进式只解到首个精细化扫描;不支持项(CMYK/YCCK、算术编码、过订阅 DHT、16bit
量化表)返回 0 由调用方回退 stb
- 新增 meow_png_stream.c:libz 逐行 inflate + 反滤波,按步长丢行丢列直写目标尺寸;
BMP 按目标行 seek + 行内跳列(8/16/24/32bpp)
- meow_ui_img.c + include/meow_ui_image.h:新增 meow_img_pick_scale /
load_file_step / load_file_step_ex / resample 四个公开入口;_dim 与 _mem_dim
先进后端,失败/不支持才回退 stb(对外语义不变)
- include/meow_ui_draw.h + meow_ui_gfx.c:M32 RGB565 页图 5 函数
(meow_gfx_rgb565 / rgba_to_rgb565 / blit_rgb565 / _scaled)。gfx 保留主仓的
`#include "../meow_launcherd/meow_font.h"` 与 <time.h>(公开仓自 vendored
meow_font.h 且无 <time.h>,机械覆盖会丢这两行)
- meow_jpeg.h 保持内部:不进 meow_ui.h 伞头,include/ 下无任何头暴露 meow_jpeg_*
- build_sdk.sh:OBJS 插入 meow_jpeg / meow_png_stream(32→34);成员数
76→78(+meow_font.o +43 freetype),--verify 全为 ELF32 MIPS32 rel2/o32
- 测试:新增 test_meow_img_stream.c(S1-S16),test_meow_img_budget.c 同步公开仓
版(B9 断言收紧为 p_new <= p_old);22 个既有宿主测试的编译注释行补
meow_jpeg.c / meow_png_stream.c / -lz(meow_ui_img.c 现引用后端符号,不加即
undefined reference)
宿主实测:S1-S16 全绿(S7 与 stb 网格像素一致:JPEG mad 4.35-13.69、PNG/BMP
mad 0.00;S9 回退矩阵 CMYK/1bpp BMP/Adam7 均拒绝且 stb 仍成功),B1-B9 全绿
(B9 PNG 峰值 25.65→0.61 MiB、BMP 14.65→0.59 MiB)。回归 test_meow_ui_widgets /
scroll / ime_fix / test_umbrella 全过。zlib 在应用链接期解析:meow_ui.a 只留
inflate* 未定义符号,四个 App 构建脚本原本就带 $SYSROOT/lib/libz.a,静态
linkcheck(meow_ui.a + libz.a -lpthread)通过。
注:主仓 patch_v2/reader_app 仍自带 vendored stb、不用 meow_img_*,本单不改变
阅读器的内存占用;RGB565 页图接入 reader_app 是另一个任务。
|
2026-11-06 20:44:22 +08:00 |
|
AskaEth
|
479d93a3bf
|
Merge branch 'feat/m24-devkit-static-mipsel-debug-tools'
|
2026-11-06 20:03:08 +08:00 |
|
AskaEth
|
adaed09f94
|
fix(devkit): M24 r2 评审 — 交付完整性闸门 + 补 sysctl + time64 文档更正
P2-1(与 P1 同类):licenses/ 与 README 缺失仍会假成功。
- 新增 meta_complete()/verify_meta():MANIFEST + README + 五份许可证副本
(procps COPYING/COPYING.LIB、ncurses、lsof、musl-COPYRIGHT)缺一即不完整
- 纳入 skip 条件与 --verify-only;产物未变但 meta 缺失时就地 write_meta 补全(不重编),
构建树不在则退回全量重建
- 回归测试新增 T6(删 licenses 后 --with-sources 必须恢复)、T7(--verify-only 必须拒绝)、
T8(删 README 后恢复);共 20 项全过
P2-2:README.txt/MANIFEST.txt 的卡上路径全错(写成 ../src、scripts/build/…)。
- 全部改为卡内相对路径:src/、licenses/、MANIFEST.txt、src/DEVKIT.md、src/build_devkit.sh
- README 增加「目录内容」段并注明 src/ 是生成目录、勿手放文件
P2-3:valgrind 的排除理由(“无 MIPS32 后端”)是错的 —— 官方支持 MIPS32/Linux。
- 改为「未尝试」:交叉构建需 autotools + 目标 vDSO 细节、产物 10MB+,56MB 单核不实用
P2-4:busybox 1.31 实测有 pgrep/kill/killall,没有 pkill、没有 sysctl。
- 表格改准确;pgrep 保留(增量有限不交付);pkill 写明「busybox 无此 applet,
killall 只能部分替代,本轮未做」
- 新增交付 sysctl(procps 4.0.4,148184 B,静态):MANIFEST/verify-only/真机
(sysctl -a 428 键、kernel.hostname/panic 读取、-n)均已验证
P2-5:free -s <小数> 也受 time64 影响(usleep 亚秒被抹平),文档说反了。
- DEVKIT §9 增加一行(free -s 0.5 实测 0s、free -s 1 实测 1s);README 同步;
“收益只有 vmstat -t 一列”改为包含 free -s
P3:
1. §9 机制改准:内核只写 8 字节 → tv_sec 高 32 位是内核写入的 nsec、offset 8 的
tv_nsec 才是未初始化;nanosleep 是把 tv_sec 高半字当 nsec 读(0)
2. 删掉脚本里 top 的旧错误归因(时长溢出/59.4 GiB),与 DEVKIT 对齐
3. 测试耗时改为「热缓存约 15s,冷启动 1–2 分钟」(实测 28s 含多次构建/修复路径)
4. --verify-only 依赖清单写全(file/readelf/sha256sum/awk/sed/grep/wc/cut/basename)
5. licenses/ 增加 musl-COPYRIGHT(zig env → libc/musl/COPYRIGHT,取不到即失败),§6 补一行
6. htop 说明改准:亚秒刷新走 ncurses halfdelay()(select + 32 位 timeval),大概率不受
time64 影响;受影响的是时钟显示与部分采样(仍注明未做运行时验证)
7. README/脚本注明 src/ 是生成目录
8. LGPL 摘要改为「源码(§6a) 或 共享库(§6b) 或 书面要约(§6c)」
9. ltrace/sysstat 从「不可行」改为「未做(成本高)」,与 perf 的硬阻塞区分
验证:test_build_devkit.sh 20/20;--verify-only 通过(7 个二进制 + MANIFEST + 5 份许可证);
独立工作区离线重建 64s/0 下载,七个 sha256 与前一次逐字节一致;设备实测 sysctl 与其余六项正常。
|
2026-11-06 19:53:45 +08:00 |
|
AskaEth
|
0531603779
|
fix(devkit): M24 r1 评审 — P1 --with-sources 静默漏源码 + time64 归因更正
P1(阻断):stamp 命中时 main 提前 return,--with-sources 在 return 之后,
于是「先构建再 --with-sources」会打印成功、退出码 0、却不产出 src/。
- 把源码整理抽成 stage_sources(),stamp 命中路径先兑现 WITH_SOURCES 再返回;
整理后按钉住的 sha256 复核(sha 漂移/缺文件都重铺),任何失败即非零退出
- 新增 test_build_devkit.sh(12 项,秒级):stamp 命中下的 --with-sources、
src 缺文件/被改坏的自愈、--verify-only 的 MANIFEST 篡改检测
- skip 条件额外要求 MANIFEST 与 bin 逐条 sha 一致;--clean 同时清 out/src;
全新构建未带 --with-sources 时删除上次残留 src/(避免源码与二进制错配)
P2-a:top 排除理由改写。根因是 zig 0.17 musl 的 time64 ABI 错配(mips o32 是
ILP32 + 内核 4.4 无 time64 syscall,16B timespec 直传 32 位 syscall),不是
procps 的 bug;top 的 Mem 一栏是「百分比/总容量」不是 used GiB。新增 DEVKIT.md
§9 记录证据(vmstat -t 空列、usleep(0.5s)×10 实测 0s、sleep(1) 正常、ps lstart
正常),并说明 M16 的 patch_v2/sdk/meow_ui_time.c 可复用(本轮实测该 .o 独立
编译、仅 5 个 libc 未定义符号)但本轮不接入的取舍——需要单独 mission 做链接期
接管 + 全量回归,不是 -D 能修的。
P2-b:--with-sources 现在同时放 build_devkit.sh + DEVKIT.md(GPLv2 §3 的
"scripts used to control compilation");补 LGPL-2.1 §6 静态链接 libproc2 的
义务说明;MANIFEST/README.txt 写明 src/ 与 licenses/。
P2-c:lsof 内嵌 Termux musl 的 resolv.conf 路径记为工具链副作用(实测
lsof -i 约 3s 超时、-nP 瞬时;端口名/本机名不受影响),不换工具链。
P3:软链路径写实(/bin/ps、/usr/bin/{free,lsof,top});MANIFEST 用 zig version
而非 clang 版本;chmod 措辞改成检查挂载参数;--verify-only 增加 MANIFEST 逐条
sha256/体积比对且不再依赖 date;stamp 纳入 zig 完整版本;许可证复制失败即报错。
验证:test_build_devkit.sh 12/12 通过;--verify-only 通过(含 MANIFEST 对账);
独立工作区离线重建 85s/0 下载,六个二进制 sha256 与上一版逐字节一致;设备侧
二进制未变(sha 与 r1 真机验证过的一致)。
|
2026-11-06 19:29:27 +08:00 |
|
AskaEth
|
b52e6a08bc
|
feat(devkit): M24 静态 MIPS32r2 调试工具集 — vmstat/slabtop/free/ps/pmap/lsof
TF 卡 MeowOS/opt/devkit 用的交叉编译流水线:zig 0.17 musl 静态链接,
产物 fw_apps/devkit/{bin,MANIFEST.txt,licenses,README.txt}(gitignore 内)。
- build_devkit.sh:源码固定 版本+sha256+URL(ncurses 6.5 / procps-ng 4.0.4 /
lsof 4.99.4),缓存命中零联网;冷启动 3 次下载约 80s,重跑 0 次联网约 65s,
stamp 命中 1s 跳过;--force/--clean/--offline/--with-sources/--verify-only
- 自编静态 ncursesw 只为 slabtop(procps 的 slabtop 硬依赖 ncurses);
terminfo 目录钉到设备 /usr/share/terminfo,交互式 slabtop 可用
- 逐项自检 file/readelf(static、ELF32、mips32r2、0 NEEDED),任一不过即失败
- 不交付 top(真机输出不可信)、htop(卡上已有 3.5.3 且我们的更大)
- DEVKIT.md:许可证留痕(procps=GPL-2+ 需提供源码、ncurses=MIT、lsof=Purdue)、
可行性实测、上卡 PATH 与 busybox 同名关系、真机验证输出
|
2026-11-06 19:05:15 +08:00 |
|
AskaEth
|
2a2e6988ae
|
Merge branch 'feat/m23-network-time-sync'
|
2026-11-06 13:43:24 +08:00 |
|
AskaEth
|
1f692ae2a7
|
fix(ntp): M22 r1 评审 P2×3 — 孤儿 ntpd 收尸 / 日志失败不吞 hwclock / 锁自证
r1 评审 verdict=p2-3items,三条均已按评审给的修法修掉,且都补了可证伪的回归断言。
P2-1 SIGTERM 后遗留孤儿 ntpd,之后每次静默 skip 且 rc=0
- attempt() 把子进程记进全局 NTPD_CHILD 并落盘 <锁目录>/ntpd.pid;
新增 cleanup()(EXIT trap)先收尸子进程再释放锁;新增 on_signal()
(INT/TERM/HUP)清理后 exit 143——旧代码的 TERM trap 只清锁、不 exit,
被杀后还会继续跑完看门狗。
- 取代原来的 "pidof ntpd 就 skip":新增 reap_or_skip_ntpd(),用
/proc/<pid>/cmdline 是否含本锁目录来区分「我们自己遗留的孤儿」(收尸)
与「别人的常驻 ntpd」(让路)。崩溃后留下的 ntpd.pid 也据此校验,
pid 被复用时不会误判成外部 daemon(不会永久 skip)。
- S45 stop 不再按陈旧 pid 文件盲杀:只从 <锁目录>/<pid> 取持有者,
再核对 /proc/<pid>/cmdline 含 meow_ntpsync 才 kill。
P2-2 日志重定向失败导致 hwclock 根本不执行
- persist_rtc 改 ">/dev/null 2>>$LOG_FILE":hwclock 的 rc 与日志写入解耦。
- 新增 prepare_log():启动时在子 shell 里试写日志,不可写就整体退化 /dev/null。
必须用子 shell——`:` 是 POSIX 特殊内建,它的重定向失败会让 dash 直接
退出整个脚本(本单自测先踩到了,rc=2)。
P2-3 锁 mkdir→写 pid 有窗口,并发抑制非原子
- 锁的持有者改由 <锁目录>/<pid> 子目录自证(mkdir 原子),不再写 pid 文件;
读到空锁时给 3s 宽限而不是立刻判死。
- 清失效锁改用 mv 到 <锁目录>.stale.$$(rename 原子),并发清锁时只有
一个能成功,避免「抢锁失败方删掉胜方的锁」。
- 退出只删自己的锁:没有自己的 <pid> 子目录就不动锁目录。
验证
- 新增回归断言 11-16(run_tests.sh):SIGTERM 收尸/锁释放、遗留孤儿收尸、
日志不可写仍写 RTC、锁被占用不抢、死锁可接管、pid 复用不阻断。
- 可证伪性:新套件跑旧代码(c9f0cba 的 meow_ntpsync)→ 6 条 FAIL
(孤儿 alive、日志不可写 rc=1 且 hwclock 未执行、抢锁并发调 ntpd×3)。
再跑「仅回退 STALE_NTPD 硬化」的变体 → pid 复用那条 FAIL
(rc=0 但 RTC 未写,正是评审指出的静默失效)。
- dash 与 busybox ash 各 41/41 PASS,rc=0;dash -n / busybox sh -n 均 0 错。
- 时间断言放宽为负载容忍(宿主是多 agent 共享机,实测 sleep 5 能跑成 85s),
假 ntpd 改为挂 3600s,保证上界仍能证明是看门狗掐断而非自然退出。
fw_content.conf: meow_ntpsync md5 ad324e3f→f3c3abe4,
S45meow_ntpsync md5 25454a3f→115e7592(50meow_ntpsync 未改,md5 不变)。
|
2026-11-06 13:34:45 +08:00 |
|
AskaEth
|
c9f0cbaab6
|
feat(ntp): M22 网络校时 — 复用固件自带 ntpd + hwclock 回写 RTC
真机实测(0.8.47,2026-11-06)定位到的根因不是「没有 NTP 客户端」:
固件自带 ntp-4.2.8p14(/usr/sbin/ntpd)和 /etc/ntp.conf(4 个 pool.ntp.org),
但没有任何 init 脚本/进程启动它 —— 有客户端、没启动。
实测证据:
- RTC 存在且可读(/dev/rtc0,10003000.rtc),内核开机 hctosys=1,
冷启动不回到 1970 → 掉电保持;RTC 存 UTC(since_epoch == date +%s)。
- 但 RTC 会走慢且无人纠正:实测系统时间比 NTP 慢 49 s;把系统时间人为
拨错 4 h 后观察 65 s 无人纠正,RTC 也不跟随系统时间。
- UDP/123 出网正常(ntpd -q 一次成功 time set +48.877s);DNS/HTTP 正常。
- udhcpc 的 default.script 不处理 option 42,实测热点也不下发。
- ntpd -q 不会回写 RTC(步进后 sys-rtc 仍差 49 s)→ 必须 hwclock -w -u。
- ntpd 没有超时参数,无服务器时永久存活 → 必须自带看门狗。
- -p 形式的 server 首个 poll 要 ~64 s;配置里加 iburst 后 8 s 内返回。
实现(新增,不改任何基线文件):
- /usr/bin/meow_ntpsync:一次性校时器。复用 ntpd -q -g + 运行时生成的
iburst 配置;内置 IP 兜底(阿里/腾讯/Cloudflare)→ DNS 名兜底;
单次 15 s 看门狗、默认路由轮询、1 h 短路、进程锁、失败静默。
成功严格校时后 hwclock -w -u 回写 RTC(RTC 存 UTC,必须 -u)。
- /etc/init.d/S45meow_ntpsync:开机只 setsid 后台拉起并立即返回
(关键路径 +0 秒;后台最长约 150 s 自行退出),失败不阻断 init。
- /usr/share/udhcpc/default.script.d/50meow_ntpsync:只在 bound/renew
触发(设备上唯一可靠的「网络就绪」事件),后台拉起后立即 exit 0;
有 $ntpsrv(option 42)就优先用。基线 default.script 本就有 HOOK_DIR
循环,目录原本为空。
- tests/run_tests.sh:宿主沙箱单测(假 ntpd/hwclock/网络探测)。
验证:dash 与 busybox sh 各 sh -n 通过;单测两套 shell 各 23/23 通过;
真机(/tmp 下运行,未装系统目录)首跑 8 s rc=0、ntpd slew 成功、
RTC 由 1793940395 写成 1793940452(49 s 偏差被纠正)、hwclock -u -r 吻合;
二次运行 0 s 短路;-b -t 3 无网络 5 s 退出 rc=1;
DHCP 灌入不可达服务器 192.0.2.x 仍靠 IP 兜底在 ~10 s 内成功。
|
2026-11-06 12:49:13 +08:00 |
|
AskaEth
|
2cfac55b4e
|
Merge branch 'feat/m22-boot-logo-and-brightness-init-script'
|
2026-11-05 22:04:56 +08:00 |
|
AskaEth
|
f9c2ea42a4
|
fix(boot): M21 r1 评审 P1 — bl_cap 把 cur=0 当「无有效值」照写 20(+P2 两处)
P1 根因(评审给的真机驱动证据链,已按其最小修法改):
/module_driver/pwm_backlight.sh 是 `insmod pwm_backlight.ko ... default_brightness=0
max_brightness=101`,模块 init 把 props.brightness 置 0 再 update_status(PWM 0 分支)→ 上电即灭屏;
该模块由 S11module_driver_default 加载,rcS 自然序排在 S11jpeg_display_shell 之后。于是:
- 函数入口那次 bl_cap:背光节点还不存在,写必然失败(no-op);
- fb0 出现后画图前那次 bl_cap:读到 cur=0,旧条件 `cur <= cap` 命中「只降不升」跳过分支,
20 从未写入 → 画图时刻亮度 0。
物理后果两头都是缺陷:面板仍亮则 logo 依旧亮(原问题没修);面板灭则 S11~S21 无可见 logo,
settings.conf 缺失时直到 launcherd 才亮屏(相对 stock「0.1s 后写 60」是回归)。
修改:
- bl_cap 判定改为「仅 1 <= cur <= cap 才视为已有有效值跳过;0 / 空 / 非数字一律照写」。
S22 抢写 1 的竞态保护不变(1 落在 1..20 → 仍跳过,不会被顶回 20)。
- 删掉函数入口那次 bl_cap(节点未建,必然 no-op,且是重定向报错的来源);只保留 fb0 就绪后、
画 logo 前那一次 —— 同一驱动脚本里 pwm_backlight.ko 先于 soc_fb.ko 加载,fb0 出现即节点就绪。
- 写入成功时打一行 `backlight: -> 20 (was N)` 便于刷机后复核;`2>/dev/null` 移到 `>` 之前,
节点未就绪时连重定向报错一起吞掉(P2-1)。
- S22:失败路径日志由「保持 S11 低亮度」改为「不改背光(当前 brightness=N)」(P2-2);写入失败
日志同样附当前读数;写入处的 2>/dev/null 次序一并改正。
- 头部注释按时序真相重写(驱动默认 0 = 灭屏,不是「上电很亮」)。
实测(沙箱重写 sysfs 路径 + stub cmd_jpeg_display/nanddump/sleep,dash 与 busybox 各一遍):
cur=0 → 画图时刻 20、结束 20(修复前 0/0);cur=1 → 1/1;cur=60、cur=101 → 20/20;
竞态(fb0 就绪前外部写 1)→ 画图时刻 1、结束 1(未被顶回);节点不可写时 stderr 0 行、仍画图、rc=0。
S22 19 用例全 rc=0(仅 usage rc=1,rcS 不可达)、sleep 调用 0、行为与改文案前一致。
S11 与 S22 内容均变 → fw_content.conf 两条 md5 同步更新为
3ad33301fb9f0884102a47e17a1985ed / 1a0aa33d6f806f3f1648168cc1d8b953
(未改动 meow_watchdog 那条:main tip dfde95b 仍是 553840cd,与本单无关,已在报告中提示。)
|
2026-11-05 22:04:16 +08:00 |
|
AskaEth
|
0ab23161b6
|
fix(boot): M21 开机 logo 亮度 — S11 画前压到 <=20 + 新增 S22brightness 按用户设置恢复
冷启动 logo 很亮:stock S11 是画完 logo 之后才写死 60,画图那一刻用的是面板上电默认高亮;
而 S11 早于 S21mount_ubifs,那时 /usr/data 未挂载、读不到 settings.conf,只能写死一个值。
- S11jpeg_display_shell:以基线(249220f2)为底,把背光压制挪到画 logo 之前(jpeg_display
函数体内、先于 cmd_jpeg_display),值 20(只降不升:S22 已写更暗则不覆盖);删除画完后
写死 60 的那行;/dev/fb0 等待、mtd5 主题判定、后台 jpeg_display & 语义不变;
主题判定 `==` 改 `=`(POSIX test,语义不变,便于宿主验证)。
- S22brightness(新):S21mount_ubifs 之后运行,读 /usr/data/meow/settings.conf 的
display.brightness,按 display.brightness_min 与 sysfs max_brightness clamp 后写入;
读不到/解析失败不改背光(保持 S11 低值)、无 sleep/重试、任何运行期失败 exit 0;幂等。
- scripts/build/fw_content.conf:登记两条(S11 覆盖基线同名文件、S22 新增),md5 实算,
说明列写清 S11 < S21mount_ubifs < S22 的顺序依赖。
- scripts/build/README.md:补「覆盖基线已有文件」语义(无条件 cp -f,不校验基线 md5、
也不跳过已存在文件;唯一基线校验仍是 hiby_player)。
固件需整包刷机才生效,未刷机、未写设备。
|
2026-11-05 22:04:16 +08:00 |
|
AskaEth
|
316d9d42c7
|
build(fw): 同步 watchdog 的 md5 到 M19 重编后的二进制
M19 把 meow_watchdog 重编为 ad8c1f2b(首次把 M6.23 的亮屏亮度修复真正编入),但
fw_content.conf 第 32 条仍 pin 旧值 553840cd,build_fw_apps.sh 的强校验会在该条中止。
本提交只改这一行 md5 与该条说明。
注:scripts/build/ 被 .gitignore:68 忽略,而这些文件本就已跟踪,故需 git add -f。
|
2026-11-05 21:54:23 +08:00 |
|
AskaEth
|
dfde95b020
|
Merge branch 'feat/m20-watchdog-brightness-boot-fallback'
|
2026-11-05 21:41:25 +08:00 |
|
AskaEth
|
f2551a28a2
|
fix(watchdog): M20 亮屏亮度不再回退固定 60(settings 读失败重试 + 未知快照语义拆分)— 重编出货二进制
真机 0.8.46(2026-11-05 21:09 日志)复现「开机/亮屏背光跳到固定值」:
76646 [watchdog] power: panel ON (blank 0 + brightness 60)
而 /usr/data/meow/settings.conf 是 display.brightness = 1。根因链:
- 出货 /usr/bin/meow_watchdog(md5 553840cd…,与仓库内二进制逐字节相同)是
2026-09-01 6a4d330 编出的旧版:不含 M6.23 的 settings 读取(strings 无
display.brightness_min,日志仍是旧格式 "brightness %d")—— M6.23 只改了源码、
没重编。旧版 wd_panel_on 无条件 write_brightness(wd_saved_bri>0?wd_saved_bri:60),
wd_saved_bri 初值 60 且该 session 未熄屏过 → 亮屏写 60,覆盖 launcher 刚设的 1。
- 源码侧同样有 60 兜底:wd_settings_brightness() fopen 失败即 -1 → 回退快照 60。
改动(patch_v2/meow_watchdog/):
- wd_settings_brightness():fopen 失败重试 3×10ms。覆盖 settings.conf「写 tmp+rename」
原子替换、开机早期 /usr/data 未就绪的瞬时窗口;只在失败时发生(成功路径 0 开销),
最坏 20ms,远小于亮屏轮询粒度 REFRESH_MS=100ms。
- 新增 wd_bri_decide() 纯函数:settings(clamp 后)→ 本次运行期真实快照 → 不写
(面板已亮,untouched)→ 安全低亮 10(面板黑且无任何值来源)。任何路径都不再写 60。
- wd_saved_bri 初值 60 → WD_BRI_UNKNOWN(0);wd_panel_off 只在读到正亮度时更新快照,
读失败/被 launcher 抢先写 0 时保留上一次真实值(不再是 (br>0)?br:60)。
- panel ON/OFF 日志带 src=settings|snapshot|untouched|safe-low 与 user/saved/cur,
真机一眼看出用的是哪一支。
- 背光路径加 MEOW_BACKLIGHT env 测试钩子(与 MEOW_SETTINGS_FILE 同款,真机无 env)。
- test_wd_fw.c 新增 21 条用例(决策 7 + settings 解析/重试 6 + panel OFF/ON 端到端 8);
端到端用例真的读写背光文件,复现「settings=1 → 亮屏写 1」而不是 60。
- 重编 meow_watchdog:mipsel 静态 MIPS32r2 ELF,md5 ad8c1f2b…,0 warning。
验证:./build_watchdog.sh host → 38 pass / 0 fail;交叉编译(-Os -Wall -Wextra -static)
0 warning,产物静态 MIPS32r2。
生效条件:meow_watchdog 在 squashfs /usr/bin,必须刷固件(或等价的重打包流程)才生效。
后续(不在本单 scope):scripts/build/fw_content.conf 第 32 行仍 pin 旧 md5 553840cd…,
不更新会让 build_fw_apps.sh 的强校验失败。
|
2026-11-05 21:32:27 +08:00 |
|
AskaEth
|
bcb11e012f
|
fix(reader): stb_image 实现改由 meow_ui.a 提供,修 M6.45 收编后的重复定义
SDK 在 M6.45 收编 stb_image.c.o 进 meow_ui.a 后,reader 的 engine/rdimg.c 仍带
-DSTB_IMAGE_IMPLEMENTATION,链接期 stbi_* 与归档内 stb_image.o 重复定义,reader
无法重链。改为只编封装、实现取自归档;rdimg.c 用到的 stbi_info_from_memory /
stbi_load_from_memory 在归档中均已导出。
|
2026-11-05 21:12:42 +08:00 |
|
AskaEth
|
62b3603ade
|
merge(sdk): 回补 M19 滚动池槽泄漏修复(wrap 上下文归还 + 44 断言回归)
|
2026-11-05 21:00:22 +08:00 |
|
AskaEth
|
72c6e9889d
|
fix(sdk): 从公开仓回补 M19 滚动池修复(free 归还 wrap 槽)+ 宿主回归
meow_scroll_wrap 从 MEOW_SCROLL_POOL(=8) 个 wrap 上下文槽占一个,meow_scroll_free
只归还会话槽、从不归还 wrap 槽也不清 ctx.w → 进程内最多成功 wrap 8 次,之后恒 NULL。
主仓此前仍是有缺陷版本;本提交把公开 SDK 仓 c85877b 的修复按内容移植到主仓(唯一权威
源),meow_ui_scroll.c 结果与公开仓 sdk/meow_ui_scroll.c 逐字节一致,diff 仅 wrap 相关
hunk(之前两文件差异也只有这些 hunk,故无其它改动被覆盖)。
- meow_scroll 结构记 wrap_slot(create 置 -1),wrap 时写槽下标;free 按该下标清
g_wrap_ctx[].w/lv 并归还 g_wrap_used,随后置 -1(二次 free 不触碰任何槽)。
直接 create 的实例 wrap_slot=-1,free 不误清别人的槽。池满语义不变:8 个同时活跃时
第 9 个 wrap 仍返回 NULL;free(NULL) 安全。
- test_meow_ui_scroll 断言 17 → 44:64 次 wrap/free(修复前 8/64、修复后 64/64)、
free 后再 wrap、8 满 + 第 9 个 NULL + 释放其一即复用、交错 free 不串槽、直接 create
不误清槽、list→listview→list 槽复用不残留 kind、free(NULL)/二次 free。
头注释补 meow_ui_nav.c:原配方缺该 TU,链接期 meow_ui_nav_request_exit 未定义。
验证:LAUNCHER_DIR=主检出/patch_v2/meow_launcherd build_sdk_host.sh --verify
exit=0、0 warning/error、成员清单 76 项一致且全 MIPS32r2;宿主测试 44 pass / 0 fail;
同测试对修复前 meow_ui_scroll.c 为 13 pass / 19 fail(首条即 ok=8 first_fail=8)。
meow_ui.a 中 meow_ui_scroll.o 已变化,链 meow_ui.a 的 App 需重链。
|
2026-11-05 20:59:36 +08:00 |
|
AskaEth
|
8d2c098deb
|
Merge branch 'feat/files-hello-row-height-followup'
|
2026-11-05 20:57:43 +08:00 |
|
AskaEth
|
9600f10352
|
fix(hello): demo 左滑列表行高按字体行高派生 — 首行标题不再丢失(M17)
根因(承接 M12/M15 finding):SDK meow_listview 双行 renderer 把
block = 2*meow_ui_text_height(font_body) + MEOW_LISTV_SUB_GAP(2) 在行内垂直居中
(ty = 行顶 + (item - block)/2),标题只在 ty >= 视口顶时才画。demo 的 swipe 列表
显式 set_item_renderer(2,1,0)(双行档位)却 set_item_h(48):真机 source_han_serif
20px 行高 30 → block 62 → ty = 行顶 - 7,首行「notif 0」标题被裁剪守卫整条跳过,
只剩 swipe left 副标题。
修复:
- hello_internal.h:新增 HELLO_ROW_H_MIN(56) / HELLO_ROW_SUB_GAP(2,与 SDK
MEOW_LISTV_SUB_GAP 同口径) + hello_row_h_for_line_h(line_h)
= max(56, 2*line_h + 2) + hello_row_h()(取当前主题 font_body 的
meow_ui_text_height)。本地派生,不依赖 M15 新增导出 meow_listview_item_h;
显式传值在 M15 之前的 SDK(默认 48/56 档位)与 M15 之后(默认已派生 62)都成立且一致。
- ui/demo/sdk2/sdk2.c:set_item_h(48) 改传 hello_row_h()(行高 30 → 62)。
列表视口 LV_H / 三节布局推进量不变(行高只影响行内几何与滚动量),其它 demo 区块
(LazyColumn 列表、宫格、选择器)不受影响。
验证(宿主,未碰真机):
- zig-mipsel-cc -Os -static -Wall -Wextra(另用 -Werror 复验)交叉编译 hello:0 warning;
- 新宿主 test/test_row_text_fit.c(32 断言,纯头文件派生、不链 SDK):
字库行高矩阵 14/21/26/27/30 下 block <= item、首行 ty >= 0;行高 30 → 62;
旧 48 → ty=-7 的 bug 形态哨兵;全 PASS。
影响:meow_ui.a 未改 → 无需重编 SDK;本 App 需重链重部署(热更 hello 二进制)。
|
2026-11-05 20:57:13 +08:00 |
|
AskaEth
|
e69b8efeeb
|
fix(files): 目录列表行高按字体行高派生 — 20px 字库下首行标题不再空白(M17)
根因(承接 M12/M15 finding):SDK meow_listview 双行 renderer 把
block = 2*meow_ui_text_height(font_body) + MEOW_LISTV_SUB_GAP(2) 在行内垂直居中
(ty = 行顶 + (item - block)/2),标题只在 ty >= 视口顶时才画。目录行是双行档位,
真机 source_han_serif 20px(ascent 24 + descent 6)行高 30 → block 62,而
browser_page_create 显式 set_item_h(56) → ty = 行顶 - 3:首行(行顶 == 视口顶)
标题被裁剪守卫整条跳过,只剩大小/时间副标题。
修复:
- files_internal.h:APP_ROW_H 降级为行高下限(56);新增 FILES_ROW_SUB_GAP(2,与
SDK MEOW_LISTV_SUB_GAP 同口径) + files_row_h_for_line_h(line_h)
= max(56, 2*line_h + 2) + files_row_h()(取当前主题 meow_theme_get()->font_body
的 meow_ui_text_height)。本地派生,不依赖 M15 新增导出 meow_listview_item_h;
显式传值在 M15 之前的 SDK(默认 56)与 M15 之后(默认已派生 62)都成立且一致。
- ui/browser.c:browser_page_create 的 set_item_h 改传 files_row_h()
(行高 30 → 62)。SDK 的 draw/hit/max_scroll 全部按这一个值换算,滚动/命中天然
同步;页脚几何(r_list 单独扣 APP_FOOTER_H)与行高无关。其它几何/交互不变。
验证(宿主,未碰真机):
- zig-mipsel-cc -Os -static -Wall -Wextra(另用 -Werror 复验)交叉编译 files:0 warning;
- 新宿主 test/test_row_text_fit.c(32 断言,纯头文件派生、不链 SDK):
字库行高矩阵 14/21/26/27/30 下 block <= item、首行 ty >= 0;行高 30 → 62;
旧 56 → ty=-3 的 bug 形态哨兵;全 PASS。
影响:meow_ui.a 未改 → 无需重编 SDK;本 App 需重链重部署(热更 files 二进制)。
|
2026-11-05 20:57:13 +08:00 |
|
AskaEth
|
c2635daf3f
|
Merge branch 'feat/sdk-time64-syscall-gaps'
|
2026-11-05 20:46:36 +08:00 |
|
AskaEth
|
a7bd6c44d9
|
fix(sdk): 评审 p2 修正 — 负 nsec 判 EINVAL / 音频条件等待计数改 5 处
- meow_ui_time.c `__timedwait_cp`:`at->tv_nsec` 是有符号 long,原写法
`>= 1000000000L` 会让负 nsec 通过(musl 原文用 `>= 1000000000UL`,负值按无符号
比较同样判 EINVAL)。改为 `< 0 || >= 1000000000L`,与本文件 M11 的
`__clock_nanosleep` 写法一致;同时避开 `-Wsign-compare`(UL 写法在 -Wextra 下
会告警)。反汇编确认 llvm 合并成一条 `sltu`,与 musl 的 unsigned 比较等价。
- CLOCK_ABI.md:meow_ui_audio.c 的 pthread_cond_timedwait 调用点由「3 处」改为
5 处(:312/:351/:417/:431/:499),§5/§6 两处同步;核对文中其它计数(四 App、
23 项单测)无误。
验证:宿主 gcc -Wall -Wextra 0 warning,test_meow_time64 23 项 PASS、
test_meow_time 回归 PASS;交叉 zig -Wall -Wextra 0 warning;
build_sdk_host.sh --verify 76 成员全 MIPS32r2、0 warning;归档内 meow_ui_time.o
与新鲜交叉构建逐字节一致。
|
2026-11-05 20:46:26 +08:00 |
|
AskaEth
|
40ac1e40f0
|
docs(sdk): CLOCK_ABI 补 M16 全量清点/接管机制/验证
§6 把「同源残余」换成逐条清点:zig musl 源里 20 处
`#if defined(SYS_*_time64) && !defined(__ILP32__)` 守卫,分「受损(回退分支直传
time64 结构)」与「不受影响(回退分支自带 long[]/宽度判断)」两类,各给源文件:行号
与反汇编证据;纠正 M11 对 pselect/ppoll 的保守判断(它们本来就正确)。
§7 记录接管机制:_REDIR_TIME64 让应用引用 __x_time64(不是 API 名);同一 .o 的
libc 符号必须一起定义(只定义 __timedwait_cp 会与 libc 的 __timedwait.o 撞
duplicate symbol,lld 实测);hidden 不阻止静态链接接管但要保证本 TU 被拉入;
结构宽度前提(整链 zig 头)。附未修 7 项的 5 步配方与「不建议顺手全改」的理由。
§8 记录本单验证:宿主 23 项换算单测 + M11 回归、build_sdk_host.sh --verify
(76 成员/全 MIPS32r2/0 warning)、四个入口的 syscall 号与缓冲宽度反汇编、
真实 meow_ui.a 链接探针的符号绑定。真机确认仍待设备。
|
2026-11-05 20:46:26 +08:00 |
|
AskaEth
|
0412994953
|
test(sdk): time64→o32 time32 换算宿主单测(23 项)
test_meow_time64.c 覆盖 meow_ui_time.c 接管入口共用的纯换算层:
- 相对超时(futex):普通值、亚秒、INT32_MAX 上界、上/下溢饱和、负值透传;
- 绝对时刻(clock_settime/timerfd):int32 上下界接受、越界拒绝(对应 ENOTSUP);
- itimerspec 四字段不串位、任一越界整体失败且不写坏输出、反向展开对称;
- 结构宽度 before/after:同一块 16B time64 内存按内核 o32 只读 8B 时,
0.4s 被读成 {0,0}(立即超时)、2.3s 被读成 2s 整;经本层转换后内核读到
{2, 300000000}。
宿主编译/运行:
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o /tmp/m.o
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time64.c /tmp/m.o -o test_meow_time64
./test_meow_time64 # == PASS ==
M11 的 test_meow_time.c 仍 PASS(回归)。
|
2026-11-05 20:46:26 +08:00 |
|
AskaEth
|
3b5ffece57
|
feat(sdk): 接管 mipsel time64 同源缺口 — futex 相对超时/timerfd/clock_settime
zig clang 给 mipsel 预定义 __ILP32__,打穿 musl 的 time64 回退守卫,以下入口把
16/32 字节 time64 结构直接交给 o32 time32 系统调用:
- thread/__timedwait.c:14:futex 相对超时(pthread_cond_timedwait / sem_timedwait /
pthread_join 超时 / aio_suspend / 非 PI timedlock / rwlock timed)。内核只读前 8B,
tv_nsec 落在 tv_sec 高半字(恒 0)→ 亚秒等待立即超时、整秒等待丢纳秒。SDK 的
meow_ui_audio 三处条件等待直接受害。按 musl 原文接管 __timedwait_cp/__timedwait,
只在发 futex 前把相对超时转成 time32;取消点仍走 libc 的 __syscall_cp。
- linux/timerfd.c:14/42:32B itimerspec 直传 → 内核按 16B 读、字段错位;接管
create/settime/gettime(同一 .o 的三个符号必须一起定义,否则重复定义)。
- time/clock_settime.c:9:16B 直传 → nsec 恒 0;接管 __clock_settime64。
换算抽成平台无关的 meow_time32_{rel,abs}_from_parts() / itimerspec 转换
(meow_ui_internal.h),宿主可测。pselect/ppoll/select、getrusage、wait4、
clock_adjtime 逐条反汇编确认回退分支自带宽度转换,不受影响(纠正 M11 §6 的保守
估计)。未修项(sigtimedwait/semtimedop/timer_settime/gettime/utimensat/recvmmsg/
mq_timedsend/PI timedlock)的清单与配方见 CLOCK_ABI.md §6/§7。
验证:宿主 gcc -Wall -Wextra 0 warning;交叉 zig -Wall -Wextra 0 warning;
build_sdk_host.sh --verify 76 成员全 MIPS32r2;真实 meow_ui.a 链接探针确认
__pthread_cond_timedwait_time64 的 jal 指向本实现、futex=4238 且超时为 8B、
timerfd 4323/4322、clock_settime 4262。
|
2026-11-05 20:46:26 +08:00 |
|
AskaEth
|
cfcb94b8d2
|
Merge branch 'feat/sdk-listview-row-height'
|
2026-11-05 20:38:09 +08:00 |
|
AskaEth
|
31c12892d6
|
test(sdk): 宿主回归 — --wrap 行高 30 下首行标题必须画出(listview 行高派生)
新 test_meow_ui_listview_rowh.c(35 断言)用链接期
-Wl,--wrap=meow_ui_text_height 把宿主 5x7 回退行高 14 换成真机 source_han_serif
20px 实测行高 30,驱动真实 SDK 渲染(同 M12 app/test/test_first_row_title_render.c
手法),断言全部读 back_buf 像素:
- T0 wrap 生效(meow_ui_text_height(font_body) == 30);
- T1 默认(不设 item_h):item_h == 62 = max(56, 2*30+2) ≥ 2*行高+间距;
max_scroll/hit 按 62 换算;scroll=0 时首行标题带与副标题带都有文字像素;
视口上方无标题泄漏;
- T2 裸 create(不调 renderer)同样派生;set_item_h(0) 忽略并保持派生;
- T3 显式 set_item_h(70)(ncm 语义):item_h/hit/max_scroll 全按 70,首行标题可见;
- T4 档位:tier(2) 恢复派生 62、tier(1) 48,单行档位不画副标题且标题可见;
- T5 pool 模式(set_vsource + 默认 renderer):同样派生 62,首行标题可见;
- T6 旧行为哨兵:显式 56 < block 62 时首行标题带 0 像素、副标题仍有 → 证明
像素断言能区分「画出/被跳过」,即 A/B 复现 bug 形态。
回归(宿主 gcc -O1 -Wall -Wextra,全部 0 warning):
- 新 test:35 pass / 0 fail;
- listvirt 44 / widgets ALL PASS / md3 ALL / scroll 17 / select 6 / swipe 35 /
lazygrid 32 全 PASS;
- 交叉 build_sdk_host.sh --verify:76 成员全 MIPS32r2 rel2,meow_ui_widget.c
-Wall -Wextra 0 warning(meow_ui.a 变更 → 下游 App 重链)。
|
2026-11-05 20:38:02 +08:00 |
|
AskaEth
|
4d9ba46a7b
|
fix(sdk): listview 默认行高按字体行高派生 — 双行列表首行标题不再被裁剪
根因(M12 finding / SDK 侧):meow_listview 双行 renderer 把「标题+副标题」
文本块 block = 2*meow_ui_text_height(font_body) + MEOW_LISTV_SUB_GAP(2) 在行内
垂直居中(ty = 行顶 + (item-block)/2),且文本只在 `ty >= 视口顶` 时才画。
旧默认行高固定 56 假定行高 ≤ 27;真机 source_han_serif 20px(ascent 24 +
descent 6)行高 30 → block 62 > 56 → ty = 行顶 - 3,首行(行顶 == 视口顶)
标题被裁剪守卫整条跳过、副标题仍在 → 所有用默认行高的 App 两行列表首行
标题消失。
修复(patch_v2/sdk/meow_ui_widget.c):
- item_h=0 改为「派生」哨兵:listv_item_h() 在未显式定行高时按当前主题正文
字体行高算 —— lines=2: max(56, 2*line_h+2)(行高 30 → 62)、lines=1:
max(48, line_h)。draw/hit/max_scroll 共用同一函数,行几何恒一致;
- meow_listview_create 不再写死 56;set_item_renderer 不再写死 48/56;
set_item_tier 恢复派生默认(仍是 1→≥48 / 2→≥56 的口径,字体大时自动长高);
- 显式 meow_listview_set_item_h(>0) 语义不变、优先且固定(ncm 传 70、files
传 56 等调用方不受影响);set_item_h(<=0) 仍忽略,返回值改为「当前生效行高」;
- 新增 meow_listview_item_h() 读当前生效行高(App 自绘行装饰对齐用,避免再
硬编码 56);
- src2 分支的双行判断补 `w->lines == 2`,与 pool 分支同口径:tier/renderer 设
单行档位时明确不画副标题(此前单行档位 48px 里仍塞 62px 文本块 → 同样丢标题)。
同类路径评估(不改):
- LazyColumn/LazyRow(W_LIST):单行 label,item_h 由 create 显式给(默认 44),
且 item_h 兼作横向列宽,按行高派生意味不明;默认 44 > 真机行高 30,当前不触发;
- lazygrid:格子几何由 App 显式给、文本底部对齐并整体 clip_push 裁剪,不受影响。
影响面:所有用默认行高的两行列表(hello/reader/files 及其它 App)在 20px 字库
下默认行高 56 → 62;显式 set_item_h 的调用方(ncm 70/launcher/files 等)零变化。
meow_ui.a 变更 → 下游 App 需重链(追加导出符号 meow_listview_item_h,ABI 兼容,
老 App 不重编也能跑)。
验证:见下一条 test 提交(宿主 --wrap 行高 30 + 像素断言 35 断言)。
|
2026-11-05 20:38:02 +08:00 |
|
AskaEth
|
805cfe11cc
|
Merge branch 'feat/firmware-build-m646-killfix'
|
2026-11-05 20:16:38 +08:00 |
|
AskaEth
|
c811913c06
|
build(fw): M6.46 固件 — 编入 M7 杀后台修复 + M11 SDK 时钟修复(0.8.46)
- launcher bb2d5f12 (863160B) 重编,链 meow_ui.a 4c4a1554:
M7(bgmgr_can_kill 门控 framework→protected_app / 后台页 sub 指针持久化 /
am kill 走 meow_bg_terminate 与页面按钮同通道) + M11(meow_ui_time 按 o32 time32 取时、
链接期接管 __clock_gettime*)
- meow_amsd b698f176 (139252B) 重编:M7 am kill 同通道 + cmd_kill/cmd_start 失败原因细分
(am CLI 9e323faf 保持不动:am.c 自 ca2290e 未变,与仓库发布物一致)
- fw_content.conf 8 条源 md5 对齐:bootmenu 4441822d / watchdog 553840cd / audiod b122c9b8 /
meow_audio 891ab264 / netmgrd×2 20243ea1 / launcher bb2d5f12 / amsd b698f176;头注 M6.46/0.8.46
- build_fw_apps.sh:repack.sh 改 bash 显式执行(#!/usr/bin/env bash 在 su 下 bad interpreter)
- 打包 r3proii_m6_46.upt md5 66501aaf(42,930,176B):rootfs 38,756,352<=45,088,768,
unpack 0 diff,87 files/1 folder 与 m6_45 一致
- 卡上写 r3proii.upt(66501aaf),原 0.8.45(3f4a8ad3) 备份 r3proii.upt.bak.0.8.45;
写前写后 du -sk Music 均 199164544;未刷机(运行中仍 43ac2312 / 0.8.45)
|
2026-11-05 20:16:29 +08:00 |
|
AskaEth
|
278e7eaa93
|
merge(sdk): 收编公开 SDK 仓的图片预算解码,主仓成为 SDK 唯一权威源
|
2026-11-05 20:07:26 +08:00 |
|
AskaEth
|
d9950d75eb
|
Merge branch 'feat/ncm-list-first-row-title'
|
2026-11-05 20:04:49 +08:00 |
|
AskaEth
|
e23bdca26a
|
fix(sdk): 收编公开 SDK 仓独有改进(图片预算解码 + JPEG 解码期缩解),主仓成为唯一权威源
公开仓 ~/Proj/MeowOS-SDK 的 sdk/ 与主仓 patch_v2/sdk 无共同 git 历史(subtree split
重写了提交哈希,不做跨仓 merge/subtree pull),按内容逐文件裁决。公开仓侧的真增量只有
commit 21bffa1(图片预算解码)及其依赖;其余差异是公开仓的自包含打包(扁平 include/、
vendored 头与 meow_font.c)或主仓早已超越的旧快照。
一、取公开侧(纯追加,主仓该文件无独有改动)
- stb_image.h:新增 stbi_load_scaled / stbi_load_scaled_from_memory——JPEG 输出级按
步长抽取行/列,整幅原尺寸输出缓冲从不物化;进升级 JPEG 各分量系数 IDCT 进像素平面后
立即释放。scale<=1 与旧 API 等价,非 JPEG 忽略。主仓版本的 diff 全部是公开侧新增
(无主仓独有 hunk),故整体替换。
- meow_ui_img.c:追加 img_pick_scale / img_finish_dim / meow_img_probe_file /
meow_img_load_file_dim / meow_img_load_mem_dim。既有加载入口一字未改。
- include/meow_ui_image.h:追加上述三个公共声明。
- 新增 test_meow_img_budget.c + test_meow_img_budget_gen.py(--wrap malloc/free 打点
统计瞬时峰值,B1-B9 覆盖矩阵)。
二、取主仓(公开侧为旧快照或自包含适配,并入会回退功能)
- meow_ui_ime.c / test_ime_fix.c:主仓 bug3 修复(IME 打开瞬间 200ms TAP 冷却窗 +
drain/40ms/再 drain)及其回归用例为公开侧所无;公开侧同名文件不含该修复。
- meow_ui_input.c / meow_ui_internal.h:主仓 meow_input_touch_reset(drain 与叠加抑制
进入沿共用)、FreeType 渲染/字形声明、meow_time_ms_from_parts。公开侧此三处无独有声明
可并,取主仓即并集。
- meow_ui_gfx.c / meow_ui_sys.c / meow_cachegov.c:主仓时钟 ABI 根因修复(o32 time32,
meow_ui_time.c 链接期接管 __clock_gettime*)已把这些文件的本地 now_ms 统一为
meow_ui_time_ms();公开侧仍是各自 clock_gettime,回落即回退根因修复。
- meow_ui_dialog.c / meow_select.c:主仓 MD3 后续(对话框按钮胶囊 MEOW_SHAPE_FULL、
bottom sheet 顶角 28)晚于公开侧快照,公开侧等于旧版。
- include/meow_ui_font.h:主仓多出 FreeType 段与 stdint 自洽 include,公开侧缺。
- build_sdk.sh / build_sdk_host.sh:主仓含 FreeType vendor 裁剪构建、meow_ui_time TU、
launcher meow_font.a 打包;公开侧为自包含重写,不回流。
- 其余 test_*.c:差异仅为编译配方注释(公开仓用 -Iinclude、无 meow_ui_time.c、标注
「需主仓组件故公开仓跳过」等),对主仓目录布局不成立,不改。
三、不引入(vendored 副本 / 打包产物)
- meow_font.c、include/meow_font.h、include/meow_ipc.h、include/meow_notif.h:公开仓
为自包含 vendored 副本(meow_font.c 与主仓 font/meow_font.c 逐字节相同),主仓已有
权威源,复制会制造双源。
裁决说明(两边都改过的文件):
- test_meow_ui_select.c:公开仓把 T1 探针从 x=10 改到 x=12(commit 432875d)。主仓 x=10
在本树实测 6/6 PASS(容器卡左缘 8、文本左缘 24,x=10 已在卡内且避开圆角),无失败
可修,故不跟进该改动,避免无谓 churn。
- 公开侧「meow_media_playctl」一类 API 名:仅出现在公开仓 vendored 头/文档,主仓
sdk 无对应实现,未硬造。
四、验证(worktree / 分支 feat/sdk-absorb-public-deltas)
- build_sdk_host.sh --verify:0 warning / 0 error;成员清单 76 个(OBJS 32 +
meow_font.o + freetype 43)一致,全部 ELF32 MIPS32 rel2 (o32)。
- nm 确认 meow_ui.a 导出 meow_img_probe_file / meow_img_load_file_dim /
meow_img_load_mem_dim / stbi_load_scaled / stbi_load_scaled_from_memory。
- 宿主测试:对「未改动的 main SDK」跑同一套矩阵做基线对比,40 项同名结果逐项完全一致
(22 PASS / 11 FAIL / 7 建链失败,失败项与失败计数一字不差);本提交新增
test_meow_img_budget 为 PASS。补充按各测试正确配方单编:test_meow_img_color、
test_meow_lyric、test_meow_lyric_view、test_meow_ui_ft(显式传入 MiSans 字体)、
test_umbrella 均通过/0 warning。
- 既有失败全部是 Termux 环境问题,与本次合并无关(基线同样失败):/tmp 不可写导致
mkdtemp/fopen/bind EACCES(config/vol/notif_open/notify_ipc/notify_integration/
sb_collapse/overlay_suppress/input_keys),audiod 守护不在场(ui_audio_host),
test_sdk_fixes 阻塞循环超时(基线同样 124)。
- hello_app 用新 meow_ui.a 重链成功(0 warning,静态 MIPS32r2),可执行文件
471380→473636 字节(+2256,新解码路径)。
五、影响
meow_ui.a 内容变化(新增 4 个 MEOW_API + stb 解码路径改变)→ 需重编重链 hello / files /
reader / ncm 四 App 与 meow_launcherd。
|
2026-11-05 20:03:20 +08:00 |
|
AskaEth
|
22f9dab2ce
|
fix(ncm): 列表首行标题不显示 — 行高按字体行高派生(M12)
根因(真机 seated,非数据/颜色/遮挡):SDK meow_listview 双行渲染把
「2*行高 + 2」的文本块在行内垂直居中,且标题只在 ty >= 视口顶时才画
(meow_ui_widget.c:2061 的越界守卫)。真机 source_han_serif 20px 面
ascent 24 + descent 6 → 行高 30 → 文本块 62px,而 ncm 行高 APP_ROW_H=56
→ ty = 行顶 - 3;首行行顶 == 视口顶 → ty < 视口顶 → 标题被整条跳过,
副标题(ty+32)仍在视口内照画。两类列表同用该路径 + 同一行高,故症状一致。
修复(不动 SDK):ncm_app_internal.h 新增 ncm_row_h()/ncm_row_h_for_line_h(),
行高 = max(56, 2*字体行高 + 2 + 8)(偶数;本机 30 → 70,卡高 62 == 文本块);
home/playlist/queue/cache/search 在 page create 取一次 g_item_h,统一用于
set_item_h 与卡片几何/可见行/页脚/补载视口换算。
回归:
- app/test/test_meow_row_text_fit.c(新):真机字库行高矩阵 14/21/26/27/30
48 断言(文本块 ≤ 行高、首行 ty ≥ 0、卡高 ≥ 文本块、旧 56 哨兵)。
- app/test/test_first_row_title_render.c(新):--wrap 行高=30 驱动真实 SDK
渲染 + 读回 PPM 像素计数:旧 56 → 首行标题 0 像素/副标题 186;新 70 →
标题 84/副标题 100。
- test_meow_row_gap、plpage_sim 全 PASS;ncm_host 7 页出图与改动前逐字节相同
(宿主行高 14 → 行高仍 56,证重构无视觉回归);5 页自检 PASS;0 warning。
- plpage_sim 临时目录改 $TMPDIR(Termux /tmp 不可写,原写死无法运行)。
- REPORT §43:根因/排除方向/修复/验证/真机验收要点。
|
2026-11-05 19:51:01 +08:00 |
|
AskaEth
|
98b9169b86
|
build(ncm): 宿主构建补 meow_ui_time.c(M11 漏编,main 上 ncm_host 链接失败)
M11 新增 patch_v2/sdk/meow_ui_time.c 并把 SDK 各处取时收敛到 meow_ui_time_ms,
但 build_ncm_host.sh 的 SDK_SRC 未同步 → main 上跑本脚本链接报
`undefined symbol: meow_ui_time_ms`(且失败链接会删掉已入库的 ncm_host 产物)。
补一个源文件即可,宿主分支只编 meow_ui_time_ms 的非 mips 实现。
|
2026-11-05 19:50:51 +08:00 |
|
AskaEth
|
4e60f440f6
|
Merge branch 'feat/sdk-clock-abi-fix'
|
2026-11-05 13:42:04 +08:00 |
|
AskaEth
|
38be5e78bc
|
fix(sdk): 时钟 ABI 根因修复 — meow_ui_time 按 o32 time32 取时并链接期接管
真机 meow_ui_time_ms() 返回「低 32=秒×1000、高 32 随机」的根因不是 SDK 写法,
而是 zig 0.17 自带 musl 在 mipsel 上的 time64 ABI 自相矛盾:
- zig 头(mips-linux-musl/bits/alltypes.h)无条件 64 位 time_t、struct timespec 16 字节;
- zig 的 clang 给 32 位 mips 预定义 __ILP32__,musl 的 time64 回退分支
`#if defined(SYS_x_time64) && !defined(__ILP32__)`(本意只排除 x32)被编译掉;
- 于是 libc 的 __clock_gettime64 实为 o32 time32 实现(SYS_clock_gettime 4263),
只写 8 字节 {int32 sec; int32 nsec},与头的 16 字节布局错配。
修复(patch_v2/sdk/meow_ui_time.c):
- meow_ui_time_ms() 直接按内核 o32 ABI 取 8 字节 time32,再升 64 位毫秒;
- 同 TU 强定义 __clock_gettime/__clock_gettime64(归档先于 -lc,链接期接管,
time()/gettimeofday()/pthread 定时等待的内部取时随之修正);
- 同源缺陷一并修:__nanosleep_time64/__clock_nanosleep(_time64) 转换 time32 后重发
(修复前 usleep(16000) 等亚秒睡眠立即返回、变忙等空转);
- gfx/sys/input/cachegov 四处重复 now_ms 收敛到 meow_ui_time_ms;
- build_sdk.sh OBJS 增加 meow_ui_time;新增 test_meow_time 宿主单测、
tools/clock_abi_probe.c 探针、CLOCK_ABI.md 根因/验证/影响面文档;
- 26 个宿主测试的编译注释补 meow_ui_time.c(cachegov 测试补时钟源)。
验证:宿主单测 PASS;anim(32)/view_event 宿主用例全过;build_sdk_host.sh --verify
76 成员全 MIPS32r2、0 warning;探针链接后无 libc cgt_init/__vdsosym,
__time64/__timedwait_cp 的取时指向本 TU,睡眠走 4166/4265 且用本地 8 字节结构。
|
2026-11-05 13:41:52 +08:00 |
|
AskaEth
|
8a115f6583
|
Merge branch 'feat/ncm-offline-retry-storm'
|
2026-11-05 13:16:54 +08:00 |
|