Files
MeowOS-SDK/meow_ui_sys.c
T

229 lines
7.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 <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"
/* 蓝牙 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 写同文件)。
* 与 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);
}
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);
/* 文字垂直锚 y:(栏高-行高)/2 居中;下限 1 防极端行高 > 栏高时负 y 溢出顶端(不越界优先)。 */
int ty = (h - meow_ui_text_height(MUI_SBAR_FONT)) / 2;
if (ty < 1) ty = 1;
/* 时钟(左):每秒现取 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);
/* 右侧信息串:batt% WIFI 蓝牙 VOL nn% */
char right[96];
statusbar_right_str(right, sizeof right);
if (right[0])
meow_ui_text_right(w - 8, ty, right, MUI_SBAR_FONT, th->fg);
/* 分隔线(栏内最底 1px,主题次要色);内容 inset=statusbar_height()=28 从 y=28 起即干净 */
meow_ui_fill_rect(0, h - 1, w, 1, th->dim);
g_statusbar_force = 0;
}