feat(font): gen_font.py 支持 tier/languages/label/--json-name (agent-188, 思源黑体7字重10套已产出入卡) + APPS.md §3.7 音频通路实测定案(MeowOS常驻模式声卡空闲App直开hw:0,0) + §3.8 版本号规则(version_code 安卓式)
This commit is contained in:
1 parent
cf86cbd387
commit
f7e45f472e
2 files changed
+32
-6
No files matched your search
@@ -392,8 +392,13 @@ MeowData/<package>/
|
|||||||
2. 解压到 `MeowAPPs/<包名>/`(若已有同名,确认升级/覆盖);
|
2. 解压到 `MeowAPPs/<包名>/`(若已有同名,确认升级/覆盖);
|
||||||
3. 触发**首次启动数据初始化**(建 `MeowData/<包名>/{data,cache}`);
|
3. 触发**首次启动数据初始化**(建 `MeowData/<包名>/{data,cache}`);
|
||||||
4. 登入应用列表。
|
4. 登入应用列表。
|
||||||
|
- **更新规范 [源:用户定案 2026-08-31]**:
|
||||||
|
1. **同包名安装 = 更新**:旧程序目录整体备份为 `.bak_<pid>` 后替换,`MeowData/<pkg>/{data,cache}` 数据保留;安装成功后清理自身产生的 `.bak_<pid>`。
|
||||||
|
2. **不允许降级安装**:新包 `app.json` 的 `version` 必须 > 已装版本(版本比较;相等视为重装,允许)。版本缺失/不可比 → 拒绝(提示版本无法判定)。
|
||||||
|
3. **签名分级**:有签名的包**必须验签**(失败拒装);无签名的第三方包**直接安装**;**`os.meow.*` 级别必须有有效签名,无签名/验签失败一律拒装**(防伪造系统包名)。
|
||||||
- **后续**:Web 后台(阶段 C)提供上传/安装/卸载/导出 `.mpk` 界面;本地文件管理器可调用安装程序 UI(`meow_pkg_install` 的交互入口)。
|
- **后续**:Web 后台(阶段 C)提供上传/安装/卸载/导出 `.mpk` 界面;本地文件管理器可调用安装程序 UI(`meow_pkg_install` 的交互入口)。
|
||||||
- **签名(Ⅱ期,见 `devdocs/plan/SYSTEM_FRAMEWORK_SERVICES.md §6`)**:`.mpk` 增加 `signature`(sha256 + 设备密钥/证书,**签名覆盖 `app.json` 的 `package`** 以防伪造 `os.meow.*` 包名);`meow_pkg_install`/`nyai install` **解包验签**,**验签失败/`os.meow.*` 无签名 → 拒绝安装**;`meow_pkg_export`/`nyai export` **保留签名**(不二次签名)。
|
- **导出过滤(随更新规范落地)**:`nyai export`/`meow_pkg_export` 打包时排除 `*.pre_*`(热更新备份)、`*.bak_*`(安装备份)、`logs/` 等运行残留,只打 `app.json`+`exec`+资源(防 2MB 垃圾进包)。
|
||||||
|
- **签名(Ⅱ期,见 `devdocs/plan/SYSTEM_FRAMEWORK_SERVICES.md §6` 与 `devdocs/plan/MPK_SIGNATURE_DESIGN.md`)**:`.mpk` 增加 `signature`(Ed25519,**签名覆盖 `app.json` 的 `package`+payload 哈希** 以防伪造 `os.meow.*` 包名);`meow_pkg_install`/`nyai install` **解包验签**,按上条「签名分级」执行;`meow_pkg_export`/`nyai export` **保留签名**(不二次签名;安装时原签包存 `MeowData/<pkg>/data/<pkg>.mpk.src`,导出优先复制之)。
|
||||||
- **包名 `os.meow.*` 约定 + 系统 APP 鉴权规划(Ⅱ期)**:`os.meow.*` 为 MeowOS 系统应用保留反域名前缀(如 `os.meow.hello` 示例);**非系统(第三方)App 不得使用 `os.meow.*`** 前缀——`.mpk` 签名规划强制签名覆盖 `package`,且 `os.meow.*` 无签名即拒绝安装,从源头防伪造系统包名。**二期关键 IPC 鉴权**:只接受 `os.meow.*` 系统包(关键 IPC 走 `os.meow.*` 鉴权,拒绝第三方),第三方走受限能力。
|
- **包名 `os.meow.*` 约定 + 系统 APP 鉴权规划(Ⅱ期)**:`os.meow.*` 为 MeowOS 系统应用保留反域名前缀(如 `os.meow.hello` 示例);**非系统(第三方)App 不得使用 `os.meow.*`** 前缀——`.mpk` 签名规划强制签名覆盖 `package`,且 `os.meow.*` 无签名即拒绝安装,从源头防伪造系统包名。**二期关键 IPC 鉴权**:只接受 `os.meow.*` 系统包(关键 IPC 走 `os.meow.*` 鉴权,拒绝第三方),第三方走受限能力。
|
||||||
|
|
||||||
### 4. 实现挂点(MeowOS/launcher)
|
### 4. 实现挂点(MeowOS/launcher)
|
||||||
@@ -447,3 +452,13 @@ Description: A simulated app
|
|||||||
```
|
```
|
||||||
|
|
||||||
**与 launcher 一致**:install/export 复用 M2.12 `meow_pkg_install/export` 同套语义(tar.gz、gzip 管道、包名=文件名茎、同名先备份、校验 app.json、数据 init);exFAT 无符号链接,数据目录由 MeowOS 经 `MEOW_DATA_DIR/CACHE_DIR` 注入,CLI 只建目录不建链接。
|
**与 launcher 一致**:install/export 复用 M2.12 `meow_pkg_install/export` 同套语义(tar.gz、gzip 管道、包名=文件名茎、同名先备份、校验 app.json、数据 init);exFAT 无符号链接,数据目录由 MeowOS 经 `MEOW_DATA_DIR/CACHE_DIR` 注入,CLI 只建目录不建链接。
|
||||||
|
|
||||||
|
### 3.7 音频通路与媒体键(2026-08-31 实测定案,覆盖 §5 旧推论)
|
||||||
|
- **MeowOS 常驻模式(boot 直进,hiby_player 未运行)声卡空闲**:真机实测 `fuser /dev/snd/pcmC0D0p /dev/snd/controlC0` 无任何占用、`pidof hiby_player` 不存在——**MeowAPP 可直接 open `hw:0,0` 出声**(audio.output 设置键即此);无混音,后开者失败,音频焦点=先占先得(媒体会话/MPRIS 落地后再规范抢占语义)。
|
||||||
|
- **会话覆盖模式(HibyOS 运行中热键唤起)**:SIGSTOP 冻结播放器,声卡仍被其持有,App 无声(§5 旧结论,仅适用该模式)。
|
||||||
|
- **物理媒体键路由**:MeowOS 与 HibyOS 不同时运行,不存在「转发给 hiby」问题;MeowOS 界面下播放/上下曲 = 全局媒体控制,路由给**最近活跃媒体会话**(媒体会话/MPRIS 落地后接线),无会话时「抓住」不动作。
|
||||||
|
|
||||||
|
### 3.8 版本号规则(2026-08-31 定案,对齐 Android)
|
||||||
|
- `app.json` 增加 **`version_code`**(纯数字,单调递增,**安装/升级比较的唯一依据**);`version` 仅作显示字符串(versionName 角色,可为 `1.2.3-beta` 等任意形态)。
|
||||||
|
- 防降级比较只看 `version_code`:新版 code < 已装 → 拒装;相等 → 允许重装;**code 缺失 → 拒装**(提示缺 version_code)。
|
||||||
|
- `nyai index` 的 `Packages` 索引同步记录 `Version-Code`。
|
||||||
+16
-5
@@ -282,7 +282,7 @@ def write_idx(path, bit_depth, faces, glyph_tabs):
|
|||||||
# font.json(元数据 + 完整性校验;schema 见 docs/MEOWOS_SPEC_REF.md §5.2)
|
# font.json(元数据 + 完整性校验;schema 见 docs/MEOWOS_SPEC_REF.md §5.2)
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
def write_font_json(out_dir, name, family, weight, tier, glyph_count, sizes, bitdepth,
|
def write_font_json(out_dir, name, family, weight, tier, glyph_count, sizes, bitdepth,
|
||||||
png_path, idx_path, source="", license_=""):
|
png_path, idx_path, source="", license_="", languages=None, label=""):
|
||||||
def sha256(path):
|
def sha256(path):
|
||||||
h = hashlib.sha256()
|
h = hashlib.sha256()
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
@@ -294,12 +294,15 @@ def write_font_json(out_dir, name, family, weight, tier, glyph_count, sizes, bit
|
|||||||
"name": name, # 下拉显示名(缺则回退目录名)
|
"name": name, # 下拉显示名(缺则回退目录名)
|
||||||
"weight": weight or "regular", # 字重 slug(regular|bold|italic|…;默认 regular)
|
"weight": weight or "regular", # 字重 slug(regular|bold|italic|…;默认 regular)
|
||||||
"glyph_count": glyph_count, # 信息用
|
"glyph_count": glyph_count, # 信息用
|
||||||
"charset": tier, # common|gbk|full(档位;驱动按需加载)
|
"charset": tier, # common|gbk|full(档位;驱动按需加载——现行运行时读此作 tier)
|
||||||
|
"tier": tier, # 档位(FONT_SYSTEM 新增;同 charset 值,供后续运行时迁移)
|
||||||
|
"languages": languages or ["zh_cn", "en_us"], # 详细区域码(FONT_SYSTEM §2.1)
|
||||||
"size_list": sizes, # 原生面字号
|
"size_list": sizes, # 原生面字号
|
||||||
"bit_depth": bitdepth, # 4|8
|
"bit_depth": bitdepth, # 4|8
|
||||||
"sha256_png": sha256(png_path), # font.png 校验(扫描端仅 WARN)
|
"sha256_png": sha256(png_path), # font.png 校验(扫描端仅 WARN)
|
||||||
"sha256_idx": sha256(idx_path), # font.idx 校验(扫描端仅 WARN)
|
"sha256_idx": sha256(idx_path), # font.idx 校验(扫描端仅 WARN)
|
||||||
}
|
}
|
||||||
|
if label: meta["label"] = label # 可选:显示名(如 `思源黑体 CN·粗体·常用`)
|
||||||
if source: meta["source"] = source # 可选:来源(如 "MiSans Regular (Xiaomi)")
|
if source: meta["source"] = source # 可选:来源(如 "MiSans Regular (Xiaomi)")
|
||||||
if license_: meta["license"] = license_ # 可选:许可(如 "SIL Open Font License 1.1")
|
if license_: meta["license"] = license_ # 可选:许可(如 "SIL Open Font License 1.1")
|
||||||
jpath = os.path.join(out_dir, "font.json")
|
jpath = os.path.join(out_dir, "font.json")
|
||||||
@@ -316,7 +319,10 @@ def main():
|
|||||||
ap.add_argument("--font", required=True)
|
ap.add_argument("--font", required=True)
|
||||||
ap.add_argument("--sizes", default="28,20")
|
ap.add_argument("--sizes", default="28,20")
|
||||||
ap.add_argument("--out", required=True)
|
ap.add_argument("--out", required=True)
|
||||||
ap.add_argument("--name", default="font")
|
ap.add_argument("--name", default="font",
|
||||||
|
help="输出面基名(font.png/font.idx 基名,缺省 font;font.json name 缺省同此)")
|
||||||
|
ap.add_argument("--json-name", default="",
|
||||||
|
help="font.json name 显示名(缺省 = --name;用于 png 基名与显示名解耦)")
|
||||||
ap.add_argument("--family", default="", help="font.json family(缺省 = --name;信息用)")
|
ap.add_argument("--family", default="", help="font.json family(缺省 = --name;信息用)")
|
||||||
ap.add_argument("--weight", default="regular",
|
ap.add_argument("--weight", default="regular",
|
||||||
help="字重 slug(regular|bold|italic|…,缺省 regular;写进 font.json weight)")
|
help="字重 slug(regular|bold|italic|…,缺省 regular;写进 font.json weight)")
|
||||||
@@ -326,11 +332,15 @@ def main():
|
|||||||
ap.add_argument("--extra", default="")
|
ap.add_argument("--extra", default="")
|
||||||
ap.add_argument("--source", default="", help="可选:font.json source 来源说明")
|
ap.add_argument("--source", default="", help="可选:font.json source 来源说明")
|
||||||
ap.add_argument("--license", default="", help="可选:font.json license 许可")
|
ap.add_argument("--license", default="", help="可选:font.json license 许可")
|
||||||
|
ap.add_argument("--languages", default="zh_cn,en_us",
|
||||||
|
help="font.json languages 区域码(逗号分隔,FONT_SYSTEM §2.1;默认 zh_cn,en_us)")
|
||||||
|
ap.add_argument("--label", default="", help="可选:font.json label 显示名")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
os.makedirs(args.out, exist_ok=True)
|
os.makedirs(args.out, exist_ok=True)
|
||||||
sizes = [int(s) for s in args.sizes.split(",") if s.strip()]
|
sizes = [int(s) for s in args.sizes.split(",") if s.strip()]
|
||||||
tier = CHARSET_ALIAS.get(args.charset, "common")
|
tier = CHARSET_ALIAS.get(args.charset, "common")
|
||||||
|
json_name = args.json_name if args.json_name else args.name
|
||||||
cps = build_charset(tier, args.extra)
|
cps = build_charset(tier, args.extra)
|
||||||
print(f"字符集: {len(cps)} 码位 (charset={tier})")
|
print(f"字符集: {len(cps)} 码位 (charset={tier})")
|
||||||
|
|
||||||
@@ -401,9 +411,10 @@ def main():
|
|||||||
idx_size = write_idx(idx_path, args.bitdepth, face_meta, [p[3] for p in all_planes])
|
idx_size = write_idx(idx_path, args.bitdepth, face_meta, [p[3] for p in all_planes])
|
||||||
|
|
||||||
# ---- font.json(写入 weight + charset + glyph_count + sha256,供扫描端标注档位/大小标记) ----
|
# ---- font.json(写入 weight + charset + glyph_count + sha256,供扫描端标注档位/大小标记) ----
|
||||||
jpath = write_font_json(args.out, args.name, args.family, args.weight, tier, charset_glyph_count(cps),
|
languages = [l.strip() for l in args.languages.split(",") if l.strip()]
|
||||||
|
jpath = write_font_json(args.out, json_name, args.family, args.weight, tier, charset_glyph_count(cps),
|
||||||
sizes, args.bitdepth, png_path, idx_path,
|
sizes, args.bitdepth, png_path, idx_path,
|
||||||
args.source, args.license)
|
args.source, args.license, languages, args.label)
|
||||||
|
|
||||||
png_size = os.path.getsize(png_path)
|
png_size = os.path.getsize(png_path)
|
||||||
total = png_size + idx_size
|
total = png_size + idx_size
|
||||||
|
|||||||
Reference in new issue
Block a user