Files
MeowOS-For-R3P2/patch_v2/files_app
AskaEth 837cd47e7c fix(sdk+launcher): M39 点击输入门 — 固定时长吞窗改为「启动排空 + 短系统手势防穿透」
根因(M36 已查实):不是布局竞态,而是固定时长的输入封闭窗口且窗口起点在首帧之后。
- SDK meow_ui_run:M2.22(C) 500ms 内丢弃 TAP/SWIPE/HOME/NOTIF_PULL/TOUCH_DOWN/MOVE/LONG_PRESS,
  窗口在 App 画完首帧后才 arm → 用户看到界面后第一下必被吞。
  改为入口 meow_input_drain() 排空一次(清环形队列 + 复位触摸状态机),
  TAP/TOUCH_DOWN/TOUCH_MOVE/LONG_PRESS 立即生效;仅系统手势(左缘 SWIPE、底部上滑 HOME、
  顶部下拉 NOTIF_PULL、SWIPE_PROGRESS)保留 STARTUP_GESTURE_GUARD_MS=150 防穿透。
- launcher:menu_after_app_exit 1200ms→150ms;子页返回 800ms→150ms;菜单/子页打开
  300ms→150ms(统一 MENU_TOUCH_GUARD_MS)。menu_drain_touch() 同时复位 t_down/原点/坐标:
  只清队列会把「按下已被 drain、抬手稍后才到」误判成一次 tap(幽灵重放的另一半)。
  历史理由已核对:旧窗口是为 Bug2/4(App 运行期事件堆积在 launcher 自身 touch_fd 里被重放)
  加的,真正的机制就是排空本进程 fd —— evdev 每 fd 各收一份,App/子页运行期全量事件都在
  这个 fd 里,drain 后无历史事件可重放;窗口只兜面板重配尾巴。
- 单测:test_meow_ui_home_gesture 新增 T4(11 断言:立即点击生效/立即上滑被守卫挡+守卫后生效/
  立即左缘 SWIPE 同理/TOUCH_DOWN+MOVE 立即下发/LONG_PRESS 立即下发/run 入口 drain 丢弃预置残留;
  旧 SDK 下 8 条红、新 SDK 30 pass/0 fail);test_meow_ui_dispatch 新增 M5(drain 清队列+复位在途
  手势+新点击不受影响,ALL PASS);swipe 35/0、view_event ALL PASS(回归)。
- 重编重链(铁律):meow_ui.a 8525aa6b(meow_ui_input.o 8248a6c6,旧 e54903ca)→
  meow_launcherd e342d24f(891164) / files 17edac0f(383072) / hello 65c1c575(499380) /
  ncm 1c2f5ff7(1225676) / reader a463645f(1716212,gitignore 不入库)。
  picbrowser 仓库内 0 个 tracked 文件(源码不存在)→ 无法重链,见 finding。
- 真机:未验证(纪律禁止覆盖设备已装组件;uinput 注入是 M36 设备失联诱因)。
2026-11-10 21:58:13 +08:00
..

os.meow.files — MeowOS 外置文件管理器

TF 卡应用(framework=1 走 meow_ui SDK)。浏览 TF 卡目录树,支持进/退目录、列表虚拟化、 行操作(删除/重命名/新建目录/属性)、文本预览(≤256KB 截断)、空态/错误页;写操作严格限制在 TF 卡内(/usr/data/mnt/sd_0),拒绝 .. 越界与卡外路径。

目录结构(多文件,批次②拆分范本)

patch_v2/files_app/
├── app.c                  # main + 装配 + 事件分发(key/tap/swipe/tick/touch → 当前页)
├── files_internal.h       # 跨 TU 共享头(Activity id / 布局 / chrome/browser/viewer 声明)
├── build_files.sh         # 交叉编译脚本(zig-mipsel-cc 静态链 meow_ui.a libz.a -lpthread)
├── app.json               # {"name":"Files","exec":".../os.meow.files/files",...,"framework":1,"immersive":1}
├── README.md
├── ui/
│   ├── browser.c          # 目录浏览页(ACT_BROWSER 根页):虚拟化列表 + 空态 + 行操作
│   ├── viewer.c           # 文本预览页(ACT_VIEWER 子页):只读、可滚动、≤256KB 截断
│   └── common/
│       └── chrome.c       # 共享 chrome:topbar 建置 / 内容区几何 / 空态&页脚绘制
├── logic/
│   ├── fsx.h              # 纯业务接口(不依赖 meow_ui)
│   └── fsx.c              # 路径安全/格式化/枚举排序/虚拟化区间/写操作(可宿主测)
└── test/
    ├── test_fsx.c         # 宿主单元自测(gcc 直接编,指向临时卡根)
    └── test_row_text_fit.c # 宿主几何断言:列表行高 ≥ 双行文本块(M17)

构建

cd patch_v2/files_app && ./build_files.sh
# 产物:files(MIPS32r2 静态 ELF,~270KB)+ ./files + app.json

构建脚本仿 hello_app/build_hello.sh:zig-mipsel-cc -Os -static -Wall -Wextra -I../sdk -I. 链 ../sdk/meow_ui.a "$SYSROOT/lib/libz.a" -lpthread。本应用是 §5.5 结构化列表(meow_listview)的 首个消费方(patch_v2/sdk/meow_ui_widget.c 新增 W_LISTVIEW:图标+标题+副标题+右侧操作+选中); -Wall -Wextra 0 warning。行滚动/选中/命中由 SDK 收口,App 只驱动 scroll/sel 并分发(行体=进目录/ 开预览;右侧 ⋯ 或长按→BottomSheet)。

部署

/usr/data/mnt/sd_0/MeowAPPs/os.meow.files/
├── app.json
└── files

数据目录 MeowData/os.meow.files/{data,cache} 由 launcher 经 MEOW_DATA_DIR/MEOW_CACHE_DIR 注入; v1 无需持久化,暂未使用(启动恒进卡根 /usr/data/mnt/sd_0/)。

宿主自测(不跨编/不刷机)

cd patch_v2/files_app
gcc -O2 -Wall -Wextra -DFSX_SD_ROOT='"/tmp/meowfsx_fsx_test"' -Ilogic \
    test/test_fsx.c logic/fsx.c -o /tmp/test_fsx && /tmp/test_fsx

# 列表行高几何(无需 meow_ui.a;Termux 下产物落 $TMPDIR)
gcc -O2 -Wall -Wextra -I. -I../sdk test/test_row_text_fit.c \
    -o "$TMPDIR/test_row_text_fit" && "$TMPDIR/test_row_text_fit"

覆盖:路径安全(within_sd/join/sanitize/dirname/basename)、展示格式化(fmt_size/fmt_time)、 目录枚举排序(scan_dir/sort)、虚拟化区间(visible_range/max_scroll)、写操作(mkdir/rename/ delete,含卡外/越界拒绝)、非文本写拒绝。宿主全 PASS。

test_row_text_fit 是 M17 行高回归:目录列表行恒为双行档位,行高必须 ≥ 文本块 2*meow_ui_text_height(font_body) + 2(真机 20px 字库行高 30 → 62),否则 SDK 居中后首行标题 ty < 视口顶 被裁剪守卫整条跳过。断言矩阵覆盖真机五种字库行高 + 旧固定 56 的 bug 形态。

交互

  • 触摸:行 body=进目录/开文本预览;右侧 ⋯ 或长按→BottomSheet(删除[确认弹窗]/重命名[IME]/新建目录[IME]/属性[大小·mtime]); TopAppBar NEW=当前目录新建。
  • 左缘左滑/返回钮:预览页→回目录;浏览器→上级目录;卡根再左滑=退出 App。
  • 物理键不映射(APPS.md §3.4.1):息屏/音量/媒体语义归系统/SDK。

已知限制 / SDK 能力说明

  1. 结构化列表(meow_listview)已落地:ui/browser.c 的自绘 custom 列表已迁移到 SDK W_LISTVIEW (行数据源 browser_src2 填 meow_list_item)。图标(目录/文件内置符号)、名称、大小/时间副标题、 右侧 ⋯ 操作钮、虚拟化、空态/页脚、禁用行不响应均由 SDK 收口;fsx_scan_dir 仍以有界数组缓存 (FSX_MAX_ENTRIES=1024),列表虚拟化只画可见行——大目录不一次全读进内存。
    • long-press 已下沉 SDK(§6.3):SDK 通用长按判定「按下 ≥ 阈值(默认 500ms,可配)未抬手且 位移 ≤ 容忍」→ 经 meow_ui_set_long_press_cb 注册的回调带坐标交给本页;browser_on_long_press 用 meow_listview_hit 行命中打开 BottomSheet。App 层长按计时(browser_tick/g_press_ms)已删除, 其抬手 TAP 由 SDK 抑制(长按不再触发点击)。
    • 行高按字体行高派生(M17):files_row_h() = max(APP_ROW_H, 2*行高 + 2)(当前主题正文 font_body 的 meow_ui_text_height)。目录行是双行档位,行高小于双行文本块时 SDK 居中后首行 标题落在视口顶之上被裁剪(20px 字库行高 30 → 块 62,旧的显式 56 首行名称空白)。滚动/命中/ 最大滚动量由 SDK 按同一行高换算,页脚几何独立(r_list 单独扣 APP_FOOTER_H)。
  2. 中文字形:SDK 默认 common 字库(≈4k)不含 CJK 全量;exFAT 中文文件名/中文文本预览在 common 档会显示缺字豆腐。 预览页不声明 fonts(app.json 未加 "fonts":"full"/"gbk"),如需中文预览可在 app.json 声明大字号档。
  3. 预览仅文本:按扩展名白名单 + 首 512 字节 NUL 嗅探判文本;二进制/图片无预览(提示「not a text file」)。 超 256KB 只读前 256KB,页脚标注 >256KB (truncated)。
  4. 符号链接不追链:v1 视 symlink 为普通文件展示/操作(lstat),避免链出卡外。
  5. 单目录条目上限:>1024 项时只展示前 1024 并在页脚标注 showing first 1024(分页流式读目录为二期)。

验证状态

  • 编译:-Wall -Wextra 0 warning/0 error,产物 files(MIPS32r2 静态 ELF)。
  • 宿主:test/test_fsx.c 全 PASS;test/test_row_text_fit.c 32 断言全 PASS(M17 行高几何)。
  • qemu-mipsel(无 /dev/fb0 → SDK 离屏 480×720):正常运行到事件循环,导出离屏帧, 确认 init/建页/topbar(返回+NEW)/空态(NO STORAGE CARD)渲染正确、SIGTERM 干净退出。
  • 实机目录列表渲染(有真实 TF 卡目录)需刷机/设备挂载验证——宿主模拟无法造出 /usr/data/mnt/sd_0。