diff --git a/patch_v2/hello_app/hello b/patch_v2/hello_app/hello index bf190e4..723852b 100755 Binary files a/patch_v2/hello_app/hello and b/patch_v2/hello_app/hello differ diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index 2364438..63730e1 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meow_launcherd/meow_launcherd.c b/patch_v2/meow_launcherd/meow_launcherd.c index 28c9a49..39b3590 100644 --- a/patch_v2/meow_launcherd/meow_launcherd.c +++ b/patch_v2/meow_launcherd/meow_launcherd.c @@ -3035,6 +3035,7 @@ static void meow_sys_light(void) #define SB_BAR_Y 0 #define SB_BAR_H 28 /* rows 0..27;分隔线在 y=28 不在此(40→28,用户定) */ #define SB_FONT 20 /* 状态栏字体 28→20(28px 栏放不下 28 号;垂直居中锚点见 sb_text_y) */ +#define MEOW_SBAR_ANIM_MS 200 /* App 前台全屏/收合切换的顶条「上移出屏」过渡时长(ms,agent-146) */ #if 0 /* [RETIRED Stage-One] 旧自绘状态栏的时钟/右侧包围盒变量,仅供 sb_redraw 用,现已退役 */ static int g_sb_clock_x, g_sb_clock_y, g_sb_clock_w, g_sb_clock_h; static int g_sb_right_x, g_sb_right_y, g_sb_right_w, g_sb_right_h; @@ -8884,14 +8885,52 @@ static void meow_write_app_state(int immersive) /* ---- App 前台状态栏叠加(「同一个状态栏」闭环,FRAMEWORK_DESIGN §10.2 / MEOW_APP_SDK §3.1)---- * App 前台时状态栏由 launcher(系统进程)作为**常驻**系统叠加层绘制顶条 28px(App 进程不渲染、 * 只 inset,§6.1 ③)。本函数由 launch_app 的 waitpid 循环定时调用: - * 1) 刷新权威数据 meow_sys_light()(电池/WiFi/音量/蓝牙)+ 喂 SDK → 唯一数据源(主界面与 App 一致); - * 2) 标脏顶条 + meow_ui_flush() → SDK present() 在上屏前把全局状态栏画进 back_buf 顶部 MUI_SBAR_H - * 行并原子 blit 到 buffer0(仅顶条); - * 3) 只 blit 顶条(不碰 y>=28 的 App 内容)→ 不覆盖 App、不 SIGSTOP App(App 继续播/响应)。 - * 与通知中心/电源菜单(SIGSTOP + 全屏叠画)的差异:状态栏常驻、非模态、只碰顶条。 */ + * 1) 读 App 顶条模式(/tmp/meowapp_sbar,App 进程 SDK 写)→ 感知全屏/收合态(agent-146); + * 2) 刷新权威数据 meow_sys_light()(电池/WiFi/音量/蓝牙)+ 喂 SDK → 唯一数据源(主界面与 App 一致); + * 3) 标脏「当前可见条高」+ meow_ui_flush() → SDK present() 画顶条/收合提示线并原子 blit(仅可见条高); + * 4) 只 blit 可见条高(收合时其余行=App 内容,保留)→ 不覆盖 App、不 SIGSTOP App(App 继续播/响应)。 + * 与通知中心/电源菜单(SIGSTOP + 全屏叠画)的差异:状态栏常驻、非模态、只碰顶条。 + * 全屏/收合:见「全屏状态栏收合(agent-146)」——收合只叠提示线(无时钟/图标/值),热区事件与渲染分离。 */ + +/* App 前台顶条模式文件(App 进程 SDK 写,launcher 读;与 /tmp/meowapp_state 语义独立)。 + * 值:'0'=展开 28px 整条;'1'=收合(提示线);'2'=隐藏(沉浸 inset0)。 */ +#define MEOW_SBAR_MODE_FILE "/tmp/meowapp_sbar" + +/* 上次读到的 App 顶条模式(用于在 launch_app waitpid 循环里判「模式新变 → 立即叠一次」)。 + * 高整文件作用域;每次启动 App 前重置为 -1(强制首帧同步一次)。 */ +static int g_sb_mode_last = -1; + +/* 读 App 顶条模式:0=展开 1=收合 2=隐藏;-1=无文件/读取失败(按展开处理,默认安全)。 */ +static int app_sbar_mode_read(void) +{ + int fd = open(MEOW_SBAR_MODE_FILE, O_RDONLY); + if (fd < 0) return -1; + char ch[8]; + int n = read(fd, ch, sizeof ch - 1); + close(fd); + if (n <= 0) return -1; + if (ch[0] == '1') return 1; + if (ch[0] == '2') return 2; + return 0; +} + static void app_overlay_statusbar(void) { if (!back_buf_ok || !fb) return; /* 无 SDK back_buf(直写模式):无法叠加,跳过 */ + int mode = app_sbar_mode_read(); + if (mode == 2) { /* 全屏隐藏(immersive,inset=0):launcher 不叠顶条 */ + meow_ui_set_fullscreen(1); + meow_ui_statusbar_set_collapsed(0); + return; /* 无内容可叠:交 App 全屏(其 present inset0 已写满屏) */ + } + if (mode == 1) { /* 收合:SDK 切收合(上移过渡 + 提示线),无时钟/图标/值 */ + meow_ui_set_fullscreen(0); + meow_ui_statusbar_set_collapsed(1); + meow_ui_statusbar_animate(MEOW_SBAR_ANIM_MS); /* 平滑上移/恢复过渡时长 */ + } else { /* 展开(默认/无文件):整条 28px */ + meow_ui_set_fullscreen(0); + meow_ui_statusbar_set_collapsed(0); + } meow_sys_light(); /* 刷新 g_sys(电池/WiFi/音量/蓝牙;唯一权威源) */ meow_theme_reload(); /* SDK 状态栏主题随 launcher 换肤(重读 settings) */ meow_ui_feed f; @@ -8905,8 +8944,14 @@ static void app_overlay_statusbar(void) f.vol = g_sys.vol; f.brightness = g_sys.brightness; meow_ui_sys_feed(&f); /* 喂权威快照 + 置 force(顶条必重画) */ - meow_dirty_add(0, 0, fb_xres, SB_BAR_H); /* 标脏顶条 → present() 时 SDK 重画状态栏(仅顶条) */ - meow_ui_flush(); /* SDK present:vsync + 原子 blit 脏区(仅顶条) */ + /* 撤销 set_collapsed 可能引发的「整帧脏」:launcher 在 App 上叠加只应 blit 顶条,绝不能整帧 blit + * (否则会把自身 back_buf 的 stale 菜单内容盖到 App 内容上,属灾难性回归——agent-146)。 + * 随后只标「心跳」脏(顶条左上角 1px)唤醒 present();实际 blit 区由 SDK 渲染在 present() 内标脏决定 + * (render_off 画 [0, 28-off) 可见条高 / 收合稳定画 [0,4) 提示线 / 展开画 [0,28) 整条),故上移时 + * 只 blit 可见条高、其余行保留 App 内容 —— 平滑上收无残留、不黑框。 */ + meow_gfx_dirty_reset(); + meow_dirty_add(0, SB_BAR_Y, 1, 1); + meow_ui_flush(); /* SDK present:vsync + 原子 blit(区随渲染标脏) */ } static int launch_app(struct app *a); @@ -9127,8 +9172,11 @@ static int launch_app(struct app *a) memcpy(save_keys, kp_keys.cur, sizeof save_keys); /* 「同一个状态栏」闭环:App 前台状态栏由 launcher 常驻叠加(顶条 28px、非模态、不 SIGSTOP App)。 * 立即叠一次(App 首帧前顶条就有),其后每 ~1s 重画(时钟每秒变 + 数据新鲜)。app 级 - * fullscreen 声明(沉浸,整条隐藏)时不叠。 */ + * fullscreen 声明(沉浸,整条隐藏)时不叠。agent-146:读 App 顶条模式(meowapp_sbar),App 运行时 + * 全屏/收合 → launcher 顶条切收合提示线(无时钟/图标/值),热区事件与渲染分离。 */ unsigned long long last_sb_overlay_ms = 0; + g_sb_mode_last = -1; /* 强制首帧同步(进入新 App 会话清零,避免沿用上一 App 的缓存态) */ + unlink(MEOW_SBAR_MODE_FILE); /* 清可能残留的上一 App 顶条模式(崩溃/被杀未清),防新 App 误收合 */ if (!a->fullscreen) app_overlay_statusbar(); /* M2.24:清上一次会话可能残留的电源菜单标记,只认本次新 watchdog 生成的请求。 */ unlink(POWMENU_MARKER); @@ -9226,12 +9274,29 @@ static int launch_app(struct app *a) app_last_input = now_ms(); /* M2.17:菜单交互即输入,重置应用会话自动息屏计时 */ if (pm_ret != 0) break; /* 关机/重启/回HibyOS:系统通常已 reboot,防御性退出 */ } - /* 「同一个状态栏」闭环:常驻顶条每 ~1s 重画(时钟/数据新鲜;仅标脏顶条上屏,不碰 App 内容)。 */ - if (!a->fullscreen && now_ms() - last_sb_overlay_ms >= 1000) { - last_sb_overlay_ms = now_ms(); - app_overlay_statusbar(); + /* 「同一个状态栏」闭环:常驻顶条叠加(agent-146 全屏收合接入)。 + * - 展开态每 ~1s 慢刷(时钟/数据新鲜;仅标脏顶条上屏,不碰 App 内容); + * - App 顶条模式新变(进入/退出全屏收合)→ 立即叠一次(即时切换,消除「半秒闪」); + * - 收合/恢复过渡期(meow_ui_statusbar_animating)每 ~30ms 快刷驱动平滑上移出屏。 */ + if (!a->fullscreen) { + unsigned long long now2 = now_ms(); + int m = app_sbar_mode_read(); + if (m != g_sb_mode_last) { + g_sb_mode_last = m; + last_sb_overlay_ms = now2; + app_overlay_statusbar(); + } else if (meow_ui_statusbar_animating()) { + if (now2 - last_sb_overlay_ms >= 30) { + last_sb_overlay_ms = now2; + app_overlay_statusbar(); + } + } else if (now2 - last_sb_overlay_ms >= 1000) { + last_sb_overlay_ms = now2; + app_overlay_statusbar(); + } } - usleep(100000); + /* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */ + usleep(meow_ui_statusbar_animating() ? 20000 : 100000); } if (wpid > 0) { /* reap the watchdog: SIGTERM then wait up to 2s (it exits when the @@ -9244,6 +9309,14 @@ static int launch_app(struct app *a) } } meow_write_app_state(-1); /* GESTURE_BACK §2.4:应用退出,清 immersive 状态 */ + /* agent-146:App 退出 → 清顶条模式文件 + 复位 launcher SDK 收合/全屏态(菜单恢复整条 28px)。 + * 模式文件由 App 进程 SDK 写;此处 launcher 在退出时清理,防下一 App 读到残留 '1'(收合) 误收合。 */ + unlink(MEOW_SBAR_MODE_FILE); + g_sb_mode_last = -1; + meow_ui_statusbar_set_collapsed(0); + meow_ui_statusbar_animate(0); /* 回菜单:顶条恢复整条,瞬切(不做上移动画) */ + meow_ui_set_fullscreen(0); + meow_dirty_add(0, SB_BAR_Y, fb_xres, SB_BAR_H); /* 菜单状态栏需重画整条(恢复展开) */ /* M2.24:应用运行期音量由 watchdog 写(settings.conf 同源)。返回菜单前 re-load, * 让菜单音量/状态栏与真实 DAC 一致(watchdog 可能已改)。 */ settings_load(); diff --git a/patch_v2/sdk/meow_ui.h b/patch_v2/sdk/meow_ui.h index bcb94b7..9b89ba3 100644 --- a/patch_v2/sdk/meow_ui.h +++ b/patch_v2/sdk/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/patch_v2/sdk/meow_ui_gfx.c b/patch_v2/sdk/meow_ui_gfx.c index b23c363..4eb9c3c 100644 --- a/patch_v2/sdk/meow_ui_gfx.c +++ b/patch_v2/sdk/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/patch_v2/sdk/meow_ui_internal.h b/patch_v2/sdk/meow_ui_internal.h index 67cb73b..361e769 100644 --- a/patch_v2/sdk/meow_ui_internal.h +++ b/patch_v2/sdk/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/patch_v2/sdk/meow_ui_sys.c b/patch_v2/sdk/meow_ui_sys.c index ea70d87..aea79f7 100644 --- a/patch_v2/sdk/meow_ui_sys.c +++ b/patch_v2/sdk/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/patch_v2/sdk/meow_ui_sys.h b/patch_v2/sdk/meow_ui_sys.h index 3a58d6b..4acfaa3 100644 --- a/patch_v2/sdk/meow_ui_sys.h +++ b/patch_v2/sdk/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 */