基座+设计文档(三特性): meow_ui.h 字体档/状态栏收合API + launcher fonts/fullscreen解析+env注入+通知dump钩子 + meow_ui_gfx.c 收合渲染+字体档; spec-ref/SYSTEM_SETTINGS/APPS/NOTIFICATION 三特性设计落档

This commit is contained in:
MeowOS Dev committed 2026-08-30 11:28:11 +08:00
1 parent 9b8a1e5c8b
commit 0b4d1d3215
8 files changed
+297 -25

No files matched your search

Binary file not shown.
+102 -15
View File
@@ -915,6 +915,20 @@ static int meow_font_effective_paths(char *out_png, int plen, char *out_idx, int
return -1;
}
/* 【tier 选择入口,font 域占位】按字体档位取「当前生效字体」png/idx,供 launch_app 注入
* MEOW_FONT_PNG/IDX/APP(配合 MEOW_FONT_TIER)。基座只实现 tier=COMMON(=meow_font_effective_paths);
* tier>0 的大字库(GBK/Full)绝对路径由后续 font 代理据此演进填充(约定 MeowOS/fonts/<family>/
* <tier>/…,或 settings 键 font.tier_gbk / font.tier_full —— 见 MEOW_APP_SDK.md 字体分层)。
* 未实现前暂回退全局字体。返回 0 成功,-1 无可用。 */
static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
char *out_idx, int ilen, char *out_family, int flen)
{
if (tier == MEOW_FONT_TIER_COMMON || tier < MEOW_FONT_TIER_COMMON)
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
/* TODO(font 代理):按 tier 解析大字库绝对路径(GBK/Full);暂回退全局(tier0)。 */
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
}
/* M2.5x 多字体:从 settings 读 display.multi_font + 6 类 font.class_*,把各自相对路径按卡根
* 解析为绝对路径,去重装载到 meow_font 槽位(同 family 去重 & ≤4 套上限由
* meow_font_config_multifont 统一处理)。仅当全局字库已装载(mfont_ready / slot0)时生效;
@@ -1308,6 +1322,7 @@ struct meow_route {
char act[16]; /* route 键后缀,如 "nowplaying" */
char chain[MEOW_CHAIN_MAX]; /* 如 "home;search;nowplaying" */
int keep_awake; /* M2.17:该 activity 常亮(app.json awake.<id>=1 / route.<id>.keep_awake=1) */
int fullscreen; /* 活动级全屏声名(app.json route.<id>.fullscreen=1;M3 活动切换时消费) */
};
/* 服务行:service.<svc>=mode:restart:max:window_ms(M1 仅解析存储,M4 保活) */
@@ -1328,6 +1343,8 @@ struct app {
/* ---- 阶段 D · M1:app.json 路由/框架/服务/图标(scan_apps 解析)---- */
char framework; /* '1'=framework=true(看门狗返回发 SIGUSR1) */
char immersive; /* '1'=immersive=true(隐藏虚拟返回键,返回手势归 SDK) */
char fullscreen; /* '1'=app.json fullscreen=true(注入 MEOW_FULLSCREEN=1,SDK 预先设全屏) */
char fonts; /* 字库档:0=默认4K常用 1=大字库GBK 2=Full(app.json "fonts";注入 MEOW_FONT_TIER) */
char icon[129]; /* icon= 路径 */
int nroute; /* route.<id> 行数 */
struct meow_route routes[MEOW_MAX_ROUTES];
@@ -1805,6 +1822,8 @@ static void meow_parse_app_json(FILE *f, struct app *a)
char name[17] = "", exec[129] = "", icon[129] = "";
char package[MEOW_APP_ID_MAX] = "";
int fram = 0, immersive = 0;
int fullscreen = 0; /* app.json fullscreen=<true|1>(注入 MEOW_FULLSCREEN env,SDK 预先设全屏) */
int fonts = 0, fonts_set = 0; /* app.json "fonts":<档>(0=默认4K常用 1=大字库GBK 2=Full;缺省 0) */
struct app tmp;
memset(&tmp, 0, sizeof tmp);
while (fgets(buf, sizeof buf, f)) {
@@ -1816,8 +1835,8 @@ static void meow_parse_app_json(FILE *f, struct app *a)
char *rp = strstr(buf, "route.");
if (rp && tmp.nroute < MEOW_MAX_ROUTES) {
rp += 6; /* 跳过 "route." */
int idl = 0; char id[16] = "";
while (*rp && *rp != '"' && *rp != ':' && *rp != '=' && idl < 15)
int idl = 0; char id[32] = ""; /* 读满整键(识别 ".fullscreen" 后缀,cap 31) */
while (*rp && *rp != '"' && *rp != ':' && *rp != '=' && idl < 31)
id[idl++] = *rp++;
id[idl] = 0;
if (idl > 0) {
@@ -1826,16 +1845,30 @@ static void meow_parse_app_json(FILE *f, struct app *a)
if (*vp == ':' || *vp == '=') vp++;
while (*vp == ' ' || *vp == '\t') vp++;
if (*vp == '"') vp++;
int vl = 0; char chain[MEOW_CHAIN_MAX] = "";
while (*vp && *vp != '"' && *vp != ',' && *vp != '\n' &&
*vp != '\r' && vl < MEOW_CHAIN_MAX - 1)
chain[vl++] = *vp++;
chain[vl] = 0;
while (vl > 0 && (chain[vl - 1] == ' ' || chain[vl - 1] == '\t'))
chain[--vl] = 0;
strncpy(tmp.routes[tmp.nroute].act, id, 15);
strncpy(tmp.routes[tmp.nroute].chain, chain, MEOW_CHAIN_MAX - 1);
tmp.nroute++;
/* route.<id>.fullscreen=<on>:活动级全屏声明(M1 顺带;M3 活动切换时消费)。
* 命中既有 route.<id> 置 fullscreen;不新建 route(避免 id 被截断误建 "…fulls")。 */
if (idl >= 11 && !strcmp(id + idl - 11, ".fullscreen")) {
id[idl - 11] = 0; /* 去掉 ".fullscreen" 还原 act */
char v[8] = ""; int k = 0;
while (*vp && *vp != '"' && *vp != ',' && *vp != '\n' &&
*vp != '\r' && k < 7) v[k++] = *vp++;
v[k] = 0;
int on = (!strcmp(v, "1") || !strcmp(v, "true") || !strcmp(v, "yes"));
for (int i = 0; i < tmp.nroute; i++)
if (!strcmp(tmp.routes[i].act, id)) { tmp.routes[i].fullscreen = on; break; }
} else {
int vl = 0; char chain[MEOW_CHAIN_MAX] = "";
while (*vp && *vp != '"' && *vp != ',' && *vp != '\n' &&
*vp != '\r' && vl < MEOW_CHAIN_MAX - 1)
chain[vl++] = *vp++;
chain[vl] = 0;
while (vl > 0 && (chain[vl - 1] == ' ' || chain[vl - 1] == '\t'))
chain[--vl] = 0;
strncpy(tmp.routes[tmp.nroute].act, id, 15);
tmp.routes[tmp.nroute].act[15] = 0; /* 显式收尾(原依赖 id 读长 ≤15) */
strncpy(tmp.routes[tmp.nroute].chain, chain, MEOW_CHAIN_MAX - 1);
tmp.nroute++;
}
}
}
/* awake.<id>=1(M2.17 keep_awake:该 activity 常亮,超时不自动息屏)。
@@ -1881,6 +1914,29 @@ static void meow_parse_app_json(FILE *f, struct app *a)
immersive = 1;
}
}
/* fullscreen=true(app 级:注入 MEOW_FULLSCREEN=1,SDK 端 meow_ui_init 预先设全屏)。
* 与 immersive 是两回事:fullscreen 隐藏状态栏/沉浸,immersive 关虚拟返回键。 */
if (!fullscreen && strstr(buf, "fullscreen")) {
char v[8] = "";
if (json_str(buf, "fullscreen", v, sizeof v) > 0) {
if (!strcmp(v, "true") || !strcmp(v, "1") || !strcmp(v, "yes"))
fullscreen = 1;
}
}
/* "fonts":<档>(字库档:0=默认4K常用 1=大字库GBK 2=Full;可选,缺省 0)。
* launcher 注入 MEOW_FONT_TIER;大字库装载由后续 font 代理实现。 */
if (!fonts_set && strstr(buf, "fonts")) {
char v[8] = "";
if (json_str(buf, "fonts", v, sizeof v) > 0) {
if (!strcmp(v, "common") || !strcmp(v, "0")) fonts = 0;
else if (!strcmp(v, "gbk") || !strcmp(v, "1")) fonts = 1;
else if (!strcmp(v, "full") || !strcmp(v, "2")) fonts = 2;
else fonts = atoi(v); /* 数字兜底(越界收敛到 [0,2]) */
if (fonts < 0) fonts = 0;
if (fonts > 2) fonts = 2;
fonts_set = 1;
}
}
/* service.<svc>=mode:restart:max:window_ms(M1 解析存储,保活 M4) */
char *sp = strstr(buf, "service.");
if (sp && tmp.nsvc < MEOW_MAX_SVCS) {
@@ -1911,6 +1967,8 @@ static void meow_parse_app_json(FILE *f, struct app *a)
strncpy(a->package, package, sizeof a->package - 1);
a->framework = fram ? '1' : 0;
a->immersive = immersive ? '1' : 0;
a->fullscreen = fullscreen ? '1' : 0; /* 注入 MEOW_FULLSCREEN env */
a->fonts = (char)(fonts < 0 ? 0 : (fonts > 2 ? 2 : fonts)); /* 注入 MEOW_FONT_TIER env */
strncpy(a->icon, icon, 128);
a->nroute = tmp.nroute;
if (tmp.nroute) memcpy(a->routes, tmp.routes, sizeof(a->routes[0]) * (size_t)tmp.nroute);
@@ -2263,6 +2321,19 @@ static void settings_sync_cache_listener(const char *key)
else if (!strcmp(key, "power.forceoff_ms")) { int v; if (meow_settings_get_int("power.forceoff_ms", &v) == 0) power_forceoff_ms = v; }
}
/* ---- 通知 dump 回调占位(通知#6 转储 TF 卡;接线/占位,不做 TF 写)----
* meow_notif 在 TTL 到点(meow_notif_tick,§8 10s)调用此回调归档一条通知,随后释放槽。
* 基座只把回调挂上来(见 settings_load 的 meow_notif_set_dump_cb);真实 TF 落盘
* (按 NOTIFICATION_SYSTEM.md §8 / TF_CARD_LAYOUT.md 写 <卡>/MeowOS/notify/...)由后续
* 通知代理在回调体内实现。注意 meow_notif.c 的 archive_one 自身已 log 一行 "dump: ...",
* 故本回调保持轻量(避免重复刷屏),落盘逻辑填在下方 TODO 处。 */
static void notif_dump_tf_placeholder(const meow_notif_t *n)
{
(void)n;
/* TODO(通知代理, M2c):在此写 TF 卡 <MeowOS>/notify/<date>/<id>.json 等;
* 基座不实现,仅保留挂接点(触发时机=meow_notif_tick 内 10s 到点/止机转储)。 */
}
/* 启动加载(M2.11/M2.24):统一由 meow_settings 后端读 settings.conf;
* 此处仅注册监听(一次性)+ 把 backend 当前值同步到缓存全局。重载(应用期
* watchdog 可能改音量)等价于 meow_settings_init() + 手动同步。 */
@@ -2271,6 +2342,7 @@ static void settings_load(void)
{
meow_settings_init(); /* 幂等:注册默认 + 读 settings.conf */
meow_notif_init(); /* M2a:通知存储初始化(幂等,读 notify.* 配额) */
meow_notif_set_dump_cb(notif_dump_tf_placeholder); /* 基座(通知#6):挂 dump 回调(TF 转储占位,M2c 落盘由后续代理实现) */
if (!settings_cache_registered) {
meow_settings_add_listener(settings_sync_cache_listener);
settings_cache_registered = 1;
@@ -6732,8 +6804,10 @@ static int launch_app(struct app *a)
char env_app[MEOW_APP_ID_MAX + 16], env_lvl[48];
char env_fpng[400], env_fidx[400], env_fapp[96];
char env_mf[24];
char env_tier[24];
char env_fs[20];
char env_cls[MEOW_FCLS_COUNT][2][400];
char *envp[32]; /* PATH,HOME,log_dir,log_app,log_lvl,font 全局(3),多字体(1),class(≤12),data,cache */
char *envp[40]; /* PATH,HOME,log_dir,log_app,log_lvl,font 全局(3),多字体(1),tier(1),class(≤12),fullscreen(1),data,cache */
int ne = 0;
const char *cp = getenv("PATH");
snprintf(env_path, sizeof env_path, "PATH=%s", cp ? cp : "/usr/bin:/bin");
@@ -6772,8 +6846,13 @@ static int launch_app(struct app *a)
meow_settings_get_bool("display.multi_font", &mf_on);
snprintf(env_mf, sizeof env_mf, "MEOW_MULTI_FONT=%d", mf_on ? 1 : 0);
envp[ne++] = env_mf;
have_global = (meow_font_effective_paths(fpng, sizeof fpng, fidx, sizeof fidx,
fam, sizeof fam) == 0);
/* M2.x(基座):字库档 MEOW_FONT_TIER=<0|1|2> —— 按 app.json "fonts" 声明注入;
* 0=默认4K常用(未声明/缺省),1=大字库GBK,2=Full。SDK meow_ui_gfx 读入存档,
* 真正按档装载大字库由后续 font 代理实现(见 meow_font_effective_paths_tiered)。 */
snprintf(env_tier, sizeof env_tier, "MEOW_FONT_TIER=%d", a->fonts);
envp[ne++] = env_tier;
have_global = (meow_font_effective_paths_tiered(a->fonts, fpng, sizeof fpng,
fidx, sizeof fidx, fam, sizeof fam) == 0);
if (have_global) {
snprintf(env_fpng, sizeof env_fpng, "MEOW_FONT_PNG=%s", fpng);
snprintf(env_fidx, sizeof env_fidx, "MEOW_FONT_IDX=%s", fidx);
@@ -6801,6 +6880,14 @@ static int launch_app(struct app *a)
envp[ne++] = env_cls[c][1];
}
}
/* 全屏声明:app.json "fullscreen"(app 级)→ 注入 MEOW_FULLSCREEN=1,SDK 端
* meow_ui_init 据此预先设全屏。基座只接 app 级;活动级 route.<id>.fullscreen
* (meow_route.fullscreen)在 M3 活动切换时由后续代理消费(此处暂不下发)。
* 未声明则不注入(SDK 默认非全屏)。 */
if (a->fullscreen) {
snprintf(env_fs, sizeof env_fs, "MEOW_FULLSCREEN=1");
envp[ne++] = env_fs;
}
char pkg[64];
if (meow_pkg_name(a->exec, pkg, sizeof pkg) == 0) {
snprintf(env_data, sizeof env_data, "MEOW_DATA_DIR=/usr/data/mnt/sd_0/MeowData/%s/data", pkg);