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 字节结构。
This commit is contained in:
AskaEth committed 2026-11-05 13:41:52 +08:00
1 parent 8a115f6583
commit 38be5e78bc
37 files changed
+577 -75

No files matched your search

+187
View File
@@ -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 等)再次错配。
+5 -1
View File
@@ -6,6 +6,10 @@
# 禁 zlib/png/brotli/bzip2/harfbuzz(-DFT_DISABLE_*),静态 musl(zig-mipsel-cc);
# 产物 sdk/vendor/freetype-build/libfreetype.a,其 .o 编入 meow_ui.a —— 应用只链 meow_ui.a
# 即获得矢量字能力;不调 meow_ft_* 的 App 链接器不会拉取 freetype 成员(零增量)。
#
# 时钟(meow_ui_time):mipsel 上 zig 0.17 musl 的 time64 ABI 自相矛盾(头 64 位 / 实现
# time32),该 TU 按内核 o32 ABI 取时并强定义 __clock_gettime*/睡眠入口,链接期接管。
# 根因与验证见 sdk/CLOCK_ABI.md;改动后必须重编 meow_ui.a 并重链四 App。
set -e
ROOT="$HOME/Project/R3ProII"
cd "$(dirname "$0")"
@@ -40,7 +44,7 @@ FT_INCS="-I$FT_BUILD -I$FT_BUILD/include -I$FT_SRC/include"
rm -f meow_ui.a *.o
# -I../meow_launcherd:meow_notify_ipc.c 复用共享协议头 meow_ipc.h(同一份、杜绝两端漂移)
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_ui_box meow_ui_anim meow_ui_scroll meow_lazygrid meow_select meow_ui_str meow_ui_ft meow_net_ipc meow_cachegov"
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_ui_box meow_ui_anim meow_ui_scroll meow_lazygrid meow_select meow_ui_str meow_ui_ft meow_net_ipc meow_cachegov"
CFLAGS="-Os -static -I$ROOT/patch_v2/meow_launcherd"
for f in $OBJS; do
echo "== cc $f =="
+1 -3
View File
@@ -62,9 +62,7 @@ static int64_t g_test_now_ms = -1;
static int64_t now_ms(void)
{
if (g_test_now_ms >= 0) return g_test_now_ms;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
return (int64_t)meow_ui_time_ms();
}
void meow_cachegov_test_set_time_ms(int64_t t) { g_test_now_ms = t; }
+6 -15
View File
@@ -172,15 +172,6 @@ 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(); }
static uint16_t rgb565(uint32_t c)
{
return (uint16_t)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F));
@@ -895,7 +886,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 +923,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 +1003,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 +1057,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 +1087,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 +1124,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++;
+13 -20
View File
@@ -122,13 +122,6 @@ static uint64_t last_tick_ms = 0;
static uint64_t startup_cooldown_until = 0;
#define STARTUP_COOLDOWN_MS 500
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;
}
/* settings.conf 路径(可被 env MEOW_SETTINGS_FILE 覆盖,宿主测试用;设备默认同 meow_ui_sys.c)。 */
static const char *meow_settings_path(void)
{
@@ -294,7 +287,7 @@ 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_down_ms = now_ms(); /* 记按下时刻:供实时按住时长/按住视觉 */
t_down_ms = meow_ui_time_ms(); /* 记按下时刻:供实时按住时长/按住视觉 */
t_mv_x = -1; t_mv_y = -1; /* M2.71:拖动过程起点未定 */
t_long_fired = 0; /* §6.3:每次新按压复位长按触发标记 */
}
@@ -392,7 +385,7 @@ int meow_input_touch_y(void) { return t_cy; }
unsigned int meow_input_touch_hold_ms(void)
{
if (!t_down) return 0;
uint64_t e = now_ms() - t_down_ms;
uint64_t e = meow_ui_time_ms() - t_down_ms;
if (e > 0xFFFFFFFFu) e = 0xFFFFFFFFu;
return (unsigned int)e;
}
@@ -403,7 +396,7 @@ void meow_input_touch_set(int down, int x, int y, unsigned hold_ms)
if (down) {
t_down = 1; t_press_valid = 1; t_origin_valid = 1; t_long_fired = 0;
t_cx = x; t_cy = y; t_dx = x; t_dy = y;
uint64_t now = now_ms();
uint64_t now = meow_ui_time_ms();
t_down_ms = (now > (uint64_t)hold_ms) ? (now - (uint64_t)hold_ms) : 0;
} else {
t_down = 0; t_press_valid = 0; t_origin_valid = 0; t_long_fired = 0;
@@ -544,7 +537,7 @@ static uint64_t g_sys_press_ms = 0;
static void sys_key_press(int code)
{
uint64_t now = now_ms();
uint64_t now = meow_ui_time_ms();
if (code == g_sys_press_code && now - g_sys_press_ms < SYS_KEY_DEDUP_MS)
return; /* 事件流 + EVIOCGKEY 双源去重:同码窗口内只消化一次 */
g_sys_press_code = code;
@@ -702,7 +695,7 @@ void meow_ui_request_exit(void) { g_exit = 1; }
static uint64_t g_activity_last_ms = 0;
void meow_ui_activity_ping(void)
{
uint64_t now = now_ms();
uint64_t now = meow_ui_time_ms();
if (now - g_activity_last_ms < 500) return; /* 节流:任意交互连续触摸也只 ~2 次/s */
g_activity_last_ms = now;
if (!meow_gfx_is_device()) return; /* 离屏/宿主:无 launcher 消费 */
@@ -715,8 +708,8 @@ void meow_ui_run(const meow_ui_events *ev)
if (ev) { g_events = *ev; g_events_set = 1; }
g_exit = 0;
meow_input_start();
last_tick_ms = now_ms();
startup_cooldown_until = now_ms() + STARTUP_COOLDOWN_MS; /* M2.22(C):启动窗口丢弃 SWIPE */
last_tick_ms = meow_ui_time_ms();
startup_cooldown_until = meow_ui_time_ms() + STARTUP_COOLDOWN_MS; /* M2.22(C):启动窗口丢弃 SWIPE */
while (!g_exit) {
/* agent-119:实时触摸按压/按住/释放 → 即时重绘命中按钮区(不等 app 重绘)。
@@ -724,10 +717,10 @@ void meow_ui_run(const meow_ui_events *ev)
* §6.3 通用长按:每帧检测「按下 ≥ 阈值且位移 ≤ 容忍」跨越(IME 打开不驱动;启动窗口不触发)。 */
if (!meow_ime_active()) {
meow_widget_touch_refresh();
if (now_ms() >= startup_cooldown_until) meow_input_long_press_poll();
if (meow_ui_time_ms() >= startup_cooldown_until) meow_input_long_press_poll();
} else if (meow_ime_mode_pinyin()) {
/* 拼音 IME 打开:喂长按轮询(长按退格清空字段)。英文模式不喂(保持既有行为)。 */
if (now_ms() >= startup_cooldown_until) meow_input_long_press_poll();
if (meow_ui_time_ms() >= startup_cooldown_until) meow_input_long_press_poll();
}
meow_input_ev e;
@@ -736,7 +729,7 @@ void meow_ui_run(const meow_ui_events *ev)
if (g_exit) break; /* 已请求退出:不再分发剩余事件,杜绝「退出后又触发」的偶发再进/退 */
got = 1;
meow_ui_activity_ping(); /* M2.71-bug5:任意交互事件都喂 launcher 重置息屏计时(节流) */
int in_startup = (now_ms() < startup_cooldown_until); /* M2.22(C) */
int in_startup = (meow_ui_time_ms() < startup_cooldown_until); /* M2.22(C) */
if (e.type == MEOW_EV_TAP) {
if (in_startup) continue; /* 启动窗口也吞 TAP:旧手势残留点按(如上一次左滑抬起)
* 在进入瞬间命中 UI 按钮(如全屏按钮)会误触发——丢弃。 */
@@ -851,7 +844,7 @@ void meow_ui_run(const meow_ui_events *ev)
}
}
uint64_t now = now_ms();
uint64_t now = meow_ui_time_ms();
if (g_events_set && g_events.on_tick) {
if (now - last_tick_ms >= 16) {
last_tick_ms = now;
@@ -877,7 +870,7 @@ void meow_ui_run(const meow_ui_events *ev)
if (meow_ime_active()) { /* 软键盘覆盖层随帧绘制(30fps 节流,省 CPU) */
static uint64_t last_ime_ms = 0;
uint64_t n = now_ms();
uint64_t n = meow_ui_time_ms();
if (n - last_ime_ms >= 33) {
last_ime_ms = n;
meow_ime_draw();
@@ -897,7 +890,7 @@ void meow_ui_run(const meow_ui_events *ev)
void meow_ui_feed_tick(void)
{
if (!(g_events_set && g_events.on_tick)) return;
uint64_t now = now_ms();
uint64_t now = meow_ui_time_ms();
if (now - last_tick_ms < 16) return;
last_tick_ms = now;
g_events.on_tick();
+4
View File
@@ -300,4 +300,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 */
+1 -8
View File
@@ -46,13 +46,6 @@ static int g_statusbar_force = 0; /* 1s 刷新后置位,强制重画顶部
* present() 据此决定是否画出顶条/提示线;App 进程保持 0(不渲染)+ 仍 inset 28(launcher 叠加)。 */
static int g_sys_render = 0; /* 1=本进程为系统 UI(渲染);0=App(不渲染,仅 inset) */
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;
}
/* 读一份 sysfs/文件字符串(剥尾随换行);失败回退 "" */
static void read_str(const char *path, char *buf, size_t n)
{
@@ -163,7 +156,7 @@ void meow_ui_sys_refresh(void)
/* 蓝牙:hci0 存在性检查便宜;上电状态贵(hciconfig spawn),30s 缓存。 */
static uint64_t last_bt_ms = 0;
uint64_t n = now_ms();
uint64_t n = meow_ui_time_ms();
g_sys.bt_seen = access("/sys/class/bluetooth/hci0", F_OK) == 0;
if (!g_sys.bt_seen) {
g_sys.bt_on = 0;
+147
View File
@@ -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
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_ime_fix test_ime_fix.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_notify_ipc.c stb_image.c \
* meow_ui_anim.c meow_ui_media.c meow_ui_nav.c meow_pinyin.c meow_ui_box.c meow_ui_scroll.c \
+2 -2
View File
@@ -7,9 +7,9 @@
* - meow_cachegov_test_set_time_ms() 注入时钟驱动 refault 滚窗回退(放大 keep_hot→降L0→关A);
* - MEOW_CACHEGOV_C_FILE 指向临时文件写入 fake C 状态,断言 download_paused/prefetch_paused。
*
* 编译(patch_v2/sdk 目录下):
* 编译(patch_v2/sdk 目录下;meow_cachegov.c 的 now_ms 走 meow_ui_time.c,M11 起需一并编译):
* gcc -Os -Wall -Wextra -I. -Iinclude -I../meow_launcherd -c meow_cachegov.c -o /tmp/cg.o
* gcc -Os -Wall -Wextra -I. -Iinclude -I../meow_launcherd test_meow_cachegov.c /tmp/cg.o -lpthread -o test_meow_cachegov
* gcc -Os -Wall -Wextra -I. -Iinclude -I../meow_launcherd test_meow_cachegov.c meow_ui_time.c /tmp/cg.o -lpthread -o test_meow_cachegov
* 运行:./test_meow_cachegov (exit 0=PASS / 1=FAIL)
*/
#define _GNU_SOURCE
+1 -1
View File
@@ -13,7 +13,7 @@
* 编译(在 patch_v2/sdk 下;链法同 test_meow_lyric_view + 单链 meow_img_color.c):
* gcc -Wall -Wextra -o test_meow_img_color test_meow_img_color.c test_host_stub.c \
* meow_img_color.c meow_lyric.c meow_lyric_view.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -5,7 +5,7 @@
* 另链批1 meow_lyric.c 提供歌词数据;meow_lyric_view.c 为被测控件):
* gcc -Wall -Wextra -o test_meow_lyric_view test_meow_lyric_view.c test_host_stub.c \
* meow_lyric.c meow_lyric_view.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -6,7 +6,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_meow_md3 test_meow_md3.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+91
View File
@@ -0,0 +1,91 @@
/* test_meow_time.c — 宿主单测:SDK 时钟换算(meow_ui_time.c / meow_time_ms_from_parts)。
*
* 背景(M11):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),本测试把
* 换算边界(毫秒进位/截断/大秒数)与「真时钟单调且前进」固定在宿主上。
*
* 编译(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 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 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);
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("1s", 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("开机 1204.999999999s", 1204, 999999999, 1204999);
eq_ms("接近 2038", 2147483647, 999999999, 2147483647999ULL);
/* ---- 2) 真时钟:非零、单调不减、量级合理(不上溢成「高 32 随机」) ---- */
uint64_t prev = meow_ui_time_ms();
if (prev == 0) {
printf("FAIL 时钟返回 0(clock_gettime 失败?)\n");
g_fail = 1;
}
if (prev >= (1ULL << 40)) {
printf("FAIL 时钟量级异常(>=2^40 ms):%llu\n", (unsigned long long)prev);
g_fail = 1;
}
/* 40s 量级以内取值 = 开机时长(宿主 monotonic),并要求前进 */
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;
}
if (!advanced) {
printf("FAIL 时钟未前进(200000 次采样内无变化)\n");
g_fail = 1;
} else {
printf("ok 时钟前进且单调\n");
}
/* ---- 3) 动画常用差值:两次取样的差 = 毫秒(不能是 0 恒等/整千) ---- */
uint64_t t0 = meow_ui_time_ms();
while (meow_ui_time_ms() == t0) { }
uint64_t t1 = meow_ui_time_ms();
if (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");
return g_fail;
}
+1 -1
View File
@@ -4,7 +4,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/draw/activity/ime + 打桩 font/global):
* gcc -o test_meow_ui_anim test_meow_ui_anim.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -4,7 +4,7 @@
*
* 编译(宿主 gcc,链真实 widget/draw/activity/theme/gfx + 打桩 font/global):
* gcc -o test_meow_ui_box test_meow_ui_box.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -13,7 +13,7 @@
* 编译(宿主 gcc,链真实 widget/draw/input/activity/gfx/sys/theme/vol/log/dialog/ime/notify/ipc +
* 打桩 font/global;完整超集便于链接):
* gcc -Wall -Wextra -o test_meow_ui_dispatch test_meow_ui_dispatch.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -3,7 +3,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/notify_ipc + 打桩 font/global + meow_ui_ft + 宿主 freetype):
* gcc -o test_meow_ui_ft test_meow_ui_ft.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c meow_notify_ipc.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c meow_notify_ipc.c \
* meow_ui_ft.c \
* -I. -Iinclude -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread vendor/freetype-build-host/libfreetype.a
* 用法:./test_meow_ui_ft [font.otf] (缺省用仓库 font/src/MiSans-Regular.otf)
+1 -1
View File
@@ -16,7 +16,7 @@
* 编译(宿主 gcc,链真实 SDK 源全集 + 打桩 font/global;**不链 meow_ui_nav.c**——本测以测试内
* meow_ui_nav_request_exit 桩计数验证触发):
* gcc -Wall -Wextra -o test_meow_ui_home_gesture test_meow_ui_home_gesture.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -9,7 +9,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_meow_ui_iconbtn test_meow_ui_iconbtn.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -o test_meow_ui_lazygrid test_meow_ui_lazygrid.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_lazygrid.c meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -10,7 +10,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -o test_meow_ui_listvirt test_meow_ui_listvirt.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -4,7 +4,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/draw/activity/ime + 打桩 font/global):
* gcc -o test_meow_ui_scroll test_meow_ui_scroll.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -6,7 +6,7 @@
* 阻塞式模态用后台线程驱动(同 test_ime_fix 范式),主线程经 meow_input_inject 喂触摸。
* 编译(宿主 gcc):
* gcc -o test_meow_ui_select test_meow_ui_select.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_select.c meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc):
* gcc -o test_meow_ui_swipe test_meow_ui_swipe.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -o test_meow_ui_theme test_meow_ui_theme.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_notify_ipc.c stb_image.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
+1 -1
View File
@@ -11,7 +11,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_meow_ui_theme_export test_meow_ui_theme_export.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_meow_ui_view_event test_meow_ui_view_event.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
+1 -1
View File
@@ -3,7 +3,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -o test_meow_ui_widgets test_meow_ui_widgets.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_notify_ipc.c stb_image.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
+1 -1
View File
@@ -15,7 +15,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/ime/media/notify_ipc + 打桩 font/global):
* gcc -Wall -Wextra -o test_notif_gesture test_notif_gesture.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_ime.c meow_ui_str.c meow_ui_media.c meow_notify_ipc.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
* 运行:./test_notif_gesture
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,链 meow_notify_ipc + meow_ui_sys + gfx/theme/vol/log + 打桩 font/global):
* gcc -Wall -o test_notif_open test_notif_open.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_notify_ipc.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
* 运行:./test_notif_open
+1 -1
View File
@@ -17,7 +17,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_overlay_suppress test_overlay_suppress.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_ui_draw.c meow_ui_view.c meow_notify_ipc.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
+1 -1
View File
@@ -22,7 +22,7 @@
* -I. -I../hello_app ../hello_app/app.c -o app.o
* 2) 编译本文件 + 其余 hello 模块 + SDK 源 + 打桩,链接 app.o:
* gcc -Wall -Wextra -DMEOW_UI_HOST_TEST -o test_p1p2p3_repro test_p1p2p3_repro.c app.o \
* test_host_stub.c meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* test_host_stub.c meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_ui_img.c meow_ui_config.c meow_ui_notify.c meow_notify_ipc.c meow_ui_draw.c \
* meow_ui_view.c meow_ui_nav.c meow_ui_media.c stb_image.c \
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,同 test_ime_fix):
* gcc -Wall -Wextra -o test_pinyin test_pinyin.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_ui_media.c \
* meow_notify_ipc.c stb_image.c -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
+1 -1
View File
@@ -9,7 +9,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log + 打桩 font/global + meow_notify_ipc):
* gcc -o test_sb_collapse test_sb_collapse.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c meow_notify_ipc.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c meow_notify_ipc.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
*/
#include "meow_ui.h"
+1 -1
View File
@@ -8,7 +8,7 @@
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -o test_sdk_fixes test_sdk_fixes.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_ui_draw.c meow_ui_view.c meow_notify_ipc.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
+94
View File
@@ -0,0 +1,94 @@
/* clock_abi_probe.c — 时钟 ABI 最小探针(M11)。
*
* 用途:在宿主或真机上一眼看出「编译器头文件声明的 time_t 宽度」与「libc 实际写入的
* struct timespec 宽度」是否一致——这正是 meow_ui_time_ms 真机故障的根因(详见
* sdk/CLOCK_ABI.md)。做法:先把 struct timespec 整块填 0xAA,再调 clock_gettime,
* 看哪些字节被写回:
* - 正确(time64:tv_sec 8B + tv_nsec 4B):偏移 0..11 被写,8..11 不再是 0xAA;
* - 坏(libc 只做 o32 time32:只写 8B):偏移 8..11 仍是 0xAA → 退出码 2;
* - 头/库宽度相反(头 8B、库写 16B)也会在偏移检查里暴露。
*
* 构建(宿主):
* gcc -Os -Wall -Wextra tools/clock_abi_probe.c -o clock_abi_probe
* 构建(mipsel 静态,测裸 zig libc):
* ~/.tools/zig-mipsel-cc -Os -static tools/clock_abi_probe.c -o clock_abi_probe
* 构建(mipsel + SDK,测修复后的归档是否接管 clock_gettime):
* ~/.tools/zig-mipsel-cc -Os -static -DMEOW_PROBE_WITH_SDK -I. -Iinclude \
* tools/clock_abi_probe.c meow_ui.a <sysroot>/lib/libz.a -lpthread -o clock_abi_probe_sdk
*
* 运行:./clock_abi_probe
* 退出码:0=ABI 一致且时钟单调、量级合理;1=时钟值异常;2=libc 只写了 8 字节(ABI 不一致)。
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef MEOW_PROBE_WITH_SDK
#include "meow_ui.h"
#endif
#define PAD_BYTE 0xAA
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;
}
int main(void)
{
int rc = 0;
struct timespec ts;
unsigned char raw[sizeof(struct timespec)];
printf("sizeof(time_t)=%u sizeof(long)=%u sizeof(struct timespec)=%u\n",
(unsigned)sizeof(time_t), (unsigned)sizeof(long), (unsigned)sizeof(struct timespec));
printf("offsetof(tv_sec)=%u offsetof(tv_nsec)=%u\n",
(unsigned)offsetof(struct timespec, tv_sec), (unsigned)offsetof(struct timespec, tv_nsec));
memset(&ts, PAD_BYTE, sizeof ts);
int r = clock_gettime(CLOCK_MONOTONIC, &ts);
memcpy(raw, &ts, sizeof raw);
printf("clock_gettime ret=%d tv_sec=%lld tv_nsec=%ld\n", r, (long long)ts.tv_sec, (long)ts.tv_nsec);
printf("raw:");
for (unsigned i = 0; i < sizeof raw; i++) printf(" %02x", raw[i]);
printf("\n");
if (r != 0) {
printf("FAIL clock_gettime 失败\n");
return 1;
}
/* tv_nsec 所在 4 字节必须被 libc 写过 */
if (sizeof(struct timespec) > 8 && raw[8] == PAD_BYTE && raw[9] == PAD_BYTE &&
raw[10] == PAD_BYTE && raw[11] == PAD_BYTE) {
printf("FAIL libc 只写了 8 字节 time32(头声明 %u 字节)→ ABI 不一致\n",
(unsigned)sizeof(struct timespec));
rc = 2;
}
uint64_t a = now_ms();
uint64_t b = now_ms();
if (b < a || b - a > 5000 || b >= (1ULL << 40)) {
printf("FAIL 毫秒值异常: a=%llu b=%llu\n",
(unsigned long long)a, (unsigned long long)b);
rc = rc ? rc : 1;
} else {
printf("ok now_ms a=%llu b=%llu\n", (unsigned long long)a, (unsigned long long)b);
}
#ifdef MEOW_PROBE_WITH_SDK
uint64_t s1 = meow_ui_time_ms();
uint64_t s2 = meow_ui_time_ms();
printf("meow_ui_time_ms: %llu %llu\n", (unsigned long long)s1, (unsigned long long)s2);
if (s2 < s1 || s2 - s1 > 5000 || s2 >= (1ULL << 40) || s2 == 0) {
printf("FAIL meow_ui_time_ms 异常\n");
rc = rc ? rc : 1;
}
#endif
printf(rc ? "== FAIL (rc=%d) ==\n" : "== PASS ==\n", rc);
return rc;
}