字重结构+family→字重子菜单(agent-111: fonts/<family>/<weight>/, gen_font.py --weight, 两级扫描, s_font_dropdown子菜单) + 全屏左滑滑条修复(agent-112)
This commit is contained in:
1 parent
e8fa373a2f
commit
b75ecce5de
16 files changed
+531
-164
No files matched your search
@@ -782,27 +782,37 @@ static void draw_text(int x, int y, const char *s, int scale, uint32_t color)
|
||||
|
||||
/* ================================================================== */
|
||||
/* M2.6 meow_font 集成(agent-28 字体子系统,font/meow_font.a + libz.a)*/
|
||||
/* 字库放 TF 卡 MeowOS/fonts/default/font.png+font.idx(或固件 /usr/share/ */
|
||||
/* meowos/font/);MeowOS 启动 ensure_card_mounted 后再 load,卡晚 */
|
||||
/* M2.6 MeowOS 字重结构:字库放 TF 卡 MeowOS/fonts/default/regular/font.png+font.idx
|
||||
*(或固件 /usr/share/meowos/font/);MeowOS 启动 ensure_card_mounted 后再 load,卡晚 */
|
||||
/* 挂载时轮询重试。没加载成功时 ui_draw 回退 5x7 点阵(ASCII)。 */
|
||||
/* ================================================================== */
|
||||
#define UI_FONT_PNG_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.png"
|
||||
#define UI_FONT_IDX_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.idx"
|
||||
#define UI_FONT_PNG_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.png"
|
||||
#define UI_FONT_IDX_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.idx"
|
||||
#define UI_FONT_PNG_SYS "/usr/share/meowos/font/font.png"
|
||||
#define UI_FONT_IDX_SYS "/usr/share/meowos/font/font.idx"
|
||||
/* M2.5x:TF 卡多字体库 —— 字体目录 = <卡根>/MeowOS/fonts/<family>/(font.png+font.idx+font.json)。
|
||||
/* M2.5x 多字体库 + M2.6 字重:字体目录 = <卡根>/MeowOS/fonts/<family>/<weight>/(font.png+font.idx+font.json)。
|
||||
* <卡根> 默认 /usr/data/mnt/sd_0(card_present() 判定;--fontscan 可指定宿主根以便单测)。 */
|
||||
#define MEOW_FONTS_CFG "MeowOS/fonts"
|
||||
#define FONT_PRESETS_MAX 17 /* 1 默认 + ≤16 卡内字体(防爆) */
|
||||
/* M2.6 字重结构:卡内字体 = <卡根>/MeowOS/fonts/<family>/<weight>/(三件套)。
|
||||
* 每个 preset = 一套 <family>/<weight>(如 misans/regular)。值 = 卡根相对路径
|
||||
* MeowOS/fonts/<family>/<weight>/font.png;family/weight 单列供设置页「family→字重」子菜单
|
||||
* 分组与 meow_font_effective_paths_tiered 按 family+weight 选库。 */
|
||||
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 const char *font_preset_fams[FONT_PRESETS_MAX]; /* family slug(目录一级) */
|
||||
static const char *font_preset_wts[FONT_PRESETS_MAX]; /* weight slug(目录二级) */
|
||||
static const char *font_preset_names[FONT_PRESETS_MAX]; /* font.json name(family 显示名) */
|
||||
static char font_preset_val_buf[FONT_PRESETS_MAX][320]; /* 值持久存储(指向上面指针数组) */
|
||||
static char font_preset_label_buf[FONT_PRESETS_MAX][80]; /* 标签持久存储 */
|
||||
static char font_preset_label_buf[FONT_PRESETS_MAX][96]; /* 标签持久存储(含字重+档位标记) */
|
||||
static char font_preset_tier_buf[FONT_PRESETS_MAX][8]; /* charset 持久存储 */
|
||||
static char font_preset_fam_buf[FONT_PRESETS_MAX][64]; /* family 持久存储 */
|
||||
static char font_preset_wt_buf[FONT_PRESETS_MAX][32]; /* weight 持久存储 */
|
||||
static char font_preset_name_buf[FONT_PRESETS_MAX][64]; /* name 持久存储 */
|
||||
static int font_presets_n = 0; /* 运行时动态计数(默认=1,仅"默认字体") */
|
||||
static int mfont_ready = 0;
|
||||
static int mfont_forced = 0; /* font.file 变更→强制真实(un)load,跳过 SDK 复用捷径 */
|
||||
@@ -855,7 +865,7 @@ static int meow_font_resolve_selected(char *out_png, int png_len, char *out_idx,
|
||||
/* 尝试加载字库;成功返回 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(默认字库);
|
||||
* 2) 卡 MeowOS/fonts/default/regular/font.png(默认字库);
|
||||
* 3) 固件 /usr/share/meowos/font/font.png。
|
||||
* A-1/A-2:SDK meow_ui_init() 已用同一 meow_font.o 加载过(可能成功也可能未成功)。
|
||||
* 当字体指向「默认卡字库」或未设置时复用 SDK 已加载状态,避免重复 unload+reload;
|
||||
@@ -899,9 +909,42 @@ static int meow_font_ensure(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 从 MeowOS/fonts/<family>/<weight>/font.png 绝对路径提 family(第一级目录名)。
|
||||
* 如 .../misans/regular/font.png → "misans"、.../default/regular/font.png → "default";
|
||||
* 结构异常/找不到前缀 → 回退 "default"。 */
|
||||
static void font_family_from_png(const char *png, char *out, int olen)
|
||||
{
|
||||
const char *prefix = "/MeowOS/fonts/";
|
||||
const char *p = png ? strstr(png, prefix) : NULL;
|
||||
if (!p) { snprintf(out, olen, "default"); return; }
|
||||
const char *q = p + strlen(prefix);
|
||||
const char *slash = strchr(q, '/');
|
||||
int n = slash ? (int)(slash - q) : (int)strlen(q);
|
||||
if (n <= 0 || n >= olen) { snprintf(out, olen, "default"); return; }
|
||||
memcpy(out, q, (size_t)n); out[n] = 0;
|
||||
}
|
||||
|
||||
/* 同路径同时提取 family + weight(两级目录名)。weight 缺省 "regular"。 */
|
||||
static void font_family_weight_from_png(const char *png, char *fam, int flen, char *wt, int wlen)
|
||||
{
|
||||
const char *prefix = "/MeowOS/fonts/";
|
||||
const char *p = png ? strstr(png, prefix) : NULL;
|
||||
if (!p) { snprintf(fam, flen, "default"); snprintf(wt, wlen, "regular"); return; }
|
||||
const char *q = p + strlen(prefix);
|
||||
const char *s1 = strchr(q, '/');
|
||||
if (!s1) { snprintf(fam, flen, "%s", q); snprintf(wt, wlen, "regular"); return; }
|
||||
int nf = (int)(s1 - q);
|
||||
if (nf > 0 && nf < flen) { memcpy(fam, q, (size_t)nf); fam[nf] = 0; }
|
||||
else snprintf(fam, flen, "default");
|
||||
const char *s2 = strchr(s1 + 1, '/');
|
||||
int nw = s2 ? (int)(s2 - (s1 + 1)) : (int)strlen(s1 + 1);
|
||||
if (nw > 0 && nw < wlen) { memcpy(wt, s1 + 1, (size_t)nw); wt[nw] = 0; }
|
||||
else snprintf(wt, wlen, "regular");
|
||||
}
|
||||
|
||||
/* 求「当前生效字体」的 png/idx 绝对路径 + family,供 launch_app 注入 MEOW_FONT_PNG/IDX/APP。
|
||||
* 优先级(与 meow_font_ensure 一致):font.file(resolve_selected)→ 卡默认 MeowOS/fonts/default。
|
||||
* family 取自 MeowOS/fonts/<family>/font.png 目录名;无法判定则 "default"。
|
||||
* family 取自 MeowOS/fonts/<family>/<weight>/font.png 的第一级目录名;无法判定则 "default"。
|
||||
* 返回 0 成功(out_png/out_idx/out_family 已填),-1 无卡/无可用字体(调用方跳过注入,
|
||||
* SDK 自身会回退卡默认→固件→点阵)。 */
|
||||
static int meow_font_effective_paths(char *out_png, int plen, char *out_idx, int ilen,
|
||||
@@ -911,15 +954,7 @@ static int meow_font_effective_paths(char *out_png, int plen, char *out_idx, int
|
||||
if (meow_font_resolve_selected(fpng, sizeof fpng, fidx, sizeof fidx) == 0) {
|
||||
snprintf(out_png, plen, "%s", fpng);
|
||||
snprintf(out_idx, ilen, "%s", fidx);
|
||||
const char *prefix = "/MeowOS/fonts/";
|
||||
const char *p = strstr(fpng, 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");
|
||||
font_family_from_png(fpng, out_family, flen);
|
||||
return 0;
|
||||
}
|
||||
/* 回退卡默认 MeowOS/fonts/default */
|
||||
@@ -958,9 +993,10 @@ static int meow_font_effective_tier(int app_tier)
|
||||
* - 生效档 = 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 直接点到固件小库)。
|
||||
* - tier>0 → 在已扫描的 MeowOS/fonts/<family>/<weight>/ 里找 charset 匹配(gbk 请求接受
|
||||
* gbk/full;full 请求接受 full)的那套,以其 font.png/idx 绝对路径返回(family = 一级目录名);
|
||||
* - M2.6 字重:优先选「与用户当前 font.file 同 family(其次同 weight)」的大字库,避免
|
||||
* 大字库跳到另一家字体;找不到匹配 → 回退 meow_font_effective_paths(避免点到固件小库)。
|
||||
* 返回 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)
|
||||
@@ -969,20 +1005,32 @@ static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
|
||||
if (eff <= MEOW_FONT_TIER_COMMON)
|
||||
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
|
||||
/* 大字库:从扫描结果选 charset 匹配的一套。
|
||||
* 打分(高优先):gbk 请求 → gbk=2 > full=1;full 请求 → full=2 > gbk=1(降级可接受)。 */
|
||||
* 打分(档位主导 ×100):gbk 请求 → gbk=200 > full=100;full 请求 → full=200 > gbk=100;
|
||||
* + 同 family +30(其次同 weight +10,M2.6)仅作同档位内 tie-break:大字库沿用用户所选
|
||||
* family,但绝不因「同 family」而把 common 小库越档选成大字库。 */
|
||||
const char *want = font_tier_charset(eff);
|
||||
char curfam[64] = "", curwt[32] = "";
|
||||
{ /* 用户当前 font.file 所选 family/weight(无法判定 → 空,不参与加成) */
|
||||
char fpng[400], fidx[400];
|
||||
if (meow_font_resolve_selected(fpng, sizeof fpng, fidx, sizeof fidx) == 0)
|
||||
font_family_weight_from_png(fpng, curfam, sizeof curfam, curwt, sizeof curwt);
|
||||
}
|
||||
char ref[320];
|
||||
int found_family = -1, best_score = 0;
|
||||
int found = -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";
|
||||
/* 档位优先(×100 主导):gbk 请求 → gbk=200 > full=100;full 请求 → full=200 > gbk=100。
|
||||
* M2.6:同 family +30、同 weight +10 仅作同档位内 tie-break(不越档选小库)。 */
|
||||
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 (!strcmp(cs, want)) score = 200;
|
||||
else if (!strcmp(cs, "full") || !strcmp(cs, "gbk")) score = 100; /* 降级 */
|
||||
if (curfam[0] && font_preset_fams[i] && !strcmp(font_preset_fams[i], curfam)) score += 30;
|
||||
if (curfam[0] && curwt[0] && font_preset_wts[i] && !strcmp(font_preset_wts[i], curwt)) score += 10;
|
||||
if (score > best_score) { best_score = score; found = i; }
|
||||
}
|
||||
if (found_family >= 0)
|
||||
snprintf(ref, sizeof ref, "%s", font_presets[found_family]);
|
||||
if (found_family >= 0) {
|
||||
if (found >= 0)
|
||||
snprintf(ref, sizeof ref, "%s", font_presets[found]);
|
||||
if (found >= 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;
|
||||
@@ -992,15 +1040,7 @@ static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
|
||||
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");
|
||||
font_family_from_png(out_png, out_family, flen);
|
||||
return 0;
|
||||
}
|
||||
fallback:
|
||||
@@ -1680,15 +1720,17 @@ static int ensure_card_mounted(void)
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
/* M2.5x:TF 卡多字体库扫描 + preset 动态化 */
|
||||
/* 字体目录 = <卡根>/MeowOS/fonts/<family>/,每套含三件套: */
|
||||
/* M2.5x 多字体库扫描 + M2.6 字重结构 / preset 动态化 */
|
||||
/* 字体目录 = <卡根>/MeowOS/fonts/<family>/<weight>/,每套含三件套: */
|
||||
/* font.png(预渲染灰度 PNG,bit depth 4|8)+ font.idx(MFNI v2 索引) */
|
||||
/* + font.json(元数据 + 完整性校验)。缺任一件 → 该套不进列表 + 日志。 */
|
||||
/* font.json schema(定版): */
|
||||
/* { */
|
||||
/* "family": "家族标识(同目录名)", // 信息用,不参与校验 */
|
||||
/* "family": "家族标识(同目录名/family)", // 信息用,不参与校验 */
|
||||
/* "name": "下拉显示名", // 缺则回退目录名 */
|
||||
/* "weight": "regular", // 字重 slug(缺省 regular) */
|
||||
/* "glyph_count": 10000, // 信息用 */
|
||||
/* "charset": "common|gbk|full", // 档位(M2.5x,驱动按需加载) */
|
||||
/* "size_list": [28,20], // 信息用 */
|
||||
/* "bit_depth": 4, // 信息用(4|8) */
|
||||
/* "sha256_png": "<64hex>", // font.png 校验 */
|
||||
@@ -1746,14 +1788,17 @@ static int font_sha_eq(const char *a, const char *b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 解析 font.json:提取 name / sha256_png / sha256_idx(文件缺失或解析不出则留空)。 */
|
||||
/* 解析 font.json:提取 name / sha256_png / sha256_idx / weight(M2.6,缺省 "regular")。
|
||||
* 文件缺失或解析不出则留空(name 空 → 调用方回退目录名;weight 空 → 回退 "regular")。 */
|
||||
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 *charset, int clen, int *glyph_count)
|
||||
char *charset, int clen, int *glyph_count,
|
||||
char *weight, int wlen)
|
||||
{
|
||||
name[0] = 0; sha_png[0] = 0; sha_idx[0] = 0;
|
||||
if (charset) charset[0] = 0;
|
||||
if (glyph_count) *glyph_count = 0;
|
||||
if (weight) weight[0] = 0;
|
||||
FILE *f = fopen(json_path, "r");
|
||||
if (!f) return;
|
||||
char buf[256];
|
||||
@@ -1772,10 +1817,31 @@ static void meow_font_json_probe(const char *json_path, char *name, int nlen,
|
||||
if (json_str(buf, "glyph_count", v, sizeof v) > 0 && v[0])
|
||||
*glyph_count = atoi(v);
|
||||
}
|
||||
/* M2.6 字重:weight(regular|bold|italic|…),缺省 "regular"。 */
|
||||
if (weight && !weight[0]) {
|
||||
char v[32] = "";
|
||||
if (json_str(buf, "weight", v, sizeof v) > 0 && v[0])
|
||||
snprintf(weight, wlen, "%s", v);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* 字重 slug → 显示名(首字母大写、连字符/下划线转空格:regular→Regular、bold→Bold、italic→Italic)。 */
|
||||
static void font_weight_display(const char *weight, char *out, int olen)
|
||||
{
|
||||
const char *w = (weight && weight[0]) ? weight : "regular";
|
||||
out[0] = 0;
|
||||
int o = 0, cap = 1;
|
||||
for (const char *p = w; *p && o < olen - 1; p++) {
|
||||
char c = *p;
|
||||
if (c == '_' || c == '-') { if (o < olen - 1) out[o++] = ' '; cap = 1; continue; }
|
||||
out[o++] = cap ? (char)((c >= 'a' && c <= 'z') ? c - 32 : c) : c;
|
||||
cap = 0;
|
||||
}
|
||||
out[o] = 0;
|
||||
}
|
||||
|
||||
/* 档位大小标记(设置页字体选项标注):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)
|
||||
@@ -1788,62 +1854,78 @@ static void font_tier_marker(const char *charset, int glyph_count, char *out, in
|
||||
snprintf(out, olen, "%s", tier);
|
||||
}
|
||||
|
||||
/* 探测一个 <dirpath>/<family> 套件是否合法:三件套齐全 → 解析 label + 档位 + 校验 sha。
|
||||
/* 探测一个 <卡根>/MeowOS/fonts/<family>/<weight> 套件是否合法:三件套齐全 → 解析 label+档位+字重。
|
||||
* 返回 1=进列表,0=跳过(缺组件/json 不可读)。sha 失败仅 WARN。
|
||||
* 输出:label=显示名+大小标记 `名字(common≈4k)`;charset/glyph_count=档位信息。 */
|
||||
static int meow_font_probe_family(const char *dirpath, const char *family,
|
||||
* 输出:label=显示名 `<name> <Weight>(common≈4k)`(如 `MiSans Regular(common≈4k)`);
|
||||
* charset/glyph_count=档位信息;wtout=weight slug(缺省 "regular")。 */
|
||||
static int meow_font_probe_weight(const char *dirpath, const char *family, const char *weight,
|
||||
char *label, int llen,
|
||||
char *charset, int clen, int *glyph_count)
|
||||
char *charset, int clen, int *glyph_count,
|
||||
char *wtout, int wtlen, char *nameout, int namelen)
|
||||
{
|
||||
char ppath[400], ipath[400], jpath[400];
|
||||
snprintf(ppath, sizeof ppath, "%s/font.png", dirpath);
|
||||
snprintf(ipath, sizeof ipath, "%s/font.idx", dirpath);
|
||||
snprintf(jpath, sizeof jpath, "%s/font.json", dirpath);
|
||||
if (access(ppath, R_OK) != 0 || access(ipath, R_OK) != 0 || access(jpath, R_OK) != 0) {
|
||||
logmsg("fonts: skip '%s' (缺 font.png/font.idx/font.json)", family);
|
||||
logmsg("fonts: skip '%s/%s' (缺 font.png/font.idx/font.json)", family, weight);
|
||||
return 0;
|
||||
}
|
||||
char name[64] = "", jsha_png[80] = "", jsha_idx[80] = "";
|
||||
char cs[8] = ""; int gc = 0;
|
||||
char cs[8] = ""; int gc = 0; char wts[32] = "";
|
||||
meow_font_json_probe(jpath, name, sizeof name, jsha_png, sizeof jsha_png,
|
||||
jsha_idx, sizeof jsha_idx, cs, sizeof cs, &gc);
|
||||
/* 显示名 = 名 + 档位大小标记(如 `default(common≈4k)` / `gbk≈20k` / `full`) */
|
||||
jsha_idx, sizeof jsha_idx, cs, sizeof cs, &gc, wts, sizeof wts);
|
||||
const char *wt = wts[0] ? wts : (weight ? weight : "regular");
|
||||
if (wtout) snprintf(wtout, wtlen, "%s", wt);
|
||||
if (nameout) snprintf(nameout, namelen, "%s", name[0] ? name : family);
|
||||
/* 显示名 = `<name> <Weight>(common≈4k)`(字重缺省 Regular) */
|
||||
char wd[24]; font_weight_display(wt, wd, sizeof wd);
|
||||
char marker[24];
|
||||
font_tier_marker(cs, gc, marker, sizeof marker);
|
||||
snprintf(label, llen, "%s(%s)", name[0] ? name : family, marker);
|
||||
snprintf(label, llen, "%s %s(%s)", name[0] ? name : family, wd, 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)
|
||||
logmsg("fonts: '%s' sha256(png) 不可用,跳过校验", family);
|
||||
logmsg("fonts: '%s/%s' sha256(png) 不可用,跳过校验", family, wt);
|
||||
else if (jsha_png[0] && !font_sha_eq(apng, jsha_png))
|
||||
logmsg("fonts: '%s' WARN sha256_png 不匹配(仍可选)", family);
|
||||
logmsg("fonts: '%s/%s' WARN sha256_png 不匹配(仍可选)", family, wt);
|
||||
if (font_sha256_file(ipath, aidx, sizeof aidx) != 0)
|
||||
logmsg("fonts: '%s' sha256(idx) 不可用,跳过校验", family);
|
||||
logmsg("fonts: '%s/%s' sha256(idx) 不可用,跳过校验", family, wt);
|
||||
else if (jsha_idx[0] && !font_sha_eq(aidx, jsha_idx))
|
||||
logmsg("fonts: '%s' WARN sha256_idx 不匹配(仍可选)", family);
|
||||
logmsg("fonts: add '%s' (%s) charset=%s glyph_count=%d", label, family, cs[0]?cs:"common", gc);
|
||||
logmsg("fonts: '%s/%s' WARN sha256_idx 不匹配(仍可选)", family, wt);
|
||||
logmsg("fonts: add '%s/%s' (%s) charset=%s glyph_count=%d", family, wt, label,
|
||||
cs[0]?cs:"common", gc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 扫描 <card_root>/MeowOS/fonts/ 下每个家族子目录,重建字体 preset 列表(默认 + 卡内合法套件)。
|
||||
* 上限 FONT_PRESETS_MAX(1 默认 + ≤16 用户字体)。无卡/无目录时仅默认。 */
|
||||
/* 扫描 <card_root>/MeowOS/fonts/ 下两级 <family>/<weight>/,重建字体 preset 列表。
|
||||
* 每个 (family,weight) 组合一套(如 misans/regular、misans/bold);family 级 'default' 由
|
||||
* 索引 0(默认字体)单独处理,不进循环,避免重复。上限 FONT_PRESETS_MAX
|
||||
* (1 默认 + ≤16 用户字体)。无卡/无目录时仅默认。 */
|
||||
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_label_buf[0]; /* 指向缓冲(运行时派生含大小标记) */
|
||||
/* 默认套也读 font.json 档位(存在则跟真实档,缺则 common);档位信息供分层装载/标注。 */
|
||||
font_presets[0] = "MeowOS/fonts/default/regular/font.png"; /* 默认字库 = MeowOS/fonts/default/regular */
|
||||
font_preset_labels[0] = font_preset_label_buf[0]; /* 指向缓冲(运行时派生含字重+大小标记) */
|
||||
font_preset_fams[0] = font_preset_fam_buf[0];
|
||||
font_preset_wts[0] = font_preset_wt_buf[0];
|
||||
font_preset_names[0] = font_preset_name_buf[0];
|
||||
/* 默认套也读 font.json 档位+字重(存在则跟真实档,缺则 common/regular);供分层装载/标注。 */
|
||||
{
|
||||
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);
|
||||
char nm0[64] = "", sp0[80] = "", si0[80] = "", wt0[32] = "";
|
||||
snprintf(jp, sizeof jp, "%s/%s/default/regular/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);
|
||||
cs, sizeof cs, &gc, wt0, sizeof wt0);
|
||||
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);
|
||||
char wd[24]; font_weight_display(wt0[0] ? wt0 : "regular", wd, sizeof wd);
|
||||
snprintf(font_preset_label_buf[0], sizeof font_preset_label_buf[0], "默认字体 %s(%s)", wd, mk);
|
||||
snprintf(font_preset_tier_buf[0], sizeof font_preset_tier_buf[0], "%s", cs[0]?cs:"common");
|
||||
snprintf(font_preset_fam_buf[0], sizeof font_preset_fam_buf[0], "default");
|
||||
snprintf(font_preset_wt_buf[0], sizeof font_preset_wt_buf[0], "%s", wt0[0]?wt0:"regular");
|
||||
snprintf(font_preset_name_buf[0], sizeof font_preset_name_buf[0], "默认字体");
|
||||
font_preset_tiers[0] = font_preset_tier_buf[0];
|
||||
font_preset_glyphs[0] = gc;
|
||||
}
|
||||
@@ -1855,21 +1937,37 @@ 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[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,
|
||||
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++;
|
||||
if (!strcmp(de->d_name, "default")) continue; /* 默认字体由索引 0 单独处理 */
|
||||
char fampath[400];
|
||||
snprintf(fampath, sizeof fampath, "%s/%s", root, de->d_name);
|
||||
DIR *w = opendir(fampath); /* 一级:<family>/ */
|
||||
if (!w) continue;
|
||||
struct dirent *we;
|
||||
while ((we = readdir(w)) && font_presets_n < FONT_PRESETS_MAX) {
|
||||
if (we->d_name[0] == '.') continue;
|
||||
char dirpath[400], label[96], cs[8] = ""; int gc = 0; char wt[32] = "", nm[64] = "";
|
||||
snprintf(dirpath, sizeof dirpath, "%s/%s", fampath, we->d_name);
|
||||
if (meow_font_probe_weight(dirpath, de->d_name, we->d_name, label, sizeof label,
|
||||
cs, sizeof cs, &gc, wt, sizeof wt, nm, sizeof nm) == 0)
|
||||
continue; /* 缺组件/损坏 → 跳过该套 */
|
||||
int i = font_presets_n;
|
||||
snprintf(font_preset_val_buf[i], sizeof font_preset_val_buf[i],
|
||||
"%s/%s/%s/font.png", MEOW_FONTS_CFG, de->d_name, we->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");
|
||||
snprintf(font_preset_fam_buf[i], sizeof font_preset_fam_buf[i], "%s", de->d_name);
|
||||
snprintf(font_preset_wt_buf[i], sizeof font_preset_wt_buf[i], "%s", wt);
|
||||
snprintf(font_preset_name_buf[i], sizeof font_preset_name_buf[i], "%s", nm);
|
||||
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_fams[i] = font_preset_fam_buf[i];
|
||||
font_preset_wts[i] = font_preset_wt_buf[i];
|
||||
font_preset_names[i] = font_preset_name_buf[i];
|
||||
font_preset_glyphs[i] = gc;
|
||||
font_presets_n++;
|
||||
}
|
||||
closedir(w);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
@@ -4630,7 +4728,7 @@ static const srow_t SMENU[] = {
|
||||
* 开 → 强制全系统用大字库 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>/ 列表;
|
||||
/* M2.5x 多字体:开关 + 6 个字符类别字体(preset=可用 MeowOS/fonts/<family>/<weight>/ 列表;
|
||||
* 值空 = 跟随全局 font.file)。仅开关开启时 6 行可见(s_row_visible)。 */
|
||||
{ "多字体", "display.multi_font", SC_SWITCH, 0,0,0, NULL,NULL,0 },
|
||||
{ "中文(CJK)", "font.class_cjk", SC_PRESET, 0,0,0, font_presets, font_preset_labels, 0 },
|
||||
@@ -4733,8 +4831,14 @@ static int s_next_setting(int i)
|
||||
return s_first_setting(); /* 回绕 */
|
||||
}
|
||||
|
||||
/* 字体行(font.file + font.class_*)走「family→字重」两级子菜单(M2.6 字重结构) */
|
||||
static int s_row_is_font(const srow_t *r)
|
||||
{
|
||||
return r && r->key && (!strcmp(r->key, "font.file") || !strncmp(r->key, "font.class_", 11));
|
||||
}
|
||||
|
||||
/* SC_PRESET 行取「选项表 + 数量」:字体行(font.file + font.class_*)用运行时动态数组
|
||||
* (TF 卡 MeowOS/fonts/<family>/ 扫描结果,见字体区 font_presets/font_presets_n),
|
||||
* (TF 卡 MeowOS/fonts/<family>/<weight>/ 扫描结果,见字体区 font_presets/font_presets_n),
|
||||
* 其余行用编译期静态表(r->presets / r->preset_n)。 */
|
||||
static const char *const *s_row_presets(const srow_t *r, int *n)
|
||||
{
|
||||
@@ -6036,6 +6140,71 @@ ddone:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---- 字体行「family→字重」两级子菜单(M2.6 字重结构)----
|
||||
* 一级:列出扫描到的去重 family(class 行首项「跟随全局」=空值);选 family 后自动进入
|
||||
* 二级:该 family 的可用字重(regular/bold/italic…)。选中写 out_val = 卡根相对路径
|
||||
* MeowOS/fonts/<family>/<weight>/font.png(或 class 行「跟随全局」=空串)。
|
||||
* 复用 s_dd_modal(与 WiFi/关于 同款模态)。返回 1=已选中(out_val 已填),0=取消。 */
|
||||
static int s_font_dropdown(const srow_t *r, char *out_val, int vlen)
|
||||
{
|
||||
if (!r) return 0;
|
||||
int is_cls = r->key && !strncmp(r->key, "font.class_", 11);
|
||||
char cur[MEOW_SET_MAX_LEN];
|
||||
meow_settings_get_str(r->key, cur, sizeof cur);
|
||||
char curfam[64] = "", curwt[32] = "";
|
||||
if (cur[0])
|
||||
font_family_weight_from_png(cur, curfam, sizeof curfam, curwt, sizeof curwt);
|
||||
|
||||
/* ---- 一级:去重 family 列表(class 行首项「跟随全局」)---- */
|
||||
char fam_slug[S_NET_MAX][64], fam_disp[S_NET_MAX][64];
|
||||
int famn = 0;
|
||||
if (is_cls) {
|
||||
if (famn < S_NET_MAX) { snprintf(fam_slug[famn], sizeof fam_slug[0], "");
|
||||
snprintf(fam_disp[famn], sizeof fam_disp[0], "跟随全局字体"); famn++; }
|
||||
}
|
||||
for (int i = 0; i < font_presets_n && famn < S_NET_MAX; i++) {
|
||||
const char *f = font_preset_fams[i] ? font_preset_fams[i] : "";
|
||||
int dup = 0;
|
||||
for (int k = 0; k < famn; k++) if (!strcmp(fam_slug[k], f)) { dup = 1; break; }
|
||||
if (dup) continue;
|
||||
snprintf(fam_slug[famn], sizeof fam_slug[0], "%s", f);
|
||||
snprintf(fam_disp[famn], sizeof fam_disp[0], "%s",
|
||||
(font_preset_names[i] && font_preset_names[i][0]) ? font_preset_names[i] : f);
|
||||
famn++;
|
||||
}
|
||||
if (famn <= 0) return 0;
|
||||
const char *fitem[S_NET_MAX];
|
||||
char ftext[S_NET_MAX][64];
|
||||
for (int i = 0; i < famn; i++) { snprintf(ftext[i], sizeof ftext[i], "%s", fam_disp[i]); fitem[i] = ftext[i]; }
|
||||
int cur_fam = is_cls ? 0 : -1;
|
||||
for (int i = is_cls ? 1 : 0; i < famn; i++)
|
||||
if (curfam[0] && !strcmp(fam_slug[i], curfam)) cur_fam = i;
|
||||
int pick = s_dd_modal(r->label, fitem, famn, cur_fam);
|
||||
if (pick < 0) return 0;
|
||||
if (is_cls && pick == 0) { snprintf(out_val, vlen, ""); return 1; } /* 跟随全局 */
|
||||
const char *fam = fam_slug[pick];
|
||||
|
||||
/* ---- 二级:该 family 的字重列表(值=完整卡根相对路径,显示=label)---- */
|
||||
char wt_val[S_NET_MAX][MEOW_SET_MAX_LEN];
|
||||
char wt_text[S_NET_MAX][96];
|
||||
int wn = 0;
|
||||
for (int i = 0; i < font_presets_n && wn < S_NET_MAX; i++) {
|
||||
if (!font_preset_fams[i] || strcmp(font_preset_fams[i], fam)) continue;
|
||||
snprintf(wt_val[wn], sizeof wt_val[0], "%s", font_presets[i]);
|
||||
snprintf(wt_text[wn], sizeof wt_text[0], "%s", font_preset_labels[i]);
|
||||
wn++;
|
||||
}
|
||||
if (wn <= 0) return 0; /* 该 family 无字重(理论不出现:能进 family 必已探到字重) */
|
||||
const char *witem[S_NET_MAX];
|
||||
for (int i = 0; i < wn; i++) witem[i] = wt_text[i];
|
||||
int cur_wt = -1;
|
||||
for (int i = 0; i < wn; i++) if (cur[0] && !strcmp(wt_val[i], cur)) cur_wt = i;
|
||||
int wp = s_dd_modal(r->label, witem, wn, cur_wt);
|
||||
if (wp < 0) return 0;
|
||||
snprintf(out_val, vlen, "%s", wt_val[wp]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- 运行设置页(独立子循环;run_menu 触摸命中设置行后调用)---- */
|
||||
static void run_settings_page(void)
|
||||
{
|
||||
@@ -6043,7 +6212,7 @@ static void run_settings_page(void)
|
||||
int scroll_off = 0;
|
||||
int H = fb_yres;
|
||||
|
||||
/* M2.5x:进设置页扫描一次 TF 卡 MeowOS/fonts/<family>/,刷新「字体文件」下拉。
|
||||
/* M2.6:进设置页扫描一次 TF 卡 MeowOS/fonts/<family>/<weight>/,刷新「字体文件」下拉。
|
||||
* 进设置时卡通常在位;此步只枚举/校验,不加载(加载由 meow_font_ensure 按 font.file 决定)。 */
|
||||
meow_font_scan_presets();
|
||||
|
||||
@@ -6369,11 +6538,20 @@ static void run_settings_page(void)
|
||||
} else if (r->ctrl == SC_ENUM || r->ctrl == SC_PRESET) {
|
||||
/* 单次点击即开选择器子对话框 */
|
||||
after_modal = 1; /* 关闭后整帧重绘(还原被 dim 的状态栏) */
|
||||
int pick = s_dropdown(r);
|
||||
if (pick >= 0) {
|
||||
if (r->ctrl == SC_ENUM) meow_settings_set_enum(r->key, pick);
|
||||
else meow_settings_set_str(r->key, s_preset_value(r, pick));
|
||||
s_commit(r, 1);
|
||||
if (s_row_is_font(r)) {
|
||||
/* M2.6 字重:字体行走「family→字重」两级子菜单 */
|
||||
char fval[MEOW_SET_MAX_LEN];
|
||||
if (s_font_dropdown(r, fval, sizeof fval)) {
|
||||
meow_settings_set_str(r->key, fval);
|
||||
s_commit(r, 1);
|
||||
}
|
||||
} else {
|
||||
int pick = s_dropdown(r);
|
||||
if (pick >= 0) {
|
||||
if (r->ctrl == SC_ENUM) meow_settings_set_enum(r->key, pick);
|
||||
else meow_settings_set_str(r->key, s_preset_value(r, pick));
|
||||
s_commit(r, 1);
|
||||
}
|
||||
}
|
||||
/* M2.38 Bug3:s_dropdown 返回后彻底排空本页 tfd +
|
||||
* 重置触摸基线。根因:input 子系统把事件广播到所有
|
||||
@@ -7429,17 +7607,18 @@ int main(int argc, char **argv)
|
||||
* menu open so edits apply without a daemon restart) */
|
||||
settings_load(); /* M2.11:读系统设置(audio.volume 等),供音量/状态栏使用 */
|
||||
|
||||
/* M2.5x:启动扫描一次 TF 卡 MeowOS/fonts/<family>/,填充「字体文件」下拉 preset。
|
||||
/* M2.6:启动扫描一次 TF 卡 MeowOS/fonts/<family>/<weight>/,填充「字体文件」下拉 preset。
|
||||
* 无卡/无字库目录 → 仅默认字体(等价现状)。进设置页时 run_settings_page 会再扫一次。 */
|
||||
meow_font_scan_presets();
|
||||
|
||||
if (fontscan_root) {
|
||||
/* M2.5x 测试钩子:扫描指定卡根下的 MeowOS/fonts/<family>/ 并打印 preset(qemu/宿主可测)。
|
||||
* 用于验证 font.json 解析/三件套/缺件跳过/sha 校验 + preset 动态化逻辑。 */
|
||||
/* M2.6 测试钩子:扫描指定卡根下的 MeowOS/fonts/<family>/<weight>/ 并打印 preset(qemu/宿主可测)。
|
||||
* 用于验证 font.json 解析(含 weight)/三件套/缺件跳过/sha 校验 + preset 动态化逻辑。 */
|
||||
meow_font_scan_presets_root(fontscan_root);
|
||||
printf("fonts=%d\n", font_presets_n);
|
||||
for (int i = 0; i < font_presets_n; i++)
|
||||
printf(" %d %s -> %s\n", i, font_preset_labels[i], font_presets[i]);
|
||||
printf(" %d %s -> %s (fam=%s wt=%s)\n", i, font_preset_labels[i], font_presets[i],
|
||||
font_preset_fams[i], font_preset_wts[i] ? font_preset_wts[i] : "regular");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user