三特性: 字体分层(gen_font.py --charset + meow_font_reload_tier + font.load_full + 尺寸标记) + 全屏机制/状态栏收合(meow_ui_sys/input) + 通知#6 TF转储(CSV+10MB+开机清板载+内存表)
This commit is contained in:
1 parent
0b4d1d3215
commit
a2673d5922
18 files changed
+819
-80
No files matched your search
@@ -33,6 +33,15 @@ int meow_font_load(const char *png_path, const char *idx_path);
|
||||
/* 释放字库。 */
|
||||
void meow_font_unload(void);
|
||||
|
||||
/* 按档装载大字库(字体档位分层,见 MEOWOS_SPEC_REF §5.2/§5.7.8 与 meow_ui.h §10):
|
||||
* tier = 0 common≈4k(小/默认)/ 1 gbk≈20k+(大字库)/ 2 full(全,可选)。
|
||||
* png/idx 为调用方已按档解析的设备绝对路径(launcher 经 env 注入)。切换时先 unload 旧
|
||||
* 再 load 新(释放旧 atlas —— 大字库 GBK 解码约 5-15MB,避免新旧同时驻留内存)。
|
||||
* 返回 0 成功,负值失败(失败时旧库已释放,调用方应回退卡默认/固件)。
|
||||
* 由 meow_ui_gfx.c 的 meow_font_set_tier() 在档位变化时唤起;meow_font_load() 仍是装载
|
||||
* 系统默认(slot0)字库的入口。 */
|
||||
int meow_font_reload_tier(int tier, const char *png, const char *idx);
|
||||
|
||||
/* 某字号是否存在(命中原生面返回 1)。 */
|
||||
int meow_font_has_size(int size);
|
||||
|
||||
|
||||
Binary file not shown.
@@ -791,9 +791,14 @@ static void draw_text(int x, int y, const char *s, int scale, uint32_t color)
|
||||
#define MEOW_FONTS_CFG "MeowOS/fonts"
|
||||
#define FONT_PRESETS_MAX 17 /* 1 默认 + ≤16 卡内字体(防爆) */
|
||||
static const char *font_presets[FONT_PRESETS_MAX]; /* SC_PRESET 值 = 卡根相对路径 */
|
||||
static const char *font_preset_labels[FONT_PRESETS_MAX]; /* SC_PRESET 中文标签 */
|
||||
static const char *font_preset_labels[FONT_PRESETS_MAX]; /* SC_PRESET 中文标签(含档位大小标记) */
|
||||
/* M2.5x 字体档位:每套的 charset 档("common"/"gbk"/"full",取自 font.json,缺省 "common")
|
||||
* 与 glyph_count(信息用)。供档位分层装载(meow_font_effective_paths_tiered)与设置页标注。 */
|
||||
static const char *font_preset_tiers[FONT_PRESETS_MAX];
|
||||
static int font_preset_glyphs[FONT_PRESETS_MAX];
|
||||
static char font_preset_val_buf[FONT_PRESETS_MAX][320]; /* 值持久存储(指向上面指针数组) */
|
||||
static char font_preset_label_buf[FONT_PRESETS_MAX][64]; /* 标签持久存储 */
|
||||
static char font_preset_label_buf[FONT_PRESETS_MAX][80]; /* 标签持久存储 */
|
||||
static char font_preset_tier_buf[FONT_PRESETS_MAX][8]; /* charset 持久存储 */
|
||||
static int font_presets_n = 0; /* 运行时动态计数(默认=1,仅"默认字体") */
|
||||
static int mfont_ready = 0;
|
||||
static int mfont_forced = 0; /* font.file 变更→强制真实(un)load,跳过 SDK 复用捷径 */
|
||||
@@ -821,6 +826,11 @@ static int meow_font_resolve_path(const char *ref, char *out, int olen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 前向声明:meow_font_ensure 在下方 font 区中调用(定义在 meow_font_effective_paths_tiered 处)。 */
|
||||
static int meow_font_effective_tier(int app_tier);
|
||||
static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
|
||||
char *out_idx, int ilen, char *out_family, int flen);
|
||||
|
||||
/* 解析 font.file 指向的 png + 派生同目录 idx(font.png → font.idx)。
|
||||
* 仅当指向的 png 存在(access R_OK)返回 0;否则 -1(交由回退链)。
|
||||
* 返回 0:out_png/out_idx 已填设备绝对路径。 */
|
||||
@@ -838,22 +848,25 @@ static int meow_font_resolve_selected(char *out_png, int png_len, char *out_idx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 尝试加载字库;成功返回 1。优先级(M2.5x):
|
||||
* 1) font.file(设置键,真正接管加载;相对路径按卡根解析)→ 存在且能 load 即用;
|
||||
/* 尝试加载字库;成功返回 1。优先级(M2.5x,font.load_full 已接入):
|
||||
* 0) 生效档 = font.load_full ? gbk : common(launcher 自身 UI 跟随系统大字库开关);
|
||||
* 1) 生效字体(tier 解析,tier0 即 font.file 所选;相对路径按卡根解析);
|
||||
* 2) 卡 MeowOS/fonts/default/font.png(默认字库);
|
||||
* 3) 固件 /usr/share/meowos/font/font.png。
|
||||
* A-1/A-2:SDK meow_ui_init() 已用同一 meow_font.o 加载过(可能成功也可能未成功)。
|
||||
* 当 font.file 指向「默认卡字库」或未设置时复用 SDK 已加载状态,避免重复 unload+reload;
|
||||
* 当 font.file 指向自定义字库时必须显式 load(SDK 只加载默认,不读 font.file)。
|
||||
* mfont_forced=1(font.file 变更触发)时无条件真实 reload,跳过上述两条捷径。
|
||||
* 当字体指向「默认卡字库」或未设置时复用 SDK 已加载状态,避免重复 unload+reload;
|
||||
* 当字体指向自定义字库时必须显式 load(SDK 只加载默认,不读 font.file)。
|
||||
* mfont_forced=1(font.file/font.load_full 变更触发)时无条件真实 reload,跳过上述捷径。
|
||||
* 全失败 → mfont_ready=0,ui_draw 回退 5x7 点阵(系统不死)。 */
|
||||
static int meow_font_ensure(void)
|
||||
{
|
||||
if (mfont_ready && !mfont_forced) return 1;
|
||||
mfont_forced = 0;
|
||||
|
||||
char fpng[320], fidx[320];
|
||||
int have_sel = (meow_font_resolve_selected(fpng, sizeof fpng, fidx, sizeof fidx) == 0);
|
||||
char fpng[400], fidx[400], fam[96];
|
||||
int tier = meow_font_effective_tier(MEOW_FONT_TIER_COMMON); /* launcher 自身 UI 跟随 load_full */
|
||||
int have_sel = (meow_font_effective_paths_tiered(tier, fpng, sizeof fpng,
|
||||
fidx, sizeof fidx, fam, sizeof fam) == 0);
|
||||
|
||||
/* SDK 已加载且所选字库为「默认卡字库」(或未设置)→ 复用,避免重复解码。 */
|
||||
if (meow_gfx_mfont_ready() && (!have_sel || !strcmp(fpng, UI_FONT_PNG_CARD))) {
|
||||
@@ -862,15 +875,15 @@ static int meow_font_ensure(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* font.file 优先。 */
|
||||
/* 生效字体优先(tier=common 时即 font.file;load_full → 大字库 gbk 档)。 */
|
||||
if (have_sel && meow_font_load(fpng, fidx) == 0) {
|
||||
mfont_ready = 1;
|
||||
logmsg("meow_font: loaded font.file %s (%d/%d sizes)",
|
||||
fpng, meow_font_has_size(28), meow_font_has_size(20));
|
||||
logmsg("meow_font: loaded %s (%d/%d sizes), tier=%d", fpng,
|
||||
meow_font_has_size(28), meow_font_has_size(20), tier);
|
||||
return 1;
|
||||
}
|
||||
if (have_sel)
|
||||
logmsg("meow_font: font.file %s load failed, fallback", fpng);
|
||||
logmsg("meow_font: %s load failed, fallback", fpng);
|
||||
/* 卡默认 → 固件。 */
|
||||
if (meow_font_load(UI_FONT_PNG_CARD, UI_FONT_IDX_CARD) == 0 ||
|
||||
meow_font_load(UI_FONT_PNG_SYS, UI_FONT_IDX_SYS) == 0) {
|
||||
@@ -915,17 +928,79 @@ 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 无可用。 */
|
||||
/* 档位 → font.json charset 字符串名(common/gbk/full)。 */
|
||||
static const char *font_tier_charset(int tier)
|
||||
{
|
||||
if (tier >= MEOW_FONT_TIER_FULL) return "full";
|
||||
if (tier >= MEOW_FONT_TIER_GBK) return "gbk";
|
||||
return "common";
|
||||
}
|
||||
|
||||
/* 生效档:app 声明档(0/1/2,越界 clamp)合并「加载大字库」开关(font.load_full)。
|
||||
* 开 → 强制 ≥GBK(gbk≈20k 档大字库);关(默认)→ 保持 app 声明(缺省 common≈4k)。
|
||||
* 断言:0<=返回<=2。 */
|
||||
static int meow_font_effective_tier(int app_tier)
|
||||
{
|
||||
if (app_tier < MEOW_FONT_TIER_COMMON) app_tier = MEOW_FONT_TIER_COMMON;
|
||||
if (app_tier > MEOW_FONT_TIER_FULL) app_tier = MEOW_FONT_TIER_FULL;
|
||||
int lf = 0;
|
||||
if (meow_settings_get_bool("font.load_full", &lf) == 0 && lf)
|
||||
if (app_tier < MEOW_FONT_TIER_GBK) app_tier = MEOW_FONT_TIER_GBK;
|
||||
return app_tier;
|
||||
}
|
||||
|
||||
/* 【tier 选择入口,M2.5x 已实现】按字体档位取「当前生效字体」png/idx,供 launch_app 注入
|
||||
* MEOW_FONT_PNG/IDX/APP(配合 MEOW_FONT_TIER)。口径(MEOWOS_SPEC_REF §5.2/§5.7.8):
|
||||
* - 生效档 = max(app 声明档, font.load_full ? GBK : COMMON):系统「加载大字库」开 →
|
||||
* 强制 ≥gbk;
|
||||
* - tier<=common → 走既有 meow_font_effective_paths(font.file/卡默认/固件);
|
||||
* - tier>0 → 在已扫描的 MeowOS/fonts/<family>/ 里找 charset 匹配(gbk 请求接受 gbk/full;
|
||||
* full 请求接受 full)的那套,以其 font.png/idx 绝对路径返回(family = 目录名);
|
||||
* - 找不到匹配的大字库 → 回退 meow_font_effective_paths(避免 app 直接点到固件小库)。
|
||||
* 返回 0 成功(out_png/out_idx/out_family 已填),-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)
|
||||
int eff = meow_font_effective_tier(tier); /* 合并 font.load_full:开 → 强制 ≥GBK */
|
||||
if (eff <= MEOW_FONT_TIER_COMMON)
|
||||
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
|
||||
/* TODO(font 代理):按 tier 解析大字库绝对路径(GBK/Full);暂回退全局(tier0)。 */
|
||||
/* 大字库:从扫描结果选 charset 匹配的一套。
|
||||
* 打分(高优先):gbk 请求 → gbk=2 > full=1;full 请求 → full=2 > gbk=1(降级可接受)。 */
|
||||
const char *want = font_tier_charset(eff);
|
||||
char ref[320];
|
||||
int found_family = -1, best_score = 0;
|
||||
for (int i = 0; i < font_presets_n; i++) {
|
||||
const char *cs = font_preset_tiers[i] ? font_preset_tiers[i] : "common";
|
||||
int score = 0;
|
||||
if (!strcmp(cs, want)) score = 2;
|
||||
else if (!strcmp(cs, "full") || !strcmp(cs, "gbk")) score = 1; /* 降级 */
|
||||
if (score > best_score) { best_score = score; found_family = i; }
|
||||
}
|
||||
if (found_family >= 0)
|
||||
snprintf(ref, sizeof ref, "%s", font_presets[found_family]);
|
||||
if (found_family >= 0) {
|
||||
/* 相对路径按卡根解析为设备绝对路径(与 meow_font_effective_paths 同源)。 */
|
||||
if (meow_font_resolve_path(ref, out_png, plen) != 0) return -1;
|
||||
if (access(out_png, R_OK) != 0) goto fallback;
|
||||
snprintf(out_idx, ilen, "%s", out_png);
|
||||
size_t n = strlen(out_idx);
|
||||
if (n > 4 && !strcmp(out_idx + n - 4, ".png")) {
|
||||
out_idx[n-4] = '.'; out_idx[n-3] = 'i'; out_idx[n-2] = 'd';
|
||||
out_idx[n-1] = 'x'; out_idx[n] = 0;
|
||||
}
|
||||
const char *prefix = "/MeowOS/fonts/";
|
||||
const char *p = strstr(out_png, prefix);
|
||||
if (p) {
|
||||
const char *q = p + strlen(prefix);
|
||||
const char *end = strrchr(q, '/');
|
||||
int rest = end ? (int)(end - q) : 0;
|
||||
if (rest > 0 && rest < flen) { memcpy(out_family, q, rest); out_family[rest] = 0; }
|
||||
else snprintf(out_family, flen, "default");
|
||||
} else snprintf(out_family, flen, "default");
|
||||
return 0;
|
||||
}
|
||||
fallback:
|
||||
/* 无匹配大字库/不可读 → 回退全局(font.file 所选;其可能是大字库或默认)。 */
|
||||
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
|
||||
}
|
||||
|
||||
@@ -942,7 +1017,10 @@ static void meow_font_reload_multifont(void)
|
||||
int on = 0;
|
||||
meow_settings_get_bool("display.multi_font", &on);
|
||||
char gpng[400], gidx[400], gfam[96];
|
||||
if (meow_font_effective_paths(gpng, sizeof gpng, gidx, sizeof gidx, gfam, sizeof gfam) != 0)
|
||||
/* 全局跟生效档(合并 font.load_full → 大字库),与 meow_font_ensure 同源,避免槽0实际装载与
|
||||
* 此处理解不一致(多字体去重以全局 png 为基准)。 */
|
||||
if (meow_font_effective_paths_tiered(meow_font_effective_tier(MEOW_FONT_TIER_COMMON),
|
||||
gpng, sizeof gpng, gidx, sizeof gidx, gfam, sizeof gfam) != 0)
|
||||
return; /* 无全局字体:无法多字体 */
|
||||
/* 各类候选(静态缓冲保活;config_multifont 同步读取,不持指针) */
|
||||
static char cpng[MEOW_FCLS_COUNT][400], cidx[MEOW_FCLS_COUNT][400];
|
||||
@@ -1666,9 +1744,12 @@ static int font_sha_eq(const char *a, const char *b)
|
||||
|
||||
/* 解析 font.json:提取 name / sha256_png / sha256_idx(文件缺失或解析不出则留空)。 */
|
||||
static void meow_font_json_probe(const char *json_path, char *name, int nlen,
|
||||
char *sha_png, int slen, char *sha_idx, int xlen)
|
||||
char *sha_png, int slen, char *sha_idx, int xlen,
|
||||
char *charset, int clen, int *glyph_count)
|
||||
{
|
||||
name[0] = 0; sha_png[0] = 0; sha_idx[0] = 0;
|
||||
if (charset) charset[0] = 0;
|
||||
if (glyph_count) *glyph_count = 0;
|
||||
FILE *f = fopen(json_path, "r");
|
||||
if (!f) return;
|
||||
char buf[256];
|
||||
@@ -1676,14 +1757,39 @@ static void meow_font_json_probe(const char *json_path, char *name, int nlen,
|
||||
if (!name[0]) json_str(buf, "name", name, nlen);
|
||||
if (!sha_png[0]) json_str(buf, "sha256_png", sha_png, slen);
|
||||
if (!sha_idx[0]) json_str(buf, "sha256_idx", sha_idx, xlen);
|
||||
/* M2.5x 字体档位:charset(common|gbk|full)+ glyph_count(信息用,驱动档位分层/标注)。 */
|
||||
if (charset && !charset[0]) {
|
||||
char v[8] = "";
|
||||
if (json_str(buf, "charset", v, sizeof v) > 0 && v[0])
|
||||
snprintf(charset, clen, "%s", v);
|
||||
}
|
||||
if (glyph_count && *glyph_count == 0) {
|
||||
char v[16] = "";
|
||||
if (json_str(buf, "glyph_count", v, sizeof v) > 0 && v[0])
|
||||
*glyph_count = atoi(v);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* 探测一个 <dirpath>/<family> 套件是否合法:三件套齐全 → 解析 label + 校验 sha。
|
||||
* 返回 1=进列表,0=跳过(缺组件/json 不可读)。sha 失败仅 WARN。 */
|
||||
/* 档位大小标记(设置页字体选项标注):common≈4k / gbk≈20k / full。
|
||||
* 由 font.json 的 charset + glyph_count 派生;缺 charset 回退 common。 */
|
||||
static void font_tier_marker(const char *charset, int glyph_count, char *out, int olen)
|
||||
{
|
||||
const char *tier = (charset && charset[0]) ? charset : "common";
|
||||
if (!strcmp(tier, "full")) { snprintf(out, olen, "full"); return; }
|
||||
if (glyph_count > 0)
|
||||
snprintf(out, olen, "%s≈%dk", tier, (glyph_count + 500) / 1000);
|
||||
else
|
||||
snprintf(out, olen, "%s", tier);
|
||||
}
|
||||
|
||||
/* 探测一个 <dirpath>/<family> 套件是否合法:三件套齐全 → 解析 label + 档位 + 校验 sha。
|
||||
* 返回 1=进列表,0=跳过(缺组件/json 不可读)。sha 失败仅 WARN。
|
||||
* 输出:label=显示名+大小标记 `名字(common≈4k)`;charset/glyph_count=档位信息。 */
|
||||
static int meow_font_probe_family(const char *dirpath, const char *family,
|
||||
char *label, int llen)
|
||||
char *label, int llen,
|
||||
char *charset, int clen, int *glyph_count)
|
||||
{
|
||||
char ppath[400], ipath[400], jpath[400];
|
||||
snprintf(ppath, sizeof ppath, "%s/font.png", dirpath);
|
||||
@@ -1694,9 +1800,15 @@ static int meow_font_probe_family(const char *dirpath, const char *family,
|
||||
return 0;
|
||||
}
|
||||
char name[64] = "", jsha_png[80] = "", jsha_idx[80] = "";
|
||||
char cs[8] = ""; int gc = 0;
|
||||
meow_font_json_probe(jpath, name, sizeof name, jsha_png, sizeof jsha_png,
|
||||
jsha_idx, sizeof jsha_idx);
|
||||
snprintf(label, llen, "%s", name[0] ? name : family); /* 显示名:name 或回退目录名 */
|
||||
jsha_idx, sizeof jsha_idx, cs, sizeof cs, &gc);
|
||||
/* 显示名 = 名 + 档位大小标记(如 `default(common≈4k)` / `gbk≈20k` / `full`) */
|
||||
char marker[24];
|
||||
font_tier_marker(cs, gc, marker, sizeof marker);
|
||||
snprintf(label, llen, "%s(%s)", name[0] ? name : family, marker);
|
||||
if (charset) snprintf(charset, clen, "%s", cs[0] ? cs : "common");
|
||||
if (glyph_count) *glyph_count = gc;
|
||||
/* 完整性校验:仅 WARN,不硬失败;该套仍标记可选。 */
|
||||
char apng[80] = "", aidx[80] = "";
|
||||
if (font_sha256_file(ppath, apng, sizeof apng) != 0)
|
||||
@@ -1707,7 +1819,7 @@ static int meow_font_probe_family(const char *dirpath, const char *family,
|
||||
logmsg("fonts: '%s' sha256(idx) 不可用,跳过校验", family);
|
||||
else if (jsha_idx[0] && !font_sha_eq(aidx, jsha_idx))
|
||||
logmsg("fonts: '%s' WARN sha256_idx 不匹配(仍可选)", family);
|
||||
logmsg("fonts: add '%s' (%s)", label, family);
|
||||
logmsg("fonts: add '%s' (%s) charset=%s glyph_count=%d", label, family, cs[0]?cs:"common", gc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1717,7 +1829,20 @@ static void meow_font_scan_presets_root(const char *card_root)
|
||||
{
|
||||
font_presets_n = 0;
|
||||
font_presets[0] = "MeowOS/fonts/default/font.png"; /* 默认字库 = MeowOS/fonts/default */
|
||||
font_preset_labels[0] = "默认字体";
|
||||
font_preset_labels[0] = font_preset_label_buf[0]; /* 指向缓冲(运行时派生含大小标记) */
|
||||
/* 默认套也读 font.json 档位(存在则跟真实档,缺则 common);档位信息供分层装载/标注。 */
|
||||
{
|
||||
char jp[400], cs[8] = ""; int gc = 0;
|
||||
char nm0[64] = "", sp0[80] = "", si0[80] = "";
|
||||
snprintf(jp, sizeof jp, "%s/%s/default/font.json", card_root, MEOW_FONTS_CFG);
|
||||
meow_font_json_probe(jp, nm0, sizeof nm0, sp0, sizeof sp0, si0, sizeof si0,
|
||||
cs, sizeof cs, &gc);
|
||||
char mk[24]; font_tier_marker(cs, gc, mk, sizeof mk);
|
||||
snprintf(font_preset_label_buf[0], sizeof font_preset_label_buf[0], "默认字体(%s)", mk);
|
||||
snprintf(font_preset_tier_buf[0], sizeof font_preset_tier_buf[0], "%s", cs[0]?cs:"common");
|
||||
font_preset_tiers[0] = font_preset_tier_buf[0];
|
||||
font_preset_glyphs[0] = gc;
|
||||
}
|
||||
font_presets_n = 1;
|
||||
char root[340];
|
||||
snprintf(root, sizeof root, "%s/%s", card_root, MEOW_FONTS_CFG);
|
||||
@@ -1726,16 +1851,20 @@ static void meow_font_scan_presets_root(const char *card_root)
|
||||
struct dirent *de;
|
||||
while ((de = readdir(d)) && font_presets_n < FONT_PRESETS_MAX) {
|
||||
if (de->d_name[0] == '.') continue;
|
||||
char dirpath[400], label[64];
|
||||
char dirpath[400], label[80], cs[8] = ""; int gc = 0;
|
||||
snprintf(dirpath, sizeof dirpath, "%s/%s", root, de->d_name);
|
||||
if (meow_font_probe_family(dirpath, de->d_name, label, sizeof label) == 0)
|
||||
if (meow_font_probe_family(dirpath, de->d_name, label, sizeof label,
|
||||
cs, sizeof cs, &gc) == 0)
|
||||
continue; /* 缺组件/损坏 → 跳过该套 */
|
||||
int i = font_presets_n;
|
||||
snprintf(font_preset_val_buf[i], sizeof font_preset_val_buf[i],
|
||||
"%s/%s/font.png", MEOW_FONTS_CFG, de->d_name);
|
||||
snprintf(font_preset_label_buf[i], sizeof font_preset_label_buf[i], "%s", label);
|
||||
snprintf(font_preset_tier_buf[i], sizeof font_preset_tier_buf[i], "%s", cs[0]?cs:"common");
|
||||
font_presets[i] = font_preset_val_buf[i];
|
||||
font_preset_labels[i] = font_preset_label_buf[i];
|
||||
font_preset_tiers[i] = font_preset_tier_buf[i];
|
||||
font_preset_glyphs[i] = gc;
|
||||
font_presets_n++;
|
||||
}
|
||||
closedir(d);
|
||||
@@ -2321,17 +2450,14 @@ 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 写)----
|
||||
/* ---- 通知 dump 回调(通知#6 转储 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 处。 */
|
||||
* 本回调把 meow_notif 结构体转成一行 CSV 追加写入 <卡>/MeowOS/notifications/notifications.log
|
||||
* (含 mkdir -p 与按总大小滚动截断、保留最新 10MB),落盘实现在 meow_notif.c 的
|
||||
* meow_notif_dump_tf()。无卡/不可写仅打点降级(NOTIFICATION_SYSTEM.md §5/§8 + MEWSPEC §5.3)。 */
|
||||
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 到点/止机转储)。 */
|
||||
meow_notif_dump_tf(n);
|
||||
}
|
||||
|
||||
/* 启动加载(M2.11/M2.24):统一由 meow_settings 后端读 settings.conf;
|
||||
@@ -2342,7 +2468,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 落盘由后续代理实现) */
|
||||
meow_notif_set_dump_cb(notif_dump_tf_placeholder); /* 通知#6:挂 dump 回调 = TF 转储(meow_notif_dump_tf:一行 CSV 追加 + 10MB 滚动截断) */
|
||||
if (!settings_cache_registered) {
|
||||
meow_settings_add_listener(settings_sync_cache_listener);
|
||||
settings_cache_registered = 1;
|
||||
@@ -4483,6 +4609,10 @@ static const srow_t SMENU[] = {
|
||||
/* 字体主题 */
|
||||
{ "字体主题", NULL, SC_HEADER, 0,0,0, NULL,NULL,0 },
|
||||
{ "字体文件", "font.file", SC_PRESET, 0,0,0, font_presets, font_preset_labels, 0 },
|
||||
/* M2.5x 字体档位分层/按需大字库:「加载大字库」= font.load_full(int 0/1 默认 0)。
|
||||
* 开 → 强制全系统用大字库 gbk≈20k 档(覆盖 app 未声明/覆盖默认 font.file);
|
||||
* 关(默认)→ 小字库 common≈4k,仅 app 声明 fonts:"full" 才大字库。 */
|
||||
{ "加载大字库", "font.load_full", SC_SWITCH, 0,0,0, NULL,NULL,0 },
|
||||
/* M2.5x 多字体:开关 + 6 个字符类别字体(preset=可用 MeowOS/fonts/<family>/ 列表;
|
||||
* 值空 = 跟随全局 font.file)。仅开关开启时 6 行可见(s_row_visible)。 */
|
||||
{ "多字体", "display.multi_font", SC_SWITCH, 0,0,0, NULL,NULL,0 },
|
||||
@@ -5433,6 +5563,20 @@ static void s_commit(const srow_t *r, int save_now)
|
||||
meow_font_reload_multifont(); /* M2.5x:全局字体变更 → 重配多字体(各类跟随全局者随之更新) */
|
||||
meow_dirty_full(); /* 字体变化可能影响所有文本渲染,整帧重绘 */
|
||||
}
|
||||
/* M2.5x「加载大字库」开关(font.load_full)变更 → 强制重载 launcher 自身字体(小/大字库切换),
|
||||
* 并重配多字体;大字库 atlas 大,meow_font_ensure 先 unload 旧再 load 新(防双驻留)。 */
|
||||
else if (!strcmp(r->key, "font.load_full")) {
|
||||
mfont_ready = 0;
|
||||
mfont_forced = 1; /* 跳过 SDK 复用捷径,强制按生效档(gbk/common)真实 reload */
|
||||
if (!meow_font_ensure())
|
||||
logmsg("font.load_full: reload failed -> dotmatrix fallback");
|
||||
else {
|
||||
int lf = 0; meow_settings_get_bool("font.load_full", &lf);
|
||||
logmsg("font.load_full: reloaded (load_full=%d)", lf);
|
||||
}
|
||||
meow_font_reload_multifont();
|
||||
meow_dirty_full();
|
||||
}
|
||||
/* M2.5x 多字体:开关或任一类别字体变更 → 重新去重装载类别字库并重绘(多字体可能影响文本渲染)。 */
|
||||
else if (!strcmp(r->key, "display.multi_font") || !strncmp(r->key, "font.class_", 11)) {
|
||||
meow_font_reload_multifont();
|
||||
@@ -6846,12 +6990,14 @@ 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;
|
||||
/* 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);
|
||||
/* M2.5x:字库档 MEOW_FONT_TIER=<0|1|2> —— 按 app.json "fonts" 声明注入,并合并
|
||||
* 「加载大字库」开关(font.load_full → 强制 ≥GBK)得**生效档**;0=默认4K常用,
|
||||
* 1=大字库GBK,2=Full。SDK meow_ui_gfx 读入存档并按档装载(见
|
||||
* meow_font_effective_paths_tiered;真正装载由 meow_font_reload_tier 实现)。 */
|
||||
int eff_tier = meow_font_effective_tier(a->fonts);
|
||||
snprintf(env_tier, sizeof env_tier, "MEOW_FONT_TIER=%d", eff_tier);
|
||||
envp[ne++] = env_tier;
|
||||
have_global = (meow_font_effective_paths_tiered(a->fonts, fpng, sizeof fpng,
|
||||
have_global = (meow_font_effective_paths_tiered(eff_tier, fpng, sizeof fpng,
|
||||
fidx, sizeof fidx, fam, sizeof fam) == 0);
|
||||
if (have_global) {
|
||||
snprintf(env_fpng, sizeof env_fpng, "MEOW_FONT_PNG=%s", fpng);
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
#include <strings.h> /* strcasecmp */
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h> /* mkdir / stat */
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h> /* open/close/write/pread/lseek/ftruncate */
|
||||
#include <fcntl.h> /* O_* */
|
||||
#include <errno.h> /* errno/strerror */
|
||||
|
||||
#include "meow_notif.h"
|
||||
#include "meow_settings.h" /* B2a:读 notify.* 配额(MEOW_OK/MEOW_ERR_*) */
|
||||
@@ -89,6 +94,192 @@ static void n_strncpy(char *dst, const char *src, size_t len)
|
||||
dst[len - 1] = 0;
|
||||
}
|
||||
|
||||
/* ================= TF 卡转储(通知#6 / M2c,NOTIFICATION_SYSTEM.md §5/§8)=================
|
||||
* 每条通知一行 CSV 追加到 <卡根>/MeowOS/notifications/notifications.log,并按日志总大小
|
||||
* 滚动截断(保留最新 10MB)。系统文件统一放 <卡根>/MeowOS/ 下(TF_CARD_LAYOUT.md)。 */
|
||||
#define MEOW_NOTIF_TF_DIR "/usr/data/mnt/sd_0/MeowOS/notifications"
|
||||
#define MEOW_NOTIF_TF_LOG MEOW_NOTIF_TF_DIR "/notifications.log"
|
||||
#define MEOW_NOTIF_LOG_MAX_BYTES ((size_t)10 * 1024 * 1024) /* §8:保留最新 10MB */
|
||||
|
||||
/* 单行 CSV 上限:app_id(32)/category(24)/summary(64)/body(96)/target_activity(48)
|
||||
* 全引号转义后仍远小于此。 */
|
||||
#define MEOW_NOTIF_CSV_LINE_MAX 1024
|
||||
|
||||
static const char *g_tf_path = MEOW_NOTIF_TF_LOG;
|
||||
static size_t g_tf_max = MEOW_NOTIF_LOG_MAX_BYTES;
|
||||
|
||||
/* 递归 mkdir -p:沿 path 把每一级目录建出来(EEXIST 无害;无卡/只读则静默失败) */
|
||||
static void tf_mkdirs(const char *path)
|
||||
{
|
||||
char tmp[512];
|
||||
n_strncpy(tmp, path, sizeof tmp);
|
||||
for (char *p = tmp + 1; *p; p++) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
mkdir(tmp, 0755);
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全量写入(处理短写/EINTR) */
|
||||
static int tf_write_all(int fd, const char *buf, size_t len)
|
||||
{
|
||||
size_t off = 0;
|
||||
while (off < len) {
|
||||
ssize_t w = write(fd, buf + off, len - off);
|
||||
if (w < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
off += (size_t)w;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CSV 字段:去换行(\r\n->空格,保证单行)+ 转义(双引号包裹,内部 " 翻倍) */
|
||||
static void csv_field(char *dst, size_t dstsz, const char *src)
|
||||
{
|
||||
if (!dst || dstsz == 0) return;
|
||||
size_t o = 0;
|
||||
dst[o++] = '"';
|
||||
for (const char *p = src ? src : ""; *p; p++) {
|
||||
if (o + 4 > dstsz) break; /* 留 2 字节给翻倍引号 + 收尾引号 + NUL */
|
||||
if (*p == '\r' || *p == '\n') dst[o++] = ' '; /* 去换行 */
|
||||
else if (*p == '"') { dst[o++] = '"'; dst[o++] = '"'; }
|
||||
else dst[o++] = *p;
|
||||
}
|
||||
if (o + 1 >= dstsz) o = dstsz - 1;
|
||||
dst[o++] = '"';
|
||||
dst[o] = '\0';
|
||||
}
|
||||
|
||||
/* 生成一行 CSV:
|
||||
* timestamp,id,app_id,category,urgency,title,body,target_activity,ongoing,media,dismissed
|
||||
* title<-summary;urgency:meow_notif 结构无该字段(保持既有 API),列值留空 ""。
|
||||
* 返回写入长度(不含 NUL)。 */
|
||||
static size_t tf_line(const meow_notif_t *n, char *buf, size_t bufsz)
|
||||
{
|
||||
char a_app [2 + 2 * MEOW_NOTIF_APP_ID_MAX + 2];
|
||||
char a_cat [2 + 2 * MEOW_NOTIF_CAT_MAX + 2];
|
||||
char a_sum [2 + 2 * MEOW_NOTIF_SUMMARY_MAX + 2];
|
||||
char a_body[2 + 2 * MEOW_NOTIF_BODY_MAX + 2];
|
||||
char a_tgt [2 + 2 * MEOW_NOTIF_ACTIVITY_MAX + 2];
|
||||
csv_field(a_app, sizeof a_app, n->app_id);
|
||||
csv_field(a_cat, sizeof a_cat, n->category);
|
||||
csv_field(a_sum, sizeof a_sum, n->summary);
|
||||
csv_field(a_body, sizeof a_body, n->body);
|
||||
csv_field(a_tgt, sizeof a_tgt, n->target_activity);
|
||||
int r = snprintf(buf, bufsz,
|
||||
"%llu,%u,%s,%s,\"\",%s,%s,%s,%u,%u,%u\n",
|
||||
(unsigned long long)n->created_ms,
|
||||
(unsigned)n->id,
|
||||
a_app, a_cat, a_sum, a_body, a_tgt,
|
||||
n->ongoing ? 1u : 0u,
|
||||
n->media ? 1u : 0u,
|
||||
n->dismissed ? 1u : 0u);
|
||||
if (r < 0) return 0;
|
||||
return ((size_t)r < bufsz) ? (size_t)r : bufsz - 1;
|
||||
}
|
||||
|
||||
/* 滚动截断:文件超过 g_tf_max 时删除最旧行(从尾部读窗口定位第一条换行作为保留起点),
|
||||
* 把结尾 ≤max 字节的完整行重写回文件头。窗口内无换行则保留整窗(只保证 ≤max)。 */
|
||||
static int tf_trim(const char *path)
|
||||
{
|
||||
int fd = open(path, O_RDWR);
|
||||
if (fd < 0) return -1;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) { close(fd); return -1; }
|
||||
off_t size = st.st_size;
|
||||
if (size <= (off_t)g_tf_max) { close(fd); return 0; }
|
||||
|
||||
off_t start = size - (off_t)g_tf_max; /* 保留窗口起点 */
|
||||
off_t keep = start; /* 最终保留起点(行对齐后) */
|
||||
int found = 0;
|
||||
char chunk[8192];
|
||||
for (off_t pos = start; pos < size && !found; ) {
|
||||
size_t want = (size_t)((size - pos) < (off_t)sizeof chunk ? (size - pos) : (off_t)sizeof chunk);
|
||||
ssize_t rd = pread(fd, chunk, want, pos);
|
||||
if (rd <= 0) break;
|
||||
for (ssize_t i = 0; i < rd; i++) {
|
||||
if (chunk[i] == '\n') { keep = pos + i + 1; found = 1; break; }
|
||||
}
|
||||
pos += rd;
|
||||
if (rd < (ssize_t)want) break; /* 读到文件尾 */
|
||||
}
|
||||
|
||||
/* 重写:把 [keep, size) 拷贝回文件头,再 ftruncate 掉尾部旧数据 */
|
||||
off_t remain = size - keep;
|
||||
if (lseek(fd, 0, SEEK_SET) < 0) { close(fd); return -1; }
|
||||
off_t wpos = 0;
|
||||
char buf[8192];
|
||||
while (remain > 0) {
|
||||
size_t want = (size_t)(remain < (off_t)sizeof buf ? remain : (off_t)sizeof buf);
|
||||
ssize_t rd = pread(fd, buf, want, keep + wpos);
|
||||
if (rd <= 0) break;
|
||||
if (tf_write_all(fd, buf, (size_t)rd) < 0) { close(fd); return -1; }
|
||||
wpos += rd;
|
||||
remain -= rd;
|
||||
}
|
||||
ftruncate(fd, wpos);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 把一条通知追加为一行 CSV 到 TF 日志(含 mkdir -p 与 10MB 滚动截断)。
|
||||
* 返回 NOTIF_OK(0);无卡/不可写/参数非法返回负(降级:仅打点,不崩)。 */
|
||||
int meow_notif_dump_tf(const meow_notif_t *n)
|
||||
{
|
||||
if (!n) return NOTIF_ERR_INVAL;
|
||||
if (!g_tf_path || !g_tf_path[0]) return NOTIF_ERR_INVAL;
|
||||
tf_mkdirs(g_tf_path); /* 目录不存在则 mkdir -p*/
|
||||
|
||||
char line[MEOW_NOTIF_CSV_LINE_MAX];
|
||||
size_t len = tf_line(n, line, sizeof line);
|
||||
if (len == 0) return NOTIF_ERR_INVAL;
|
||||
|
||||
int fd = open(g_tf_path, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
if (fd < 0) {
|
||||
notif_log("tf: append open fail %s (%s)", g_tf_path, strerror(errno));
|
||||
return NOTIF_ERR_IO;
|
||||
}
|
||||
if (tf_write_all(fd, line, len) != 0) {
|
||||
close(fd);
|
||||
notif_log("tf: append write fail %s (%s)", g_tf_path, strerror(errno));
|
||||
return NOTIF_ERR_IO;
|
||||
}
|
||||
long size = -1;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0) size = (long)st.st_size;
|
||||
close(fd);
|
||||
|
||||
if (size > (long)g_tf_max) { /* >10MB:滚动截断,保留最新 */
|
||||
tf_trim(g_tf_path);
|
||||
notif_log("tf: rolled to <= %zuB", g_tf_max);
|
||||
}
|
||||
return NOTIF_OK;
|
||||
}
|
||||
|
||||
/* 开机清板载通知:清空转储日志文件,保留目录(通知#6 / §8:启动时清板载) */
|
||||
void meow_notif_clear_tf(void)
|
||||
{
|
||||
if (!g_tf_path || !g_tf_path[0]) return;
|
||||
tf_mkdirs(g_tf_path);
|
||||
int fd = open(g_tf_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd >= 0) close(fd);
|
||||
else notif_log("tf: clear fail %s (%s)", g_tf_path, strerror(errno));
|
||||
}
|
||||
|
||||
/* 覆盖转储日志路径/滚动上限(宿主单测用;NULL/0 恢复默认) */
|
||||
void meow_notif_set_tf_path(const char *path)
|
||||
{
|
||||
g_tf_path = (path && path[0]) ? path : MEOW_NOTIF_TF_LOG;
|
||||
}
|
||||
void meow_notif_set_tf_max(size_t max_bytes)
|
||||
{
|
||||
g_tf_max = max_bytes ? max_bytes : MEOW_NOTIF_LOG_MAX_BYTES;
|
||||
}
|
||||
|
||||
/* ---- 配额读取(B2a;键缺省/取失败用默认值)---- */
|
||||
static void read_quota(void)
|
||||
{
|
||||
@@ -113,7 +304,11 @@ int meow_notif_init(void)
|
||||
g_dumped_total = 0;
|
||||
read_quota(); /* B2a:notify.* 配额 */
|
||||
g_inited = 1;
|
||||
notif_log("init: slots=%d default dump_cb=log-only (M2c -> TF)", MEOW_NOTIF_MAX_SLOTS);
|
||||
/* 通知#6:启动清板载通知 + 内存表(内存表上面已 memset;这里清 TF 转储日志,
|
||||
* 保留目录)。每会话从零开始(通知会话级),不被电源事件清(before_poweroff 只管内存)。 */
|
||||
meow_notif_clear_tf();
|
||||
notif_log("init: slots=%d tf_log=%s (dump_cb wired by launcher -> meow_notif_dump_tf)",
|
||||
MEOW_NOTIF_MAX_SLOTS, g_tf_path);
|
||||
return NOTIF_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ enum {
|
||||
NOTIF_ERR_NOMEM = -6, /* 槽满/内存不足 */
|
||||
NOTIF_ERR_NOTINIT = -7, /* 未 init */
|
||||
NOTIF_ERR_QUOTA = -12, /* 配额超限(App 类型数 / 类型条数 / 媒体数) */
|
||||
NOTIF_ERR_NOENT = -2 /* 通知 id 不存在 */
|
||||
NOTIF_ERR_NOENT = -2, /* 通知 id 不存在 */
|
||||
NOTIF_ERR_IO = -5 /* 文件/存储 IO 失败(TF 转储,无卡/不可写) */
|
||||
};
|
||||
|
||||
/* == 容量/长度(单设备上限;定长,控制静态内存)== */
|
||||
@@ -118,6 +119,17 @@ void meow_notif_before_poweroff(void);
|
||||
/* 覆盖转储回调(NULL=恢复默认日志;M2c 接 TF 落盘)。 */
|
||||
void meow_notif_set_dump_cb(meow_notif_dump_cb cb);
|
||||
|
||||
/* == TF 卡转储(通知#6 / M2c,NOTIFICATION_SYSTEM.md §5/§8 + MEOWOS_SPEC_REF §5.3)==
|
||||
* 把 dump_cb 接到 meow_notif_dump_tf 即把每条通知追加为一行 CSV 到
|
||||
* <卡根>/MeowOS/notifications/notifications.log,并按日志总大小滚动截断
|
||||
* (保留最新 10MB,不被电源事件清;启动时 meow_notif_init 先清板载+内存表)。
|
||||
* 字段按 meow_notif 结构(title<-summary;urgency 结构无字段故列留空;均做 CSV
|
||||
* 转义/去换行)。无卡/不可写仅打点降级,绝不崩。 */
|
||||
int meow_notif_dump_tf(const meow_notif_t *n); /* 0=成功;负=失败(无卡/IO/参数) */
|
||||
void meow_notif_clear_tf(void); /* 开机清板载通知(清空文件,保留目录) */
|
||||
void meow_notif_set_tf_path(const char *path); /* 覆盖转储日志路径;NULL=恢复默认卡路径(宿主单测用) */
|
||||
void meow_notif_set_tf_max(size_t max_bytes); /* 覆盖滚动截断上限;0=恢复默认 10MB(宿主单测用) */
|
||||
|
||||
/* 调试:打印当前 Active 表/App 表(前缀)。 */
|
||||
void meow_notif_dump(const char *log_prefix);
|
||||
|
||||
|
||||
@@ -104,6 +104,10 @@ static const meow_setting_def g_defs[] = {
|
||||
{ "font.class_latin", SET_STR, "", 0,0, NULL, NULL },
|
||||
{ "font.class_latin_sym", SET_STR, "", 0,0, NULL, NULL },
|
||||
{ "font.class_other", SET_STR, "", 0,0, NULL, NULL },
|
||||
/* M2.5x 字体档位分层 / 按需大字库:font.load_full(int 0/1 默认 0)=「加载大字库」开关,
|
||||
* 1=强制全系统用大字库 gbk≈20k 档(覆盖 app 未声明/覆盖默认 font.file);
|
||||
* 0=默认小字库 common≈4k,仅 app 声明 fonts:"full" 才大字库。见 §5.2/§5.7.8。 */
|
||||
{ "font.load_full", SET_INT, "0", 0, 1, NULL, NULL },
|
||||
{ "font.size.title", SET_INT, "28", 0, 0, NULL, NULL },
|
||||
{ "font.size.body", SET_INT, "20", 0, 0, NULL, NULL },
|
||||
{ "ui.style", SET_ENUM, "md3", 0, 0, "md3,miui", "Material You,MIUI" },
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/* test_notif_tf.c — 宿主单测:通知#6 TF 转储(CSV 转义 / 10MB 滚动截断 / init 清板载+内存表)。
|
||||
*
|
||||
* 直接 #include "meow_notif.c"(同 TU 平移其 static 常量/函数),stub 掉 meow_settings_get_int
|
||||
* (配额读——返回负=键缺失即用默认,涵盖 CSV/截断/init 逻辑)。宿主 gcc 编译运行即可,不进固件。
|
||||
*
|
||||
* 运行:gcc -D_GNU_SOURCE -o test_notif_tf test_notif_tf.c && ./test_notif_tf
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* stub:meow_notif.c 的 read_quota() 读 notify.* 配额。<0 => 键缺失=> 用默认值 */
|
||||
int meow_settings_get_int(const char *key, int *out)
|
||||
{
|
||||
(void)key; (void)out;
|
||||
return -2; /* MEOW_ERR_NOENT */
|
||||
}
|
||||
|
||||
#include "meow_notif.c" /* 把全部实现(含 statics)收进本 TU */
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, msg) do { \
|
||||
if (!(cond)) { fprintf(stderr, "FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); g_fail++; } \
|
||||
else fprintf(stderr, "ok: %s\n", msg); \
|
||||
} while (0)
|
||||
|
||||
static char *read_all(const char *path, long *out_len)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
char *s = malloc((size_t)n + 1);
|
||||
if (!s) { fclose(f); return NULL; }
|
||||
size_t got = fread(s, 1, (size_t)n, f);
|
||||
s[got] = 0;
|
||||
fclose(f);
|
||||
if (out_len) *out_len = n;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void make_notif(meow_notif_t *n, uint32_t id, const char *sum, const char *body)
|
||||
{
|
||||
memset(n, 0, sizeof *n);
|
||||
n->id = id;
|
||||
n->created_ms = 1234567890123ULL;
|
||||
strcpy(n->app_id, "com.app");
|
||||
strcpy(n->category, "general");
|
||||
strcpy(n->summary, sum);
|
||||
strcpy(n->body, body);
|
||||
strcpy(n->target_activity, "act");
|
||||
n->ongoing = 1;
|
||||
}
|
||||
|
||||
/* --- 1) CSV 转义 + 去换行 --- */
|
||||
static void test_csv_escape(const char *path)
|
||||
{
|
||||
meow_notif_set_tf_path(path);
|
||||
meow_notif_set_tf_max(1024 * 1024); /* 放大上限避免此测触发截断 */
|
||||
|
||||
meow_notif_t n;
|
||||
/* summary: 逗号 + 双引号 + 换行;body: CRLF + tab */
|
||||
make_notif(&n, 42, "Hel\"lo, world\nline2", "Body\r\nwith\ttab");
|
||||
n.media = 1; /* 让 media 列=1 */
|
||||
CHECK(meow_notif_dump_tf(&n) == 0, "dump_tf returns 0");
|
||||
|
||||
char *s = read_all(path, NULL);
|
||||
CHECK(s != NULL, "file exists after dump");
|
||||
const char *expect =
|
||||
"1234567890123,42,\"com.app\",\"general\",\"\",\"Hel\"\"lo, world line2\","
|
||||
"\"Body with\ttab\",\"act\",1,1,0\n"; /* ongoing=1 media=1 dismissed=0 */
|
||||
CHECK(s && strcmp(s, expect) == 0, "CSV line matches expected (escape+newline-strip)");
|
||||
if (s) { fprintf(stderr, " got: %s", s); fprintf(stderr, " exp: %s", expect); }
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* --- 2) 10MB 滚动截断(假文件:把上限压到很小模拟)--- */
|
||||
static void test_trim(const char *path)
|
||||
{
|
||||
meow_notif_set_tf_path(path);
|
||||
meow_notif_set_tf_max(256); /* 用 256B 模拟 10MB 上限 */
|
||||
|
||||
/* 先写 4 条不同标记,让文件超过 256B */
|
||||
meow_notif_t n;
|
||||
make_notif(&n, 1, "lineAAAA", "");
|
||||
meow_notif_dump_tf(&n);
|
||||
make_notif(&n, 2, "lineBBBB", "");
|
||||
meow_notif_dump_tf(&n);
|
||||
make_notif(&n, 3, "lineCCCC", "");
|
||||
meow_notif_dump_tf(&n);
|
||||
make_notif(&n, 4, "lineDDDD", "");
|
||||
meow_notif_dump_tf(&n);
|
||||
|
||||
long len = 0;
|
||||
char *s = read_all(path, &len);
|
||||
CHECK(s != NULL && len <= 256, "after roll, file size <= limit (10MB analog)");
|
||||
CHECK(s && strstr(s, "lineDDDD") != NULL, "newest row retained after roll");
|
||||
CHECK(s && strstr(s, "lineAAAA") == NULL, "oldest row rolled away");
|
||||
if (s) fprintf(stderr, " rolled content (%ldB):\n%s", len, s);
|
||||
|
||||
/* 追加一条仍 0 成功 */
|
||||
make_notif(&n, 5, "lineEEEE", "");
|
||||
CHECK(meow_notif_dump_tf(&n) == 0, "append after roll ok");
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* --- 3) init 清板载 + 清内存表,且 post 正常 --- */
|
||||
static void test_init_clear(const char *path)
|
||||
{
|
||||
meow_notif_set_tf_path(path);
|
||||
meow_notif_set_tf_max(1024 * 1024);
|
||||
|
||||
/* 先塞点板载内容 */
|
||||
meow_notif_t n;
|
||||
make_notif(&n, 99, "junk", "");
|
||||
meow_notif_dump_tf(&n);
|
||||
|
||||
/* init:应清板载文件 + 清内存表 */
|
||||
CHECK(meow_notif_init() == NOTIF_OK, "meow_notif_init ok");
|
||||
long len = -1;
|
||||
char *s = read_all(path, &len);
|
||||
CHECK(s != NULL && len == 0, "board log cleared at init (dir kept)");
|
||||
CHECK(meow_notif_count(NULL) == 0, "memory table cleared at init");
|
||||
|
||||
/* init 后 post 一条,应活跃 + 可转储 */
|
||||
make_notif(&n, 0, "fresh", "");
|
||||
uint32_t id = 0;
|
||||
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "post after init ok");
|
||||
CHECK(id != 0 && meow_notif_count(NULL) == 1, "one active notif after post");
|
||||
free(s);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char tmp[] = "/tmp/meow_notif_tf_test_XXXXXX";
|
||||
char *dir = mkdtemp(tmp);
|
||||
if (!dir) { perror("mkdtemp"); return 2; }
|
||||
|
||||
char p1[512], p2[512], p3[512];
|
||||
snprintf(p1, sizeof p1, "%s/csv.log", dir);
|
||||
snprintf(p2, sizeof p2, "%s/trim.log", dir);
|
||||
snprintf(p3, sizeof p3, "%s/init.log", dir);
|
||||
|
||||
CHECK(p1[0] != 0, "tmpdir ready");
|
||||
test_csv_escape(p1);
|
||||
test_trim(p2);
|
||||
test_init_clear(p3);
|
||||
|
||||
/* 清理:删除日志文件 + 目录(保底) */
|
||||
unlink(p1); unlink(p2); unlink(p3); rmdir(dir);
|
||||
|
||||
fprintf(stderr, "\n%s (failures=%d)\n", g_fail ? "== TEST FAILED ==" : "== TEST PASSED ==", g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
@@ -328,22 +328,24 @@ const char *meow_notify_urgency_str(meow_notify_urgency u);
|
||||
const char *meow_notify_category_str(meow_notify_category cat);
|
||||
|
||||
/* ============================================================ */
|
||||
/* §10 字体分层 / 大字库(草案/待定案 —— 本批仅加声明/枚举/宏,不实现) */
|
||||
/* §10 字体分层 / 大字库(M2.5x:基座声明 + font 域已实现按档装载) */
|
||||
/* ============================================================ */
|
||||
/* 字体「档位」概念:launcher 按 app.json 声明的字库档("fonts":<档>)向 app 注入
|
||||
* MEOW_FONT_TIER=<0|1|2>;SDK(meow_ui_gfx / 后续 meow_font 大字库加载)据档选库:
|
||||
* MEOW_FONT_TIER=<0|1|2>;SDK(meow_ui_gfx / meow_font 大字库加载)据档选库:
|
||||
* MEOW_FONT_TIER_COMMON=0 默认 4K 常用字库;
|
||||
* MEOW_FONT_TIER_GBK =1 大字库 GBK;
|
||||
* MEOW_FONT_TIER_FULL =2 Full(全字符集)。
|
||||
* 后续 font 域代理据此在 meow_font.c / gen_font.py 实现大字库生成与按档装载。
|
||||
* 既有 meow_font_* 符号(class_of/has_glyph/load_slot/config_multifont…)保留不动;
|
||||
* 大字库装载以 set_tier/tier 作为入口,或由后续代理按 meow_font_config_multifont 扩展。 */
|
||||
* 大字库生成由 font/gen/gen_font.py --charset 按档产出;按档装载由 meow_font_reload_tier()
|
||||
* 实现(先 unload 旧再 load 新,释放旧 atlas 防大字库双驻留)。launcher 经
|
||||
* meow_font_effective_paths_tiered() 按档解析大字库绝对路径并注入 MEOW_FONT_PNG/IDX。
|
||||
* 既有 meow_font_* 符号(class_of/has_glyph/load_slot/config_multifont…)保留不动。
|
||||
* 判缺字:meow_font_has_glyph(cp) 预判当前字库是否含该码位。 */
|
||||
#define MEOW_FONT_TIER_COMMON 0
|
||||
#define MEOW_FONT_TIER_GBK 1
|
||||
#define MEOW_FONT_TIER_FULL 2
|
||||
|
||||
/* 设当前字体档(0/1/2,越界 clamp 到合法值),返回生效档。
|
||||
* 基座在 meow_ui_gfx.c 定义(只存档 + 触发重载请求);真正按档装载大字库由后续 font 代理实现。 */
|
||||
* meow_ui_gfx.c 定义并触发 meow_font_reload_tier() 真重载(大字库切换释放旧 atlas)。 */
|
||||
int meow_font_set_tier(int tier);
|
||||
/* 当前字体档(初始 = MEOW_FONT_TIER_COMMON;meow_ui_init 时读 MEOW_FONT_TIER env 对齐)。 */
|
||||
int meow_font_tier(void);
|
||||
|
||||
+23
-11
@@ -77,8 +77,8 @@ static unsigned long long g_sbar_collapse_start_ms = 0; /* 本次过渡起始
|
||||
|
||||
/* ---- 字体档位(MEOW_FONT_TIER,基座存档 + 公共 get/set)----
|
||||
* launcher 按 app.json "fonts":<档> 注入 MEOW_FONT_TIER=<0|1|2>;SDK 读入并以
|
||||
* meow_font_set_tier/tier 暴露。真正按档装载大字库(GBK/Full)由后续 font 代理在
|
||||
* meow_font.c / meow_gfx_load_multifont 挂钩实现——此处仅记录档位不换库。 */
|
||||
* meow_font_set_tier/tier 暴露。按档装载大字库(GBK/Full)由 meow_font_reload_tier()
|
||||
* 在 meow_font.c 实现;meow_font_set_tier() 在档位变化时经 env/settings 路由到该钩子真重载。 */
|
||||
static int g_font_tier = MEOW_FONT_TIER_COMMON;
|
||||
|
||||
/* ---- 帧率调度(A-1/A-2:从 launcher M2.16 迁入,刷新率调度归口 SDK)----
|
||||
@@ -665,20 +665,32 @@ unsigned long long meow_ui_frames_presented(void) { return g_frames_presented; }
|
||||
int meow_gfx_is_device(void) { return g_fb_mem != NULL; }
|
||||
int meow_gfx_mfont_ready(void) { return g_mfont_ready; }
|
||||
|
||||
/* ---- 字体档位(公共 API,基座定义)----
|
||||
* 注意:meow_font_tier()/meow_font_set_tier() 已在 meow_ui_gfx.c 定义(符号唯一),
|
||||
* 后续 font 代理勿在 meow_font.c 重复定义;可在 meow_font.c 实现「按档装载大字库」并在
|
||||
* meow_font_set_tier 的钩子处唤起(见下)。 */
|
||||
/* ---- 字体档位(公共 API,基座定义 + font 域真重载钩子)----
|
||||
* meow_font_tier()/meow_font_set_tier() 在 meow_ui_gfx.c 定义(符号唯一),font 代理勿在
|
||||
* meow_font.c 重复定义;大字库按档装载由 meow_font.c meow_font_reload_tier() 实现,并在
|
||||
* meow_font_set_tier() 处唤起(见下)。 */
|
||||
int meow_font_tier(void) { return g_font_tier; }
|
||||
|
||||
int meow_font_set_tier(int tier)
|
||||
{
|
||||
int old = g_font_tier;
|
||||
if (tier < MEOW_FONT_TIER_COMMON) tier = MEOW_FONT_TIER_COMMON;
|
||||
if (tier > MEOW_FONT_TIER_FULL) tier = MEOW_FONT_TIER_FULL;
|
||||
if (tier == g_font_tier) return g_font_tier;
|
||||
g_font_tier = tier;
|
||||
/* TBD(基座):真正按档装载大字库(meow_font_load_tier 或 config_multifont 扩展)由
|
||||
* 后续 font 代理在 meow_font.c 实现;此处仅记录档位 + 标记重载(下一轮 load 读 tier)。 */
|
||||
/* 真重载(font 域钩子,M2.5x):按档装载大字库。launcher 已按档解析并注入
|
||||
* MEOW_FONT_PNG/IDX(tier>0 → GBK/Full 大字库绝对路径);重读 env 重载。
|
||||
* 无 env/失败 → 回退卡默认 MeowOS/fonts/default → 固件。
|
||||
* 大字库 atlas 大(GBK≈5-15MB+),meow_font_reload_tier 先 unload 旧再 load 新,防双驻留。 */
|
||||
const char *ep = getenv("MEOW_FONT_PNG");
|
||||
const char *ei = getenv("MEOW_FONT_IDX");
|
||||
if (ep && ep[0] && ei && ei[0] && meow_font_reload_tier(tier, ep, ei) == 0)
|
||||
return g_font_tier;
|
||||
if (meow_font_reload_tier(tier, MEOW_FONT_PNG_CARD, MEOW_FONT_IDX_CARD) == 0)
|
||||
return g_font_tier;
|
||||
if (meow_font_reload_tier(tier, MEOW_FONT_PNG_SYS, MEOW_FONT_IDX_SYS) == 0)
|
||||
return g_font_tier;
|
||||
g_font_tier = old; /* 全部失败 → 回退旧档(字库可能已不可用,5x7 点阵兜底,系统不死) */
|
||||
return g_font_tier;
|
||||
}
|
||||
|
||||
@@ -692,9 +704,9 @@ int meow_font_set_tier(int tier)
|
||||
static int load_font(void)
|
||||
{
|
||||
/* 字体档位(MEOW_FONT_TIER env,由 launcher 按 app.json "fonts" 注入):
|
||||
* 0=默认4K常用 1=大字库GBK 2=Full。基座只读入存档 + 对齐公共 get/set;
|
||||
* 真正按档装载大字库(GBK/Full 的字库路径/面)由后续 font 代理在 meow_font.c /
|
||||
* meow_gfx_load_multifont 挂钩实现——此处仍先装载默认(tier0)字库,档位仅记录。 */
|
||||
* 0=默认4K常用 1=大字库GBK 2=Full。launcher 已按档解析大字库路径并经 MEOW_FONT_PNG/IDX
|
||||
* 注入(见 meow_font_effective_paths_tiered),故下方 meow_font_load(ep,ei) 即装载
|
||||
* 对应档位字库(tier>0 → GBK/Full 大字库);此处 read env 存档对齐公共 get/set。 */
|
||||
const char *etr = getenv("MEOW_FONT_TIER");
|
||||
if (etr && etr[0]) {
|
||||
g_font_tier = atoi(etr);
|
||||
|
||||
@@ -38,6 +38,20 @@
|
||||
#define QUEUE_N 128
|
||||
#define MAX_DEVS 8
|
||||
|
||||
/* ── 内部事件:系统级「下拉展开通知中心」(M2.5x 接线)──
|
||||
* 触摸状态机在抬手判定处识别「SWIPE_DOWN 起始于顶部热区」后压入;事件循环按系统手势处理
|
||||
* (路由 meow_ui_sys_notif_pull),不下发给 APP 的 on_swipe(顶部热区为系统保留手势)。
|
||||
* 与左缘返回(SWIPE_LEFT)/ meow_ui_swipe_hint 互不影响——后者仍全屏生效,详见下方触摸机。 */
|
||||
#define MEOW_EV_NOTIF_PULL 5
|
||||
|
||||
/* 顶部下滑 → 通知中心判定:纵向 DOWN 手势的起点(origin_y)是否落在顶部热区内。
|
||||
* 热区由 meow_ui_statusbar_hotzone() 给出(非全屏=状态栏高、全屏=MEOW_SBAR_HOTZONE_FS、
|
||||
* 收合=提示线高)。供宿主单测直调(判据与触摸状态机共用)。 */
|
||||
int meow_ui_input_is_notif_pull(int origin_y, int hotzone)
|
||||
{
|
||||
return origin_y >= 0 && origin_y < hotzone;
|
||||
}
|
||||
|
||||
static int g_thread_started = 0;
|
||||
static pthread_t g_thread;
|
||||
static volatile int g_thread_run = 1;
|
||||
@@ -165,7 +179,14 @@ static void touch_release(void)
|
||||
}
|
||||
} else {
|
||||
int dir = mdy > 0 ? MEOW_SWIPE_DOWN : MEOW_SWIPE_UP;
|
||||
push_ev(MEOW_EV_SWIPE, dir, ady > adx ? ady : adx);
|
||||
/* 顶部下滑 → 通知中心(系统手势,不下发 APP):起点在顶部热区内才认。
|
||||
* 热区高度随状态变化(非全屏=整条状态栏、全屏=MEOW_SBAR_HOTZONE_FS 缩小、
|
||||
* 收合=提示线高)——全屏 app 只在顶部极窄热区被系统占用,故「不影响全屏 app」。 */
|
||||
if (dir == MEOW_SWIPE_DOWN &&
|
||||
meow_ui_input_is_notif_pull(t_dy, meow_ui_statusbar_hotzone()))
|
||||
push_ev(MEOW_EV_NOTIF_PULL, t_dy, ady > adx ? ady : adx);
|
||||
else
|
||||
push_ev(MEOW_EV_SWIPE, dir, ady > adx ? ady : adx);
|
||||
}
|
||||
} else {
|
||||
push_ev(MEOW_EV_TAP, t_cx, t_cy);
|
||||
@@ -287,6 +308,11 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
} else if (e.a == MEOW_SWIPE_LEFT && sb_active) {
|
||||
meow_ui_request_exit();
|
||||
}
|
||||
} else if (e.type == MEOW_EV_NOTIF_PULL) {
|
||||
if (in_startup) continue; /* 启动窗口顶区误触残留:丢弃,防进入即开通知中心 */
|
||||
/* 系统级「下拉展开通知中心」:起始于顶部热区的下滑为系统保留手势,不下发
|
||||
* APP on_swipe;路由到系统钩子(当前为记录占位,通知中心 UI 为后续里程碑)。 */
|
||||
meow_ui_sys_notif_pull();
|
||||
} else if (e.type == MEOW_EV_SWIPE_PROGRESS) {
|
||||
if (in_startup) continue; /* 启动窗口进度条静音 */
|
||||
int x0 = e.a, xc = e.b, dx = xc - x0; /* dx=当前X-按下X */
|
||||
|
||||
@@ -156,18 +156,31 @@ void meow_ui_set_fullscreen(int on)
|
||||
|
||||
int meow_ui_statusbar_height(void)
|
||||
{
|
||||
return meow_ui_sys_statusbar_active() ? MUI_SBAR_H : 0;
|
||||
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)
|
||||
{
|
||||
return g_statusbar_on && !g_fullscreen;
|
||||
/* 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)
|
||||
{
|
||||
return meow_ui_sys_statusbar_active() ? MUI_SBAR_H : 0;
|
||||
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(void)
|
||||
{
|
||||
/* 系统级「下拉展开通知中心」手势接线(M2.5x):输入层识别出「起始于顶部热区的
|
||||
* SWIPE_DOWN」后路由到此。通知中心 UI 为后续里程碑——此处先落记录(避免哑无痕);
|
||||
* 届时由对应代理在该入口挂真实展开逻辑(IPC SHOW_ACTIVITY / 覆盖层渲染)。 */
|
||||
meow_log(MEOW_LOG_WARN, "notif_pull: notification center TBD (wired)");
|
||||
}
|
||||
|
||||
int meow_ui_sys_statusbar_force(void)
|
||||
|
||||
@@ -34,6 +34,13 @@ typedef struct meow_sys_state {
|
||||
#define MUI_SBAR_H 28
|
||||
#define MUI_SBAR_FONT 20
|
||||
|
||||
/* 收合(collapsed)状态下状态栏的有效加载高度(inset)= 顶部提示线高度(px)。
|
||||
* 收合=状态栏内容上移出屏、只留一条提示线:app 内容据此上移(非满 28),
|
||||
* 形成「不完全沉浸(留提示线)」;与 fullscreen(整条隐藏、inset0)区别。
|
||||
* 取值沿用公共宏 MEOW_SBAR_HOTZONE_FS(=4,全屏时顶部热区/提示线的缩后高度),
|
||||
* 具体绘制高度(gfx.c 的 MUI_SBAR_HINT_H=6)在渲染端,这里按提示线高度口径即可。 */
|
||||
#define MUI_SBAR_COLLAPSE_H MEOW_SBAR_HOTZONE_FS
|
||||
|
||||
/* 首次全量读取;失败仍返回 0(字段置 -1/0)。 */
|
||||
int meow_ui_sys_init(void);
|
||||
|
||||
@@ -43,12 +50,21 @@ void meow_ui_sys_refresh(void);
|
||||
/* 当前快照(内部只读)。 */
|
||||
const meow_sys_state *meow_ui_sys_get(void);
|
||||
|
||||
/* 状态栏是否应绘制(= set_statusbar(1) 且 非 fullscreen)。 */
|
||||
/* 状态栏是否应绘制(= set_statusbar(1) 且 非 fullscreen),收合(collapsed)兼容:
|
||||
* collapsed 视为状态栏仍在(只留提示线),故 collapsed 时即使 fullscreen 也返回 1,
|
||||
* 让渲染端/输入层继续驱动上收动画与提示线;fullscreen 且不 collapsed → 0(整条隐藏,
|
||||
* 沉浸,保持既有语义)。 */
|
||||
int meow_ui_sys_statusbar_active(void);
|
||||
|
||||
/* 状态栏高度(px):active 时 28,否则 0(供 meow_ui_statusbar_height 用)。 */
|
||||
/* 状态栏有效高度(px,供 inset/提示条定位):active 时 28;collapsed 时=提示线高度
|
||||
* MUI_SBAR_COLLAPSE_H(非满 28,app 内容可上移);否则 0(供 meow_ui_statusbar_height 用)。 */
|
||||
int meow_ui_sys_statusbar_height(void);
|
||||
|
||||
/* 系统级「下拉展开通知中心」手势挂钩(M2.5x 接线):输入层识别出「起始于顶部热区的
|
||||
* SWIPE_DOWN」后路由到这里。通知中心 UI 为后续里程碑,此入口作记录/占位;届时由对应
|
||||
* 代理在此挂真实展开逻辑(IPC SHOW_ACTIVITY / 覆盖层渲染)。 */
|
||||
void meow_ui_sys_notif_pull(void);
|
||||
|
||||
/* 自上次渲染后状态栏数据是否已变(1s 轻刷/显隐切换置位);读取即清除。
|
||||
* 渲染后端 present() 用它决定是否重画顶部条(配合脏区/整帧判断)。 */
|
||||
int meow_ui_sys_statusbar_force(void);
|
||||
|
||||
Reference in new issue
Block a user