fix(watchdog+launcher+sdk): M40 电源键 time64 ABI + 锁屏/叠加残留链

- sdk/meow_time32: 抽出可独立链接的 o32 time32 取时/睡眠设施(不依赖 meow_ui.a,
  watchdog 等独立静态组件可直接链;可选 -DMEOW_TIME32_LIBC_OVERRIDES 接管 libc 入口)
- watchdog: now_ms/usleep 改走 time32(修复 held 变天文数字→每次单击都弹菜单);
  阈值读 power.longpress_menu_ms/forceoff_ms(缺省 3000/8000),删除
  PWR_TOGGLE_MS==PWR_MENU_MS;新增 wd_power.h 纯判定 + 宿主行为矩阵测试
- launcher: App 前台电源菜单补齐 §10.5(notif 全抑制 + 帧恢复 + SIGCONT 排空 +
  标记翻回前台);show_power_menu 恢复进入前背景帧(关闭后不再停留菜单画面);
  重绘挪到首轮按键采样之后;activity 标记排空窗 + 会话入口清理 + manual_off 去闩锁
- lock_screen: 电源改按下边沿 + 进场 300ms 才熄屏;电源熄屏退出也恢复底层帧;
  launcher 在 SIGCONT 前写 /tmp/meow_app_repaint,SDK present 消费并 dirty_full
- power_menu: 菜单内强关接 power.forceoff_ms(按 longpress 抵扣),日志不再谎称 8s
- 重编 meow_ui.a 并重链 meow_launcherd/ncm/files/hello/reader(picbrowser 无源码)
- fw_content.conf: 同步 launcherd/watchdog 新 md5;顺带修正 M35 遗留的 nyai 失效钉值

Host: test_wd_fw 66/66、test_overlay_gate 40/40、test_meow_time64/time/repaint PASS。
真机未验证(设备关机);未刷机、未写设备。
This commit is contained in:
AskaEth committed 2026-11-10 22:51:00 +08:00
1 parent b1acc9096b
commit 63b278e13e
28 files changed
+977 -217

No files matched your search

+30
View File
@@ -316,3 +316,33 @@ llvm-nm probe_sdk | grep -c __timedwait_cp # 1(libc 那份未被拉入)
真机验证仍待设备(本单未碰真机):`pthread_cond_timedwait` 传 100ms 超时的等待应真的等
约 100ms 再超时,而不是立即返回。
## 9. M40:独立链接的 time32 基础设施(系统组件解药)
M16 的全部修复都只在 **链接 meow_ui.a** 的进程里生效。不链 SDK 的系统组件(watchdog /
sysui / svcd / bootmenu / amsd / audiod / netmgrd / crash / svc_perf / powermgr / meowpanel)
仍然各自中招:它们的 `now_ms()`/超时/退避全部按 16 字节 time64 读、libc 只写 8 字节 time32。
M40 把与 SDK 无关的部分抽成可独立链接的 TU:
| 文件 | 内容 | 依赖 |
| --- | --- | --- |
| `sdk/meow_time32.h` | o32 结构、纯换算、`meow_time32_clock_gettime()` / `meow_time32_monotonic_ms()` / `meow_time32_sleep_us()` | 仅 stdint |
| `sdk/meow_time32.c` | 上述实现;mips o32 走 `SYS_clock_gettime32` / `SYS_nanosleep`,宿主回退 libc | libc |
| `sdk/meow_ui_time.c` | 保留 futex/timerfd/clock_settime 接管;换算/取时改为调用 `meow_time32.c` | SDK |
系统组件构建脚本只需把 `../sdk/meow_time32.c` 加进链接命令;若希望链接期整体接管
libc 的取时/睡眠入口(不必逐个改调用点),再编译该 TU 时加 `-DMEOW_TIME32_LIBC_OVERRIDES`
(**不要**与 meow_ui.a 同时链接,否则 `__clock_gettime` 等重复定义)。
当前已接入:`meow_watchdog`(`build_watchdog.sh`)。待接入清单与各自脚本见 M40 报文;
本单不改那些组件的源码/脚本(scope 限制),只提供设施与接入范式。
宿主单测:
```
cd patch_v2/meow_watchdog
bash build_watchdog.sh host # time32 取时/睡眠 + 电源阈值矩阵(66 项)
cd ../sdk
gcc -Wall -Wextra -I. test_meow_repaint.c -o /tmp/tr && /tmp/tr
```
+4 -3
View File
@@ -7,8 +7,9 @@
# 产物 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*/睡眠入口,链接期接管。
# 时钟(meow_ui_time + meow_time32):mipsel 上 zig 0.17 musl 的 time64 ABI 自相矛盾(头 64 位 /
# 实现 time32)。meow_time32.c 是可独立链接的 o32 取时/睡眠设施(系统组件不链 SDK 也能用);
# meow_ui_time.c 在其上做链接期接管(__clock_gettime*/futex/timerfd/clock_settime)。
# 根因与验证见 sdk/CLOCK_ABI.md;改动后必须重编 meow_ui.a 并重链四 App。
set -e
ROOT="$HOME/Project/R3ProII"
@@ -44,7 +45,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_time meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_jpeg meow_png_stream 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_time32 meow_ui_time meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_jpeg meow_png_stream 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 =="
+191
View File
@@ -0,0 +1,191 @@
/* meow_time32.c — mipsel o32 内核 time32 ABI 兜底(独立链接,不依赖 meow_ui.a)。
*
* 根因与逐项证据:sdk/CLOCK_ABI.md(M11/M16)。核心一句话:
* zig 0.17 的 musl 在 mipsel 上按 `#if defined(SYS_x_time64) && !defined(__ILP32__)`
* 守卫 time64 回退,而 zig clang 又预定义了 __ILP32__,于是「time64 入口」被编成
* 老的 time32 实现:内核只写 8 字节 {int32 sec; int32 nsec;},调用方却按 16 字节
* 结构读。本文件不猜 libc 头的结构宽度,**直接按内核 ABI 取数/睡眠**。
*
* 本 TU 不引用 SDK 符号;系统组件(watchdog 等)可单独链接它。宿主(非 mips o32)
* 自动退回 libc 的 clock_gettime/nanosleep,行为等价,宿主单测可直接编译运行。
*/
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include "meow_time32.h"
#if defined(__mips__) && !defined(__mips64) && (__SIZEOF_LONG__ == 4)
#define MEOW_TIME32_KERNEL_ABI 1
#include <sys/syscall.h>
#else
#define MEOW_TIME32_KERNEL_ABI 0
#endif
/* ---- 纯换算 ---- */
void meow_time32_rel_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out)
{
if (sec > INT32_MAX) sec = INT32_MAX;
else if (sec < INT32_MIN) sec = INT32_MIN;
out->tv_sec = (int32_t)sec;
out->tv_nsec = (int32_t)nsec;
}
int meow_time32_abs_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out)
{
if (sec > INT32_MAX || sec < INT32_MIN) return -1;
out->tv_sec = (int32_t)sec;
out->tv_nsec = (int32_t)nsec;
return 0;
}
int meow_time32_itimerspec_from_64(const struct meow_time64_itimerspec *in,
struct meow_itimerspec32 *out)
{
struct meow_itimerspec32 t;
if (meow_time32_abs_from_parts(in->it_interval.tv_sec, in->it_interval.tv_nsec,
&t.it_interval)) return -1;
if (meow_time32_abs_from_parts(in->it_value.tv_sec, in->it_value.tv_nsec,
&t.it_value)) return -1;
*out = t;
return 0;
}
void meow_time64_itimerspec_from_32(const struct meow_itimerspec32 *in,
struct meow_time64_itimerspec *out)
{
out->it_interval.tv_sec = in->it_interval.tv_sec;
out->it_interval.tv_nsec = in->it_interval.tv_nsec;
out->it_value.tv_sec = in->it_value.tv_sec;
out->it_value.tv_nsec = in->it_value.tv_nsec;
}
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec)
{
return (uint64_t)sec * 1000u + (uint64_t)nsec / 1000000u;
}
/* ---- 内核取时 ---- */
int meow_time32_clock_gettime(int clk, struct meow_time32 *out)
{
if (!out) { errno = EFAULT; return -1; }
#if MEOW_TIME32_KERNEL_ABI
out->tv_sec = 0;
out->tv_nsec = 0;
if (syscall(SYS_clock_gettime32, clk, out) != 0) return -1;
return 0;
#else
struct timespec ts;
if (clock_gettime((clockid_t)clk, &ts) != 0) return -1;
out->tv_sec = (int32_t)ts.tv_sec;
out->tv_nsec = (int32_t)ts.tv_nsec;
return 0;
#endif
}
uint64_t meow_time32_monotonic_ms(void)
{
struct meow_time32 t;
if (meow_time32_clock_gettime(CLOCK_MONOTONIC, &t) != 0) return 0;
return meow_time_ms_from_parts(t.tv_sec, t.tv_nsec);
}
/* ---- 睡眠(usleep 同源受损:16 字节 time64 直接交给 time32 系统调用时 nsec 恒 0,
* 亚秒睡眠立即返回 → 轮询/退避变忙等空转)---- */
int meow_time32_sleep_us(unsigned usec)
{
#if MEOW_TIME32_KERNEL_ABI
struct meow_time32 rq, rm;
rq.tv_sec = (int32_t)(usec / 1000000u);
rq.tv_nsec = (int32_t)((usec % 1000000u) * 1000u);
for (;;) {
long r = syscall(SYS_nanosleep, &rq, &rm);
if (r == 0) return 0;
if (errno != EINTR) return -1;
rq = rm; /* 被信号打断:按剩余时间续睡 */
}
#else
struct timespec rq, rm;
rq.tv_sec = (time_t)(usec / 1000000u);
rq.tv_nsec = (long)((usec % 1000000u) * 1000u);
for (;;) {
if (nanosleep(&rq, &rm) == 0) return 0;
if (errno != EINTR) return -1;
rq = rm;
}
#endif
}
/* ---- 可选:链接期接管 libc 取时/睡眠入口 ----
* 系统组件加 -DMEOW_TIME32_LIBC_OVERRIDES 编译本文件即可让未改写的 clock_gettime()/
* usleep() 调用点也恢复正确(静态链接时本对象的强定义覆盖 libc 归档成员)。
* 不要在同时链接 meow_ui.a(meow_ui_time.o) 的进程里启用——两者符号重复。
* 与 meow_ui_time.c 的差别:这里只接管取时 + nanosleep;futex 相对超时 / timerfd /
* clock_settime 的接管仍在 meow_ui_time.c(SDK 消费方自动获得)。 */
#if MEOW_TIME32_KERNEL_ABI && defined(MEOW_TIME32_LIBC_OVERRIDES)
static int32_t meow_time32_clamp(int64_t v)
{
if (v > INT32_MAX) return INT32_MAX;
if (v < 0) return 0;
return (int32_t)v;
}
int __clock_gettime(clockid_t clk, struct timespec *ts)
{
if (!ts) { errno = EFAULT; return -1; }
struct meow_time32 t;
if (meow_time32_clock_gettime(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);
}
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 = meow_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 /* MEOW_TIME32_KERNEL_ABI && MEOW_TIME32_LIBC_OVERRIDES */
+78
View File
@@ -0,0 +1,78 @@
/* meow_time32.h — mipsel o32 内核 time32 ABI 兜底(**可独立链接**的公共设施)。
*
* 为什么单独成文件(M40):
* zig 0.17 给 32 位 mips 预定义 __ILP32__,打穿 musl 的 time64 回退守卫:调用方按
* 16 字节 time64 结构取数,libc 却只发 time32 系统调用(只写前 8 字节)。结果是
* 「秒在低 32 位、毫秒位恒 0、高 32 位是 nsec」的垃圾时间,任何 now_ms()/超时/退避
* 都会失真。meow_ui_time.c 的修复只对**链接 meow_ui.a** 的进程生效;系统组件
* (watchdog / sysui / svcd / bootmenu / amsd / audiod / netmgrd / crash / svc_perf /
* powermgr / meowpanel)各自独立静态链接,不链 SDK,因此需要一份不依赖 SDK 的实现。
*
* 本 TU 只依赖 libc(stdint/unistd/time/syscall),不引用 meow_ui.a 的任何符号:
* - 纯换算函数(与内核结构宽度解耦,宿主可直接单测);
* - 内核 o32 time32 取时(meow_time32_clock_gettime / meow_time32_monotonic_ms);
* - 亚秒睡眠(meow_time32_sleep_us;usleep 同源受损,见 meow_ui_time.c 注释)。
*
* 用法(系统组件的构建脚本):
* $CC ... meow_watchdog.c ../sdk/meow_time32.c -o meow_watchdog
* 若希望**链接期整体接管 libc 的取时/睡眠入口**(不再逐个改调用点),额外加宏
* -DMEOW_TIME32_LIBC_OVERRIDES
* 该宏只在 mipsel o32 上生效;此时**不要**同时链接 meow_ui.a 里的 meow_ui_time.o
* (两者都会强定义 __clock_gettime 等符号)。
*/
#ifndef MEOW_TIME32_H
#define MEOW_TIME32_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* mipsel o32 内核结构(与 libc 头里的 time_t 宽度无关):
* { int32 tv_sec; int32 tv_nsec; } —— 固定 8 字节。 */
struct meow_time32 {
int32_t tv_sec;
int32_t tv_nsec;
};
struct meow_itimerspec32 {
struct meow_time32 it_interval;
struct meow_time32 it_value;
};
/* 与 libc 结构解耦的 64 位镜像(宿主/交叉两端 time_t 宽度不同,测试不依赖它)。 */
struct meow_time64_spec {
int64_t tv_sec;
int64_t tv_nsec;
};
struct meow_time64_itimerspec {
struct meow_time64_spec it_interval;
struct meow_time64_spec it_value;
};
/* ---- 纯换算(无系统调用;宿主单测直接覆盖)---- */
/* 相对超时(futex / nanosleep 的「等多久」):sec 超出 int32 时饱和
* (正→INT32_MAX、负→INT32_MIN,与 musl CLAMP 一致),nsec 原样。 */
void meow_time32_rel_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out);
/* 绝对时刻(clock_settime / timerfd):sec 落不进 int32 返回 -1,nsec 原样。 */
int meow_time32_abs_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out);
/* itimerspec 转换;任一侧越界返回 -1 且 *out 不变。 */
int meow_time32_itimerspec_from_64(const struct meow_time64_itimerspec *in,
struct meow_itimerspec32 *out);
void meow_time64_itimerspec_from_32(const struct meow_itimerspec32 *in,
struct meow_time64_itimerspec *out);
/* 秒/纳秒 → 单调毫秒的唯一换算点。 */
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec);
/* ---- 内核取时 / 睡眠 ---- */
/* 按内核 o32 ABI 取时(clk 用 <time.h> 的 CLOCK_*)。返回 0=成功 / -1=失败(errno)。 */
int meow_time32_clock_gettime(int clk, struct meow_time32 *out);
/* CLOCK_MONOTONIC 毫秒;失败返回 0。 */
uint64_t meow_time32_monotonic_ms(void);
/* 睡眠 usec 微秒(EINTR 自动续睡)。返回 0=正常 / -1=失败(errno)。 */
int meow_time32_sleep_us(unsigned usec);
#ifdef __cplusplus
}
#endif
#endif /* MEOW_TIME32_H */
+3
View File
@@ -1140,6 +1140,9 @@ unsigned long long meow_gfx_overlay_suppressed_count(void) { return g_overlay_su
static void present(void)
{
if (!g_init_ok || !g_back_buf) return;
/* M40 F5/P5-P6:launcher 关闭锁屏/菜单前写的「强制整帧重画」请求。必须在脏区早退与
* 叠加抑制早退**之前**消费:抑制期只置 dirty_full(标记翻回前台后的下一帧整屏上屏)。 */
if (meow_ui_repaint_requested()) dirty_full();
if (!(g_dirty_full || g_ndirty > 0)) return;
/* §10.5 叠加让位自抑(仅 App 进程):App 前台播放中唤起通知中心时 launcher 不 SIGSTOP
* (保 PCM 供流,ncm 播放线程同进程),改由 App 进程在此自抑上屏——绘制照常进 back_buf,
+10 -37
View File
@@ -169,6 +169,12 @@ unsigned long long meow_gfx_overlay_suppressed_count(void);
* 系统 UI(launcher)恒 0;200ms 节流缓存。meow_ui_gfx.c 实现,input 层复用。 */
int meow_ui_overlay_suppressed_active(void);
/* M40 F5/P5-P6:解锁后强制 App 整帧重画。launcher 在 SIGCONT 前写请求文件;present 层
* 调用 meow_ui_repaint_requested():文件存在 → 删除并返回 1(调用方 dirty_full 一次)。
* 抑制期也消费(dirty_full 保留到标记翻回前台后的下一帧)。实现见 meow_ui_repaint.h
* (static inline,宿主单测只 include 头即可,不必链接渲染栈)。 */
#include "meow_ui_repaint.h"
/* 中文/日文字库(mfont)是否已加载(状态栏「蓝牙」CJK 与回退 "BT" 二选一)。 */
int meow_gfx_mfont_ready(void);
@@ -300,42 +306,9 @@ 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);
/* ---- mipsel o32 的 time32 结构(内核布局,与 libc 头的 time_t 宽度无关)----
* 背景:zig clang 给 32 位目标预定义 __ILP32__,打穿 musl 的 time64 回退守卫,
* 一批封装把 16 字节 time64 结构直接交给 time32 系统调用(CLOCK_ABI.md §6)。
* 接管这些入口时统一走下面这几个纯转换函数——它们不依赖平台,宿主单测直接覆盖。 */
struct meow_time32 {
int32_t tv_sec;
int32_t tv_nsec;
};
struct meow_itimerspec32 {
struct meow_time32 it_interval;
struct meow_time32 it_value;
};
/* 与 libc 结构解耦的 64 位镜像(避免宿主/交叉两端 time_t 宽度差异进测试)。 */
struct meow_time64_spec {
int64_t tv_sec;
int64_t tv_nsec;
};
struct meow_time64_itimerspec {
struct meow_time64_spec it_interval;
struct meow_time64_spec it_value;
};
/* 相对超时(futex / poll / select 的「等多久」):sec 超出 int32 时饱和
* (正→INT32_MAX、负→INT32_MIN,与 musl CLAMP 一致),nsec 原样。 */
void meow_time32_rel_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out);
/* 绝对时刻(clock_settime / timerfd / utimensat):sec 落不进 int32 返回 -1
* (调用方回 ENOTSUP,与 musl time64 路径的判定一致),nsec 原样。 */
int meow_time32_abs_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out);
/* itimerspec(timerfd / POSIX timer)转换;任一侧越界返回 -1 且 *out 不变。 */
int meow_time32_itimerspec_from_64(const struct meow_time64_itimerspec *in,
struct meow_itimerspec32 *out);
void meow_time64_itimerspec_from_32(const struct meow_itimerspec32 *in,
struct meow_time64_itimerspec *out);
/* ---- 时钟(meow_ui_time.c + meow_time32.c)----
* 换算/内核 time32 取时/睡眠的唯一实现在 meow_time32.c(独立 TU,系统组件可单独链接);
* 本头只把它带进来,SDK 内部仍按原名字使用。 */
#include "meow_time32.h"
#endif /* MEOW_UI_INTERNAL_H */
+39
View File
@@ -0,0 +1,39 @@
/* meow_ui_repaint.h — 解锁后「强制 App 整帧重画」请求文件(M40 F5/P5-P6)。
*
* 背景:锁屏/电源菜单是整帧不透明覆盖。关闭后 launcher 会恢复它快照的底层帧,但 App
* 进程只按自己的脏矩形 present;锁屏期间它可能被 SIGSTOP 冻结,从未观测到
* /tmp/meow_notif_active 的抑制态,因此 SDK 的「抑制→恢复」整帧重画边沿不会触发 ——
* PIN 帧/菜单帧会残留在屏幕上,直到用户点到某个控件产生新脏区才刷新(真机 P6)。
*
* 机制:launcher 在 SIGCONT 前写 /tmp/meow_app_repaint;App 的 SDK present 看到文件就
* dirty_full() 一次并删除(消费一次即失效)。请求在 present 最前面检查,**抑制期也消费**
* (先置 dirty_full,等标记翻回前台后的下一帧整屏上屏),避免排空窗口把请求吃掉后不生效。
*
* 实现放在头文件里(static inline):present 层(meow_ui_gfx.c)直接调用;宿主单测只
* include 本头即可验证写/读/删语义,不必链接整个渲染栈。路径可用 MEOW_APP_REPAINT_FILE
* 覆盖(宿主单测隔离;真机无 env 走默认)。
*/
#ifndef MEOW_UI_REPAINT_H
#define MEOW_UI_REPAINT_H
#include <stdlib.h>
#include <unistd.h>
#define MEOW_APP_REPAINT_DEFAULT "/tmp/meow_app_repaint"
static inline const char *meow_ui_repaint_path(void)
{
const char *e = getenv("MEOW_APP_REPAINT_FILE");
return (e && e[0]) ? e : MEOW_APP_REPAINT_DEFAULT;
}
/* 返回 1=消费了一次重画请求(调用方应 dirty_full);0=无请求。 */
static inline int meow_ui_repaint_requested(void)
{
const char *p = meow_ui_repaint_path();
if (access(p, F_OK) != 0) return 0;
unlink(p); /* 一次性请求:读到即消费,防每帧整帧重画 */
return 1;
}
#endif /* MEOW_UI_REPAINT_H */
+5 -59
View File
@@ -43,6 +43,7 @@
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "meow_time32.h"
#if defined(__mips__) && !defined(__mips64) && (__SIZEOF_LONG__ == 4)
#define MEOW_TIME32_KERNEL_ABI 1
@@ -54,64 +55,11 @@
#define MEOW_TIME32_KERNEL_ABI 0
#endif
/* ---- 平台无关的 time64 → 内核 o32 time32 换算(宿主单测直接覆盖)----
* 这几个函数不碰系统调用,只为“结构宽度”这件事提供唯一实现;mips 分支的接管入口
* 全部经它们转换,宿主 test_meow_time64.c 用同一份代码验边界。 */
void meow_time32_rel_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out)
{
if (sec > INT32_MAX) sec = INT32_MAX;
else if (sec < INT32_MIN) sec = INT32_MIN;
out->tv_sec = (int32_t)sec;
out->tv_nsec = (int32_t)nsec;
}
int meow_time32_abs_from_parts(int64_t sec, int64_t nsec, struct meow_time32 *out)
{
if (sec > INT32_MAX || sec < INT32_MIN) return -1;
out->tv_sec = (int32_t)sec;
out->tv_nsec = (int32_t)nsec;
return 0;
}
int meow_time32_itimerspec_from_64(const struct meow_time64_itimerspec *in,
struct meow_itimerspec32 *out)
{
struct meow_itimerspec32 t;
if (meow_time32_abs_from_parts(in->it_interval.tv_sec, in->it_interval.tv_nsec,
&t.it_interval)) return -1;
if (meow_time32_abs_from_parts(in->it_value.tv_sec, in->it_value.tv_nsec,
&t.it_value)) return -1;
*out = t;
return 0;
}
void meow_time64_itimerspec_from_32(const struct meow_itimerspec32 *in,
struct meow_time64_itimerspec *out)
{
out->it_interval.tv_sec = in->it_interval.tv_sec;
out->it_interval.tv_nsec = in->it_interval.tv_nsec;
out->it_value.tv_sec = in->it_value.tv_sec;
out->it_value.tv_nsec = in->it_value.tv_nsec;
}
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec)
{
return (uint64_t)sec * 1000u + (uint64_t)nsec / 1000000u;
}
/* 换算/取时的唯一实现已抽到 meow_time32.c(不依赖 SDK,系统组件可单独链接)。
* 本 TU 只保留「libc 入口接管」与 futex/timerfd/clock_settime 的 time64 修复。 */
uint64_t meow_ui_time_ms(void)
{
#if MEOW_TIME32_KERNEL_ABI
struct meow_time32 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
return meow_time32_monotonic_ms();
}
#if MEOW_TIME32_KERNEL_ABI
@@ -134,9 +82,7 @@ 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;
if (meow_time32_clock_gettime(clk, &t) != 0) return -1;
ts->tv_sec = t.tv_sec;
ts->tv_nsec = t.tv_nsec;
return 0;
+67
View File
@@ -0,0 +1,67 @@
/* test_meow_repaint.c — M40 F5:解锁后「强制 App 整帧重画」请求文件的写/读/删除语义。
*
* 编译(patch_v2/sdk 下):
* gcc -Wall -Wextra -I. test_meow_repaint.c -o test_meow_repaint
* 运行:./test_meow_repaint (exit 0=PASS)
*
* 语义:launcher 写文件 → SDK present 调 meow_ui_repaint_requested() 读到即删并返回 1
* (dirty_full 一次);文件不存在返回 0。路径可用 MEOW_APP_REPAINT_FILE 覆盖(测试隔离)。
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "meow_ui_repaint.h"
static int g_fail = 0;
static void check(const char *name, int ok)
{
if (ok) {
printf("ok %s\n", name);
} else {
printf("FAIL %s\n", name);
g_fail = 1;
}
}
static int write_file(const char *path)
{
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) return -1;
if (write(fd, "1", 1) != 1) { close(fd); return -1; }
close(fd);
return 0;
}
int main(void)
{
const char *tmp = getenv("TMPDIR");
char path[512];
snprintf(path, sizeof path, "%s/m40_repaint_XXXXXX", (tmp && tmp[0]) ? tmp : "/tmp");
int fd = mkstemp(path);
if (fd < 0) { perror("mkstemp"); return 2; }
close(fd);
unlink(path); /* 先清空,测试从「无请求」开始 */
setenv("MEOW_APP_REPAINT_FILE", path, 1);
check("path follows env", !strcmp(meow_ui_repaint_path(), path));
check("no request file -> 0", meow_ui_repaint_requested() == 0);
check("write request", write_file(path) == 0);
check("request present", access(path, F_OK) == 0);
check("first check consumes -> 1", meow_ui_repaint_requested() == 1);
check("file deleted after consume", access(path, F_OK) != 0);
check("second check -> 0 (one-shot)", meow_ui_repaint_requested() == 0);
/* 再写一次仍能消费(launcher 每次解锁/菜单关闭都会写) */
check("write again", write_file(path) == 0);
check("consume again -> 1", meow_ui_repaint_requested() == 1);
unlink(path);
printf("\n%s\n", g_fail ? "SOME TESTS FAILED" : "ALL PASS");
return g_fail ? 1 : 0;
}
+3 -1
View File
@@ -7,7 +7,9 @@
*
* 编译(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
* gcc -Os -Wall -Wextra -I. -Iinclude -c meow_time32.c -o /tmp/meow_time32.o
* gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c /tmp/meow_ui_time.o /tmp/meow_time32.o \
* -o test_meow_time
* 运行:./test_meow_time (exit 0=PASS / 1=FAIL)
*/
#include <stdint.h>
+2 -1
View File
@@ -7,7 +7,8 @@
*
* 编译(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_time64.c /tmp/meow_ui_time.o \
* gcc -Os -Wall -Wextra -I. -Iinclude -c meow_time32.c -o /tmp/meow_time32.o
* gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time64.c /tmp/meow_ui_time.o /tmp/meow_time32.o \
* -o test_meow_time64
* 运行:./test_meow_time64 (exit 0=PASS / 1=FAIL)
*/