From 1880aba139fe809faf35aabfb92c74eba5ae493e Mon Sep 17 00:00:00 2001 From: MeowOS Date: Sun, 30 Aug 2026 20:18:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(statusbar):=20=E5=85=A8=E5=B1=8F=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=A0=8F=E6=94=B6=E5=90=88=20=E2=80=94=20=E4=B8=8A?= =?UTF-8?q?=E7=A7=BB=E5=87=BA=E5=B1=8F+=E5=81=9C=E6=B8=B2=E6=9F=93+?= =?UTF-8?q?=E7=83=AD=E5=8C=BA28=E4=BF=9D=E7=95=99+=E5=B7=A6=E7=BC=98?= =?UTF-8?q?=E6=8C=87=E7=A4=BA=E6=9D=A1=E5=B1=8F=E9=A1=B6(agent-146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根因: launcher/App 双进程各持SDK态, hello运行时全屏(set_fullscreen+set_collapsed)只改App进程, launcher仍expanded→画28px+时序闪 - SDK render_off(yoff): 顶条内容整体上移+裁顶, 收合 statusbar_collapsed_render 只画4px提示线(无时钟/图标/值) - 热区保留: meow_ui_statusbar_hotzone() 收合时返回28(渲染与热区事件分离)→收合仍可下滑 - 左缘指示条: meow_ui_swipe_hint y=statusbar_height()=4 平移屏顶 - 模式感知: /tmp/meowapp_sbar(App进程SDK写0展开/1收合/2隐藏, launcher读); launch_app循环新变立即叠(消半秒闪)+过渡期~20ms高频刷(动画平滑) - meow_ui.a=ed0267a9 launcher=7ea80408 hello=1a3ff13e 0warning; 宿主 test_sb_collapse/test_sb_statusbar ALL PASS - 遗留: 全屏上收观感/收合下App前台打开通知中心归④M3(meow_ui_sys_notif_pull桩) --- meow_ui.h | 12 +++++++-- meow_ui_gfx.c | 26 ++++++++++++++++---- meow_ui_internal.h | 9 +++++++ meow_ui_sys.c | 61 ++++++++++++++++++++++++++++++++++++---------- meow_ui_sys.h | 9 +++++++ 5 files changed, 97 insertions(+), 20 deletions(-) diff --git a/meow_ui.h b/meow_ui.h index bcb94b7..9b89ba3 100644 --- a/meow_ui.h +++ b/meow_ui.h @@ -223,10 +223,18 @@ int meow_ui_statusbar_height(void); void meow_ui_statusbar_set_collapsed(int on); /* on=1 上收(内容上移出屏、只留提示线);on=0 恢复 */ int meow_ui_statusbar_collapsed(void); /* 当前是否处于「上收」目标(1=是) */ void meow_ui_statusbar_animate(int ms); /* TBD:上收/恢复过渡时长(ms);占位,后续代理实现插值 */ +/* 当前上收进度(0..MUI_SBAR_H:0=完全展开,28=内容全部上移出屏、仅留提示线)。 + * 供系统进程(launcher)在 App 前台叠加顶条时读取,按「当前可见条高 = 28-offset」 + * 标脏/切齐,实现收合时只 blit 可见条高、其余行保留 App 内容(平滑上收、无残留)。 */ +int meow_ui_statusbar_collapse_offset(void); +/* 上收/恢复过渡是否仍在进行(current offset != target,逐帧推进中)。 + * 供 launcher 在过渡期高频刷新顶条(launch_app 循环据此缩短 sleep),驱动平滑动画;稳定后回 1s 慢刷。 */ +int meow_ui_statusbar_animating(void); /* 顶部下滑热区(下拉展开状态栏/通知)高度(px)。 - * 非全屏=整条状态栏可视高度;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。 - * 返回「可用于起始下拉」的热区高度;由输入层/后续代理据此判定下拉起点。 */ + * 状态栏「激活」(含**收合**)时恒=整条状态栏高 MUI_SBAR_H(28)——收合只上收渲染、**不禁用顶部热区** + * (渲染与热区事件分离,全屏下顶部下滑仍开通知中心);仅「全屏隐藏」(immersive inset0)时缩小到 + * MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。返回「可用于起始下拉」的热区高度。 */ int meow_ui_statusbar_hotzone(void); #define MEOW_SBAR_HOTZONE_FS 4 /* 全屏时缩小后的顶部热区高度(px,草案值) */ diff --git a/meow_ui_gfx.c b/meow_ui_gfx.c index b23c363..4eb9c3c 100644 --- a/meow_ui_gfx.c +++ b/meow_ui_gfx.c @@ -177,6 +177,7 @@ static void dirty_add(int x, int y, int w, int h) void meow_gfx_mark_dirty(int x, int y, int w, int h) { dirty_add(x, y, w, h); } void meow_gfx_mark_full(void) { dirty_full(); } +void meow_gfx_dirty_reset(void) { dirty_reset(); } /* launcher(系统组件)与 SDK 共用同一 back_buf:launcher 的 fb 绘制指针指向它, * 从而 launcher 与 app 统一渲染栈(同一离屏 + 同一 present)。 */ @@ -674,10 +675,21 @@ void meow_ui_statusbar_set_collapsed(int on) g_sbar_collapse_from_h = g_sbar_collapse_h; g_sbar_collapse_start_ms = now_ms(); dirty_full(); /* 顶栏显隐/上收变化 → 全帧重画 */ + meow_ui_sbar_write_mode(); /* App 进程上报顶条模式(launcher 感知全屏/收合,agent-146) */ } int meow_ui_statusbar_collapsed(void) { return g_sbar_collapsed; } +/* 当前上收进度(0..MUI_SBAR_H)。供 launcher 按「可见条高 = 28-offset」标脏切齐(agent-146)。 */ +int meow_ui_statusbar_collapse_offset(void) { return g_sbar_collapse_h; } + +/* 上收/恢复过渡是否仍在推进(current offset != target)。供 launcher 过渡期高频刷(agent-146)。 */ +int meow_ui_statusbar_animating(void) +{ + int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0; + return g_sbar_collapse_h != tgt; +} + void meow_ui_statusbar_animate(int ms) { g_sbar_collapse_anim_ms = (ms > 0) ? ms : 0; /* 0=瞬切(基座默认) */ @@ -685,11 +697,12 @@ void meow_ui_statusbar_animate(int ms) int meow_ui_statusbar_hotzone(void) { - /* 非全屏=整条状态栏可视;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。 - * 上收(collapsed、非全屏)时热区=提示线下缘高度(MUI_SBAR_COLLAPSE_H,与渲染/inset 同口径)。 - * 全屏态由 meow_ui_sys 维护。 */ + /* 通知中心触发(顶部下滑)热区:与渲染分离(agent-146 定案:收合 ≠ 禁用顶部热区)。 + * 状态栏「激活」(非全屏隐藏,含收合)时热区恒 = 整条状态栏高 MUI_SBAR_H(28)——即使收合 + * 也只上收渲染,顶部下滑仍可打开通知中心(渲染与热区事件分离)。 + * 仅「全屏隐藏」(immersive,inset0)时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。 */ if (meow_ui_sys_statusbar_active()) - return g_sbar_collapsed ? MUI_SBAR_COLLAPSE_H : MUI_SBAR_H; + return MUI_SBAR_H; return MEOW_SBAR_HOTZONE_FS; } @@ -731,7 +744,10 @@ static void present(void) if (g_sbar_collapse_h >= MUI_SBAR_H) statusbar_collapsed_render(g_sbar_collapse_h); else - meow_ui_sys_statusbar_render(); + /* 收合过渡/展开态:内容上移 g_sbar_collapse_h 像素并裁顶(yoff=0=整条)。 + * 仅画可见条高 [0, 28-offset) 行,其余行=App 内容(launcher 按 statusbar_height 标脏 + * 切齐),实现「平滑上移出屏」而不残留、不黑框。 */ + meow_ui_sys_statusbar_render_off(g_sbar_collapse_h); } if (g_fb_mem) { ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */ diff --git a/meow_ui_internal.h b/meow_ui_internal.h index 67cb73b..361e769 100644 --- a/meow_ui_internal.h +++ b/meow_ui_internal.h @@ -40,6 +40,10 @@ int meow_ui_sys_render_enabled(void); /* 当前进程是否渲染系统顶 /* 把全局状态栏画进本进程 back_buf 顶部 MUI_SBAR_H 行(仅系统进程调用,见 render_enabled)。 * launcher 的 App 前台叠加(launch_app 循环)直接调它 + 标脏顶条上屏。实现见 meow_ui_sys.c。 */ void meow_ui_sys_statusbar_render(void); +/* 收合「上移出屏」过渡渲染:把顶条内容整体上移 yoff 像素并裁顶(y<0 处自然剪裁,meow_font/图标 + * blit 已带边界钳制),仅画可见条高 [0, MUI_SBAR_H-yoff) 行(其上为 App 内容、由 launcher 保留)。 + * yoff=0 等价 meow_ui_sys_statusbar_render()。实现见 meow_ui_sys.c。 */ +void meow_ui_sys_statusbar_render_off(int yoff); /* ---- 状态栏 MD3 图标簇(meow_ui_sys.c 渲染右侧;位图在 SDK 侧 sb_icons.h)---- * meow_ui_gfx.c 的 meow_gfx_blit_icon(idx,...) 用这些索引查 sb_icons[]。 @@ -97,6 +101,11 @@ void meow_gfx_mark_dirty(int x, int y, int w, int h); /* 标记整帧脏(launcher 手动脏模型桥接:自绘 raw 像素后标全屏,present 走 SDK)。 */ void meow_gfx_mark_full(void); +/* 清空脏区(含整帧标记)。用于 App 前台叠加路径:meow_ui_statusbar_set_collapsed 会标整帧脏, + * 但 launcher 在 App 上只应 blit 顶条(可见条高),绝不能整帧 blit(会把自身 back_buf 的 stale + * 菜单内容盖到 App 内容上)——叠加前用本函数撤销整帧脏,再按需标局部脏(agent-146)。 */ +void meow_gfx_dirty_reset(void); + /* 返回离屏 back_buf 指针(launcher 系统组件与 SDK 共用同一 back_buf 用)。 */ unsigned char *meow_ui_back_buf(void); diff --git a/meow_ui_sys.c b/meow_ui_sys.c index ea70d87..aea79f7 100644 --- a/meow_ui_sys.c +++ b/meow_ui_sys.c @@ -25,6 +25,11 @@ #define MEOW_SETTINGS_FILE "/usr/data/meow/settings.conf" +/* App 前台顶条模式文件(App 进程 SDK 写,launcher 读)。与 /tmp/meowapp_state(immersive,launcher 写/ + * watchdog 读)语义不同,独立文件避免冲突。值:'0'=展开 28px 整条;'1'=收合(提示线);'2'=隐藏(沉浸 inset0)。 + * 仅 App 进程写(!g_sys_render);launcher(系统 UI)不写,只读。App 退出时由 launcher unlink。 */ +#define MEOW_SBAR_MODE_FILE "/tmp/meowapp_sbar" + /* 蓝牙 powered 缓存刷新间隔(hciconfig 较贵) */ #define BT_REFRESH_MS 30000 @@ -190,6 +195,21 @@ void meow_ui_sys_set_system_mode(int on) g_statusbar_force = 1; /* 切换即强制下次 present 反映(画/不画顶条) */ } +/* ---- App 前台顶条模式上报(agent-146 全屏收合接入)---- + * 仅在 App 进程(!g_sys_render)写;launcher(system_mode=1)不写,避免覆盖 App 的状态。 + * 由 meow_ui_set_fullscreen / meow_ui_statusbar_set_collapsed 调用,使 launcher(read 进程) + * 能感知 App 运行时的全屏/收合态,从而把新顶条切到收合(提示线)而非仍画 28px 整条。 */ +void meow_ui_sbar_write_mode(void) +{ + if (g_sys_render) return; /* 系统进程(launcher)不写 */ + char m = meow_ui_statusbar_collapsed() ? '1' + : (g_fullscreen ? '2' : '0'); + int fd = open(MEOW_SBAR_MODE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) return; + if (write(fd, &m, 1) < 0) { /* ignore write error */ } + close(fd); +} + int meow_ui_sys_render_enabled(void) { return g_sys_render; @@ -200,6 +220,7 @@ void meow_ui_set_statusbar(int visible) { g_statusbar_on = visible ? 1 : 0; g_statusbar_force = 1; /* 显隐切换也要重画顶条 */ + meow_ui_sbar_write_mode(); /* App 进程上报顶条模式(launcher 感知全屏/收合) */ } int meow_ui_statusbar_visible(void) @@ -211,6 +232,7 @@ void meow_ui_set_fullscreen(int on) { g_fullscreen = on ? 1 : 0; g_statusbar_force = 1; /* 进出全屏立即反映(显示/隐藏顶条) */ + meow_ui_sbar_write_mode(); /* App 进程上报顶条模式(launcher 感知全屏/收合) */ } int meow_ui_statusbar_height(void) @@ -259,14 +281,14 @@ int meow_ui_sys_statusbar_force(void) /* 画一个右对齐项:右边界=*xr,画完向左推进 *xr(含 MEOW_SB_XGAP 项间隙)。 * icon>=0 画图标(可配右侧数值 txt;txt==NULL 仅图标)。文字/图标同用主题色 col。 * ink_h 传给 meow_gfx_blit_icon(主图标用 MEOW_SB_ICON_INK 等大;WiFi 强弱档传 0 保持自然 ink)。 - * 返回该项左边缘(供亮度可选判宽)。 */ -static int sb_r_place(int *xr, int icon, const char *txt, uint32_t col, int ink_h, int ty) + * ycy = 图标 ink 垂直中心线(收合上移时随 -off 上移);ty = 文字 y。返回该项左边缘(供亮度可选判宽)。 */ +static int sb_r_place(int *xr, int icon, const char *txt, uint32_t col, int ink_h, int ty, int ycy) { int iconw = (icon >= 0) ? MEOW_SB_ICON_SIZE : 0; int gap = (icon >= 0 && txt) ? MEOW_SB_TGAP : 0; int tw = txt ? meow_ui_text_width(txt, MUI_SBAR_FONT) : 0; int x = *xr - (iconw + gap + tw); - if (icon >= 0) { meow_gfx_blit_icon(icon, x, 0, ink_h, col, MEOW_SB_ICON_CY); x += iconw + gap; } + if (icon >= 0) { meow_gfx_blit_icon(icon, x, 0, ink_h, col, ycy); x += iconw + gap; } if (txt) meow_ui_text_right(*xr, ty, txt, MUI_SBAR_FONT, col); *xr -= (iconw + gap + tw) + MEOW_SB_XGAP; return *xr + MEOW_SB_XGAP; @@ -285,19 +307,31 @@ static int sb_wifi_icon(void) } void meow_ui_sys_statusbar_render(void) +{ + meow_ui_sys_statusbar_render_off(0); +} + +void meow_ui_sys_statusbar_render_off(int yoff) { if (!g_sys_render) return; /* 仅系统进程(launcher)渲染;App 不画顶条 */ if (!meow_ui_sys_statusbar_active()) return; + if (yoff < 0) yoff = 0; + if (yoff > MUI_SBAR_H) yoff = MUI_SBAR_H; const meow_theme *th = meow_theme_get(); int w = meow_ui_width(); int h = MUI_SBAR_H; + int vis = h - yoff; /* 收合上移:可见条高 = 28-yoff(其余行=App 内容,由 launcher 保留) */ + if (vis <= 0) return; /* 完全收合由 statusbar_collapsed_render(提示线)处理 */ - /* 整条清底:覆盖 app 可能画进顶行的内容(z 最顶)。仅系统进程(launcher)渲染本函数。 */ - meow_ui_fill_rect(0, 0, w, h, th->bg); + /* 整条清底(仅可见条高 [0,vis) 行):覆盖 app 可能画进顶行的内容(z 最顶)。仅系统进程渲染本函数。 */ + meow_ui_fill_rect(0, 0, w, vis, th->bg); - /* 文字垂直锚 y:(栏高-行高)/2 居中;下限 1 防极端行高 > 栏高时负 y 溢出顶端(不越界优先)。 */ + /* 文字垂直锚 y:(栏高-行高)/2 居中;下限 1 防极端行高 > 栏高时负 y 溢出顶端(不越界优先)。 + * 收合上移:内容整体上移 yoff 像素并裁顶(meow_font/图标 blit 已带 y<0 边界钳制,安全)。 */ int ty = (h - meow_ui_text_height(MUI_SBAR_FONT)) / 2; if (ty < 1) ty = 1; + ty -= yoff; + int ycy = MEOW_SB_ICON_CY - yoff; /* 图标 ink 垂直中心随上移 */ /* 时钟(左):每秒现取 localtime */ char timebuf[16]; @@ -314,28 +348,29 @@ void meow_ui_sys_statusbar_render(void) char val[16]; if (g_sys.batt >= 0) { /* 电量:图标+%(未知不画)——最右项 */ snprintf(val, sizeof val, "%d%%", g_sys.batt); - sb_r_place(&xr, MEOW_SB_ICON_BATT, val, col, MEOW_SB_ICON_INK, ty); + sb_r_place(&xr, MEOW_SB_ICON_BATT, val, col, MEOW_SB_ICON_INK, ty, ycy); } snprintf(val, sizeof val, "%d%%", g_sys.vol); - sb_r_place(&xr, MEOW_SB_ICON_VOL, val, col, MEOW_SB_ICON_INK, ty); /* 音量:图标+% */ + sb_r_place(&xr, MEOW_SB_ICON_VOL, val, col, MEOW_SB_ICON_INK, ty, ycy); /* 音量:图标+% */ if (g_sys.bt_seen && g_sys.bt_on) { /* 蓝牙:仅图标(上电即显示,连接态占位;多态留后续) */ - sb_r_place(&xr, MEOW_SB_ICON_BT_CONN, NULL, col2, MEOW_SB_ICON_INK, ty); + sb_r_place(&xr, MEOW_SB_ICON_BT_CONN, NULL, col2, MEOW_SB_ICON_INK, ty, ycy); } if (g_sys.wifi_on) { /* WiFi:仅图标(关不画;开未连/分档) */ int wco = sb_wifi_icon(); int is_weak = (wco == MEOW_SB_ICON_WIFI_MEDIUM || wco == MEOW_SB_ICON_WIFI_WEAK); - sb_r_place(&xr, wco, NULL, col, is_weak ? 0 : MEOW_SB_ICON_INK, ty); + sb_r_place(&xr, wco, NULL, col, is_weak ? 0 : MEOW_SB_ICON_INK, ty, ycy); } if (g_sys.brightness > 0) { /* 亮度:图标+%(>0 才显示;与时钟不重叠才画) */ int bl = (g_sys.brightness > 100) ? 100 : g_sys.brightness; snprintf(val, sizeof val, "%d%%", bl); int bw = MEOW_SB_ICON_SIZE + MEOW_SB_TGAP + meow_ui_text_width(val, MUI_SBAR_FONT) + MEOW_SB_XGAP; if (xr - bw > 8 + meow_ui_text_width(timebuf, MUI_SBAR_FONT) + MEOW_SB_CLK_MARGIN) - sb_r_place(&xr, MEOW_SB_ICON_BRIGHT, val, col, MEOW_SB_ICON_INK, ty); + sb_r_place(&xr, MEOW_SB_ICON_BRIGHT, val, col, MEOW_SB_ICON_INK, ty, ycy); } - /* 分隔线(栏内最底 1px,主题次要色);内容 inset=statusbar_height()=28 从 y=28 起即干净 */ - meow_ui_fill_rect(0, h - 1, w, 1, th->dim); + /* 分隔线(栏内最底 1px,主题次要色);上移后仍在可见条高内;内容 inset=statusbar_height() 从该线之下即干净 */ + int dy = h - 1 - yoff; + if (dy >= 0 && dy < vis) meow_ui_fill_rect(0, dy, w, 1, th->dim); g_statusbar_force = 0; } diff --git a/meow_ui_sys.h b/meow_ui_sys.h index 3a58d6b..4acfaa3 100644 --- a/meow_ui_sys.h +++ b/meow_ui_sys.h @@ -76,4 +76,13 @@ int meow_ui_sys_statusbar_force(void); /* 把状态栏画进 back_buf(顶部 MUI_SBAR_H 行)+ 标脏。调用方负责随后上屏。 */ void meow_ui_sys_statusbar_render(void); +/* 收合「上移出屏」过渡渲染(仅系统进程/launcher,见 meow_ui_sys_statusbar_render):内容整体上移 + * yoff 像素并裁顶;只画可见条高 [0, MUI_SBAR_H-yoff) 行,其余行保留(App 内容由 launcher 标脏切齐)。 + * yoff=0 等价 meow_ui_sys_statusbar_render()。实现见 meow_ui_sys.c。 */ +void meow_ui_sys_statusbar_render_off(int yoff); + +/* 把当前顶条模式写到 /tmp/meowapp_sbar(仅 App 进程 !g_sys_render 才写;launcher 读)。 + * meow_ui_statusbar_set_collapsed(gfx.c)也调用它,故在头文件声明。 */ +void meow_ui_sbar_write_mode(void); + #endif /* MEOW_UI_SYS_H */