git-subtree-dir: sdk git-subtree-mainline:17a972d46fgit-subtree-split:2e7561d74f
401 lines
19 KiB
C
401 lines
19 KiB
C
/*
|
||
* meow_ui_sys.c — SDK 内置系统状态读取器 + 全局状态栏(A-0 核心)。
|
||
*
|
||
* 职责:
|
||
* 1. 状态读取器:读 /sys/class/power_supply/(电量/充电)、WiFi operstate、
|
||
* 蓝牙实上电、音量(/usr/data/meow/settings.conf 的 audio.volume,与 launcher
|
||
* 同源单点);时钟在绘制时现取 localtime。1s 轻刷,蓝牙 30s 缓存。
|
||
* 2. 全局状态栏绘制:把时钟/电量/WiFi/蓝牙/音量画进 back_buf 顶部 MUI_SBAR_H 行;
|
||
* 由渲染后端 present() 前置调用(z 最顶),非全屏可见。
|
||
* 3. 显隐/全屏状态 + 公共 API(meow_ui_set_statusbar / set_fullscreen / ...)。
|
||
*
|
||
* 注意:所有读取在事件循环主线内做(无独立线程),1s 频率同步读取 sysfs/小文件,
|
||
* 开销可忽略;蓝牙 hciconfig 为 popen(spawn) 较贵,仅 30s 刷一次并缓存。
|
||
*/
|
||
#include "meow_ui_sys.h"
|
||
#include "meow_ui_internal.h"
|
||
#include "meow_ui_vol.h"
|
||
#include "meow_notify_ipc.h" /* meow_notify_ipc_notif_open:App 前台 #10.5 上报 */
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <time.h>
|
||
|
||
#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
|
||
|
||
static meow_sys_state g_sys;
|
||
static int g_sys_inited = 0;
|
||
|
||
/* 显隐/全屏状态(公共 API 与绘制/输入共用) */
|
||
static int g_statusbar_on = 1; /* 默认显示 */
|
||
static int g_fullscreen = 0; /* 默认非全屏 */
|
||
static int g_statusbar_force = 0; /* 1s 刷新后置位,强制重画顶部条 */
|
||
/* 「同一个状态栏」闭环(§3.1/FRAMEWORK_DESIGN §10.2):状态栏 = 系统级组件,仅系统进程
|
||
* (launcher) 渲染。g_sys_render=1 表示本进程是系统 UI —— meow_ui_sys_statusbar_render /
|
||
* present() 据此决定是否画出顶条/提示线;App 进程保持 0(不渲染)+ 仍 inset 28(launcher 叠加)。 */
|
||
static int g_sys_render = 0; /* 1=本进程为系统 UI(渲染);0=App(不渲染,仅 inset) */
|
||
|
||
static uint64_t now_ms(void)
|
||
{
|
||
struct timespec ts;
|
||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
|
||
}
|
||
|
||
/* 读一份 sysfs/文件字符串(剥尾随换行);失败回退 "" */
|
||
static void read_str(const char *path, char *buf, size_t n)
|
||
{
|
||
buf[0] = 0;
|
||
int fd = open(path, O_RDONLY);
|
||
if (fd < 0) return;
|
||
ssize_t r = read(fd, buf, n - 1);
|
||
close(fd);
|
||
if (r <= 0) { buf[0] = 0; return; }
|
||
buf[r] = 0;
|
||
while (r > 0 && (buf[r-1] == '\n' || buf[r-1] == '\r')) buf[--r] = 0;
|
||
}
|
||
|
||
static int read_int(const char *path)
|
||
{
|
||
char buf[24];
|
||
read_str(path, buf, sizeof buf);
|
||
if (!buf[0]) return -1;
|
||
return atoi(buf);
|
||
}
|
||
|
||
static int hci_powered(void)
|
||
{
|
||
FILE *p = popen("hciconfig -a hci0 2>/dev/null", "r");
|
||
if (!p) return 0;
|
||
int up = 0;
|
||
char buf[256];
|
||
while (fgets(buf, sizeof buf, p)) {
|
||
char *s = buf, *tok;
|
||
while ((tok = strsep(&s, " \t\r\n")) != NULL) {
|
||
if (!strcmp(tok, "UP")) { up = 1; break; }
|
||
}
|
||
}
|
||
pclose(p);
|
||
return up;
|
||
}
|
||
|
||
/* 音量:读 settings.conf 的 audio.volume(launcher 同源,watchdog 写同文件)。
|
||
* 与 launcher 口径一致:0-100,且尊重 audio.volume_max 上限 clamp(vol_clamp 同义)。
|
||
* 注意 settings 落盘为 "key = value"(带空格),必须按 '=' 切分解析,不能朴素前缀匹配——
|
||
* 否则永远读不到(回退默认 60,即「状态栏音量数字不对、卡在 60」的根因)。 */
|
||
static int settings_vol(void)
|
||
{
|
||
int vol = meow_ui_vol_read(MEOW_SETTINGS_FILE, "audio.volume", MEOW_UI_VOL_DEFAULT);
|
||
int vol_max = meow_ui_vol_read(MEOW_SETTINGS_FILE, "audio.volume_max", MEOW_UI_VOL_MAX_DEF);
|
||
return meow_ui_vol_clamp(vol, vol_max);
|
||
}
|
||
|
||
/* 亮度:读 settings.conf 的 display.brightness(launcher 同源),并 clamp 到
|
||
* [display.brightness_min, 100](与 launcher clamp_brightness 同口径,下限默认 1=极暗非全黑)。
|
||
* Stage-One 状态栏收敛(launcher 退役自绘状态栏)后,launcher 侧用 meow_ui_sys_feed() 喂
|
||
* 即时值(写入方=launcher);此处兜底让 App(meow_ui_run 的 1s 轻刷)也读到并显示亮度,
|
||
* 保证「launcher / App 同一实现、同日/夜」。 */
|
||
static int settings_bright(void)
|
||
{
|
||
int v = meow_ui_vol_read(MEOW_SETTINGS_FILE, "display.brightness", 101);
|
||
int mn = meow_ui_vol_read(MEOW_SETTINGS_FILE, "display.brightness_min", 1);
|
||
if (v < mn) v = mn;
|
||
if (v > 100) v = 100;
|
||
return v;
|
||
}
|
||
|
||
int meow_ui_sys_init(void)
|
||
{
|
||
memset(&g_sys, 0, sizeof g_sys);
|
||
g_sys.batt = -1;
|
||
g_sys.vol = settings_vol();
|
||
g_sys.brightness = settings_bright();
|
||
g_statusbar_on = 1;
|
||
g_fullscreen = 0;
|
||
g_statusbar_force = 1;
|
||
meow_ui_sys_refresh();
|
||
g_sys_inited = 1;
|
||
return 0;
|
||
}
|
||
|
||
void meow_ui_sys_refresh(void)
|
||
{
|
||
g_sys.batt = read_int("/sys/class/power_supply/battery/capacity");
|
||
if (g_sys.batt < 0 || g_sys.batt > 100) g_sys.batt = -1;
|
||
char st[16];
|
||
/* 充电态权威源(真机实测 2026-09-01:battery/status 恒 Discharging 撒谎):
|
||
* 1) mp2731-charger/status(Charging/Full=充电中);2) 回退 fusb302-typec-source/online==1
|
||
* (USB-C PD 插线);3) 再回退旧逻辑 battery/status==Charging || ac/online==1。 */
|
||
read_str("/sys/class/power_supply/mp2731-charger/status", st, sizeof st);
|
||
if (st[0]) {
|
||
g_sys.charging = (strstr(st, "Charg") || strstr(st, "Full")) ? 1 : 0;
|
||
} else {
|
||
int onl = read_int("/sys/class/power_supply/fusb302-typec-source/online");
|
||
if (onl >= 0) {
|
||
g_sys.charging = (onl == 1) ? 1 : 0;
|
||
} else {
|
||
read_str("/sys/class/power_supply/battery/status", st, sizeof st);
|
||
g_sys.charging = (strstr(st, "Charg") || strstr(st, "Full")) ? 1 : 0;
|
||
if (!g_sys.charging && read_int("/sys/class/power_supply/ac/online") == 1)
|
||
g_sys.charging = 1;
|
||
}
|
||
}
|
||
|
||
char op[16];
|
||
read_str("/sys/class/net/wlan0/operstate", op, sizeof op);
|
||
g_sys.wifi_connected = !strcmp(op, "up") ? 1 : 0;
|
||
g_sys.wifi_on = g_sys.wifi_connected; /* SDK 自身读不到射频 vs 关联的区分,一起走 operstate */
|
||
g_sys.wifi_level = -1000; /* 信号分档由 launcher 喂(唯一权威源);SDK 不读 /proc 无线 */
|
||
|
||
g_sys.vol = settings_vol();
|
||
g_sys.brightness = settings_bright(); /* 兜底读 settings;launcher 喂入会覆盖(更即时) */
|
||
|
||
/* 蓝牙:hci0 存在性检查便宜;上电状态贵(hciconfig spawn),30s 缓存。 */
|
||
static uint64_t last_bt_ms = 0;
|
||
uint64_t n = now_ms();
|
||
g_sys.bt_seen = access("/sys/class/bluetooth/hci0", F_OK) == 0;
|
||
if (!g_sys.bt_seen) {
|
||
g_sys.bt_on = 0;
|
||
last_bt_ms = n;
|
||
} else if (!last_bt_ms || (n - last_bt_ms) >= BT_REFRESH_MS) {
|
||
g_sys.bt_on = hci_powered();
|
||
last_bt_ms = n;
|
||
}
|
||
|
||
/* 时钟每秒变 → 强制顶条重画(时间在绘制时现取) */
|
||
g_statusbar_force = 1;
|
||
}
|
||
|
||
const meow_sys_state *meow_ui_sys_get(void) { return &g_sys; }
|
||
|
||
/* ---- 系统进程(launcher)喂入权威快照(Stage-One 状态栏收敛)----
|
||
* launcher 不跑 SDK 事件循环,其 g_sys(meow_sys_refresh/light + 事件路径维护)是权威数据源
|
||
* (尤其亮度/音量,写入方=launcher)。此处把它覆盖进 SDK 快照并置 force,驱动下一次
|
||
* present() 重画顶条。不喂的字段保持 SDK 现有值(由 meow_ui_sys_refresh 同源兜底)。 */
|
||
void meow_ui_sys_feed(const meow_ui_feed *f)
|
||
{
|
||
if (!f) return;
|
||
g_sys.batt = f->batt;
|
||
g_sys.charging = f->charging;
|
||
g_sys.wifi_on = f->wifi_on;
|
||
g_sys.wifi_connected = f->wifi_connected;
|
||
g_sys.wifi_level = f->wifi_level;
|
||
g_sys.bt_seen = f->bt_seen;
|
||
g_sys.bt_on = f->bt_on;
|
||
g_sys.vol = f->vol;
|
||
if (f->brightness <= 0) g_sys.brightness = 0; /* 未知/屏关:不入顶条(>0 才显示) */
|
||
else if (f->brightness > 100) g_sys.brightness = 100;
|
||
else g_sys.brightness = f->brightness;
|
||
g_statusbar_force = 1; /* 数据已变 → 顶条必重画 */
|
||
}
|
||
|
||
/* ---- 系统 UI 模式(「同一个状态栏」闭环;launcher 调用)----
|
||
* launcher(系统进程)链 meow_ui.a 后调 meow_ui_sys_set_system_mode(1):本进程渲染全局状态栏
|
||
* (present() 前置合成 + App 前台叠加)。App 进程默认 0:不渲染状态栏、只 inset(§6.1 ③ /
|
||
* FRAMEWORK_DESIGN §10.2)。meow_ui_sys_statusbar_active()/meow_ui_statusbar_height() 不随此
|
||
* 开关变(App 仍报告 28px inset + 顶部热区),仅渲染路径受控。 */
|
||
void meow_ui_sys_set_system_mode(int on)
|
||
{
|
||
g_sys_render = on ? 1 : 0;
|
||
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;
|
||
}
|
||
|
||
/* ---- 公共 API ---- */
|
||
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)
|
||
{
|
||
return g_statusbar_on && !g_fullscreen;
|
||
}
|
||
|
||
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)
|
||
{
|
||
if (!meow_ui_sys_statusbar_active()) return 0;
|
||
return meow_ui_statusbar_collapsed() ? MUI_SBAR_COLLAPSE_H : MUI_SBAR_H;
|
||
}
|
||
|
||
/* ---- 内部(gfx/input 用) ---- */
|
||
int meow_ui_sys_statusbar_active(void)
|
||
{
|
||
/* collapsed 视为状态栏仍激活(只留提示线):与 fullscreen 并存(全屏启 collapsed)——
|
||
* 这样 gfx.c present() 的收合动画块会运行(驱动 g_sbar_collapse_h 插值),且顶部提示线
|
||
* 正常绘制/热区可用。fullscreen 且不 collapsed → 整条隐藏(沉浸,inset=0),保持既有语义。 */
|
||
return g_statusbar_on && (meow_ui_statusbar_collapsed() || !g_fullscreen);
|
||
}
|
||
|
||
int meow_ui_sys_statusbar_height(void)
|
||
{
|
||
if (!meow_ui_sys_statusbar_active()) return 0;
|
||
return meow_ui_statusbar_collapsed() ? MUI_SBAR_COLLAPSE_H : MUI_SBAR_H;
|
||
}
|
||
|
||
void meow_ui_sys_notif_pull(int origin_y)
|
||
{
|
||
/* 系统级「下拉展开通知中心」手势接线(FRAMEWORK_DESIGN §10.5):输入层识别出「起始于
|
||
* 顶部热区的 SWIPE_DOWN」后路由到此。App 进程只做检测+上报(不发事件给 on_swipe),
|
||
* 通知中心仍由 launcher 渲染——此处经 IPC 发 MSG_NOTIF_OPEN 请求 launcher 展开叠加会话
|
||
* (SIGSTOP App + 读回其帧 + 叠加渲染通知中心)。best-effort:无 socket/launcher 不在
|
||
* → meow_notify_ipc_notif_open 静默降级。 */
|
||
static uint32_t seq = 0;
|
||
if (origin_y < 0) origin_y = 0;
|
||
meow_notify_ipc_notif_open(NULL, ++seq, 0, (uint16_t)origin_y,
|
||
(uint16_t)meow_ui_statusbar_hotzone(),
|
||
(uint8_t)(g_fullscreen ? 1 : 0));
|
||
}
|
||
|
||
int meow_ui_sys_statusbar_force(void)
|
||
{
|
||
int f = g_statusbar_force;
|
||
g_statusbar_force = 0;
|
||
return f;
|
||
}
|
||
|
||
/* ---- 右侧信息簇(MD3 图标 + 变量值,取代文本式 statusbar_right_str)----
|
||
* 「同一个状态栏」右侧 = 图标+值:电量图标+% / 音量图标+% / 蓝牙图标 / WiFi 图标(信号分档)/
|
||
* 亮度图标+%(可选,与时钟不重叠才画)。图标位图来自 sb_icons.h(复用 launcher);
|
||
* 文字=紧凑数值(%d%%),无 CJK 依赖;颜色随 meow_theme(日/夜)由主题表提供。
|
||
* 顺序(右→左):电量(最右)→ 音量 → 蓝牙 → WiFi → 亮度。 */
|
||
/* 注:旧文本式右侧串 statusbar_right_str 已退役为图标簇(同上),不再保留调用路径。 */
|
||
|
||
/* 画一个右对齐项:右边界=*xr,画完向左推进 *xr(含 MEOW_SB_XGAP 项间隙)。
|
||
* icon>=0 画图标(可配右侧数值 txt;txt==NULL 仅图标)。文字/图标同用主题色 col。
|
||
* ink_h 传给 meow_gfx_blit_icon(主图标用 MEOW_SB_ICON_INK 等大;WiFi 强弱档传 0 保持自然 ink)。
|
||
* 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, 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;
|
||
}
|
||
|
||
/* WiFi 信号分档(g_sys.wifi_connected + wifi_level)→ 图标索引。
|
||
* 网卡关 → -1(不画);开未关联 → SB_I_WIFI_NO;已关联 → 按 dBm 分 STRONG/MEDIUM/WEAK(未知→满格)。 */
|
||
static int sb_wifi_icon(void)
|
||
{
|
||
if (!g_sys.wifi_on) return MEOW_SB_ICON_WIFI_OFF;
|
||
if (!g_sys.wifi_connected) return MEOW_SB_ICON_WIFI_NO;
|
||
if (g_sys.wifi_level <= -1000) return MEOW_SB_ICON_WIFI_STRONG; /* 未知 → 满格 */
|
||
if (g_sys.wifi_level >= -60) return MEOW_SB_ICON_WIFI_STRONG;
|
||
if (g_sys.wifi_level >= -75) return MEOW_SB_ICON_WIFI_MEDIUM;
|
||
return MEOW_SB_ICON_WIFI_WEAK;
|
||
}
|
||
|
||
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(提示线)处理 */
|
||
|
||
/* 整条清底(仅可见条高 [0,vis) 行):覆盖 app 可能画进顶行的内容(z 最顶)。仅系统进程渲染本函数。 */
|
||
meow_ui_fill_rect(0, 0, w, vis, th->bg);
|
||
|
||
/* 文字垂直锚 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];
|
||
time_t t = time(NULL);
|
||
struct tm tm;
|
||
localtime_r(&t, &tm);
|
||
snprintf(timebuf, sizeof timebuf, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||
meow_ui_text(8, ty, timebuf, MUI_SBAR_FONT, th->fg);
|
||
|
||
/* 右侧 MD3 图标簇(从右端 w-8 往左)。主项色 th->fg;次级(蓝牙/弱信号)用 on_surface_variant。
|
||
* 亮度「可选」:仅在右簇左缘与时钟右缘相距足够(MEOW_SB_CLK_MARGIN)时画,避免与时钟叠字。 */
|
||
uint32_t col = th->fg, col2 = th->dim; /* col=主色;col2=次要色(蓝牙/分隔用 on_surface_variant=dim 近似) */
|
||
int xr = w - 8;
|
||
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, ycy);
|
||
if (g_sys.charging) /* 充电中:电量图标左邻加闪电(M2.92) */
|
||
sb_r_place(&xr, MEOW_SB_ICON_BATT_CHG, NULL, 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, 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, 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, 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, ycy);
|
||
}
|
||
|
||
/* 分隔线(栏内最底 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;
|
||
}
|