SDK: 内置 mipsel o32 时钟/睡眠 ABI 修复(meow_ui_time.c)+ 宿主单测
zig 0.17 内置 musl 在 mipsel 上头声明 64 位 time_t、实现走 time32 系统调用
(__ILP32__ 守卫),导致 meow_ui_time_ms() 毫秒位恒 0、高 32 位随机,
usleep(16000) 立即返回(帧节拍/轮询变忙等空转)。主仓已验证的修复逐字节
搬入公开仓:取时按内核 o32 ABI(8 字节 {int32 sec; int32 nsec})再升 64 位,
同 TU 强定义 __clock_gettime/64 与睡眠入口,链接期接管 libc。
- meow_ui_time.c / CLOCK_ABI.md:cp 搬运,md5 与主仓一致(含原注释与依据链)
- meow_ui_gfx.c:删掉 now_ms/meow_ui_time_ms(避免与 meow_ui_time.o 重复符号),
6 处内部调用改外部 meow_ui_time_ms;<time.h> 不再使用
- meow_ui_internal.h:补 meow_time_ms_from_parts 声明
- build_sdk.sh:OBJS 首位加 meow_ui_time,头注释写明归档须排在 -lc 之前
- test_meow_time.c:适配公开仓构建方式,加负 sec 回绕两例,输出断言数(15)
- sdk/README.md:公开说明本次修复、链接顺序要求与已知限制(2038 前有效)
- tuku/test/run_tests.sh、run_webtoon_tests.sh:$SDK 源文件清单补 meow_ui_time.c
(宿主集成测试直接编 SDK 源,否则链接期 undefined symbol)
验证:sdk/build_sdk.sh 0 warning;宿主 test_meow_time == PASS == checks=15;
tuku 两套测试全 PASS(run_tests 462+106+325 断言、webtoon 240 断言,0 failed);
探针/ picbrowser 反汇编确认 __clock_gettime 取自本 TU(8 字节 time32 + 升位),
无 libc VDSO 前导;picbrowser 仍为 MIPS32r2 静态 ELF。
This commit is contained in:
1 parent
4773ec1a61
commit
a07f9752d3
9 files changed
+531
-23
No files matched your search
@@ -0,0 +1,187 @@
|
||||
# SDK 时钟 ABI 说明(meow_ui_time_ms 真机故障的根因与修复)
|
||||
|
||||
面向对象:维护 SDK / 工具链 / 重链 App 的人。**结论先行**:这不是 SDK 代码写错,
|
||||
是 zig 0.17 自带 musl 在 mipsel 上的 time64 ABI 自相矛盾;修复落在 SDK 链上
|
||||
(`patch_v2/sdk/meow_ui_time.c` + 链接期强定义),改后需重编 `meow_ui.a` 并重链四 App。
|
||||
|
||||
## 1. 现象(真机)
|
||||
|
||||
M10 抓到的日志首列(`meow_ui_time_ms()`,64 位)可拆成:
|
||||
|
||||
```
|
||||
6607363222717682072 → lo=1135000 hi=1538396632
|
||||
8754846870717682072 → lo=1135000 hi=2038396632
|
||||
18138485921460348192 → lo=1140000 hi=4223195352
|
||||
```
|
||||
|
||||
- 低 32 位 = 开机秒数 × 1000,**恒为 1000 的倍数 → 毫秒位丢失**;
|
||||
- 高 32 位每次调用随机、无单调性。
|
||||
|
||||
同进程另一份取时(旧容器编的 core)正常 → 问题只在「zig 0.17 编的那部分」。
|
||||
|
||||
## 2. 根因链(三段证据)
|
||||
|
||||
### 2.1 zig 头文件:`time_t` 是 64 位,`struct timespec` 16 字节
|
||||
|
||||
`<zig lib>/libc/include/mips-linux-musl/bits/alltypes.h`:
|
||||
|
||||
```c
|
||||
#define _REDIR_TIME64 1
|
||||
#define __USE_TIME_BITS64 1
|
||||
typedef _Int64 time_t; /* 无条件 64 位 */
|
||||
struct timespec { time_t tv_sec; ...; long tv_nsec; ... }; /* tv_sec@0(8B)、tv_nsec@8 */
|
||||
```
|
||||
|
||||
`include/generic-musl/time.h` 因此在 `_REDIR_TIME64` 下把 `clock_gettime` 重定向到
|
||||
`__clock_gettime64`。用该链编译的 C 代码一律按「16 字节、tv_sec 8 字节」取数
|
||||
(`patch_v2/sdk/meow_ui.a(meow_ui_gfx.o)` 的旧 `meow_ui_time_ms` 反汇编:`lw 0x10/0x14`
|
||||
取 tv_sec、`lw 0x18` 取 tv_nsec,正是这个布局)。
|
||||
|
||||
### 2.2 zig 的 clang 给 mipsel 预定义了 `__ILP32__`
|
||||
|
||||
```
|
||||
$ echo | zig cc --target=mipsel-linux-musleabi -dM -E - | grep __ILP32__
|
||||
#define __ILP32__ 1
|
||||
```
|
||||
|
||||
(arm-linux-musleabi 同样为 1;x86-linux-musl / aarch64 为 0。容器里的 gcc 对 mips o32
|
||||
**不定义** `__ILP32__`。)
|
||||
|
||||
### 2.3 musl 的 time64 回退分支被这句 `!__ILP32__` 整体编译掉
|
||||
|
||||
`<zig lib>/libc/musl/src/time/clock_gettime.c`:
|
||||
|
||||
```c
|
||||
#if defined(SYS_clock_gettime64) && !defined(__ILP32__)
|
||||
... __syscall(SYS_clock_gettime64, clk, ts); /* 4403 */
|
||||
... 失败(ENOSYS) 再 __syscall(SYS_clock_gettime, clk, ts32); /* 4263 + 宽度转换 */
|
||||
#else
|
||||
r = __syscall(SYS_clock_gettime, clk, ts); /* 直接把 8 字节 time32 写进调用者缓冲 */
|
||||
#endif
|
||||
```
|
||||
|
||||
这句 `!__ILP32__` 本意只排除 x32(`__ILP32__` 是 x32 的标记),但 clang 把它当成
|
||||
「32 位数据模型」的通名。于是 zig 的 mipsel libc 走进 `#else`:
|
||||
`__clock_gettime64` 实际执行 **SYS_clock_gettime(4263)**,只写 8 字节
|
||||
`{ int32 tv_sec; int32 tv_nsec; }`。
|
||||
|
||||
**证据(反汇编,当前 zig 0.17 静态链接的探针)**:
|
||||
|
||||
```
|
||||
0002576c <__clock_gettime64>:
|
||||
257c8: addiu $2,$zero,0x10a7 ; 4263 = SYS_clock_gettime(time32)
|
||||
257cc: syscall ; $5 直接是调用者的 16 字节 struct 指针,无 4403 尝试、无转换
|
||||
```
|
||||
|
||||
同一符号在**旧容器产物** `patch_v2/ncm_app/ncm`(已入库,gcc 构建)里是:
|
||||
|
||||
```
|
||||
115748: addiu $2,$zero,0x1133 ; 4403 = SYS_clock_gettime64
|
||||
11574c: syscall
|
||||
...
|
||||
115774: addiu $2,$zero,0x10a7 ; ENOSYS 后回退 4263,写入本地 ts32
|
||||
...
|
||||
; 再把 ts32 升成 64 位写回调用者(tv_sec=tv_sec、tv_nsec=tv_nsec)
|
||||
```
|
||||
|
||||
两者对比即根因:**头文件 ABI(64 位 time_t)与 libc 实现 ABI(32 位 time32)不一致**。
|
||||
|
||||
### 2.4 数值解释(为什么「低 32 = 秒×1000、高 32 随机」)
|
||||
|
||||
libc 把 `{sec, nsec}` 当两个 int32 写进 16 字节缓冲的前 8 字节;SDK 按 64 位读:
|
||||
|
||||
```
|
||||
tv_sec = sec | (nsec << 32) // 低字正确、高字=纳秒
|
||||
tv_nsec = 缓冲偏移 8 的未初始化值 // 毫秒项≈0
|
||||
result = tv_sec*1000 + tv_nsec/1e6
|
||||
= sec*1000 + (nsec*1000 mod 2^32)<<32 + 垃圾/1e6
|
||||
```
|
||||
|
||||
- 低 32 位只剩 `sec*1000`(nsec 项左移 32 位不落到低字)→ 恒为 1000 的倍数;
|
||||
- 高 32 位 = `nsec*1000 mod 2^32` → 每次随机、不单调;
|
||||
- `tv_nsec` 来自越界读 → 通常是小值/0(真机上恰好 0,所以没有额外尾数)。
|
||||
|
||||
## 3. 修复(`patch_v2/sdk/meow_ui_time.c`)
|
||||
|
||||
1. **取数走内核 o32 ABI**:`meow_ui_time_ms()` 用
|
||||
`syscall(SYS_clock_gettime32, CLOCK_MONOTONIC, &{int32 sec; int32 nsec;})`,
|
||||
再 `meow_time_ms_from_parts(sec, nsec)` 升成 64 位毫秒。完全不依赖 libc 头声明的
|
||||
`struct timespec` 宽度 —— 即使将来 zig 修好(或换回容器链),内核 time32 入口始终存在。
|
||||
2. **链接期修 libc 入口**:同一 TU 强定义 `__clock_gettime` / `__clock_gettime64`
|
||||
(musl 的 `time()`、`gettimeofday()`、`__timedwait_cp()`(pthread 定时等待)内部都调
|
||||
`__clock_gettime`)。应用链接顺序是 `... meow_ui.a ... -lc`,归档先解析符号,libc 里那份
|
||||
坏实现(及其 VDSO 前导)不会被拉入 —— 用 `nm` 验证链接产物里没有 `cgt_init/__vdsosym`,
|
||||
且 `__time64`/`__timedwait_cp` 的 `jal` 指向本 TU 的实现。
|
||||
3. **睡眠入口一并修正(同源缺陷)**:`__nanosleep_time64` / `__clock_nanosleep(_time64)`
|
||||
先按 time32 转换再发 `SYS_nanosleep`/`SYS_clock_nanosleep`,`rem` 再扩回 64 位。
|
||||
修复前 zig libc 把 16 字节 time64 结构直接交给 time32 系统调用,内核读到的 nsec 恒为 0 →
|
||||
SDK 里 `usleep(16000)`(帧节拍、输入轮询、IME 冷却)全部变成忙等空转。
|
||||
4. **去掉四处重复的 `now_ms()`**:`meow_ui_gfx.c` / `meow_ui_sys.c` / `meow_ui_input.c` /
|
||||
`meow_cachegov.c` 统一调 `meow_ui_time_ms()`(cachegov 保留宿主测试注入桩)。
|
||||
5. 构建脚本 `build_sdk.sh` 的 OBJS 增加 `meow_ui_time`;宿主单测 `test_meow_time.c`
|
||||
固定换算边界与「单调且前进」;探针 `tools/clock_abi_probe.c` 用于宿主/真机一眼验收。
|
||||
|
||||
非 mips 目标(宿主单测、aarch64 宿主)走普通 `clock_gettime`,行为不变。
|
||||
|
||||
## 4. 验证
|
||||
|
||||
宿主:
|
||||
|
||||
```
|
||||
cd patch_v2/sdk
|
||||
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o /tmp/meow_ui_time.o
|
||||
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c /tmp/meow_ui_time.o -o /tmp/test_meow_time
|
||||
/tmp/test_meow_time # == PASS ==(换算边界 + 单调前进)
|
||||
```
|
||||
|
||||
交叉:
|
||||
|
||||
```
|
||||
bash patch_v2/sdk/build_sdk_host.sh --verify # 成员清单/架构自检 + 0 warning
|
||||
llvm-objdump -d meow_ui.a(meow_ui_time.o) # meow_ui_time_ms: syscall 4263, lw 0x18 取 nsec、sra 扩展 tv_sec
|
||||
```
|
||||
|
||||
链接期接管(用真实归档链一个探针,检查符号来源):
|
||||
|
||||
```
|
||||
# 不应出现 libc 的 VDSO 前导(cgt_init/__vdsosym)→ libc 的取时实现未被拉入
|
||||
strings -a clock_abi_probe_sdk | grep -c __vdso # 期望 0
|
||||
llvm-nm clock_abi_probe_sdk | grep -E 'clock_gettime|nanosleep'
|
||||
# __time64 / __timedwait_cp 的 jal 应指向本 TU 的 __clock_gettime;睡眠走 SYS_nanosleep(4166)/clock_nanosleep(4265)
|
||||
```
|
||||
|
||||
探针 `tools/clock_abi_probe.c`(宿主 `gcc` 或 mipsel 交叉;加 `-DMEOW_PROBE_WITH_SDK`
|
||||
并链 `meow_ui.a` 可同时验收 `meow_ui_time_ms`):把 `struct timespec` 预填 0xAA 后取时,
|
||||
若偏移 8..11 仍是 0xAA 即说明 libc 只写了 8 字节(ABI 不一致),退出码 2。
|
||||
|
||||
真机(塔台批准后):日志首列应连续、出现非整千差;`meow_ui_time_ms()` 两次取样差 = 真实毫秒。
|
||||
本次设备已离线,真机确认**待办**。
|
||||
|
||||
## 5. 影响面
|
||||
|
||||
- **SDK 内**:`meow_ui_time_ms()` 及其上层 —— 动画 tween(`meow_ui_anim.c`)、滚动/fling
|
||||
(`meow_ui_scroll.c`/`meow_select.c`)、IME 冷却(`meow_ui_ime.c`)、状态栏动画
|
||||
(`meow_ui_gfx.c`)、通知 TTL(`meow_ui_notify.c`)、输入层 tick/启动冷却
|
||||
(`meow_ui_input.c`)、cachegov 回退窗口(`meow_cachegov.c`)、日志首列(`meow_ui_log.c`)。
|
||||
- **App 侧**:`on_tick` 入参 `now`、`meow_anim_*`、以及任何自行调用 `meow_ui_time_ms()` 的
|
||||
App(hello/reader/files/ncm…)。
|
||||
- **产物**:`meow_ui.a` 变更 → 需**重编并重链四 App**(ncm/reader/files/hello);
|
||||
`time()`/`gettimeofday()` 的修正也在同一份归档里生效(链接期覆盖)。
|
||||
- 真机写入红线未触碰;本单只改 SDK 源与构建脚本。
|
||||
|
||||
## 6. 同源残余(未在本单修复,需单独安排)
|
||||
|
||||
同一句 `!__ILP32__` 还打穿了以下路径(睡眠族已在上文一并修掉,余下这些症状不同、
|
||||
修复面更大):
|
||||
|
||||
| 路径 | 症状 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `futex` 相对超时 | 亚秒 timed wait 立即超时 | `__timedwait_cp` 的 `__futex4_cp` 同款 `!__ILP32__` 守卫;`pthread_cond_timedwait` 的「now」已由本单修正,但相对超时仍按 8 字节读(>1s 的整秒等待基本可用,亚秒等待失效) |
|
||||
| `timerfd_settime` | 到期时间错乱 | `struct itimerspec`(32 字节)当 16 字节读;当前 SDK 未用 timerfd |
|
||||
| `clock_settime`/`clock_adjtime`/`pselect`/`ppoll`/`sigtimedwait`/`semtimedop` 等 | 未验证 | 同族守卫;SDK 目前未直接使用 |
|
||||
|
||||
建议:向 zig 上游报 musl `__ILP32__` 守卫问题(clang 对 32 位目标统一定义 `__ILP32__`,
|
||||
而该守卫本意只排除 x32);根治应换用容器旧链(gcc,不定义 `__ILP32__`)编 SDK,或
|
||||
downgrade/修补 zig 内置 musl。**不要**指望 `-D_TIME_BITS=32`:zig 的
|
||||
`alltypes.h` 是无条件 `#define __USE_TIME_BITS64 1`,命令行改不动;而只改用户侧头文件会让
|
||||
用户结构(8 字节)与 libc 内部(16 字节,pthread 等)再次错配。
|
||||
@@ -0,0 +1,59 @@
|
||||
# sdk/ — meow_ui 应用框架(源码 + 构建)
|
||||
|
||||
本目录是 `meow_ui.a` 的**自包含**源码树:公共头在 `include/`(伞头 `meow_ui.h`),
|
||||
构建脚本 `build_sdk.sh`,宿主逻辑测试 `test_*.c`。上手指南见仓库根
|
||||
[README.md](../README.md) 与 [docs/APP_DEV_GUIDE.md](../docs/APP_DEV_GUIDE.md)。
|
||||
|
||||
## 构建
|
||||
|
||||
```sh
|
||||
cd sdk
|
||||
./build_sdk.sh # 产出 sdk/meow_ui.a(mipsel 静态库)
|
||||
```
|
||||
|
||||
交叉编译器默认 `$HOME/Project/R3ProII/.tools/zig-mipsel-cc`,可用 `MEOW_CC` 覆盖。
|
||||
|
||||
## 时钟:mipsel o32 时钟/睡眠 ABI 修复(已内置)
|
||||
|
||||
**公开 SDK 现已内置 mipsel o32 时钟/睡眠 ABI 修复(见 [CLOCK_ABI.md](CLOCK_ABI.md))**,
|
||||
源码在 `meow_ui_time.c`。
|
||||
|
||||
- 背景:zig 内置 musl 在 mipsel 上「头文件声明 64 位 `time_t`、实现却走 time32 系统调用」,
|
||||
导致 `meow_ui_time_ms()` 毫秒位恒 0、高 32 位随机;`usleep()` 亚秒等待立即返回(忙等空转)。
|
||||
- 修复:`meow_ui_time_ms()` 直接按**内核 o32 ABI**(8 字节 `{int32 sec; int32 nsec;}`)取时,
|
||||
再自行升成 64 位毫秒;同一 TU 强定义 `__clock_gettime` / `__clock_gettime64` /
|
||||
`__clock_nanosleep` / `__clock_nanosleep_time64` / `__nanosleep_time64`,在**链接期接管**
|
||||
libc 里那份 ABI 不一致的实现。
|
||||
- **链接顺序要求**:应用必须把 `meow_ui.a` 放在 `-lc` **之前**(`cc ... meow_ui.a ... -lc`),
|
||||
强定义才能生效。仓库内 `examples/hello/build_hello.sh` 与 `tuku/build_tuku.sh` 均如此;
|
||||
自定义构建脚本请照抄这个顺序,否则取时/睡眠会静默回到坏实现。
|
||||
- 非 mipsel 目标(宿主单测、aarch64 等)走普通 `clock_gettime`,行为不变。
|
||||
|
||||
### 已知限制
|
||||
|
||||
- **2038 前有效**:内核侧仍是 time32(`tv_sec` 为 int32),秒数超出 `INT32_MAX` 即溢出;
|
||||
本修复只保证「头/实现的宽度一致」,不把内核升到 time64。
|
||||
- 同源缺陷**未修**:futex 相对超时(`pthread_cond_timedwait` 的亚秒等待)、`timerfd_settime`、
|
||||
`clock_settime`/`clock_adjtime`、`pselect`/`ppoll` 等仍受同一句 `!__ILP32__` 影响;
|
||||
清单与影响见 [CLOCK_ABI.md](CLOCK_ABI.md) §6。SDK 自身不直接使用这些路径。
|
||||
- 真机验收:本修复的证据是反汇编 + 宿主单测(本机无 qemu-mipsel),设备侧仍需实测确认。
|
||||
|
||||
## 宿主测试
|
||||
|
||||
无硬件即可跑(`gcc` 直接编译,不链 `meow_ui.a`):
|
||||
|
||||
```sh
|
||||
cd sdk
|
||||
# 时钟换算与单调性
|
||||
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o meow_ui_time_host.o
|
||||
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c meow_ui_time_host.o -o test_meow_time
|
||||
./test_meow_time # == PASS == / checks=N
|
||||
|
||||
# 伞头与其余模块测试(示例)
|
||||
gcc -I. -Iinclude test_umbrella.c <各 sdk 源 .c> test_host_stub.c -lpthread -o test_umbrella
|
||||
./test_umbrella
|
||||
```
|
||||
|
||||
注意:宿主编译时 `meow_ui_time.c` 走 `MEOW_TIME32_KERNEL_ABI 0` 分支(普通 `clock_gettime`),
|
||||
因此**宿主测试不能证明 mipsel o32 路径正确** —— 那条路径的正确性靠 `CLOCK_ABI.md §4` 的
|
||||
反汇编证据(`nm` 符号来源 + `syscall 4263/4265` 的 8 字节转换)。
|
||||
+8
-1
@@ -6,6 +6,11 @@
|
||||
#
|
||||
# 交叉编译器:默认 MeowOS 开发机路径 $HOME/Project/R3ProII/.tools/zig-mipsel-cc;
|
||||
# 可用环境变量 MEOW_CC 覆盖(或用你自己装好的 zig 交叉 cc,--target=mipsel-linux-musleabi soft-float)。
|
||||
#
|
||||
# 时钟(meow_ui_time):mipsel 上 zig 内置 musl 的 time64 ABI 自相矛盾(头声明 64 位 time_t、
|
||||
# 实现走 time32 取数),该 TU 按内核 o32 ABI 取时并**强定义** __clock_gettime*/睡眠入口,靠链接期
|
||||
# 接管 libc 那份坏实现。因此应用链接顺序必须是 `... meow_ui.a ... -lc`(归档在 libc 之前,
|
||||
# 现有 build_tuku.sh 即如此);根因与证据链见 sdk/CLOCK_ABI.md。
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
CC="${MEOW_CC:-$HOME/Project/R3ProII/.tools/zig-mipsel-cc}"
|
||||
@@ -16,7 +21,9 @@ rm -f meow_ui.a *.o
|
||||
# meow_font.c = 预渲染位图字体运行时(自包含:只含 meow_font.h + 标准头;最小 zlib ABI 自声明,
|
||||
# 链接时用 sysroot 的 libz.a),随 SDK 一并 vendored。新模块(box/anim/scroll/str + cachegov/lazygrid/
|
||||
# select/net_ipc)随主仓增量并入。
|
||||
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_pinyin meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media meow_ui_audio stb_image meow_font meow_ui_box meow_ui_anim meow_ui_scroll meow_ui_str meow_cachegov meow_lazygrid meow_select meow_net_ipc"
|
||||
# meow_ui_time.c 必须**编进归档**且排在前面:它提供 meow_ui_time_ms(gfx 已不再实现)与
|
||||
# __clock_gettime* 强定义,归档成员随 meow_ui_time_ms 的引用被拉入,从而在 -lc 之前解析。
|
||||
OBJS="meow_ui_time meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_pinyin meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media meow_ui_audio stb_image meow_font meow_ui_box meow_ui_anim meow_ui_scroll meow_ui_str meow_cachegov meow_lazygrid meow_select meow_net_ipc"
|
||||
CFLAGS="-Os -static -Iinclude -I."
|
||||
for f in $OBJS; do
|
||||
echo "== cc $f =="
|
||||
|
||||
+9
-16
@@ -20,7 +20,6 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
@@ -172,15 +171,9 @@ static void refresh_governor(unsigned long long cost_ms, int period)
|
||||
}
|
||||
|
||||
/* ---- 工具 ---- */
|
||||
static uint64_t now_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
|
||||
}
|
||||
|
||||
uint64_t meow_ui_time_ms(void) { return now_ms(); }
|
||||
|
||||
/* 时钟统一由 meow_ui_time.c 提供(meow_ui_time_ms,声明见 include/meow_ui_draw.h):
|
||||
* mipsel 上 zig musl 的 time64 ABI 自相矛盾,取时必须走内核 o32 ABI 再升位,
|
||||
* 故本 TU 不再保留取时实现(详见 sdk/CLOCK_ABI.md)。 */
|
||||
static uint16_t rgb565(uint32_t c)
|
||||
{
|
||||
return (uint16_t)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F));
|
||||
@@ -895,7 +888,7 @@ static void statusbar_collapse_step(void)
|
||||
int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0;
|
||||
if (g_sbar_collapse_h == tgt) return;
|
||||
if (g_sbar_collapse_anim_ms <= 0) { g_sbar_collapse_h = tgt; return; }
|
||||
unsigned long long now = now_ms();
|
||||
unsigned long long now = meow_ui_time_ms();
|
||||
unsigned long long el = now - g_sbar_collapse_start_ms;
|
||||
int span = g_sbar_collapse_anim_ms;
|
||||
if ((int)el >= span) { g_sbar_collapse_h = tgt; return; }
|
||||
@@ -932,7 +925,7 @@ void meow_ui_statusbar_set_collapsed(int on)
|
||||
if (tgt == g_sbar_collapsed) return;
|
||||
g_sbar_collapsed = tgt;
|
||||
g_sbar_collapse_from_h = g_sbar_collapse_h;
|
||||
g_sbar_collapse_start_ms = now_ms();
|
||||
g_sbar_collapse_start_ms = meow_ui_time_ms();
|
||||
dirty_full(); /* 顶栏显隐/上收变化 → 全帧重画 */
|
||||
meow_ui_sbar_write_mode(); /* App 进程上报顶条模式(launcher 感知全屏/收合,agent-146) */
|
||||
}
|
||||
@@ -1012,7 +1005,7 @@ int meow_ui_overlay_suppressed_active(void)
|
||||
snprintf(s_path, sizeof s_path, "%s", (e && e[0]) ? e : MEOW_NOTIF_ACTIVE_DEFAULT);
|
||||
s_path_init = 1;
|
||||
}
|
||||
unsigned long long now = now_ms();
|
||||
unsigned long long now = meow_ui_time_ms();
|
||||
if (s_last_chk && now - s_last_chk < MEOW_OVERLAY_CHK_MS) return s_last_res;
|
||||
s_last_chk = now;
|
||||
{
|
||||
@@ -1066,7 +1059,7 @@ static void present(void)
|
||||
}
|
||||
/* 帧率门控(A-1/A-2,从 launcher 迁入):present 距上次 ≥ period(1000/eff_rate) 才呈现;
|
||||
* 未满周期则保留脏区(不 dirty_reset),下一轮再试。refresh_always_max=1 时跳过门控(恒满速)。 */
|
||||
unsigned long long now = now_ms();
|
||||
unsigned long long now = meow_ui_time_ms();
|
||||
int period = (g_eff_rate > 0) ? (1000 / g_eff_rate) : 16;
|
||||
if (!g_refresh_always_max) {
|
||||
if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return;
|
||||
@@ -1096,7 +1089,7 @@ static void present(void)
|
||||
}
|
||||
if (g_fb_mem) {
|
||||
ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */
|
||||
unsigned long long tbg = now_ms();
|
||||
unsigned long long tbg = meow_ui_time_ms();
|
||||
/* 「同一个状态栏」闭环:App 进程不渲染状态栏(meow_ui_sys_render_enabled()==0),也绝不向
|
||||
* 真实 fb 写顶条 inset 区(顶条由 launcher 系统进程叠加绘制)。否则 App 首帧全量拷贝
|
||||
* (meow_ui_init 置 dirty_full)会把 launcher 刚叠画的顶条冲成未初始化数据——真机
|
||||
@@ -1133,7 +1126,7 @@ static void present(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
refresh_governor(now_ms() - tbg, period);
|
||||
refresh_governor(meow_ui_time_ms() - tbg, period);
|
||||
}
|
||||
g_last_present_ms = now;
|
||||
g_frames_presented++;
|
||||
|
||||
@@ -284,4 +284,8 @@ int meow_ime_handle_event(const meow_input_ev *e);
|
||||
/* 当前 IME 是否拼音模式(供 meow_ui_input.c 决定是否喂长按轮询)。 */
|
||||
int meow_ime_mode_pinyin(void);
|
||||
|
||||
/* ---- 时钟(meow_ui_time.c)---- */
|
||||
/* 秒/纳秒 → 单调毫秒的唯一换算点(meow_ui_time_ms 与宿主单测共用)。 */
|
||||
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec);
|
||||
|
||||
#endif /* MEOW_UI_INTERNAL_H */
|
||||
@@ -0,0 +1,147 @@
|
||||
/* meow_ui_time.c — SDK 单调时钟(meow_ui_time_ms)+ mipsel o32 时间 ABI 兜底。
|
||||
*
|
||||
* 根因(M11;真机日志 + 反汇编证据链见 sdk/CLOCK_ABI.md):
|
||||
* zig 0.17 的 clang 前端给 32 位 mips 目标预定义了 __ILP32__;zig 自带 musl 的
|
||||
* time64 回退分支统一写作 `#if defined(SYS_x_time64) && !defined(__ILP32__)`
|
||||
* (该 `!__ILP32__` 本意只排除 x32),于是 mipsel 的 libc 把「time64 入口」编译成了
|
||||
* 老 32 位实现:__clock_gettime64 直接做 SYS_clock_gettime(4263),只写 8 字节
|
||||
* `{ int32 tv_sec; int32 tv_nsec; }`。而 zig 头文件又无条件把 time_t 声明成 64 位
|
||||
* (struct timespec 16 字节、tv_sec 8 字节),两边 ABI 不一致:
|
||||
* tv_sec 低字 = 真 sec,高字 = 真 nsec → 64 位结果高 32 位随机、非单调;
|
||||
* tv_nsec(结构偏移 8)根本没被写 → 毫秒位恒 0(真机实测全是 1000 的倍数)。
|
||||
* 容器旧链(gcc)不定义 __ILP32__,time64 回退正确——对照 patch_v2/ncm_app/ncm:
|
||||
* 同一函数里先 SYS_clock_gettime64(4403)、ENOSYS 后走 4263 并做宽度转换。
|
||||
*
|
||||
* 修复:不依赖 libc 头声明的 struct timespec 宽度,直接按**内核 o32 ABI**
|
||||
* (8 字节 time32:int32 sec + int32 nsec)取时,再自行升成 64 位毫秒;
|
||||
* 同时在本 TU 给出 __clock_gettime / __clock_gettime64 的强定义。应用链接顺序是
|
||||
* `... meow_ui.a ... -lc`,归档先参与解析,因此链接期整体替换掉 libc 里那份
|
||||
* ABI 不一致的实现:time()、gettimeofday()、pthread 定时等待的内部取时
|
||||
* (musl 的 time()/__timedwait_cp 都调 __clock_gettime)随之修正。
|
||||
* 上层时间位宽仍是 64 位,只有内核取数按 o32 time32;2038 前有效。
|
||||
*
|
||||
* 同一句 `!__ILP32__` 还打穿了睡眠:__nanosleep_time64/__clock_nanosleep 走 `#else`
|
||||
* 把 16 字节 time64 结构直接交给 time32 系统调用,内核读到的 nsec 恒为 0 →
|
||||
* `usleep(16000)` 立即返回(SDK 里帧节拍/轮询会变忙等空转)。这里一并按 time32
|
||||
* 转换后重发,恢复亚秒睡眠。
|
||||
*
|
||||
* 残余(未修):futex 相对超时(pthread 定时等待的剩余时间按 8 字节读)、timerfd_settime、
|
||||
* clock_settime/adjtime、pselect/ppoll 超时——同为该守卫的受害者,见 CLOCK_ABI.md §6。
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "meow_ui.h"
|
||||
|
||||
#if defined(__mips__) && !defined(__mips64) && (__SIZEOF_LONG__ == 4)
|
||||
#define MEOW_TIME32_KERNEL_ABI 1
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#define MEOW_TIME32_KERNEL_ABI 0
|
||||
#endif
|
||||
|
||||
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec)
|
||||
{
|
||||
return (uint64_t)sec * 1000u + (uint64_t)nsec / 1000000u;
|
||||
}
|
||||
|
||||
uint64_t meow_ui_time_ms(void)
|
||||
{
|
||||
#if MEOW_TIME32_KERNEL_ABI
|
||||
struct meow_time32 { int32_t tv_sec; int32_t tv_nsec; } t;
|
||||
t.tv_sec = 0;
|
||||
t.tv_nsec = 0;
|
||||
if (syscall(SYS_clock_gettime32, CLOCK_MONOTONIC, &t) != 0) return 0;
|
||||
return meow_time_ms_from_parts(t.tv_sec, t.tv_nsec);
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0;
|
||||
return meow_time_ms_from_parts((int64_t)ts.tv_sec, (int64_t)ts.tv_nsec);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MEOW_TIME32_KERNEL_ABI
|
||||
/* 内核 o32 time32 布局(与 libc 头的 time_t 宽度无关)。 */
|
||||
struct meow_time32 {
|
||||
int32_t tv_sec;
|
||||
int32_t tv_nsec;
|
||||
};
|
||||
|
||||
static int32_t time32_clamp(int64_t v)
|
||||
{
|
||||
if (v > INT32_MAX) return INT32_MAX;
|
||||
if (v < 0) return 0;
|
||||
return (int32_t)v;
|
||||
}
|
||||
|
||||
/* ---- libc 取时入口的强定义 ----
|
||||
* 返回约定与 musl 一致:0 成功 / -1 失败(errno)。
|
||||
* 说明:<time.h> 在 _REDIR_TIME64 下把标识符 clock_gettime 的引用改名为
|
||||
* __clock_gettime64,故本 TU 不定义 clock_gettime 这个名字;musl 里它只是
|
||||
* __clock_gettime 的 weak alias,同一份语义已由下面的强定义接管。 */
|
||||
int __clock_gettime(clockid_t clk, struct timespec *ts);
|
||||
int __clock_gettime64(clockid_t clk, struct timespec *ts);
|
||||
|
||||
int __clock_gettime(clockid_t clk, struct timespec *ts)
|
||||
{
|
||||
struct meow_time32 t;
|
||||
if (!ts) { errno = EFAULT; return -1; }
|
||||
t.tv_sec = 0;
|
||||
t.tv_nsec = 0;
|
||||
if (syscall(SYS_clock_gettime32, clk, &t) != 0) return -1;
|
||||
ts->tv_sec = t.tv_sec;
|
||||
ts->tv_nsec = t.tv_nsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __clock_gettime64(clockid_t clk, struct timespec *ts)
|
||||
{
|
||||
return __clock_gettime(clk, ts);
|
||||
}
|
||||
|
||||
/* ---- 睡眠入口(同源缺陷:亚秒 usleep 变空转)----
|
||||
* __clock_nanosleep 按 musl 内部约定返回 0 或正 errno;__nanosleep_time64 是
|
||||
* nanosleep/usleep 的重定向目标,按公共约定返回 0/-1(errno)。 */
|
||||
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem);
|
||||
int __clock_nanosleep_time64(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem);
|
||||
int __nanosleep_time64(const struct timespec *req, struct timespec *rem);
|
||||
|
||||
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
|
||||
{
|
||||
struct meow_time32 rq, rm;
|
||||
long r;
|
||||
|
||||
if (clk == CLOCK_THREAD_CPUTIME_ID) return EINVAL;
|
||||
if (!req) return EFAULT;
|
||||
if (req->tv_nsec < 0 || req->tv_nsec >= 1000000000L) return EINVAL;
|
||||
rq.tv_sec = time32_clamp(req->tv_sec);
|
||||
rq.tv_nsec = (int32_t)req->tv_nsec;
|
||||
rm.tv_sec = 0;
|
||||
rm.tv_nsec = 0;
|
||||
|
||||
if (clk == CLOCK_REALTIME && !flags)
|
||||
r = syscall(SYS_nanosleep, &rq, rem ? &rm : 0);
|
||||
else
|
||||
r = syscall(SYS_clock_nanosleep_time32, clk, flags, &rq, rem ? &rm : 0);
|
||||
if (r != 0) return errno;
|
||||
if (rem) {
|
||||
rem->tv_sec = rm.tv_sec;
|
||||
rem->tv_nsec = rm.tv_nsec;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __clock_nanosleep_time64(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
|
||||
{
|
||||
return __clock_nanosleep(clk, flags, req, rem);
|
||||
}
|
||||
|
||||
int __nanosleep_time64(const struct timespec *req, struct timespec *rem)
|
||||
{
|
||||
int r = __clock_nanosleep(CLOCK_REALTIME, 0, req, rem);
|
||||
if (r) { errno = r; return -1; }
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/* test_meow_time.c — 宿主单测:SDK 时钟换算(meow_ui_time.c / meow_time_ms_from_parts)。
|
||||
*
|
||||
* 背景(M11/M21):mipsel 真机上 meow_ui_time_ms() 曾返回「低 32 位=秒×1000、高 32 位随机」,
|
||||
* 根因是 zig musl 头(time_t 64 位)与 libc 实现(o32 time32 取数)ABI 不一致,见
|
||||
* sdk/CLOCK_ABI.md。修复后取数统一走 meow_time_ms_from_parts(sec, nsec),本测试把
|
||||
* 换算边界(毫秒进位/截断/大秒数/负值回绕)与「真时钟单调且前进」固定在宿主上。
|
||||
*
|
||||
* 注意:宿主(非 mips)编译时 meow_ui_time.c 走 MEOW_TIME32_KERNEL_ABI=0 分支,即普通
|
||||
* clock_gettime —— 本测试**不能**证明 mipsel o32 路径正确(那条路径只靠反汇编证据)。
|
||||
*
|
||||
* 编译(在 sdk/ 目录下):
|
||||
* gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o meow_ui_time_host.o
|
||||
* gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c meow_ui_time_host.o -o test_meow_time
|
||||
* 运行:./test_meow_time (exit 0=PASS / 1=FAIL)
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h"
|
||||
|
||||
static int g_fail = 0;
|
||||
static unsigned long g_checks = 0;
|
||||
|
||||
static void eq_ms(const char *name, int64_t sec, int64_t nsec, uint64_t want)
|
||||
{
|
||||
uint64_t got = meow_time_ms_from_parts(sec, nsec);
|
||||
g_checks++;
|
||||
if (got != want) {
|
||||
printf("FAIL %s: sec=%lld nsec=%lld got=%llu want=%llu\n",
|
||||
name, (long long)sec, (long long)nsec,
|
||||
(unsigned long long)got, (unsigned long long)want);
|
||||
g_fail = 1;
|
||||
} else {
|
||||
printf("ok %s = %llu\n", name, (unsigned long long)got);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* ---- 1) 换算边界:毫秒 = sec*1000 + nsec/1e6(整除截断) ---- */
|
||||
eq_ms("零", 0, 0, 0);
|
||||
eq_ms("(sec,0) 整秒", 1, 0, 1000);
|
||||
eq_ms("999999ns 截断", 0, 999999, 0);
|
||||
eq_ms("1ms 进位", 0, 1000000, 1);
|
||||
eq_ms("1ns 截断", 0, 1, 0);
|
||||
eq_ms("开机 1135.008s", 1135, 8000000, 1135008);
|
||||
eq_ms("跨秒 1.999999999s", 1, 999999999, 1999);
|
||||
eq_ms("开机 1204.999999999s", 1204, 999999999, 1204999);
|
||||
eq_ms("接近 2038", 2147483647, 999999999, 2147483647999ULL);
|
||||
|
||||
/* ---- 2) 负值:from_parts 只做无符号升位((uint64_t)sec*1000 按二补回绕),不钳制。
|
||||
* 真正把内核 time32 的负 sec 钳成 0 的是 meow_ui_time.c 的 time32_clamp(),它只在
|
||||
* mipsel 分支编译、宿主不可达;这里把 from_parts 的回绕语义钉住,防止有人悄悄把它
|
||||
* 理解成「负值 = 0」而改掉换算。 ---- */
|
||||
eq_ms("负 sec 回绕(-1s)", -1, 0, 0xFFFFFFFFFFFFFC18ULL);
|
||||
eq_ms("负 sec+1ms 回绕", -1, 1000000, 0xFFFFFFFFFFFFFC19ULL);
|
||||
|
||||
/* ---- 3) 真时钟:非零、单调不减、量级合理(不上溢成「高 32 随机」) ---- */
|
||||
uint64_t prev = meow_ui_time_ms();
|
||||
g_checks++;
|
||||
if (prev == 0) {
|
||||
printf("FAIL 时钟返回 0(clock_gettime 失败?)\n");
|
||||
g_fail = 1;
|
||||
}
|
||||
g_checks++;
|
||||
if (prev >= (1ULL << 40)) {
|
||||
printf("FAIL 时钟量级异常(>=2^40 ms):%llu\n", (unsigned long long)prev);
|
||||
g_fail = 1;
|
||||
}
|
||||
/* 有界采样(避免时钟停走时测试挂死):要求单调不减且确实前进 */
|
||||
int advanced = 0;
|
||||
for (int i = 0; i < 200000 && !advanced; i++) {
|
||||
uint64_t now = meow_ui_time_ms();
|
||||
if (now < prev) {
|
||||
printf("FAIL 单调倒退:%llu -> %llu\n",
|
||||
(unsigned long long)prev, (unsigned long long)now);
|
||||
g_fail = 1;
|
||||
break;
|
||||
}
|
||||
advanced = now > prev;
|
||||
prev = now;
|
||||
}
|
||||
g_checks++;
|
||||
if (!advanced) {
|
||||
printf("FAIL 时钟未前进(200000 次采样内无变化)\n");
|
||||
g_fail = 1;
|
||||
} else {
|
||||
printf("ok 时钟前进且单调\n");
|
||||
}
|
||||
|
||||
/* ---- 4) 动画常用差值:两次取样的差 = 毫秒(不能是 0 恒等/整千) ---- */
|
||||
uint64_t t0 = meow_ui_time_ms();
|
||||
int waited = 0;
|
||||
for (int i = 0; i < 200000 && !waited; i++)
|
||||
waited = meow_ui_time_ms() != t0;
|
||||
uint64_t t1 = meow_ui_time_ms();
|
||||
g_checks++;
|
||||
if (!waited || t1 <= t0 || t1 - t0 > 5000) {
|
||||
printf("FAIL 取样差值异常:%llu -> %llu\n",
|
||||
(unsigned long long)t0, (unsigned long long)t1);
|
||||
g_fail = 1;
|
||||
} else {
|
||||
printf("ok 取样差值 %llu ms\n", (unsigned long long)(t1 - t0));
|
||||
}
|
||||
|
||||
printf(g_fail ? "== FAIL ==\n" : "== PASS ==\n");
|
||||
printf("checks=%lu\n", g_checks);
|
||||
return g_fail;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
||||
gcc -Wall -Wextra -O1 -DMEOW_UI_HOST_TEST -I. -I"$SDK" -I"$SDK/include" \
|
||||
test/test_settings_lock_host.c ui/settings/settings.c ui/lock/lock.c \
|
||||
logic/pin.c logic/reader.c \
|
||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||
@@ -81,7 +81,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
||||
test/test_browse_ui_host.c ui/library/library.c logic/library.c \
|
||||
logic/reader.c logic/reader/page_source.c logic/reader/archive_source.c \
|
||||
logic/reader/page_cache.c logic/zip/zip.c \
|
||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||
@@ -99,7 +99,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||
logic/reader/nav.c \
|
||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||
@@ -116,7 +116,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||
logic/reader/nav.c \
|
||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||
@@ -133,7 +133,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||
logic/reader/nav.c \
|
||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||
|
||||
@@ -34,7 +34,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||
logic/reader/nav.c \
|
||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||
|
||||
Reference in new issue
Block a user