fix(statusbar): 全屏状态栏收合 — 上移出屏+停渲染+热区28保留+左缘指示条屏顶(agent-146)

- 根因: 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桩)
This commit is contained in:
MeowOS committed 2026-08-30 20:18:13 +08:00
1 parent 03962fffac
commit 1481ae00c4
8 files changed
+182 -32

No files matched your search

Binary file not shown.
Binary file not shown.
+85 -12
View File
@@ -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();
/* 「同一个状态栏」闭环:常驻顶条叠加(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();
}
usleep(100000);
} else if (now2 - last_sb_overlay_ms >= 1000) {
last_sb_overlay_ms = now2;
app_overlay_statusbar();
}
}
/* 顶条收合/恢复过渡期把循环睡得短些(~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();
+10 -2
View File
@@ -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,草案值) */
+21 -5
View File
@@ -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); /* 等垂直同步 */
+9
View File
@@ -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);
+48 -13
View File
@@ -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;
}
+9
View File
@@ -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 */