diff --git a/patch_v2/meow_launcherd/app_launcher.c b/patch_v2/meow_launcherd/app_launcher.c index 41c54c0..20744f8 100644 --- a/patch_v2/meow_launcherd/app_launcher.c +++ b/patch_v2/meow_launcherd/app_launcher.c @@ -1394,6 +1394,22 @@ static void notify_app_crash(const char *pkg, int status) close(fd); } +/* 写 §10.5 SDK 自抑标记(/tmp/meow_notif_active)。**内容 = 当前前台屏幕归属者 app_id**; + * 空(传 NULL / "")= 菜单前台 / 叠加层覆盖 / 无前台单元(SDK 判「抑制全部 App」)。 + * SDK(meow_ui_gfx.c / meow_ui_input.c 共享判定 meow_ui_overlay_suppressed_active)据内容判 + * 本进程是否前台:内容==本进程 app_id → 前台不抑制;内容为空 → 全抑制;内容为其它 app_id + * → 后台常驻/非前台 → 自抑上屏 + 排空输入。 + * 批①多 App 后台常驻:取代旧「单标志写 1」——单标志无法区分前台/后台,清标志会连后台 App + * 一起放行(真机「前台 Hello 叠后台 ncm 帧 + 触摸穿透一次交互双响应」的根因)。 */ +static void notif_active_set(const char *owner) +{ + int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (nfd >= 0) { + if (owner && owner[0]) (void)write(nfd, owner, strlen(owner)); + close(nfd); + } +} + /* §10.5:App 前台顶部下滑 → 展开「由 launcher 渲染」的通知中心叠加会话。 * 复用电源菜单对 App 的冻结模板(show_power_menu 的 SIGSTOP/读回/叠加/接管/恢复): * ① SIGSTOP App → ② 写 /tmp/meow_notif_active(+ /tmp/meow_power_menu_active 让 watchdog 让位) @@ -1433,9 +1449,9 @@ static int run_app_notif_overlay(struct app *a, pid_t pid) } } /* 让位标记:/tmp/meow_notif_active(§10.5 语义同 powmenu_active)。当前 watchdog 只认 - * POWMENU_ACTIVE,故同时写它让 watchdog 对电源长按让位;会话结束统一 unlink。 */ - int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (nfd >= 0) { write(nfd, "1", 1); close(nfd); } + * POWMENU_ACTIVE,故同时写它让 watchdog 对电源长按让位;会话结束统一恢复通知中心前台。 + * 内容为空=抑制全部 App(叠加层覆盖前台 App + 后台常驻 App 一并自抑)。 */ + notif_active_set(""); /* 叠加期:全抑制(面板/锁屏盖在前台 App 上) */ int pfd = open(POWMENU_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (pfd >= 0) { write(pfd, "1", 1); close(pfd); } @@ -1453,30 +1469,32 @@ static int run_app_notif_overlay(struct app *a, pid_t pid) int died = g_notif_ctrl_died; if (died) { - /* App 已死:不 SIGCONT、不恢复死帧;back_buf 交还 run_menu 统一重绘 */ - unlink(NOTIF_ACTIVE); + /* App 已死:不 SIGCONT、不恢复死帧;back_buf 交还 run_menu 统一重绘。 + * 标记置空(全抑制):菜单前台,后台常驻 App 仍自抑(不再放行帧/输入)。 */ + notif_active_set(""); unlink(POWMENU_ACTIVE); logmsg("notif-overlay: app died during session -> unified exit path"); return 1; } /* 恢复 App 帧(run_notif_center 关面板后 back_buf 已还原为背景=App 帧;再拍一次兜底 - * 覆盖非动画关闭路径)。媒体保活路径(keep_running):先恢复帧再 unlink 标记—— - * App 未被冻结仍在跑,标记多留一拍可让 SDK present 自抑覆盖到恢复帧上屏之后, - * 避免「标记先消失 → App 抢在恢复帧前 present 新帧 → 又被旧帧盖回」的一帧回闪。 */ + * 覆盖非动画关闭路径)。媒体保活路径(keep_running):先恢复帧再把标记更新为前台归属 + * (a->id)——App 未被冻结仍在跑,标记多留一拍可让 SDK present 自抑覆盖到恢复帧上屏之后, + * 避免「标记先消失 → App 抢在恢复帧前 present 新帧 → 又被旧帧盖回」的一帧回闪。 + * 标记翻回 a->id:本 App 恢复前台不抑制,后台常驻 App(如 ncm)随之继续自抑(不叠帧/不穿输入)。 */ pm_capture_app_frame(); meow_ui_flush(); if (!keep_running) { /* §310 输入穿透定案(与 lock-overlay 同源):App 被 SIGSTOP 冻结过 → 在标记仍存在时 * 先 SIGCONT,让 SDK 输入线程排空冻结期缓冲的 evdev 事件 + 对齐 EVIOCGKEY,再等 - * OVERLAY_DRAIN_WINDOW_MS 后 unlink——防关面板瞬间把积累触摸回放成 App 内点击。 */ + * OVERLAY_DRAIN_WINDOW_MS 后把标记翻回前台——防关面板瞬间把积累触摸回放成 App 内点击。 */ kill(pid, SIGCONT); usleep(OVERLAY_DRAIN_WINDOW_MS * 1000); - logmsg("notif-overlay: closed -> SIGCONT app, drain %dms, then unlink mark", + logmsg("notif-overlay: closed -> SIGCONT app, drain %dms, then mark fg", OVERLAY_DRAIN_WINDOW_MS); } else { logmsg("notif-overlay: closed -> app never stopped (media keep-alive), no SIGCONT"); } - unlink(NOTIF_ACTIVE); + notif_active_set(a ? a->id : ""); /* 标记翻回前台归属(非空→本 App 解除自抑) */ unlink(POWMENU_ACTIVE); return 0; } @@ -1504,8 +1522,8 @@ static int run_app_lock_overlay(struct app *a, pid_t pid) } } } - int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (nfd >= 0) { write(nfd, "1", 1); close(nfd); } + /* 标记置空=全抑制(锁屏叠在前台 App + 后台常驻 App 上;解锁后翻回前台归属)。 */ + notif_active_set(""); int pfd = open(POWMENU_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (pfd >= 0) { write(pfd, "1", 1); close(pfd); } @@ -1520,9 +1538,9 @@ static int run_app_lock_overlay(struct app *a, pid_t pid) g_lock_overlay_pid = 0; if (lr == LCK_OVERLAY_RET_DIED) { - /* 叠加期 App 已死:不 SIGCONT(死进程)、不恢复死帧;unlink 标记交回 waitpid 循环回收, - * run_menu 统一重绘主屏(含状态栏)。launcher 不崩。 */ - unlink(NOTIF_ACTIVE); + /* 叠加期 App 已死:不 SIGCONT(死进程)、不恢复死帧;标记置空(菜单前台,后台常驻 App + * 仍自抑)交回 waitpid 循环回收,run_menu 统一重绘主屏(含状态栏)。launcher 不崩。 */ + notif_active_set(""); unlink(POWMENU_ACTIVE); logmsg("lock-overlay: app pid %d died during lock -> unified exit path", pid); return 1; @@ -1532,17 +1550,19 @@ static int run_app_lock_overlay(struct app *a, pid_t pid) meow_ui_flush(); if (!keep_running) { /* §310 输入穿透定案(overlay_gate.h):App 被 SIGSTOP 冻结过 → 在 /tmp/meow_notif_active - * 仍未 unlink 时**先 SIGCONT**,让 SDK 输入线程排空冻结期缓冲的 evdev 事件副本 + 对齐 - * EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS(> SDK 200ms 节流窗)后 unlink——防解锁瞬间 - * 把积累的触摸回放成 App 内点击(锁屏穿透→「启动奇怪界面」→ 状态栏消失的触发源)。 */ + * 仍未更新时**先 SIGCONT**,让 SDK 输入线程排空冻结期缓冲的 evdev 事件副本 + 对齐 + * EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS(> SDK 200ms 节流窗)后把标记翻回前台归属—— + * 防解锁瞬间把积累的触摸回放成 App 内点击(锁屏穿透→「启动奇怪界面」→ 状态栏消失的触发源)。 */ kill(pid, SIGCONT); usleep(OVERLAY_DRAIN_WINDOW_MS * 1000); - logmsg("lock-overlay: closed (lr=%d) -> SIGCONT app, drain %dms, then unlink mark", + logmsg("lock-overlay: closed (lr=%d) -> SIGCONT app, drain %dms, then mark fg", lr, OVERLAY_DRAIN_WINDOW_MS); } else { logmsg("lock-overlay: closed (lr=%d) -> app never stopped (media keep-alive), no SIGCONT", lr); } - unlink(NOTIF_ACTIVE); + /* 标记翻回前台归属(a->id):本 App 解除自抑恢复前台;后台常驻 App(如 ncm)随之继续自抑 + * (不叠帧 / 不穿输入——修复「锁屏解锁后 ncm 歌单帧叠加在 Hello 上 + 交互双响应」)。 */ + notif_active_set(a ? a->id : ""); unlink(POWMENU_ACTIVE); /* §310 状态栏恢复:解锁后强制 g_sb_mode_last=-1 + 立即重叠加顶条(不等 app 循环 1s 慢刷), * 消除「解锁回 App 无状态栏」的残留态。overlay_force_sb_resync() = 定案恒真。 */ @@ -1579,9 +1599,9 @@ static int launch_app_chain(struct app *a, const char *chain) logmsg("launch: %s resume from background (pid=%d)", a->name, (int)pid); if (chain && chain[0]) logmsg("launch: resume ignores chain=\"%s\"", chain); if (pick_action[0]) logmsg("launch: resume ignores picker action=%s", pick_action); - /* 回前台:清 §10.5 SDK present 自抑标记(无其他 bg App 且无叠加会话时) */ - if (meow_bg_count() == 0 && g_notif_ctrl_pid <= 0) - unlink(NOTIF_ACTIVE); + /* 回前台:标记内容置为前台归属(a->id)——本 App 解除自抑恢复前台;仍驻留后台的其它 + * App(如 ncm)随内容≠自身而继续自抑(不叠帧/不穿输入;批①多 App 常驻关键)。 */ + notif_active_set(a->id); drain_input(); meow_nav_begin(a->id, NULL); /* 转后台时导航栈已清;回前台从裸态重建 */ ams_report_fg(a->id); /* 批①:单向 best-effort 上报 amsd 置 AMS_FG */ @@ -1596,6 +1616,10 @@ static int launch_app_chain(struct app *a, const char *chain) return -1; } } + /* 冷启动:标记内容置为前台归属(a->id),让本 App 首帧即不被自抑(后台常驻 App 继续自抑)。 + * 旧版此处不写标记:若已有后台常驻 App(ncm)残留「1/空」标记,新前台 App 会被误自抑 + * → 主界面点 Hello 渲染卡死(状态栏正常)的根因。 */ + notif_active_set(a->id); meow_nav_begin(a->id, chain); /* M3:冷启动/裸启动都以当前链深初始化导航栈(chain 空→depth 0) */ /* M2.12:启动前准备 MeowData//{data,cache}(幂等;卡不在/失败只 log 不崩)。 @@ -2208,10 +2232,10 @@ static int launch_app_chain(struct app *a, const char *chain) usleep(50000); } } - /* 写 §10.5 SDK 自抑标记:App 进程见 /tmp/meow_notif_active → present 画进 - * back_buf 不上屏 + 输入线程排空(=UI 冻结);音频经 audiod socket 天然续播。 */ - int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (nfd >= 0) { write(nfd, "1", 1); close(nfd); } + /* 写 §10.5 SDK 自抑标记(内容空=菜单前台全抑制):App 进程见 /tmp/meow_notif_active + * 且内容≠自身 → present 画进 back_buf 不上屏 + 输入线程排空(=UI 冻结,不叠前台/不穿输入); + * 音频经 audiod socket 天然续播。 */ + notif_active_set(""); meow_write_app_state(-1); unlink(MEOW_SBAR_MODE_FILE); /* 顶条模式文件由 App SDK 写;转后台清理复位菜单整条 */ g_sb_mode_last = -1; @@ -2279,6 +2303,9 @@ static int launch_app_chain(struct app *a, const char *chain) * 让菜单音量/状态栏与真实 DAC 一致(watchdog 可能已改)。 */ settings_load(); meow_nav_exit(); /* M3:前台应用会话结束,清导航栈 */ + /* 回菜单:标记置空(菜单前台全抑制)——若仍驻留后台常驻 App(批①),其继续自抑不叠帧/不穿输入; + * 若已无后台 App,置空标记无害(SDK 无抑制目标)。 */ + notif_active_set(""); /* 阶段D附录B:源 App 退出 → 清其媒体会话(如该 App 上报过 META),通知中心/播放页回空态 */ meow_media_clear(a->id); logmsg("app %s exited status=%d", a->name, WEXITSTATUS(status)); diff --git a/patch_v2/meow_launcherd/overlay_gate.h b/patch_v2/meow_launcherd/overlay_gate.h index f887315..056d3ed 100644 --- a/patch_v2/meow_launcherd/overlay_gate.h +++ b/patch_v2/meow_launcherd/overlay_gate.h @@ -6,8 +6,9 @@ * * ① 输入穿透(S1 根因):App 被 SIGSTOP 冻结过 → 关闭时必须在 /tmp/meow_notif_active 仍存在 * 时先 SIGCONT,让 SDK 输入线程(meow_ui_input.c overlay_input_suppressed)排空冻结期缓冲的 - * evdev 事件副本 + 对齐 EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS 后 unlink 标记。 - * 若反序(unlink->SIGCONT,改动前写法),App 在无隔离态恢复 → 解冻瞬间把积累的触摸回放成 + * evdev 事件副本 + 对齐 EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS 后把标记内容更新为前台归属 + * (notif_active_set(a->id),批①内容化标记取代旧 unlink;清标记/置空会连后台常驻 App 一起放行)。 + * 若反序(清标记->SIGCONT,改动前写法),App 在无隔离态恢复 → 解冻瞬间把积累的触摸回放成 * App 内点击(锁屏穿透 /「启动奇怪界面」/ 解锁后状态栏消失的触发源)。 * ② 状态栏恢复(S2):解锁后强制 g_sb_mode_last=-1 让 app 循环立即重叠加,不等 1s 慢刷。 * ③ 死亡重绘(S3):锁屏叠加中 App 崩溃(/proc//stat==Z)→ 中止锁屏走统一退出路径, diff --git a/patch_v2/sdk/meow_ui_gfx.c b/patch_v2/sdk/meow_ui_gfx.c index 697ab0b..98db7a1 100644 --- a/patch_v2/sdk/meow_ui_gfx.c +++ b/patch_v2/sdk/meow_ui_gfx.c @@ -974,16 +974,33 @@ static int statusbar_touched(void) return 0; } -/* ---- §10.5 叠加让位自抑(present 层)---- - * /tmp/meow_notif_active 存在 = 通知中心叠加会话进行中(launcher 正在 fb 上渲染面板)。 - * App 进程(非系统 UI 渲染者)此时不上屏;200ms 节流缓存存在性(别每帧 stat)。 +/* ---- §10.5 叠加让位自抑(present / input 共用判定)---- + * /tmp/meow_notif_active = 前台屏幕归属标记,**内容=当前前台 app_id**(空=菜单/叠加层覆盖/ + * 无前台单元,即「抑制全部 App」)。App 进程(非系统 UI 渲染者)据此判「我是否前台」: + * - 内容==本进程 app_id(env MEOW_LOG_APP)→ 前台 → 照常上屏 / 收输入; + * - 内容为空 → 菜单前台或被叠加层覆盖 → 全抑制(含自身); + * - 内容为其它 app_id → 后台常驻(批①多 App)/非前台 → 自抑上屏 + 排空输入。 + * 200ms 节流缓存存在性+内容(别每帧 open/stat)。 * 计数 g_overlay_suppressed 供宿主单测断言(meow_gfx_overlay_suppressed_count)。 */ static unsigned long long g_overlay_suppressed = 0; /* §后台化保活-残帧:记录「上一次 present 是否处于叠加自抑态」,用于在抑制→不抑制的恢复边沿 * 强制整帧重画(盖掉 launcher 在后台化期间画进 buffer0 的桌面/主界面帧)。 */ static int g_was_overlay_suppressed = 0; -static int overlay_present_suppressed(void) +/* 本进程 app_id(env MEOW_LOG_APP,launcher 注入)= 判「我是否前台」的身份锚。 */ +static const char *overlay_own_app(void) +{ + static const char *s = NULL; + if (!s) { + const char *e = getenv("MEOW_LOG_APP"); + s = (e && e[0]) ? e : ""; + } + return s; +} + +/* §批①多 App 后台常驻:present 层与 input 层共用的「是否自抑」判定(单一实现,防两端漂移)。 + * 系统 UI(launcher 渲染面板/主界面)恒 0。返回 1=本进程应自抑上屏+排空输入。 */ +int meow_ui_overlay_suppressed_active(void) { if (meow_ui_sys_render_enabled()) return 0; /* launcher(系统 UI)渲染面板,照常上屏 */ static char s_path[108]; @@ -998,7 +1015,29 @@ static int overlay_present_suppressed(void) unsigned long long now = now_ms(); if (s_last_chk && now - s_last_chk < MEOW_OVERLAY_CHK_MS) return s_last_res; s_last_chk = now; - s_last_res = (access(s_path, F_OK) == 0) ? 1 : 0; + { + char owner[48]; + int have = 0; + owner[0] = 0; + int fd = open(s_path, O_RDONLY); + if (fd >= 0) { + ssize_t n = read(fd, owner, sizeof(owner) - 1); + close(fd); + if (n > 0) owner[n] = 0; + have = 1; /* 文件存在(无论内容)→ 处于抑制管控 */ + } else { + have = 0; /* 文件不存在 → 无抑制 */ + } + if (!have) { + s_last_res = 0; + } else if (owner[0] == 0) { + s_last_res = 1; /* 空内容=全抑制(菜单前台/叠加层覆盖) */ + } else { + const char *me = overlay_own_app(); + /* 内容==本进程 app_id → 前台不抑制;否则(后台常驻/非前台)→ 抑制 */ + s_last_res = (strcmp(owner, me) == 0) ? 0 : 1; + } + } return s_last_res; } @@ -1010,9 +1049,10 @@ static void present(void) if (!(g_dirty_full || g_ndirty > 0)) return; /* §10.5 叠加让位自抑(仅 App 进程):App 前台播放中唤起通知中心时 launcher 不 SIGSTOP * (保 PCM 供流,ncm 播放线程同进程),改由 App 进程在此自抑上屏——绘制照常进 back_buf, - * 只是不 present 到 fb,防 App 重绘穿透 launcher 叠加层。系统 UI 进程(launcher 渲染面板) - * 不抑制。抑制期脏区保留累积(超上限自然升级整帧),标记消失后下一次 present 一次上屏。 */ - if (overlay_present_suppressed()) { g_overlay_suppressed++; g_was_overlay_suppressed = 1; return; } + * 只是不 present 到 fb,防 App 重绘穿透 launcher 叠加层;后台常驻(批①)App 同判据自抑。 + * 系统 UI 进程(launcher 渲染面板)不抑制。抑制期脏区保留累积(超上限自然升级整帧), + * 判定为「回到前台」后下一次 present 一次上屏。 */ + if (meow_ui_overlay_suppressed_active()) { g_overlay_suppressed++; g_was_overlay_suppressed = 1; return; } /* §后台化保活-残帧:抑制→不抑制的**恢复边沿**强制整帧重画。App 被 launcher 转后台保活 * (/tmp/meow_notif_active 存在)期间 present 被自抑,back_buf 仍保留 App 自己的完整帧; * 但 launcher 已把桌面/主界面整帧画进 buffer0 盖在 App 残帧上。恢复前台时若只 present diff --git a/patch_v2/sdk/meow_ui_input.c b/patch_v2/sdk/meow_ui_input.c index 8863236..fe51246 100644 --- a/patch_v2/sdk/meow_ui_input.c +++ b/patch_v2/sdk/meow_ui_input.c @@ -140,28 +140,17 @@ static int meow_input_jack_debounce(void) return g_jack_changed_at && (now - g_jack_changed_at) < 800; } -/* ---- §10.5 叠加让位:通知中心叠加会话期输入排空(播放中 App 不被 SIGSTOP 的配套)---- +/* ---- §10.5 叠加让位:通知中心叠加会话期 / 批①后台常驻期 输入排空(播放中 App 不被 SIGSTOP 的配套)---- * 媒体播放中的 App 前台唤起通知中心时 launcher 不冻结 App(保 PCM 供流,ncm 播放线程同进程), * App 输入线程仍在读同一批 evdev 设备(每 fd 各收一份事件副本,与 launcher 互不剥夺)——若不 - * 抑制,面板上的一次点击会被 App 再消费一次(如面板媒体卡片「暂停」与 App 同坐标控件双重触发)。 - * 故标记存在期:排空本进程 fd 队列并丢弃、EVIOCGKEY 位图静默对齐(不产生边沿语义,系统键动作 - * 叠加期由 launcher/watchdog 单侧承担)。200ms 节流缓存存在性(与 present 层同口径,别每圈 stat)。 */ + * 抑制,面板上的一次点击会被 App 再消费一次(如面板媒体卡片「暂停」与 App 同坐标控件双重触发); + * 批①后台常驻 App 同理:前台 App(如 Hello)时,后台 ncm 的触摸必须被排空(一次交互只响应前台)。 + * 判据统一走 meow_ui_overlay_suppressed_active()(内容=前台 app_id;非前台/空=抑制)。抑制期: + * 排空本进程 fd 队列并丢弃、EVIOCGKEY 位图静默对齐(不产生边沿语义,系统键动作叠加期由 + * launcher/watchdog 单侧承担)。200ms 节流缓存由共享判定承担(与 present 层同口径)。 */ static int overlay_input_suppressed(void) { - static char s_path[108]; - static int s_path_init = 0; - static unsigned long long s_last_chk = 0; - static int s_last_res = 0; - if (!s_path_init) { - const char *e = getenv("MEOW_NOTIF_ACTIVE_FILE"); /* 宿主/单测覆写(进程内只解析一次) */ - snprintf(s_path, sizeof s_path, "%s", (e && e[0]) ? e : "/tmp/meow_notif_active"); - s_path_init = 1; - } - unsigned long long now = now_ms(); - if (s_last_chk && now - s_last_chk < 200) return s_last_res; - s_last_chk = now; - s_last_res = (access(s_path, F_OK) == 0) ? 1 : 0; - return s_last_res; + return meow_ui_overlay_suppressed_active(); } /* ---- 系统按键语义(M2.55:SDK 输入层消化,App 前台也生效)---- diff --git a/patch_v2/sdk/meow_ui_internal.h b/patch_v2/sdk/meow_ui_internal.h index c38375e..162dd2c 100644 --- a/patch_v2/sdk/meow_ui_internal.h +++ b/patch_v2/sdk/meow_ui_internal.h @@ -163,6 +163,12 @@ int meow_gfx_is_device(void); * (仅 App 进程会自抑;宿主单测断言用,设备上无调用方)。 */ unsigned long long meow_gfx_overlay_suppressed_count(void); +/* §批①多 App 后台常驻:本进程是否应自抑上屏+排空输入(present/input 层共用判定)。 + * /tmp/meow_notif_active 内容=前台 app_id:内容==本进程 app_id → 前台不抑制(返回 0); + * 内容为空或其它 app_id → 菜单前台/叠加层覆盖/后台常驻 → 自动抑制(返回 1)。 + * 系统 UI(launcher)恒 0;200ms 节流缓存。meow_ui_gfx.c 实现,input 层复用。 */ +int meow_ui_overlay_suppressed_active(void); + /* 中文/日文字库(mfont)是否已加载(状态栏「蓝牙」CJK 与回退 "BT" 二选一)。 */ int meow_gfx_mfont_ready(void); diff --git a/patch_v2/sdk/test_overlay_suppress.c b/patch_v2/sdk/test_overlay_suppress.c index 064ebab..d0f7f54 100644 --- a/patch_v2/sdk/test_overlay_suppress.c +++ b/patch_v2/sdk/test_overlay_suppress.c @@ -1,14 +1,19 @@ /* test_overlay_suppress.c — §10.5 叠加让位 present 自抑宿主验证(离屏 480x720,16bpp)。 * * 背景:媒体播放中的 App 前台唤起通知中心时 launcher 不 SIGSTOP(保 PCM 供流),改由 SDK - * present 层见 /tmp/meow_notif_active(宿主用 MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏。 + * present 层见 /tmp/meow_notif_active(宿主用 MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏; + * 批①后台常驻后标记**内容化**:内容=当前前台 app_id(空=菜单/叠加=全抑制)。本测试同时验证 + * 批①多 App 常驻的「前台/后台」判定:内容==本进程 app_id(env MEOW_LOG_APP)→ 前台不抑制; + * 内容为其它 app_id / 空 → 后台常驻/被覆盖 → 自抑。 * * 覆盖: - * T1 无标记:flush 正常上屏(frames_presented 递增,抑制计数 0); - * T2 有标记:flush 被抑制(只画进 back_buf 不 present:frames 不变、抑制计数递增); - * T2b 抑制期第二次 flush 仍被抑制(200ms 节流缓存内行为一致); - * T3 标记消失:下一次 flush 恢复上屏(抑制期累积脏区一次上屏,无需重画); - * T4 系统 UI 进程(launcher,system_mode=1)即使有标记也不抑制(面板必须照常上屏)。 + * T1 无标记:flush 正常上屏(frames_presented 递增,抑制计数 0); + * T2 标记=其它 app_id(后台常驻)→ 抑制(只画进 back_buf 不上屏:frames 不变、计数递增); + * T2b 抑制期第二次 flush 仍被抑制(200ms 节流缓存内行为一致); + * T2c 标记=自身 app_id(前台 App)→ 不抑制(恢复上屏,恢复边沿整帧重画;计数不再增长); + * T3 标记=空内容(菜单/叠加全抑制)→ 仍抑制; + * T3b 标记消失 → 恢复上屏(抑制期累积脏区一次上屏,无需重画); + * T4 系统 UI 进程(launcher,system_mode=1)即使有标记也不抑制(面板必须照常上屏)。 * * 编译(宿主 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 \ @@ -26,13 +31,16 @@ #include #include -#define MARKER "/tmp/test_notif_active_sdk" +#define MARKER "/tmp/test_notif_active_sdk" +#define SELF_ID "testapp" -static void marker_write(void) +static void marker_write(const char *content) { int fd = open(MARKER, O_WRONLY | O_CREAT | O_TRUNC, 0644); assert(fd >= 0); - if (write(fd, "1", 1) < 0) assert(0); + if (content && content[0]) { + if (write(fd, content, strlen(content)) < 0) assert(0); + } close(fd); } @@ -40,6 +48,7 @@ int main(void) { setenv("MEOW_UI_DUMP", "/tmp/overlay_suppress.ppm", 1); setenv("MEOW_NOTIF_ACTIVE_FILE", MARKER, 1); + setenv("MEOW_LOG_APP", SELF_ID, 1); /* 本测试进程身份=前台/后台的判定锚 */ unlink(MARKER); meow_ui_opts o = { 480, 720 }; assert(meow_ui_init(&o) == 0); @@ -53,15 +62,15 @@ int main(void) assert(meow_gfx_overlay_suppressed_count() == 0); printf("T1 PASS: no marker -> presented=1 suppressed=0\n"); - /* T2:有标记 → 抑制(等 >200ms 越过存在性节流缓存) */ - marker_write(); + /* T2:标记=其它 app_id(后台常驻 App)→ 抑制(等 >200ms 越过存在性节流缓存) */ + marker_write("ncm"); usleep(250000); unsigned long long f0 = meow_ui_frames_presented(); meow_ui_clear(0x223344); meow_ui_flush(); assert(meow_ui_frames_presented() == f0); assert(meow_gfx_overlay_suppressed_count() == 1); - printf("T2 PASS: marker present -> suppressed (frames frozen at %llu)\n", f0); + printf("T2 PASS: marker=other app -> suppressed (frames frozen at %llu)\n", f0); /* T2b:抑制期再次 flush 仍抑制(节流缓存内一致) */ meow_ui_clear(0x334455); @@ -70,24 +79,43 @@ int main(void) assert(meow_gfx_overlay_suppressed_count() == 2); printf("T2b PASS: still suppressed within throttle window\n"); - /* T3:标记消失 → 恢复上屏(抑制期累积的整帧脏一次上屏,无需重画) */ + /* T2c:标记=自身 app_id(前台 App)→ 不抑制(批①前后台判定核心;恢复边沿整帧重画一次上屏) */ + marker_write(SELF_ID); + usleep(250000); + meow_ui_clear(0x445566); + meow_ui_flush(); + assert(meow_ui_frames_presented() == f0 + 1); + assert(meow_gfx_overlay_suppressed_count() == 2); /* 不再增长(前台不抑制) */ + printf("T2c PASS: marker=self -> foreground, present resumed (accumulated dirty flushed)\n"); + + /* T3:标记=空内容(菜单/叠加层覆盖=全抑制)→ 仍抑制 */ + marker_write(""); + usleep(250000); + unsigned long long f1 = meow_ui_frames_presented(); + meow_ui_clear(0x556677); + meow_ui_flush(); + assert(meow_ui_frames_presented() == f1); + assert(meow_gfx_overlay_suppressed_count() == 3); + printf("T3 PASS: empty marker (menu/overlay) -> suppressed\n"); + + /* T3b:标记消失 → 恢复上屏(抑制期累积的整帧脏一次上屏) */ unlink(MARKER); usleep(250000); meow_ui_flush(); - assert(meow_ui_frames_presented() == f0 + 1); - assert(meow_gfx_overlay_suppressed_count() == 2); - printf("T3 PASS: marker gone -> present resumed (accumulated dirty flushed)\n"); + assert(meow_ui_frames_presented() == f1 + 1); + assert(meow_gfx_overlay_suppressed_count() == 3); + printf("T3b PASS: marker gone -> present resumed (accumulated dirty flushed)\n"); /* T4:系统 UI 进程(launcher)不抑制——通知中心面板本身必须照常上屏 */ meow_ui_sys_set_system_mode(1); assert(meow_ui_sys_render_enabled() == 1); - marker_write(); + marker_write("ncm"); usleep(250000); - unsigned long long f1 = meow_ui_frames_presented(); - meow_ui_clear(0x445566); + unsigned long long f2 = meow_ui_frames_presented(); + meow_ui_clear(0x667788); meow_ui_flush(); - assert(meow_ui_frames_presented() == f1 + 1); - assert(meow_gfx_overlay_suppressed_count() == 2); /* 不再增长 */ + assert(meow_ui_frames_presented() == f2 + 1); + assert(meow_gfx_overlay_suppressed_count() == 3); /* 不再增长 */ unlink(MARKER); printf("T4 PASS: system-UI process (launcher) never suppressed\n");