• 全新单提交:清除含敏感信息的旧历史(sudo 口令、内网 IP、SSH 密钥路径、热点 SSID、设备 MAC) • 本地开发文档/测试记录/构建脚本全部移入 gitignored 的 devdocs/ 与 scripts/build/(不入库) • 保留:MeowOS 源码(launcher/watchdog/bootmenu/meow_svcd/SDK)、项目规范文档、mips-repo 增补组件:SDK 统一日志+通知 API、meow_svcd 服务管理器、mips-repo 软件仓库
232 lines
7.1 KiB
C
232 lines
7.1 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 <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"
|
||
#define VOL_DEFAULT 60
|
||
|
||
/* 蓝牙 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 刷新后置位,强制重画顶部条 */
|
||
|
||
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 写同文件)。 */
|
||
static int settings_vol(void)
|
||
{
|
||
FILE *f = fopen(MEOW_SETTINGS_FILE, "r");
|
||
if (!f) return VOL_DEFAULT;
|
||
int vol = VOL_DEFAULT;
|
||
char buf[128];
|
||
while (fgets(buf, sizeof buf, f)) {
|
||
if (!strncmp(buf, "audio.volume=", 13)) {
|
||
int v = atoi(buf + 13);
|
||
if (v >= 0 && v <= 100) vol = v;
|
||
break;
|
||
}
|
||
}
|
||
fclose(f);
|
||
return vol;
|
||
}
|
||
|
||
int meow_ui_sys_init(void)
|
||
{
|
||
memset(&g_sys, 0, sizeof g_sys);
|
||
g_sys.batt = -1;
|
||
g_sys.vol = settings_vol();
|
||
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];
|
||
read_str("/sys/class/power_supply/battery/status", st, sizeof st);
|
||
g_sys.charging = (strstr(st, "Charg") || strstr(st, "Full")) ? 1 : 0;
|
||
|
||
char op[16];
|
||
read_str("/sys/class/net/wlan0/operstate", op, sizeof op);
|
||
g_sys.wifi_on = !strcmp(op, "up") ? 1 : 0;
|
||
|
||
g_sys.vol = settings_vol();
|
||
|
||
/* 蓝牙: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; }
|
||
|
||
/* ---- 公共 API ---- */
|
||
void meow_ui_set_statusbar(int visible)
|
||
{
|
||
g_statusbar_on = visible ? 1 : 0;
|
||
g_statusbar_force = 1; /* 显隐切换也要重画顶条 */
|
||
}
|
||
|
||
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; /* 进出全屏立即反映(显示/隐藏顶条) */
|
||
}
|
||
|
||
int meow_ui_statusbar_height(void)
|
||
{
|
||
return meow_ui_sys_statusbar_active() ? MUI_SBAR_H : 0;
|
||
}
|
||
|
||
/* ---- 内部(gfx/input 用) ---- */
|
||
int meow_ui_sys_statusbar_active(void)
|
||
{
|
||
return g_statusbar_on && !g_fullscreen;
|
||
}
|
||
|
||
int meow_ui_sys_statusbar_height(void)
|
||
{
|
||
return meow_ui_sys_statusbar_active() ? MUI_SBAR_H : 0;
|
||
}
|
||
|
||
int meow_ui_sys_statusbar_force(void)
|
||
{
|
||
int f = g_statusbar_force;
|
||
g_statusbar_force = 0;
|
||
return f;
|
||
}
|
||
|
||
/* 状态栏右侧信息串:batt% WIFI 蓝牙/蓝牙 VOL nn%(同 launcher statusbar_right_str) */
|
||
static void statusbar_right_str(char *out, size_t n)
|
||
{
|
||
char batt[16] = "", wifi[16] = "", vol[20] = "", bt[16] = "";
|
||
if (g_sys.batt >= 0) snprintf(batt, sizeof batt, "%d%%", g_sys.batt);
|
||
if (g_sys.wifi_on) snprintf(wifi, sizeof wifi, "WIFI");
|
||
snprintf(vol, sizeof vol, "VOL %d%%", g_sys.vol);
|
||
if (g_sys.bt_seen && g_sys.bt_on)
|
||
snprintf(bt, sizeof bt, meow_gfx_mfont_ready() ? "蓝牙" : "BT");
|
||
out[0] = 0;
|
||
if (batt[0]) strcat(out, batt);
|
||
if (wifi[0]) { if (out[0]) strcat(out, " "); strcat(out, wifi); }
|
||
if (bt[0]) { if (out[0]) strcat(out, " "); strcat(out, bt); }
|
||
if (vol[0]) { if (out[0]) strcat(out, " "); strcat(out, vol); }
|
||
}
|
||
|
||
void meow_ui_sys_statusbar_render(void)
|
||
{
|
||
if (!meow_ui_sys_statusbar_active()) return;
|
||
const meow_theme *th = meow_theme_get();
|
||
int w = meow_ui_width();
|
||
int h = MUI_SBAR_H;
|
||
|
||
/* 整条清底:覆盖 app 可能画进顶行的内容(z 最顶) */
|
||
meow_ui_fill_rect(0, 0, w, h, th->bg);
|
||
|
||
/* 时钟(左):每秒现取 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, (h - meow_ui_text_height(MUI_SBAR_FONT)) / 2, timebuf, MUI_SBAR_FONT, th->fg);
|
||
|
||
/* 右侧信息串:batt% WIFI 蓝牙 VOL nn% */
|
||
char right[96];
|
||
statusbar_right_str(right, sizeof right);
|
||
if (right[0])
|
||
meow_ui_text_right(w - 8, (h - meow_ui_text_height(MUI_SBAR_FONT)) / 2, right, MUI_SBAR_FONT, th->fg);
|
||
|
||
/* 分隔线(栏内最底 1px,主题次要色);内容 inset=statusbar_height()=40 从 y=40 起即干净 */
|
||
meow_ui_fill_rect(0, h - 1, w, 1, th->dim);
|
||
|
||
g_statusbar_force = 0;
|
||
}
|