字重结构+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
@@ -69,7 +69,7 @@
|
|||||||
| `package` | — | 反域名权威标识(如 `os.meow.hello`),launcher 取之为 app_id;**目录名=package**。缺省回退目录名 `de->d_name`;两者存在且不同 → WARN |
|
| `package` | — | 反域名权威标识(如 `os.meow.hello`),launcher 取之为 app_id;**目录名=package**。缺省回退目录名 `de->d_name`;两者存在且不同 → WARN |
|
||||||
| `version` / `author` | — | 保留字段,v2 不解析 |
|
| `version` / `author` | — | 保留字段,v2 不解析 |
|
||||||
| `fullscreen` | — | app 级全屏:`1`=进入全屏(状态栏**收合至提示线** + **保留左缘返回/顶部提示线/下滑通知中心**,见 MEOWOS_SPEC_REF §5.1);activity 级用它 `route.<id>.fullscreen`。**app.json `fullscreen` 字段此前未解析/已定未做 → 本次实现**(launcher 启动/切 activity 据声明预设 SDK 全屏,env `MEOW_FULLSCREEN`)[源:用户 2026-08-30] |
|
| `fullscreen` | — | app 级全屏:`1`=进入全屏(状态栏**收合至提示线** + **保留左缘返回/顶部提示线/下滑通知中心**,见 MEOWOS_SPEC_REF §5.1);activity 级用它 `route.<id>.fullscreen`。**app.json `fullscreen` 字段此前未解析/已定未做 → 本次实现**(launcher 启动/切 activity 据声明预设 SDK 全屏,env `MEOW_FULLSCREEN`)[源:用户 2026-08-30] |
|
||||||
| `fonts` | — | 字库档位声明:`"full"` / `"gbk"` / `"common"`(app 需大字库时可声明 `"full"`,默认小字库 `common`≈4k;launcher 据此为该 app 加载大字库 `gbk`≈20k)。**注意**:与 `"fonts":["<handle>"]`(应用自带字库句柄数组)**同名二义**,见 MEOWOS_SPEC_REF §7 A-14 [源:用户 2026-08-30] |
|
| `fonts` | — | 字库档位声明:`"full"` / `"gbk"` / `"common"`(app 需大字库时可声明 `"full"`,默认小字库 `common`≈4k;launcher 据此为该 app 加载大字库 `gbk`≈20k,从已扫描的 `MeowOS/fonts/<family>/<weight>/` 里按同 family(其次同 weight)选大字库,M2.6)。**注意**:与 `"fonts":["<handle>"]`(应用自带字库句柄数组)**同名二义**,见 MEOWOS_SPEC_REF §7 A-14 [源:用户 2026-08-30] |
|
||||||
|
|
||||||
- 目录扫描深度 1 级:只枚举 `MeowAPPs/` 下的直接子目录
|
- 目录扫描深度 1 级:只枚举 `MeowAPPs/` 下的直接子目录
|
||||||
- 应用计数上限 **8**(超出忽略;单屏 8 行)
|
- 应用计数上限 **8**(超出忽略;单屏 8 行)
|
||||||
|
|||||||
+2
-2
@@ -323,11 +323,11 @@ patch_v2/meow_launcherd/meow_font.a # 复用(静态链接)
|
|||||||
```
|
```
|
||||||
上例 `<pkg>`=`os.meow.hello`(= `apps/<pkg>/` 目录名 = `package` 字段值);exec 即 `<挂载点>/MeowAPPs/<pkg>/<binary>`。
|
上例 `<pkg>`=`os.meow.hello`(= `apps/<pkg>/` 目录名 = `package` 字段值);exec 即 `<挂载点>/MeowAPPs/<pkg>/<binary>`。
|
||||||
|
|
||||||
> **app 字体来源(M2.x 已落地)**:app 若**自带字库**(`load_handle`,见 §5.7.3,**未实现**)则用它;否则 SDK `meow_ui_gfx.c load_font()` **优先读 launcher 注入的环境变量** `MEOW_FONT_PNG`/`MEOW_FONT_IDX`(=系统 `font.file` 当前生效字体,绝对路径)/`MEOW_FONT_APP`(family),无 env 则回退卡默认 `MeowOS/fonts/default/font.png` → 固件。因此**app 未自带字库时跟随系统 `font.file`**,不再硬编码 `MeowAPPs/fonts`。
|
> **app 字体来源(M2.x 已落地)**:app 若**自带字库**(`load_handle`,见 §5.7.3,**未实现**)则用它;否则 SDK `meow_ui_gfx.c load_font()` **优先读 launcher 注入的环境变量** `MEOW_FONT_PNG`/`MEOW_FONT_IDX`(=系统 `font.file` 当前生效字体,绝对路径)/`MEOW_FONT_APP`(family),无 env 则回退卡默认 `MeowOS/fonts/default/regular/font.png`(M2.6 字重结构)→ 固件。因此**app 未自带字库时跟随系统 `font.file`**,不再硬编码 `MeowAPPs/fonts`。
|
||||||
>
|
>
|
||||||
> **app 多字体(M2.5x,`display.multi_font`)**:launcher 注入 `MEOW_MULTI_FONT=0/1` 与 6 类 `MEOW_FONT_CLS_<类>_PNG/IDX`(命中则覆盖全局;类值为空=跟随全局不注入)。SDK `meow_ui_gfx` 的 `meow_gfx_load_multifont()` 读 env → 调 `meow_font_config_multifont()`(同 png 去重、≤4 套上限、缺类回退全局)构建**逐字符分类路由**:`meow_font_drawtext/width_text/line_height` 按 `meow_font_class_of(cp)`(`MEOW_FCLS_CJK/_CJK_SYM/_NUM/_LATIN/_LATIN_SYM/_OTHER`)用对应字库取字形与度量;`meow_font_has_glyph(cp)` 可预判缺字。`display.multi_font=0`/无 env → 单字体(现状,零额外开销)。底层接口见 meow_font.h:`meow_font_load_slot`/`meow_font_unload_slot`/`meow_font_enable_multifont`/`meow_font_set_class_slot`/`meow_font_config_multifont`;`MEOW_MAX_SLOTS=5`。
|
> **app 多字体(M2.5x,`display.multi_font`)**:launcher 注入 `MEOW_MULTI_FONT=0/1` 与 6 类 `MEOW_FONT_CLS_<类>_PNG/IDX`(命中则覆盖全局;类值为空=跟随全局不注入)。SDK `meow_ui_gfx` 的 `meow_gfx_load_multifont()` 读 env → 调 `meow_font_config_multifont()`(同 png 去重、≤4 套上限、缺类回退全局)构建**逐字符分类路由**:`meow_font_drawtext/width_text/line_height` 按 `meow_font_class_of(cp)`(`MEOW_FCLS_CJK/_CJK_SYM/_NUM/_LATIN/_LATIN_SYM/_OTHER`)用对应字库取字形与度量;`meow_font_has_glyph(cp)` 可预判缺字。`display.multi_font=0`/无 env → 单字体(现状,零额外开销)。底层接口见 meow_font.h:`meow_font_load_slot`/`meow_font_unload_slot`/`meow_font_enable_multifont`/`meow_font_set_class_slot`/`meow_font_config_multifont`;`MEOW_MAX_SLOTS=5`。
|
||||||
>
|
>
|
||||||
> **app 字体档位(M2.5x,`app.json "fonts"` 声明 + `font.load_full`)**:launcher 按 `app.json "fonts":"common|gbk|full"` 与系统「加载大字库」开关(`font.load_full`,默认 0;开=强制 ≥gbk 档)算出**生效档**,注入 `MEOW_FONT_TIER=<0|1|2>`(0=**common**≈4k 常用 1=**gbk**≈20k+ 大 2=**full** 全/可选),并把 `MEOW_FONT_PNG/IDX` 指向该档字库绝对路径——`meow_font_effective_paths_tiered()` 在卡内 `MeowOS/fonts/<family>/` 按 `font.json` 的 `charset`(`common|gbk|full`)选库(gbk 请求接受 gbk/full;full 请求优先 full 再降级 gbk;无匹配回退全局 `font.file`)。SDK `meow_ui_gfx.c load_font()` 载入后,应用可经 `meow_font_tier()` 查询当前档、`meow_font_set_tier()` 切档(触发 `meow_font_reload_tier()` **真重载**:大字库切换先 unload 旧再 load 新,释放旧 atlas,防 GBK/Full 大库双驻留)。**判缺字**:`meow_font_has_glyph(cp)` 预判当前字库是否含该码位(common 小库下缺字→app 回退/提示,避免静默豆腐)。底层接口见 meow_font.h `meow_font_reload_tier`;`font/gen/gen_font.py --charset common|gbk|full` 按档生成 `font.png/font.idx/font.json`(`font.json` 写 `charset`+`glyph_count`)。
|
> **app 字体档位(M2.5x,`app.json "fonts"` 声明 + `font.load_full`)**:launcher 按 `app.json "fonts":"common|gbk|full"` 与系统「加载大字库」开关(`font.load_full`,默认 0;开=强制 ≥gbk 档)算出**生效档**,注入 `MEOW_FONT_TIER=<0|1|2>`(0=**common**≈4k 常用 1=**gbk**≈20k+ 大 2=**full** 全/可选),并把 `MEOW_FONT_PNG/IDX` 指向该档字库绝对路径——`meow_font_effective_paths_tiered()` 在卡内 `MeowOS/fonts/<family>/<weight>/` 按 `font.json` 的 `charset`(`common|gbk|full`)选库(gbk 请求接受 gbk/full;full 请求优先 full 再降级 gbk;**M2.6 优先同 family(其次同 weight)**;无匹配回退全局 `font.file`)。SDK `meow_ui_gfx.c load_font()` 载入后,应用可经 `meow_font_tier()` 查询当前档、`meow_font_set_tier()` 切档(触发 `meow_font_reload_tier()` **真重载**:大字库切换先 unload 旧再 load 新,释放旧 atlas,防 GBK/Full 大库双驻留)。**判缺字**:`meow_font_has_glyph(cp)` 预判当前字库是否含该码位(common 小库下缺字→app 回退/提示,避免静默豆腐)。底层接口见 meow_font.h `meow_font_reload_tier`;`font/gen/gen_font.py --charset common|gbk|full [--weight <name>]` 按档生成 `font.png/font.idx/font.json`(`font.json` 写 `weight`+`charset`+`glyph_count`)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
## 当前架构与组件(2026-08-30 快照)
|
## 当前架构与组件(2026-08-30 快照)
|
||||||
|
|
||||||
- **SDK 统一日志 + 通知(M2.52/53 已落地)**:`meow_log()` 按 app 分目录、每次启动新文件 `MeowOS/logs/apps/<app_id>/logs/<ts>_<pid>.log`,单文件 2MB 轮转、每 App 总量 50MB 删最旧;通知 API `meow_notify_*`(4 类型×每类型 4、媒体单例);**#5 SDK↔IPC 桥**(`meow_notify_ipc.c/.h`:app→launcher 中央存储,send/cancel 接入、回填 launcher `nid`,无 socket 降级不崩)。通知中心 UI / D-Bus 桥 / TF 转储为后续任务。
|
- **SDK 统一日志 + 通知(M2.52/53 已落地)**:`meow_log()` 按 app 分目录、每次启动新文件 `MeowOS/logs/apps/<app_id>/logs/<ts>_<pid>.log`,单文件 2MB 轮转、每 App 总量 50MB 删最旧;通知 API `meow_notify_*`(4 类型×每类型 4、媒体单例);**#5 SDK↔IPC 桥**(`meow_notify_ipc.c/.h`:app→launcher 中央存储,send/cancel 接入、回填 launcher `nid`,无 socket 降级不崩)。通知中心 UI / D-Bus 桥 / TF 转储为后续任务。
|
||||||
- **TF 卡多字体库**:`MeowOS/fonts/<family>/`(`font.png`+`font.idx`+`font.json` 三件套),默认 `MeowOS/fonts/default`;`font.file` 设置键选默认套;SDK `load_font` **优先读 env `MEOW_FONT_PNG/IDX`**(launcher 每次 `launch_app` 注入),app 未自带字库时**跟随系统 `font.file`**;≤16 套卡内(防爆);§5.7 字体管理 + 应用字库(多句柄,**草案未实现**)。已部署 4 套(思源宋体 / MiSans / GoogleSans / Meslo,位深 4)。
|
- **TF 卡多字体库(M2.6 字重结构)**:`MeowOS/fonts/<family>/<weight>/`(`font.png`+`font.idx`+`font.json` 三件套),默认 `MeowOS/fonts/default/regular`;`font.file` 设置键选默认套(family→字重两级子菜单);SDK `load_font` **优先读 env `MEOW_FONT_PNG/IDX`**(launcher 每次 `launch_app` 注入),app 未自带字库时**跟随系统 `font.file`**;≤16 套卡内(防爆);§5.7 字体管理 + 应用字库(多句柄,**草案未实现**)。已部署 6 套(思源宋体 / MiSans / GoogleSans / Meslo / 思源黑体 / Ubuntu Mono,`<family>/regular`,位深 4)。
|
||||||
- **包名/目录安卓化**:`app.json` `package`(反域名**权威 app_id**,==目录名,缺省回退目录名)→ 示例 `os.meow.hello`;目录 `MeowAPPs/os.meow.hello`;数据 `MeowData/<pkg>/{data,cache}`。
|
- **包名/目录安卓化**:`app.json` `package`(反域名**权威 app_id**,==目录名,缺省回退目录名)→ 示例 `os.meow.hello`;目录 `MeowAPPs/os.meow.hello`;数据 `MeowData/<pkg>/{data,cache}`。
|
||||||
- **meow_svcd 服务管理器**(`patch_v2/meow_svcd/`,mipsel 静态 ~99KB,进固件不入卡):类 systemd/systemctl,由 supervisor(bootmenu/launcher)`fork/exec` 托管;读 TF `MeowOS/services.d/<name>.conf`(目录式 `key=value`,未知键忽略/前向兼容);服务=svcd **独立子进程**(崩溃隔离:服务挂不影响 svcd/supervisor);事件触发 `boot`/`card-mount`/`wifi-up`(1s 轮询);`-f` 前台便于 supervisor `waitpid`;状态机 DISABLED/STOPPED/RUNNING/STOPPING/FAILED + CLI。**接入 supervisor 启动链未做**。
|
- **meow_svcd 服务管理器**(`patch_v2/meow_svcd/`,mipsel 静态 ~99KB,进固件不入卡):类 systemd/systemctl,由 supervisor(bootmenu/launcher)`fork/exec` 托管;读 TF `MeowOS/services.d/<name>.conf`(目录式 `key=value`,未知键忽略/前向兼容);服务=svcd **独立子进程**(崩溃隔离:服务挂不影响 svcd/supervisor);事件触发 `boot`/`card-mount`/`wifi-up`(1s 轮询);`-f` 前台便于 supervisor `waitpid`;状态机 DISABLED/STOPPED/RUNNING/STOPPING/FAILED + CLI。**接入 supervisor 启动链未做**。
|
||||||
- **mDNS 方案A(已拍板)**:完整 **`mdnsd`**(994KB,mipsel 静态)放 TF `MeowOS/opt/mdnsd/mdnsd`,由 meow_svcd 托管(需 root/setcap `cap_net_bind_service,cap_net_raw` 绑 UDP5353+组播 224.0.0.251);SAResponder(885KB)因"单服务注册型"无法按名访问设备(`ingenic.local`)被排除。⚠️ 手机热点组播常在客户端间丢失 → mDNS 有风险,主机 DNS 兜底。**`MeowOpt` 已废弃并入 `MeowOS/opt`**(二进制归 MeowOS/opt,拔卡降级不崩)。
|
- **mDNS 方案A(已拍板)**:完整 **`mdnsd`**(994KB,mipsel 静态)放 TF `MeowOS/opt/mdnsd/mdnsd`,由 meow_svcd 托管(需 root/setcap `cap_net_bind_service,cap_net_raw` 绑 UDP5353+组播 224.0.0.251);SAResponder(885KB)因"单服务注册型"无法按名访问设备(`ingenic.local`)被排除。⚠️ 手机热点组播常在客户端间丢失 → mDNS 有风险,主机 DNS 兜底。**`MeowOpt` 已废弃并入 `MeowOS/opt`**(二进制归 MeowOS/opt,拔卡降级不崩)。
|
||||||
|
|||||||
+3
-3
@@ -34,7 +34,7 @@
|
|||||||
| | `input.hotkey_window_ms` | int | 2500 | 组合/序列窗口 |
|
| | `input.hotkey_window_ms` | int | 2500 | 组合/序列窗口 |
|
||||||
| | `input.touch_arm_delay_ms` | int | 700 | 触摸布防延迟(防幽灵) |
|
| | `input.touch_arm_delay_ms` | int | 700 | 触摸布防延迟(防幽灵) |
|
||||||
| | `input.swipe_invert` | bool | false | 设置页滑动方向(0=手指上滑→往下翻;1=反向) |
|
| | `input.swipe_invert` | bool | false | 设置页滑动方向(0=手指上滑→往下翻;1=反向) |
|
||||||
| **字体/主题** | `font.file` | path | `MeowOS/fonts/default/font.png` | 字体图集 |
|
| **字体/主题** | `font.file` | path | `MeowOS/fonts/default/regular/font.png` | 字体图集(M2.6 指 `<family>/<weight>/font.png`,默认 `default/regular`;设置页「字体文件」行为 family→字重两级子菜单) |
|
||||||
| | `display.multi_font` | int 0/1 | 0 | **多字体**:1=按字符类别逐字符路由;0=单字体(只走 `font.file`,零额外内存/性能) |
|
| | `display.multi_font` | int 0/1 | 0 | **多字体**:1=按字符类别逐字符路由;0=单字体(只走 `font.file`,零额外内存/性能) |
|
||||||
| | `font.class_cjk` | path | *(空=跟全局)* | 多字体:CJK 表意+扩展 所用字体(family 相对路径) |
|
| | `font.class_cjk` | path | *(空=跟全局)* | 多字体:CJK 表意+扩展 所用字体(family 相对路径) |
|
||||||
| | `font.class_cjk_sym` | path | *(空=跟全局)* | 多字体:全角/中文标点 所用字体 |
|
| | `font.class_cjk_sym` | path | *(空=跟全局)* | 多字体:全角/中文标点 所用字体 |
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
| | `network.bluetooth_enabled` | bool | false | 蓝牙开关(写 `hciconfig hci0 up/down`;默认关=少耗电) |
|
| | `network.bluetooth_enabled` | bool | false | 蓝牙开关(写 `hciconfig hci0 up/down`;默认关=少耗电) |
|
||||||
| **系统** | `system.hostname` | string | `meowos` | 主机名(RFC1035 首标签;bootmenu 的 S89meow_svcd 开机读它 `sethostname`,缺省 `meowos`;供自研 mDNS 应答器 `meow_mdns` 公布 `<host>.local`)[源:M2.54] |
|
| **系统** | `system.hostname` | string | `meowos` | 主机名(RFC1035 首标签;bootmenu 的 S89meow_svcd 开机读它 `sethostname`,缺省 `meowos`;供自研 mDNS 应答器 `meow_mdns` 公布 `<host>.local`)[源:M2.54] |
|
||||||
|
|
||||||
> **字体尺寸标记(M2.5x,随「加载大字库」一起落地)**:字体/主题选择器**每个字体选项标注字库大小标记**,如 `default(common≈4k)` / `gbk≈20k` / `full`,直观标注码位/体积量级,让用户知道所选字库大小。对应 `MeowOS/fonts/<family>/font.json` 的 `charset`(`common|gbk|full`)+ `glyph_count`;档位枚举 `MEOW_FONT_TIER_COMMON`(默认/≈4k)/`GBK`(大/≈20k)/`FULL`(全/可选);`gen_font.py --charset common|gbk|full` 按档生成。定案见 MEOWOS_SPEC_REF §5.2「字体档位/大字库」与 §5.7.8 [源:用户 2026-08-30]。
|
> **字体尺寸标记(M2.5x,随「加载大字库」一起落地)**:字体/主题选择器**每个字体选项标注库大小标记**,如 `MiSans Regular(common≈4k)` / `gbk≈20k` / `full`,直观标注码位/体积量级,让用户知道所选字库大小(M2.6 含字重:`<name> <Weight>(<marker>)`)。对应 `MeowOS/fonts/<family>/<weight>/font.json` 的 `charset`(`common|gbk|full`)+ `glyph_count`;档位枚举 `MEOW_FONT_TIER_COMMON`(默认/≈4k)/`GBK`(大/≈20k)/`FULL`(全/可选);`gen_font.py --charset common|gbk|full [--weight <name>]` 按档生成。定案见 MEOWOS_SPEC_REF §5.2「字体档位/大字库」与 §5.7.8 [源:用户 2026-08-30]。
|
||||||
|
|
||||||
> 新增设置项时:在清单加一行 + 存储 schema + API 常量/枚举 + 实体 UI 控件 + web 面板控件(四端同步)。
|
> 新增设置项时:在清单加一行 + 存储 schema + API 常量/枚举 + 实体 UI 控件 + web 面板控件(四端同步)。
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ int meow_settings_import(const char *path); /* 从 .conf 读入覆盖,校
|
|||||||
- 开关(bool):`display.theme` 深浅、`notify.center_enabled`…
|
- 开关(bool):`display.theme` 深浅、`notify.center_enabled`…
|
||||||
- 滑条(int):亮度(**clamp ≥25**)、音量、息屏时长时间
|
- 滑条(int):亮度(**clamp ≥25**)、音量、息屏时长时间
|
||||||
- 选择器(enum):息屏模式、开机默认、主题、日志级别——选择器用子菜单/对话框,选项中文标签来自 `enum_label`
|
- 选择器(enum):息屏模式、开机默认、主题、日志级别——选择器用子菜单/对话框,选项中文标签来自 `enum_label`
|
||||||
- 路径(str):字体、背景——文件浏览器选择(可先用内置几个预设,自定义文件选择后期)
|
- 路径(str):字体、背景——文件浏览器选择(可先用内置几个预设,自定义文件选择后期);**字体(M2.6 字重结构)用 family→字重两级子菜单**:点「字体文件」/多字体类别行 → 一级列扫描到的 family(多字体行首项「跟随全局」)→ 二级列该 family 的可用字重(regular/bold/italic…),点字重即选中(写 `font.file`/`font.class_*`= `<family>/<weight>/font.png` 卡根相对路径)
|
||||||
- **所有控件只调 `meow_settings_set_*` / `get_*`**,绝不直接改文件;改完 `settings_save()`,UI 按 listener 重绘。保证与 web/CLI 一致。
|
- **所有控件只调 `meow_settings_set_*` / `get_*`**,绝不直接改文件;改完 `settings_save()`,UI 按 listener 重绘。保证与 web/CLI 一致。
|
||||||
- 权限/安全:本机信任,设置页不做鉴权。
|
- 权限/安全:本机信任,设置页不做鉴权。
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@
|
|||||||
├── backups/
|
├── backups/
|
||||||
│ └── settings/
|
│ └── settings/
|
||||||
│ └── setting.bak # 设置导出默认路径(SYSTEM_SETTINGS 一致)
|
│ └── setting.bak # 设置导出默认路径(SYSTEM_SETTINGS 一致)
|
||||||
├── fonts/<family>/ # 多字体库(每套一个子目录;font.file 选默认套)
|
├── fonts/<family>/<weight>/ # 多字体库(每 <family>/<weight> 一套三件套;font.file 选默认套=default/regular)
|
||||||
├── logs/ # 统一日志:系统 meow_<bootts>.log + 应用 apps/<app_id>/logs/<ts>_<pid>.log
|
├── logs/ # 统一日志:系统 meow_<bootts>.log + 应用 apps/<app_id>/logs/<ts>_<pid>.log
|
||||||
├── services.d/ # 服务配置(meow_svcd 的 <name>.conf;TF 源——与板载 /usr/data/meow/services.d 双源,同名板载优先)`[meow_svcd.md]`
|
├── services.d/ # 服务配置(meow_svcd 的 <name>.conf;TF 源——与板载 /usr/data/meow/services.d 双源,同名板载优先)`[meow_svcd.md]`
|
||||||
├── opt/ # 外围可选工具(curl/socat/tmux/jq/rsync/nano/htop/ncdu/file/xxd…)
|
├── opt/ # 外围可选工具(curl/socat/tmux/jq/rsync/nano/htop/ncdu/file/xxd…)
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
|
|
||||||
- **移工具**:从 rootfs 移除 `MeowOS/opt/` 对应二进制;MeowOS 启动时若卡在则 PATH 前插 `/usr/data/mnt/sd_0/MeowOS/opt/bin:$PATH`(卡不在跳过)。用 `meowtool` 包装提供绝对路径引用。
|
- **移工具**:从 rootfs 移除 `MeowOS/opt/` 对应二进制;MeowOS 启动时若卡在则 PATH 前插 `/usr/data/mnt/sd_0/MeowOS/opt/bin:$PATH`(卡不在跳过)。用 `meowtool` 包装提供绝对路径引用。
|
||||||
- **框架零卡依赖审查**:确认 launcher/bootmenu 没有任何"卡必须在"的硬依赖(settings 在 /data、字体/应用/日志对卡可缺省/回退)。
|
- **框架零卡依赖审查**:确认 launcher/bootmenu 没有任何"卡必须在"的硬依赖(settings 在 /data、字体/应用/日志对卡可缺省/回退)。
|
||||||
- **字库**:多字体库 `MeowOS/fonts/<family>/`(每套 `font.png/font.idx/font.json`,`font.file` 选默认套,缺省 `MeowOS/fonts/default/font.png`;无卡回退点阵,已有。[源:审计 2026-08-30])。
|
- **字库**:多字体库 `MeowOS/fonts/<family>/<weight>/`(每套 `font.png/font.idx/font.json`;`font.file` 选默认套 = `<family>/<weight>/font.png`,缺省 `MeowOS/fonts/default/regular/font.png`;无卡回退点阵,已有。[源:审计 2026-08-30])。
|
||||||
- **日志**:卡 `MeowOS/logs/`(系统日志 `meow_<bootts>.log` + 应用日志 `apps/<app_id>/logs/<ts>_<pid>.log`;无卡内存环,已有。[源:审计 2026-08-30])。
|
- **日志**:卡 `MeowOS/logs/`(系统日志 `meow_<bootts>.log` + 应用日志 `apps/<app_id>/logs/<ts>_<pid>.log`;无卡内存环,已有。[源:审计 2026-08-30])。
|
||||||
- **设置**:板载 `/data/meow/settings.conf`(小文件跨卡保留),导出默认 `MeowOS/backups/settings/setting.bak`。
|
- **设置**:板载 `/data/meow/settings.conf`(小文件跨卡保留),导出默认 `MeowOS/backups/settings/setting.bak`。
|
||||||
|
|
||||||
|
|||||||
+18
-17
@@ -26,7 +26,7 @@
|
|||||||
└── MeowOS/ # MeowOS 专属(一切非必要文件)
|
└── MeowOS/ # MeowOS 专属(一切非必要文件)
|
||||||
├── backups/settings/setting.bak # 设置导出默认路径
|
├── backups/settings/setting.bak # 设置导出默认路径
|
||||||
├── services.d/ # 服务配置(meow_svcd 的 <name>.conf;TF 源,与板载 /usr/data/meow/services.d 双源,同名板载优先)`[meow_svcd.md]`
|
├── services.d/ # 服务配置(meow_svcd 的 <name>.conf;TF 源,与板载 /usr/data/meow/services.d 双源,同名板载优先)`[meow_svcd.md]`
|
||||||
├── fonts/<family>/ # 多字体库(每套一个子目录,见 §5.2)
|
├── fonts/<family>/<weight>/ # 多字体库(每 <family>/<weight> 一套三件套;见 §5.2)
|
||||||
│ ├── font.png # 预渲染灰度 PNG(bit depth 4|8,gen/gen_font.py 产物)
|
│ ├── font.png # 预渲染灰度 PNG(bit depth 4|8,gen/gen_font.py 产物)
|
||||||
│ ├── font.idx # 索引(MFNI v2,gen/gen_font.py 产物)
|
│ ├── font.idx # 索引(MFNI v2,gen/gen_font.py 产物)
|
||||||
│ └── font.json # 元数据 + sha256 完整性校验(schema 见 §5.2)
|
│ └── font.json # 元数据 + sha256 完整性校验(schema 见 §5.2)
|
||||||
@@ -152,18 +152,18 @@
|
|||||||
- **日/夜/自动**:SDK `meow_theme` 内置 `meow_theme_light/dark`;模式键 `display.theme_mode`(SYSTEM_SETTINGS 记为 `manual`/`auto`——**与 SDK 枚举 light/dark/auto 冲突**,见 §A);AUTO 判时段 `[SYSTEM_SETTINGS §1][MEOW_APP_SDK §8]`
|
- **日/夜/自动**:SDK `meow_theme` 内置 `meow_theme_light/dark`;模式键 `display.theme_mode`(SYSTEM_SETTINGS 记为 `manual`/`auto`——**与 SDK 枚举 light/dark/auto 冲突**,见 §A);AUTO 判时段 `[SYSTEM_SETTINGS §1][MEOW_APP_SDK §8]`
|
||||||
- 背景图系统:`display.bg_mode`(color/image)/`display.bg_dark_color`/`display.bg_light_color`/`display.bg_image`/`display.bg_image_dark`/`bg_image_light`/`display.bg_overlay_alpha`(0-255 默认 ~120);图片目录 **`MeowData/backgrounds/`**(480×720 约束);叠加层颜色随主题(深=黑叠/白=白叠)`[MAIN_MD3_PLAN §6][DEV_NOTES #12/#13]`
|
- 背景图系统:`display.bg_mode`(color/image)/`display.bg_dark_color`/`display.bg_light_color`/`display.bg_image`/`display.bg_image_dark`/`bg_image_light`/`display.bg_overlay_alpha`(0-255 默认 ~120);图片目录 **`MeowData/backgrounds/`**(480×720 约束);叠加层颜色随主题(深=黑叠/白=白叠)`[MAIN_MD3_PLAN §6][DEV_NOTES #12/#13]`
|
||||||
- 主界面背景:深色用 background(可选色)、浅色用 surface(白) `[DEV_NOTES #13]`
|
- 主界面背景:深色用 background(可选色)、浅色用 surface(白) `[DEV_NOTES #13]`
|
||||||
- **TF 卡多字体库(M2.5x)**:
|
- **TF 卡多字体库(M2.5x)+ 字重结构(M2.6)**:
|
||||||
- 目录 **`<卡根>/MeowOS/fonts/<family>/`**(每套一个子目录),内含三件套:`font.png`(预渲染灰度 PNG,bit depth 4|8)+ `font.idx`(MFNI v2 索引),二者均由 `font/gen/gen_font.py` 生成;`font.json` 为元数据+完整性校验。**缺任一件 → 该套不进列表 + 日志**。
|
- 目录 **`<卡根>/MeowOS/fonts/<family>/<weight>/`**(每 `<family>/<weight>` 一套,如 `misans/regular`),内含三件套:`font.png`(预渲染灰度 PNG,bit depth 4|8)+ `font.idx`(MFNI v2 索引),二者均由 `font/gen/gen_font.py` 生成;`font.json` 为元数据+完整性校验。**缺任一件 → 该套不进列表 + 日志**。
|
||||||
- **font.json schema(定版)**:`family`(同目录名,信息用)/`name`(下拉显示名,缺则回退目录名)/`glyph_count`(信息用)/`charset`(`common|gbk|full`,字库档位,信息用+驱动按需加载,见「字体档位/大字库」)/`size_list`(信息用)/`bit_depth`(4|8,信息用)/`sha256_png`(font.png 的 sha256 低 64hex)/`sha256_idx`(font.idx 的 sha256)/`source`(可选)/`license`(可选)。校验失败**仅 WARN 不硬失败**(该套仍可选);缺 font.json 或解析不出 `name` → 跳过。
|
- **font.json schema(定版)**:`family`(同目录名,信息用)/`name`(下拉显示名,缺则回退目录名)/`weight`(`regular|bold|italic|…`,字重 slug,缺省 `regular`;对应 `<family>/<weight>/` 目录第二级)/`glyph_count`(信息用)/`charset`(`common|gbk|full`,字库档位,信息用+驱动按需加载,见「字体档位/大字库」)/`size_list`(信息用)/`bit_depth`(4|8,信息用)/`sha256_png`(font.png 的 sha256 低 64hex)/`sha256_idx`(font.idx 的 sha256)/`source`(可选)/`license`(可选)。校验失败**仅 WARN 不硬失败**(该套仍可选);缺 font.json 或解析不出 `name` → 跳过。
|
||||||
- **加载优先级**:`font.file`(设置键,默认 `MeowOS/fonts/default/font.png`)指向的字体**存在且能 load → 用它**;否则回退卡默认 `MeowOS/fonts/default/font.png` → 固件 `/usr/share/meowos/font/font.png`。相对路径按卡根 `/usr/data/mnt/sd_0/` 解析。全失败 → 字库不可用,`ui_draw` 回退 5x7 点阵(系统不死)。
|
- **加载优先级**:`font.file`(设置键,默认 `MeowOS/fonts/default/regular/font.png`)指向的字体**存在且能 load → 用它**;否则回退卡默认 `MeowOS/fonts/default/regular/font.png` → 固件 `/usr/share/meowos/font/font.png`。相对路径按卡根 `/usr/data/mnt/sd_0/` 解析。全失败 → 字库不可用,`ui_draw` 回退 5x7 点阵(系统不死)。
|
||||||
- **默认字库目录**:默认字体统一放 **`<卡根>/MeowOS/fonts/default/`**(`font.png`+`font.idx`,可选 `font.json`),不再用 `MeowAPPs/fonts/font.png`;设备旧 `MeowAPPs/fonts/` **保留**(勿删,避免破坏老约定/已有文件),仅新引用一律指向 `MeowOS/fonts/default`。
|
- **默认字库目录**:默认字体统一放 **`<卡根>/MeowOS/fonts/default/regular/`**(`font.png`+`font.idx`,可选 `font.json`),不再用 `MeowAPPs/fonts/font.png`;设备旧 `MeowAPPs/fonts/` 及旧 `MeowOS/fonts/<family>/`(无 weight 级)**保留**(勿删,避免破坏老约定/已有文件),仅新引用一律指向 `MeowOS/fonts/default/regular`。
|
||||||
- **`font.file` 变更即 unload+reload 生效**(设置页回调);拔卡 → 回退固件内置。
|
- **`font.file` 变更即 unload+reload 生效**(设置页回调);拔卡 → 回退固件内置。
|
||||||
- **preset 动态化**:启动/进设置页扫描一次 `MeowOS/fonts/*/`,重建「字体文件」下拉(默认字体 + ≤16 套卡内字体;上限防爆)。扫描仅此二时机,勿放热路径。
|
- **preset 动态化(两级扫描)**:启动/进设置页扫描一次 `MeowOS/fonts/<family>/<weight>/`,重建「字体文件」下拉(默认字体 + ≤16 套卡内 <family>/<weight>;上限防爆)。扫描仅此二时机,勿放热路径。**存量卡片迁移**:旧版扫 `MeowOS/fonts/<family>/`(图片直接在该层);迁移到 `<family>/regular/` 后,旧 `<family>/` 下不再有三件套 → 新扫默认按两级找,故若未重做成 `<family>/<weight>/` 则扫不到(需先用 mv 迁成 `<family>/<weight>/` 再重扫)。
|
||||||
- **MeowPanel 预留**:`meow_font_import_zip()`/`meow_font_export_zip()` 为 TODO 声明(不实现),供 MeowPanel 后续接入(`.zip` 导入校验→解压到 `MeowOS/fonts/<family>/`、导出打包)。
|
- **MeowPanel 预留**:`meow_font_import_zip()`/`meow_font_export_zip()` 为 TODO 声明(不实现),供 MeowPanel 后续接入(`.zip` 导入校验→解压到 `MeowOS/fonts/<family>/<weight>/`、导出打包)。
|
||||||
- **多字体(M2.5x,`display.multi_font`)**:按字符类别(CJK/CJK_SYM/NUM/LATIN/LATIN_SYM/OTHER)逐字符路由,每类可各选一套字库(`font.class_*`,默认空=跟随全局 `font.file`);同 family 去重、同时装载不同字体 ≤4 套(超出 WARN「过多不同字体会增内存」并截断);开关关 = 单字体(现状)。详见 §5.7.7「多字体」。
|
- **多字体(M2.5x,`display.multi_font`)**:按字符类别(CJK/CJK_SYM/NUM/LATIN/LATIN_SYM/OTHER)逐字符路由,每类可各选一套字库(`font.class_*`,默认空=跟随全局 `font.file`);同 family 去重、同时装载不同字体 ≤4 套(超出 WARN「过多不同字体会增内存」并截断);开关关 = 单字体(现状)。详见 §5.7.7「多字体」。
|
||||||
- **字体档位 / 大字库(M2.5x,本次实现)**:抽象出**字库大小档位**,让系统/应用/设置页都知道选的是多大字库 `[源:用户 2026-08-30]`。
|
- **字体档位 / 大字库(M2.5x,本次实现)**:抽象出**字库大小档位**,让系统/应用/设置页都知道选的是多大字库 `[源:用户 2026-08-30]`。
|
||||||
- 档位枚举:`MEOW_FONT_TIER_COMMON`(**默认 / ≈4k** 码位)/ `MEOW_FONT_TIER_GBK`(**大 / ≈20k+**)/ `MEOW_FONT_TIER_FULL`(**全 / 可选**,码位/体积最大)。
|
- 档位枚举:`MEOW_FONT_TIER_COMMON`(**默认 / ≈4k** 码位)/ `MEOW_FONT_TIER_GBK`(**大 / ≈20k+**)/ `MEOW_FONT_TIER_FULL`(**全 / 可选**,码位/体积最大)。
|
||||||
- `font/gen/gen_font.py` `--charset common|gbk|full` 按档生成 `font.png/font.idx/font.json`(**已实现**,现行仅 `--extra` 思路升级为**档位选择**);每个 `MeowOS/fonts/<family>/font.json` 标 `charset` + `glyph_count`(覆盖声明/信息用,launcher/settings 据此标注与按需装载)。
|
- `font/gen/gen_font.py` `--charset common|gbk|full [--weight <name>]` 按档生成 `font.png/font.idx/font.json`(**已实现**,现行仅 `--extra` 思路升级为**档位选择**;M2.6 加 `--weight` 写 `font.json.weight`,缺省 `regular`);每个 `MeowOS/fonts/<family>/<weight>/font.json` 标 `weight`+`charset` + `glyph_count`(覆盖声明/信息用,launcher/settings 据此标注与按需装载)。
|
||||||
- **默认用小字库(`common`≈4k)**;仅当 app 声明需大字库(`app.json "fonts":"full"` 或档位)才为该 app 加载大字库(`gbk`≈20k+),见 §5.7.8。
|
- **默认用小字库(`common`≈4k)**;仅当 app 声明需大字库(`app.json "fonts":"full"` 或档位)才为该 app 加载大字库(`gbk`≈20k+),见 §5.7.8。
|
||||||
- **系统设置「加载大字库」开关**(`font.load_full`,强制全系统用大字库),见 SYSTEM_SETTINGS §1;**每个字体选项标注字库大小标记**(如 `default(common≈4k)` / `gbk≈20k` / `full`),让用户知道所选字库量级。
|
- **系统设置「加载大字库」开关**(`font.load_full`,强制全系统用大字库),见 SYSTEM_SETTINGS §1;**每个字体选项标注字库大小标记**(如 `default(common≈4k)` / `gbk≈20k` / `full`),让用户知道所选字库量级。
|
||||||
- 判缺字:SDK `meow_font_has_glyph(cp)` 预判当前字库是否含该码位(缺字 → app 回退/提示)。
|
- 判缺字:SDK `meow_font_has_glyph(cp)` 预判当前字库是否含该码位(缺字 → app 回退/提示)。
|
||||||
@@ -205,19 +205,20 @@
|
|||||||
> **app 未自带字库 → 跟随系统 `font.file`(M2.x 已落地)**:launcher 每次 `launch_app` 前把「当前生效字体」注入为环境变量 `MEOW_FONT_PNG`/`MEOW_FONT_IDX`(绝对路径,可选 `MEOW_FONT_APP=<family>`);SDK `meow_ui_gfx.c load_font()` **优先读 env**,无 env 则回退卡默认 `MeowOS/fonts/default` → 固件。§5.7.3 的 `load_handle`(app 自带字库)**未实现**,故当前 app 只能跟随系统字体;一旦实现自带字库,其加载优先级应高于 env。
|
> **app 未自带字库 → 跟随系统 `font.file`(M2.x 已落地)**:launcher 每次 `launch_app` 前把「当前生效字体」注入为环境变量 `MEOW_FONT_PNG`/`MEOW_FONT_IDX`(绝对路径,可选 `MEOW_FONT_APP=<family>`);SDK `meow_ui_gfx.c load_font()` **优先读 env**,无 env 则回退卡默认 `MeowOS/fonts/default` → 固件。§5.7.3 的 `load_handle`(app 自带字库)**未实现**,故当前 app 只能跟随系统字体;一旦实现自带字库,其加载优先级应高于 env。
|
||||||
|
|
||||||
- **5.7.1 字体管理器(launcher 侧)= 可用字库唯一权威来源**
|
- **5.7.1 字体管理器(launcher 侧)= 可用字库唯一权威来源**
|
||||||
- 唯一维护并扫描 `MeowOS/fonts/<family>/`(+默认),产出「字体列表」(family slug / 显示名 / glyph_count / size_list / bit_depth)。**SDK 不自行发现字库**,一律经本接口取 launcher 扫描结果。
|
- 唯一维护并扫描 `MeowOS/fonts/<family>/<weight>/`(+默认),产出「字体列表」(family slug / weight slug / 显示名 / glyph_count / size_list / bit_depth)。**SDK 不自行发现字库**,一律经本接口取 launcher 扫描结果。
|
||||||
- 扫描与校验规则沿用 §5.2:三件套 `font.png/font.idx/font.json` **缺任一件 → 该套跳过 + 日志**;`font.json` 解析 `name`(缺回退目录名)/`glyph_count`/`size_list`/`bit_depth`;`sha256_png/sha256_idx` 仅 **WARN** 不硬失败(该套仍可选)。
|
- 扫描与校验规则沿用 §5.2:三件套 `font.png/font.idx/font.json` **缺任一件 → 该套跳过 + 日志**;`font.json` 解析 `name`(缺回退目录名)/`weight`(缺省 `regular`)/`glyph_count`/`size_list`/`bit_depth`;`sha256_png/sha256_idx` 仅 **WARN** 不硬失败(该套仍可选)。
|
||||||
- 列表上限沿用 `FONT_PRESETS_MAX = 17`(1 默认 + ≤16 卡内套装,防爆);顺序 = 默认(0) + 扫描顺序。扫描时机不变(启动 / 进设置页),**勿放热路径** `[源:agent-79 实现]`。
|
- 列表上限沿用 `FONT_PRESETS_MAX = 17`(1 默认 + ≤16 卡内套装,防爆);顺序 = 默认(0) + 扫描顺序。扫描时机不变(启动 / 进设置页),**勿放热路径** `[源:agent-79 实现]`。
|
||||||
- 权威列表数据模型(SDK 侧快照,枚举时填充):
|
- 权威列表数据模型(SDK 侧快照,枚举时填充):
|
||||||
```c
|
```c
|
||||||
typedef struct meow_font_info {
|
typedef struct meow_font_info {
|
||||||
char family[64]; /* family slug = MeowOS/fonts/<family>/ 目录名 */
|
char family[64]; /* family slug = MeowOS/fonts/<family>/ 第一级目录名 */
|
||||||
char name[64]; /* font.json "name",缺则回退 family */
|
char name[64]; /* font.json "name",缺则回退 family */
|
||||||
|
char weight[32]; /* font.json "weight"(regular|bold|italic|…),缺省 "regular" */
|
||||||
int glyph_count; /* font.json "glyph_count",信息用 */
|
int glyph_count; /* font.json "glyph_count",信息用 */
|
||||||
int size_list[16]; /* font.json "size_list"(原生面字号,0 结束) */
|
int size_list[16]; /* font.json "size_list"(原生面字号,0 结束) */
|
||||||
int size_count; /* 有效字号数 */
|
int size_count; /* 有效字号数 */
|
||||||
int bit_depth; /* 4 | 8 */
|
int bit_depth; /* 4 | 8 */
|
||||||
char path_png[320]; /* 系统库:相对卡根 MeowOS/fonts/<family>/font.png */
|
char path_png[320]; /* 系统库:相对卡根 MeowOS/fonts/<family>/<weight>/font.png */
|
||||||
} meow_font_info;
|
} meow_font_info;
|
||||||
```
|
```
|
||||||
- **5.7.2 应用可读 TF 卡字体列表(统一规范接口,app 面向)** `[源:agent-79 实现][源:meow_font.h]`
|
- **5.7.2 应用可读 TF 卡字体列表(统一规范接口,app 面向)** `[源:agent-79 实现][源:meow_font.h]`
|
||||||
@@ -230,7 +231,7 @@
|
|||||||
```
|
```
|
||||||
- 语义:`id` = 列表序号(§5.7.1 快照);`0` = **跟随系统默认**(用户 `font.file` 所选,app 会话不覆盖);`>0` = **app 会话内固定用该套**(覆盖 `font.file` 至 app 结束)。成功 0,越界/加载失败返回负值。
|
- 语义:`id` = 列表序号(§5.7.1 快照);`0` = **跟随系统默认**(用户 `font.file` 所选,app 会话不覆盖);`>0` = **app 会话内固定用该套**(覆盖 `font.file` 至 app 结束)。成功 0,越界/加载失败返回负值。
|
||||||
- **列表快照是临时的**:launcher 仅启动/进设置页重扫,期间拔卡或改卡内字库不刷新;app 应**每次会话重新枚举**、按 `family` 持久化,勿缓存数值 id。
|
- **列表快照是临时的**:launcher 仅启动/进设置页重扫,期间拔卡或改卡内字库不刷新;app 应**每次会话重新枚举**、按 `family` 持久化,勿缓存数值 id。
|
||||||
- 覆盖范围:系统库 `MeowOS/fonts/<family>/` 与默认(含固件 `/usr/share/meowos/font` 回退)都反映在列表里;app 侧「选用已有字库」**只用本接口,不直接拼路径**。
|
- 覆盖范围:系统库 `MeowOS/fonts/<family>/<weight>/` 与默认(含固件 `/usr/share/meowos/font` 回退)都反映在列表里;app 侧「选用已有字库」**只用本接口,不直接拼路径**。
|
||||||
- **5.7.3 应用自带字库(按 handle 加载)** `[源:meow_font.h]`
|
- **5.7.3 应用自带字库(按 handle 加载)** `[源:meow_font.h]`
|
||||||
- 目录:`MeowAPPs/<pkg>/fonts/<handle>/{font.png,font.idx,font.json}`;`<pkg>` = `app.json.package`(反域名 app_id,见 §5.5),`<handle>` = 应用自设名字(如 `lyrics`/`reader`)。
|
- 目录:`MeowAPPs/<pkg>/fonts/<handle>/{font.png,font.idx,font.json}`;`<pkg>` = `app.json.package`(反域名 app_id,见 §5.5),`<handle>` = 应用自设名字(如 `lyrics`/`reader`)。
|
||||||
- `app.json` 声明(**可选**):`"fonts": ["lyrics","reader"]`。作用 = MeowPanel 打包校验 / launcher 装包校验 / SDK 提供取证;**不声明也可运行时按 handle 加载**。
|
- `app.json` 声明(**可选**):`"fonts": ["lyrics","reader"]`。作用 = MeowPanel 打包校验 / launcher 装包校验 / SDK 提供取证;**不声明也可运行时按 handle 加载**。
|
||||||
@@ -287,15 +288,15 @@
|
|||||||
- 说明:句柄状态为 app 进程内**全局**;同一屏需混排时,推荐每段绘制前显式 `meow_ui_use_font`,或全部走 `*_h` 显式传 handle,避免依赖上次绘制遗留的句柄状态。
|
- 说明:句柄状态为 app 进程内**全局**;同一屏需混排时,推荐每段绘制前显式 `meow_ui_use_font`,或全部走 `*_h` 显式传 handle,避免依赖上次绘制遗留的句柄状态。
|
||||||
- **5.7.6 MeowPanel 上传 / 导出(.zip,预留)** `[源:meow_font.h]`
|
- **5.7.6 MeowPanel 上传 / 导出(.zip,预留)** `[源:meow_font.h]`
|
||||||
- 复用已声明的 `meow_font_import_zip(zip_path)` / `meow_font_export_zip(family,out_zip)`(§5.2 已列为 TODO,未实现)。
|
- 复用已声明的 `meow_font_import_zip(zip_path)` / `meow_font_export_zip(family,out_zip)`(§5.2 已列为 TODO,未实现)。
|
||||||
- **系统字库导入**:`.zip` 内含 `font.png+font.idx+font.json` 三件套;校验三件套就绪 + `font.json` 可解析;sha256 沿用 `font.json` 约定(建议与运行时同口径 = **WARN**,仅缺件/不可解析硬失败);`family` 取自 `font.json.family` 或 zip 名;解压到 `MeowOS/fonts/<family>/`;**覆盖冲突需先确认**;导入后应触发 launcher 重扫(刷新 §5.7.1 列表)。
|
- **系统字库导入**:`.zip` 内含 `font.png+font.idx+font.json` 三件套;校验三件套就绪 + `font.json` 可解析;sha256 沿用 `font.json` 约定(建议与运行时同口径 = **WARN**,仅缺件/不可解析硬失败);`family`/`weight` 取自 `font.json.family`/`font.json.weight`(缺省 `regular`)或 zip 名;解压到 `MeowOS/fonts/<family>/<weight>/`;**覆盖冲突需先确认**;导入后应触发 launcher 重扫(刷新 §5.7.1 列表)。
|
||||||
- **应用字库导入**:目标 `MeowAPPs/<pkg>/fonts/<handle>/`,与 `app.json "fonts"` 声明对齐,校验同上。
|
- **应用字库导入**:目标 `MeowAPPs/<pkg>/fonts/<handle>/`,与 `app.json "fonts"` 声明对齐,校验同上。
|
||||||
- **导出**:系统库把 `MeowOS/fonts/<family>/` 三件套打包;应用库把 `MeowAPPs/<pkg>/fonts/<handle>/` 打包。
|
- **导出**:系统库把 `MeowOS/fonts/<family>/<weight>/` 三件套打包;应用库把 `MeowAPPs/<pkg>/fonts/<handle>/` 打包。
|
||||||
- 约定:`.zip` 布局与校验沿用 `font.json` schema(§5.2);导入/导出后的一致性(列表刷新、应用声明同步)归 MeowPanel + launcher 协作,本小节仅列接口语义。
|
- 约定:`.zip` 布局与校验沿用 `font.json` schema(§5.2);导入/导出后的一致性(列表刷新、应用声明同步)归 MeowPanel + launcher 协作,本小节仅列接口语义。
|
||||||
- **5.7.7 多字体(按字符类别逐字符路由,`display.multi_font`,M2.5x 新增)** `[源:meow_font.h][源:MEOWOS_SPEC_REF §5.2/§5.5]`
|
- **5.7.7 多字体(按字符类别逐字符路由,`display.multi_font`,M2.5x 新增)** `[源:meow_font.h][源:MEOWOS_SPEC_REF §5.2/§5.5]`
|
||||||
- **背景**:一款字体难兼顾中文(大图集、字形全)与拉丁/数字(干净、省内存)。多字体让**不同字符类别各用一套字库**,按 codepoint 逐字符路由取字形。
|
- **背景**:一款字体难兼顾中文(大图集、字形全)与拉丁/数字(干净、省内存)。多字体让**不同字符类别各用一套字库**,按 codepoint 逐字符路由取字形。
|
||||||
- **设置键**(launcher 读,SDK 经 env 读同一映射):
|
- **设置键**(launcher 读,SDK 经 env 读同一映射):
|
||||||
- `display.multi_font`(int **0/1,默认 0=单字体**):1=按字符类别逐字符路由;0=只走 `font.file`(现状,零额外内存/性能开销)。
|
- `display.multi_font`(int **0/1,默认 0=单字体**):1=按字符类别逐字符路由;0=只走 `font.file`(现状,零额外内存/性能开销)。
|
||||||
- 6 个类别字体(SET_STR,值 = 字体 family 相对路径,**默认空 = 跟随全局 `font.file`**):`font.class_cjk`(CJK 表意+扩展)、`font.class_cjk_sym`(全角/中文标点)、`font.class_num`(0-9)、`font.class_latin`(A-Za-z)、`font.class_latin_sym`(半角标点)、`font.class_other`(其他)。
|
- 6 个类别字体(SET_STR,值 = 字体 `<family>/<weight>` 相对路径(如 `misans/bold`,M2.6);**默认空 = 跟随全局 `font.file`**):`font.class_cjk`(CJK 表意+扩展)、`font.class_cjk_sym`(全角/中文标点)、`font.class_num`(0-9)、`font.class_latin`(A-Za-z)、`font.class_latin_sym`(半角标点)、`font.class_other`(其他)。
|
||||||
- **字符分类器**(`meow_font_class_of(cp)`,按 codepoint):`MEOW_FCLS_CJK`/`_CJK_SYM`/`_NUM`/`_LATIN`/`_LATIN_SYM`/`_OTHER`。规则(ASCII 优先 + Unicode 区间):0x30-39→NUM、0x41-5A/61-7A→LATIN、<0x80(其余)→LATIN_SYM;0x3000-303F/0xFF00-FFEF/0x2014/0x2018-201D/0x2026/0x00B7→CJK_SYM;CJK 表意+扩展+兼容表意区(0x3400-4DBF/4E00-9FFF/20000-2FA1F…)→CJK;其余(假名/韩文/其他文字/扩展区)→OTHER。
|
- **字符分类器**(`meow_font_class_of(cp)`,按 codepoint):`MEOW_FCLS_CJK`/`_CJK_SYM`/`_NUM`/`_LATIN`/`_LATIN_SYM`/`_OTHER`。规则(ASCII 优先 + Unicode 区间):0x30-39→NUM、0x41-5A/61-7A→LATIN、<0x80(其余)→LATIN_SYM;0x3000-303F/0xFF00-FFEF/0x2014/0x2018-201D/0x2026/0x00B7→CJK_SYM;CJK 表意+扩展+兼容表意区(0x3400-4DBF/4E00-9FFF/20000-2FA1F…)→CJK;其余(假名/韩文/其他文字/扩展区)→OTHER。
|
||||||
- **逐字符路由**:`meow_font_drawtext`/`_outline`/`width_text`/`line_height`/`ascent`/`has_size` 在 multi_font 开启时**逐字符分类 → 用类别对应槽的字库取字形与度量**(宽度/行高按该字体);缺某类字体/槽未载 → **回退全局**(slot0);该字符其字体无字形 → **豆腐缺字**(可经 `meow_font_has_glyph(cp)` 预判)。开关关 → 一律 slot0,与单字体一致。
|
- **逐字符路由**:`meow_font_drawtext`/`_outline`/`width_text`/`line_height`/`ascent`/`has_size` 在 multi_font 开启时**逐字符分类 → 用类别对应槽的字库取字形与度量**(宽度/行高按该字体);缺某类字体/槽未载 → **回退全局**(slot0);该字符其字体无字形 → **豆腐缺字**(可经 `meow_font_has_glyph(cp)` 预判)。开关关 → 一律 slot0,与单字体一致。
|
||||||
- **SDK 接口**:`meow_font_load_slot(slot,png,idx)`/`meow_font_unload_slot(slot)`/`meow_font_enable_multifont(on)`/`meow_font_set_class_slot(cls,slot)`/`meow_font_config_multifont(on,global_png,global_idx,cfg[],&distinct,&truncated)`;`MEOW_MAX_SLOTS=5`(slot0=全局 + ≤4 类别)。`meow_font_config_multifont` 统一做**同 png 去重**(含与全局相同 → follow slot0)与**≤4 套上限**(超出 → 置 `*truncated=1`,调用方 WARN「过多不同字体会增内存」,已装载者保留、新类别回退全局)。
|
- **SDK 接口**:`meow_font_load_slot(slot,png,idx)`/`meow_font_unload_slot(slot)`/`meow_font_enable_multifont(on)`/`meow_font_set_class_slot(cls,slot)`/`meow_font_config_multifont(on,global_png,global_idx,cfg[],&distinct,&truncated)`;`MEOW_MAX_SLOTS=5`(slot0=全局 + ≤4 类别)。`meow_font_config_multifont` 统一做**同 png 去重**(含与全局相同 → follow slot0)与**≤4 套上限**(超出 → 置 `*truncated=1`,调用方 WARN「过多不同字体会增内存」,已装载者保留、新类别回退全局)。
|
||||||
|
|||||||
+14
-6
@@ -281,8 +281,8 @@ 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, tier, glyph_count, sizes, bitdepth,
|
def write_font_json(out_dir, name, family, weight, tier, glyph_count, sizes, bitdepth,
|
||||||
png_path, idx_path):
|
png_path, idx_path, source="", license_=""):
|
||||||
def sha256(path):
|
def sha256(path):
|
||||||
h = hashlib.sha256()
|
h = hashlib.sha256()
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
@@ -292,6 +292,7 @@ def write_font_json(out_dir, name, family, tier, glyph_count, sizes, bitdepth,
|
|||||||
meta = {
|
meta = {
|
||||||
"family": family or name, # 同目录名(信息用;扫描端以目录名为 family)
|
"family": family or name, # 同目录名(信息用;扫描端以目录名为 family)
|
||||||
"name": name, # 下拉显示名(缺则回退目录名)
|
"name": name, # 下拉显示名(缺则回退目录名)
|
||||||
|
"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(档位;驱动按需加载)
|
||||||
"size_list": sizes, # 原生面字号
|
"size_list": sizes, # 原生面字号
|
||||||
@@ -299,6 +300,8 @@ def write_font_json(out_dir, name, family, tier, glyph_count, sizes, bitdepth,
|
|||||||
"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 source: meta["source"] = source # 可选:来源(如 "MiSans Regular (Xiaomi)")
|
||||||
|
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")
|
||||||
with open(jpath, "w", encoding="utf-8") as f:
|
with open(jpath, "w", encoding="utf-8") as f:
|
||||||
json.dump(meta, f, ensure_ascii=False, indent=2)
|
json.dump(meta, f, ensure_ascii=False, indent=2)
|
||||||
@@ -315,10 +318,14 @@ def main():
|
|||||||
ap.add_argument("--out", required=True)
|
ap.add_argument("--out", required=True)
|
||||||
ap.add_argument("--name", default="font")
|
ap.add_argument("--name", default="font")
|
||||||
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",
|
||||||
|
help="字重 slug(regular|bold|italic|…,缺省 regular;写进 font.json weight)")
|
||||||
ap.add_argument("--charset", default="common", choices=CHARSET_TIERS,
|
ap.add_argument("--charset", default="common", choices=CHARSET_TIERS,
|
||||||
help="字体档位:common≈4k / gbk≈20k+ / full(可选,默认不推)")
|
help="字体档位:common≈4k / gbk≈20k+ / full(可选,默认不推)")
|
||||||
ap.add_argument("--bitdepth", type=int, default=4, choices=(4, 8))
|
ap.add_argument("--bitdepth", type=int, default=4, choices=(4, 8))
|
||||||
ap.add_argument("--extra", default="")
|
ap.add_argument("--extra", default="")
|
||||||
|
ap.add_argument("--source", default="", help="可选:font.json source 来源说明")
|
||||||
|
ap.add_argument("--license", default="", help="可选:font.json license 许可")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
os.makedirs(args.out, exist_ok=True)
|
os.makedirs(args.out, exist_ok=True)
|
||||||
@@ -393,15 +400,16 @@ def main():
|
|||||||
idx_path = os.path.join(args.out, f"{args.name}.idx")
|
idx_path = os.path.join(args.out, f"{args.name}.idx")
|
||||||
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(写入 charset + glyph_count + sha256,供扫描端标注档位/大小标记) ----
|
# ---- font.json(写入 weight + charset + glyph_count + sha256,供扫描端标注档位/大小标记) ----
|
||||||
jpath = write_font_json(args.out, args.name, args.family, tier, charset_glyph_count(cps),
|
jpath = write_font_json(args.out, args.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)
|
||||||
|
|
||||||
png_size = os.path.getsize(png_path)
|
png_size = os.path.getsize(png_path)
|
||||||
total = png_size + idx_size
|
total = png_size + idx_size
|
||||||
print(f"\nfont.png {png_size} B")
|
print(f"\nfont.png {png_size} B")
|
||||||
print(f"font.idx {idx_size} B")
|
print(f"font.idx {idx_size} B")
|
||||||
print(f"font.json {os.path.getsize(jpath)} B (charset={tier}, glyph_count={len(cps)})")
|
print(f"font.json {os.path.getsize(jpath)} B (charset={tier}, weight={args.weight or 'regular'}, glyph_count={len(cps)})")
|
||||||
print(f"合计 {total} B = {total/1024:.1f} KiB = {total/1048576:.2f} MiB (预算 2MiB)")
|
print(f"合计 {total} B = {total/1024:.1f} KiB = {total/1048576:.2f} MiB (预算 2MiB)")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -109,8 +109,9 @@ int meow_font_config_multifont(int on,
|
|||||||
/* ---- MeowPanel 预留:TF 卡字体库 .zip 导入/导出(TODO,未实现)----
|
/* ---- MeowPanel 预留:TF 卡字体库 .zip 导入/导出(TODO,未实现)----
|
||||||
* 当前仅声明占位,供 MeowPanel 后续接入;不实现、不接 zip 库。
|
* 当前仅声明占位,供 MeowPanel 后续接入;不实现、不接 zip 库。
|
||||||
* 调用方约定(待实现):导入=校验 .zip 内含 font.png+font.idx+font.json 三件套,
|
* 调用方约定(待实现):导入=校验 .zip 内含 font.png+font.idx+font.json 三件套,
|
||||||
* 校验通过后解压到 TF 卡 MeowOS/fonts/<family>/(family 取自 font.json 的 family 或 zip 名,
|
* 校验通过后解压到 TF 卡 MeowOS/fonts/<family>/<weight>/(family/weight 取自 font.json 的
|
||||||
* 冲突时覆盖需先确认);导出=把 MeowOS/fonts/<family>/ 三件套打包为 <out_zip>。
|
* family/weight 或 zip 名,缺省 weight=regular;冲突时覆盖需先确认);导出=把
|
||||||
|
* MeowOS/fonts/<family>/<weight>/ 三件套打包为 <out_zip>。
|
||||||
* 目录布局与 font.json schema 见 docs/MEOWOS_SPEC_REF.md §1.2/§5.2。
|
* 目录布局与 font.json schema 见 docs/MEOWOS_SPEC_REF.md §1.2/§5.2。
|
||||||
* 返回码:MEOW_OK=0 成功,负值错误码(沿用本模块失败负值约定)。 */
|
* 返回码:MEOW_OK=0 成功,负值错误码(沿用本模块失败负值约定)。 */
|
||||||
int meow_font_import_zip(const char *zip_path); /* TODO:MeowPanel */
|
int meow_font_import_zip(const char *zip_path); /* TODO:MeowPanel */
|
||||||
|
|||||||
Binary file not shown.
@@ -109,8 +109,9 @@ int meow_font_config_multifont(int on,
|
|||||||
/* ---- MeowPanel 预留:TF 卡字体库 .zip 导入/导出(TODO,未实现)----
|
/* ---- MeowPanel 预留:TF 卡字体库 .zip 导入/导出(TODO,未实现)----
|
||||||
* 当前仅声明占位,供 MeowPanel 后续接入;不实现、不接 zip 库。
|
* 当前仅声明占位,供 MeowPanel 后续接入;不实现、不接 zip 库。
|
||||||
* 调用方约定(待实现):导入=校验 .zip 内含 font.png+font.idx+font.json 三件套,
|
* 调用方约定(待实现):导入=校验 .zip 内含 font.png+font.idx+font.json 三件套,
|
||||||
* 校验通过后解压到 TF 卡 MeowOS/fonts/<family>/(family 取自 font.json 的 family 或 zip 名,
|
* 校验通过后解压到 TF 卡 MeowOS/fonts/<family>/<weight>/(family/weight 取自 font.json 的
|
||||||
* 冲突时覆盖需先确认);导出=把 MeowOS/fonts/<family>/ 三件套打包为 <out_zip>。
|
* family/weight 或 zip 名,缺省 weight=regular;冲突时覆盖需先确认);导出=把
|
||||||
|
* MeowOS/fonts/<family>/<weight>/ 三件套打包为 <out_zip>。
|
||||||
* 目录布局与 font.json schema 见 docs/MEOWOS_SPEC_REF.md §1.2/§5.2。
|
* 目录布局与 font.json schema 见 docs/MEOWOS_SPEC_REF.md §1.2/§5.2。
|
||||||
* 返回码:MEOW_OK=0 成功,负值错误码(沿用本模块失败负值约定)。 */
|
* 返回码:MEOW_OK=0 成功,负值错误码(沿用本模块失败负值约定)。 */
|
||||||
int meow_font_import_zip(const char *zip_path); /* TODO:MeowPanel */
|
int meow_font_import_zip(const char *zip_path); /* TODO:MeowPanel */
|
||||||
|
|||||||
Binary file not shown.
@@ -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)*/
|
/* M2.6 meow_font 集成(agent-28 字体子系统,font/meow_font.a + libz.a)*/
|
||||||
/* 字库放 TF 卡 MeowOS/fonts/default/font.png+font.idx(或固件 /usr/share/ */
|
/* M2.6 MeowOS 字重结构:字库放 TF 卡 MeowOS/fonts/default/regular/font.png+font.idx
|
||||||
/* meowos/font/);MeowOS 启动 ensure_card_mounted 后再 load,卡晚 */
|
*(或固件 /usr/share/meowos/font/);MeowOS 启动 ensure_card_mounted 后再 load,卡晚 */
|
||||||
/* 挂载时轮询重试。没加载成功时 ui_draw 回退 5x7 点阵(ASCII)。 */
|
/* 挂载时轮询重试。没加载成功时 ui_draw 回退 5x7 点阵(ASCII)。 */
|
||||||
/* ================================================================== */
|
/* ================================================================== */
|
||||||
#define UI_FONT_PNG_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.png"
|
#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/font.idx"
|
#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_PNG_SYS "/usr/share/meowos/font/font.png"
|
||||||
#define UI_FONT_IDX_SYS "/usr/share/meowos/font/font.idx"
|
#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 可指定宿主根以便单测)。 */
|
* <卡根> 默认 /usr/data/mnt/sd_0(card_present() 判定;--fontscan 可指定宿主根以便单测)。 */
|
||||||
#define MEOW_FONTS_CFG "MeowOS/fonts"
|
#define MEOW_FONTS_CFG "MeowOS/fonts"
|
||||||
#define FONT_PRESETS_MAX 17 /* 1 默认 + ≤16 卡内字体(防爆) */
|
#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_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")
|
/* M2.5x 字体档位:每套的 charset 档("common"/"gbk"/"full",取自 font.json,缺省 "common")
|
||||||
* 与 glyph_count(信息用)。供档位分层装载(meow_font_effective_paths_tiered)与设置页标注。 */
|
* 与 glyph_count(信息用)。供档位分层装载(meow_font_effective_paths_tiered)与设置页标注。 */
|
||||||
static const char *font_preset_tiers[FONT_PRESETS_MAX];
|
static const char *font_preset_tiers[FONT_PRESETS_MAX];
|
||||||
static int font_preset_glyphs[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_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_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 font_presets_n = 0; /* 运行时动态计数(默认=1,仅"默认字体") */
|
||||||
static int mfont_ready = 0;
|
static int mfont_ready = 0;
|
||||||
static int mfont_forced = 0; /* font.file 变更→强制真实(un)load,跳过 SDK 复用捷径 */
|
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 已接入):
|
/* 尝试加载字库;成功返回 1。优先级(M2.5x,font.load_full 已接入):
|
||||||
* 0) 生效档 = font.load_full ? gbk : common(launcher 自身 UI 跟随系统大字库开关);
|
* 0) 生效档 = font.load_full ? gbk : common(launcher 自身 UI 跟随系统大字库开关);
|
||||||
* 1) 生效字体(tier 解析,tier0 即 font.file 所选;相对路径按卡根解析);
|
* 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。
|
* 3) 固件 /usr/share/meowos/font/font.png。
|
||||||
* A-1/A-2:SDK meow_ui_init() 已用同一 meow_font.o 加载过(可能成功也可能未成功)。
|
* A-1/A-2:SDK meow_ui_init() 已用同一 meow_font.o 加载过(可能成功也可能未成功)。
|
||||||
* 当字体指向「默认卡字库」或未设置时复用 SDK 已加载状态,避免重复 unload+reload;
|
* 当字体指向「默认卡字库」或未设置时复用 SDK 已加载状态,避免重复 unload+reload;
|
||||||
@@ -899,9 +909,42 @@ static int meow_font_ensure(void)
|
|||||||
return 0;
|
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。
|
/* 求「当前生效字体」的 png/idx 绝对路径 + family,供 launch_app 注入 MEOW_FONT_PNG/IDX/APP。
|
||||||
* 优先级(与 meow_font_ensure 一致):font.file(resolve_selected)→ 卡默认 MeowOS/fonts/default。
|
* 优先级(与 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 无卡/无可用字体(调用方跳过注入,
|
* 返回 0 成功(out_png/out_idx/out_family 已填),-1 无卡/无可用字体(调用方跳过注入,
|
||||||
* SDK 自身会回退卡默认→固件→点阵)。 */
|
* SDK 自身会回退卡默认→固件→点阵)。 */
|
||||||
static int meow_font_effective_paths(char *out_png, int plen, char *out_idx, int ilen,
|
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) {
|
if (meow_font_resolve_selected(fpng, sizeof fpng, fidx, sizeof fidx) == 0) {
|
||||||
snprintf(out_png, plen, "%s", fpng);
|
snprintf(out_png, plen, "%s", fpng);
|
||||||
snprintf(out_idx, ilen, "%s", fidx);
|
snprintf(out_idx, ilen, "%s", fidx);
|
||||||
const char *prefix = "/MeowOS/fonts/";
|
font_family_from_png(fpng, out_family, flen);
|
||||||
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");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* 回退卡默认 MeowOS/fonts/default */
|
/* 回退卡默认 MeowOS/fonts/default */
|
||||||
@@ -958,9 +993,10 @@ static int meow_font_effective_tier(int app_tier)
|
|||||||
* - 生效档 = max(app 声明档, font.load_full ? GBK : COMMON):系统「加载大字库」开 →
|
* - 生效档 = max(app 声明档, font.load_full ? GBK : COMMON):系统「加载大字库」开 →
|
||||||
* 强制 ≥gbk;
|
* 强制 ≥gbk;
|
||||||
* - tier<=common → 走既有 meow_font_effective_paths(font.file/卡默认/固件);
|
* - tier<=common → 走既有 meow_font_effective_paths(font.file/卡默认/固件);
|
||||||
* - tier>0 → 在已扫描的 MeowOS/fonts/<family>/ 里找 charset 匹配(gbk 请求接受 gbk/full;
|
* - tier>0 → 在已扫描的 MeowOS/fonts/<family>/<weight>/ 里找 charset 匹配(gbk 请求接受
|
||||||
* full 请求接受 full)的那套,以其 font.png/idx 绝对路径返回(family = 目录名);
|
* gbk/full;full 请求接受 full)的那套,以其 font.png/idx 绝对路径返回(family = 一级目录名);
|
||||||
* - 找不到匹配的大字库 → 回退 meow_font_effective_paths(避免 app 直接点到固件小库)。
|
* - M2.6 字重:优先选「与用户当前 font.file 同 family(其次同 weight)」的大字库,避免
|
||||||
|
* 大字库跳到另一家字体;找不到匹配 → 回退 meow_font_effective_paths(避免点到固件小库)。
|
||||||
* 返回 0 成功(out_png/out_idx/out_family 已填),-1 无可用。 */
|
* 返回 0 成功(out_png/out_idx/out_family 已填),-1 无可用。 */
|
||||||
static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
|
static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
|
||||||
char *out_idx, int ilen, char *out_family, int flen)
|
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)
|
if (eff <= MEOW_FONT_TIER_COMMON)
|
||||||
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
|
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
|
||||||
/* 大字库:从扫描结果选 charset 匹配的一套。
|
/* 大字库:从扫描结果选 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);
|
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];
|
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++) {
|
for (int i = 0; i < font_presets_n; i++) {
|
||||||
const char *cs = font_preset_tiers[i] ? font_preset_tiers[i] : "common";
|
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;
|
int score = 0;
|
||||||
if (!strcmp(cs, want)) score = 2;
|
if (!strcmp(cs, want)) score = 200;
|
||||||
else if (!strcmp(cs, "full") || !strcmp(cs, "gbk")) score = 1; /* 降级 */
|
else if (!strcmp(cs, "full") || !strcmp(cs, "gbk")) score = 100; /* 降级 */
|
||||||
if (score > best_score) { best_score = score; found_family = i; }
|
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)
|
if (found >= 0)
|
||||||
snprintf(ref, sizeof ref, "%s", font_presets[found_family]);
|
snprintf(ref, sizeof ref, "%s", font_presets[found]);
|
||||||
if (found_family >= 0) {
|
if (found >= 0) {
|
||||||
/* 相对路径按卡根解析为设备绝对路径(与 meow_font_effective_paths 同源)。 */
|
/* 相对路径按卡根解析为设备绝对路径(与 meow_font_effective_paths 同源)。 */
|
||||||
if (meow_font_resolve_path(ref, out_png, plen) != 0) return -1;
|
if (meow_font_resolve_path(ref, out_png, plen) != 0) return -1;
|
||||||
if (access(out_png, R_OK) != 0) goto fallback;
|
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-4] = '.'; out_idx[n-3] = 'i'; out_idx[n-2] = 'd';
|
||||||
out_idx[n-1] = 'x'; out_idx[n] = 0;
|
out_idx[n-1] = 'x'; out_idx[n] = 0;
|
||||||
}
|
}
|
||||||
const char *prefix = "/MeowOS/fonts/";
|
font_family_from_png(out_png, out_family, flen);
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
fallback:
|
fallback:
|
||||||
@@ -1680,15 +1720,17 @@ static int ensure_card_mounted(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================================== */
|
/* ================================================================== */
|
||||||
/* M2.5x:TF 卡多字体库扫描 + preset 动态化 */
|
/* M2.5x 多字体库扫描 + M2.6 字重结构 / preset 动态化 */
|
||||||
/* 字体目录 = <卡根>/MeowOS/fonts/<family>/,每套含三件套: */
|
/* 字体目录 = <卡根>/MeowOS/fonts/<family>/<weight>/,每套含三件套: */
|
||||||
/* font.png(预渲染灰度 PNG,bit depth 4|8)+ font.idx(MFNI v2 索引) */
|
/* font.png(预渲染灰度 PNG,bit depth 4|8)+ font.idx(MFNI v2 索引) */
|
||||||
/* + font.json(元数据 + 完整性校验)。缺任一件 → 该套不进列表 + 日志。 */
|
/* + font.json(元数据 + 完整性校验)。缺任一件 → 该套不进列表 + 日志。 */
|
||||||
/* font.json schema(定版): */
|
/* font.json schema(定版): */
|
||||||
/* { */
|
/* { */
|
||||||
/* "family": "家族标识(同目录名)", // 信息用,不参与校验 */
|
/* "family": "家族标识(同目录名/family)", // 信息用,不参与校验 */
|
||||||
/* "name": "下拉显示名", // 缺则回退目录名 */
|
/* "name": "下拉显示名", // 缺则回退目录名 */
|
||||||
|
/* "weight": "regular", // 字重 slug(缺省 regular) */
|
||||||
/* "glyph_count": 10000, // 信息用 */
|
/* "glyph_count": 10000, // 信息用 */
|
||||||
|
/* "charset": "common|gbk|full", // 档位(M2.5x,驱动按需加载) */
|
||||||
/* "size_list": [28,20], // 信息用 */
|
/* "size_list": [28,20], // 信息用 */
|
||||||
/* "bit_depth": 4, // 信息用(4|8) */
|
/* "bit_depth": 4, // 信息用(4|8) */
|
||||||
/* "sha256_png": "<64hex>", // font.png 校验 */
|
/* "sha256_png": "<64hex>", // font.png 校验 */
|
||||||
@@ -1746,14 +1788,17 @@ static int font_sha_eq(const char *a, const char *b)
|
|||||||
return 1;
|
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,
|
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)
|
char *charset, int clen, int *glyph_count,
|
||||||
|
char *weight, int wlen)
|
||||||
{
|
{
|
||||||
name[0] = 0; sha_png[0] = 0; sha_idx[0] = 0;
|
name[0] = 0; sha_png[0] = 0; sha_idx[0] = 0;
|
||||||
if (charset) charset[0] = 0;
|
if (charset) charset[0] = 0;
|
||||||
if (glyph_count) *glyph_count = 0;
|
if (glyph_count) *glyph_count = 0;
|
||||||
|
if (weight) weight[0] = 0;
|
||||||
FILE *f = fopen(json_path, "r");
|
FILE *f = fopen(json_path, "r");
|
||||||
if (!f) return;
|
if (!f) return;
|
||||||
char buf[256];
|
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])
|
if (json_str(buf, "glyph_count", v, sizeof v) > 0 && v[0])
|
||||||
*glyph_count = atoi(v);
|
*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);
|
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。
|
/* 档位大小标记(设置页字体选项标注):common≈4k / gbk≈20k / full。
|
||||||
* 由 font.json 的 charset + glyph_count 派生;缺 charset 回退 common。 */
|
* 由 font.json 的 charset + glyph_count 派生;缺 charset 回退 common。 */
|
||||||
static void font_tier_marker(const char *charset, int glyph_count, char *out, int olen)
|
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);
|
snprintf(out, olen, "%s", tier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 探测一个 <dirpath>/<family> 套件是否合法:三件套齐全 → 解析 label + 档位 + 校验 sha。
|
/* 探测一个 <卡根>/MeowOS/fonts/<family>/<weight> 套件是否合法:三件套齐全 → 解析 label+档位+字重。
|
||||||
* 返回 1=进列表,0=跳过(缺组件/json 不可读)。sha 失败仅 WARN。
|
* 返回 1=进列表,0=跳过(缺组件/json 不可读)。sha 失败仅 WARN。
|
||||||
* 输出:label=显示名+大小标记 `名字(common≈4k)`;charset/glyph_count=档位信息。 */
|
* 输出:label=显示名 `<name> <Weight>(common≈4k)`(如 `MiSans Regular(common≈4k)`);
|
||||||
static int meow_font_probe_family(const char *dirpath, const char *family,
|
* 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 *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];
|
char ppath[400], ipath[400], jpath[400];
|
||||||
snprintf(ppath, sizeof ppath, "%s/font.png", dirpath);
|
snprintf(ppath, sizeof ppath, "%s/font.png", dirpath);
|
||||||
snprintf(ipath, sizeof ipath, "%s/font.idx", dirpath);
|
snprintf(ipath, sizeof ipath, "%s/font.idx", dirpath);
|
||||||
snprintf(jpath, sizeof jpath, "%s/font.json", 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) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
char name[64] = "", jsha_png[80] = "", jsha_idx[80] = "";
|
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,
|
meow_font_json_probe(jpath, name, sizeof name, jsha_png, sizeof jsha_png,
|
||||||
jsha_idx, sizeof jsha_idx, cs, sizeof cs, &gc);
|
jsha_idx, sizeof jsha_idx, cs, sizeof cs, &gc, wts, sizeof wts);
|
||||||
/* 显示名 = 名 + 档位大小标记(如 `default(common≈4k)` / `gbk≈20k` / `full`) */
|
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];
|
char marker[24];
|
||||||
font_tier_marker(cs, gc, marker, sizeof marker);
|
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 (charset) snprintf(charset, clen, "%s", cs[0] ? cs : "common");
|
||||||
if (glyph_count) *glyph_count = gc;
|
if (glyph_count) *glyph_count = gc;
|
||||||
/* 完整性校验:仅 WARN,不硬失败;该套仍标记可选。 */
|
/* 完整性校验:仅 WARN,不硬失败;该套仍标记可选。 */
|
||||||
char apng[80] = "", aidx[80] = "";
|
char apng[80] = "", aidx[80] = "";
|
||||||
if (font_sha256_file(ppath, apng, sizeof apng) != 0)
|
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))
|
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)
|
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))
|
else if (jsha_idx[0] && !font_sha_eq(aidx, jsha_idx))
|
||||||
logmsg("fonts: '%s' WARN sha256_idx 不匹配(仍可选)", family);
|
logmsg("fonts: '%s/%s' WARN sha256_idx 不匹配(仍可选)", family, wt);
|
||||||
logmsg("fonts: add '%s' (%s) charset=%s glyph_count=%d", label, family, cs[0]?cs:"common", gc);
|
logmsg("fonts: add '%s/%s' (%s) charset=%s glyph_count=%d", family, wt, label,
|
||||||
|
cs[0]?cs:"common", gc);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 扫描 <card_root>/MeowOS/fonts/ 下每个家族子目录,重建字体 preset 列表(默认 + 卡内合法套件)。
|
/* 扫描 <card_root>/MeowOS/fonts/ 下两级 <family>/<weight>/,重建字体 preset 列表。
|
||||||
* 上限 FONT_PRESETS_MAX(1 默认 + ≤16 用户字体)。无卡/无目录时仅默认。 */
|
* 每个 (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)
|
static void meow_font_scan_presets_root(const char *card_root)
|
||||||
{
|
{
|
||||||
font_presets_n = 0;
|
font_presets_n = 0;
|
||||||
font_presets[0] = "MeowOS/fonts/default/font.png"; /* 默认字库 = MeowOS/fonts/default */
|
font_presets[0] = "MeowOS/fonts/default/regular/font.png"; /* 默认字库 = MeowOS/fonts/default/regular */
|
||||||
font_preset_labels[0] = font_preset_label_buf[0]; /* 指向缓冲(运行时派生含大小标记) */
|
font_preset_labels[0] = font_preset_label_buf[0]; /* 指向缓冲(运行时派生含字重+大小标记) */
|
||||||
/* 默认套也读 font.json 档位(存在则跟真实档,缺则 common);档位信息供分层装载/标注。 */
|
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 jp[400], cs[8] = ""; int gc = 0;
|
||||||
char nm0[64] = "", sp0[80] = "", si0[80] = "";
|
char nm0[64] = "", sp0[80] = "", si0[80] = "", wt0[32] = "";
|
||||||
snprintf(jp, sizeof jp, "%s/%s/default/font.json", card_root, MEOW_FONTS_CFG);
|
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,
|
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);
|
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_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_tiers[0] = font_preset_tier_buf[0];
|
||||||
font_preset_glyphs[0] = gc;
|
font_preset_glyphs[0] = gc;
|
||||||
}
|
}
|
||||||
@@ -1855,21 +1937,37 @@ static void meow_font_scan_presets_root(const char *card_root)
|
|||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
while ((de = readdir(d)) && font_presets_n < FONT_PRESETS_MAX) {
|
while ((de = readdir(d)) && font_presets_n < FONT_PRESETS_MAX) {
|
||||||
if (de->d_name[0] == '.') continue;
|
if (de->d_name[0] == '.') continue;
|
||||||
char dirpath[400], label[80], cs[8] = ""; int gc = 0;
|
if (!strcmp(de->d_name, "default")) continue; /* 默认字体由索引 0 单独处理 */
|
||||||
snprintf(dirpath, sizeof dirpath, "%s/%s", root, de->d_name);
|
char fampath[400];
|
||||||
if (meow_font_probe_family(dirpath, de->d_name, label, sizeof label,
|
snprintf(fampath, sizeof fampath, "%s/%s", root, de->d_name);
|
||||||
cs, sizeof cs, &gc) == 0)
|
DIR *w = opendir(fampath); /* 一级:<family>/ */
|
||||||
continue; /* 缺组件/损坏 → 跳过该套 */
|
if (!w) continue;
|
||||||
int i = font_presets_n;
|
struct dirent *we;
|
||||||
snprintf(font_preset_val_buf[i], sizeof font_preset_val_buf[i],
|
while ((we = readdir(w)) && font_presets_n < FONT_PRESETS_MAX) {
|
||||||
"%s/%s/font.png", MEOW_FONTS_CFG, de->d_name);
|
if (we->d_name[0] == '.') continue;
|
||||||
snprintf(font_preset_label_buf[i], sizeof font_preset_label_buf[i], "%s", label);
|
char dirpath[400], label[96], cs[8] = ""; int gc = 0; char wt[32] = "", nm[64] = "";
|
||||||
snprintf(font_preset_tier_buf[i], sizeof font_preset_tier_buf[i], "%s", cs[0]?cs:"common");
|
snprintf(dirpath, sizeof dirpath, "%s/%s", fampath, we->d_name);
|
||||||
font_presets[i] = font_preset_val_buf[i];
|
if (meow_font_probe_weight(dirpath, de->d_name, we->d_name, label, sizeof label,
|
||||||
font_preset_labels[i] = font_preset_label_buf[i];
|
cs, sizeof cs, &gc, wt, sizeof wt, nm, sizeof nm) == 0)
|
||||||
font_preset_tiers[i] = font_preset_tier_buf[i];
|
continue; /* 缺组件/损坏 → 跳过该套 */
|
||||||
font_preset_glyphs[i] = gc;
|
int i = font_presets_n;
|
||||||
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);
|
closedir(d);
|
||||||
}
|
}
|
||||||
@@ -4630,7 +4728,7 @@ static const srow_t SMENU[] = {
|
|||||||
* 开 → 强制全系统用大字库 gbk≈20k 档(覆盖 app 未声明/覆盖默认 font.file);
|
* 开 → 强制全系统用大字库 gbk≈20k 档(覆盖 app 未声明/覆盖默认 font.file);
|
||||||
* 关(默认)→ 小字库 common≈4k,仅 app 声明 fonts:"full" 才大字库。 */
|
* 关(默认)→ 小字库 common≈4k,仅 app 声明 fonts:"full" 才大字库。 */
|
||||||
{ "加载大字库", "font.load_full", SC_SWITCH, 0,0,0, NULL,NULL,0 },
|
{ "加载大字库", "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)。 */
|
* 值空 = 跟随全局 font.file)。仅开关开启时 6 行可见(s_row_visible)。 */
|
||||||
{ "多字体", "display.multi_font", SC_SWITCH, 0,0,0, NULL,NULL,0 },
|
{ "多字体", "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 },
|
{ "中文(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(); /* 回绕 */
|
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_*)用运行时动态数组
|
/* 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)。 */
|
* 其余行用编译期静态表(r->presets / r->preset_n)。 */
|
||||||
static const char *const *s_row_presets(const srow_t *r, int *n)
|
static const char *const *s_row_presets(const srow_t *r, int *n)
|
||||||
{
|
{
|
||||||
@@ -6036,6 +6140,71 @@ ddone:
|
|||||||
return result;
|
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 触摸命中设置行后调用)---- */
|
/* ---- 运行设置页(独立子循环;run_menu 触摸命中设置行后调用)---- */
|
||||||
static void run_settings_page(void)
|
static void run_settings_page(void)
|
||||||
{
|
{
|
||||||
@@ -6043,7 +6212,7 @@ static void run_settings_page(void)
|
|||||||
int scroll_off = 0;
|
int scroll_off = 0;
|
||||||
int H = fb_yres;
|
int H = fb_yres;
|
||||||
|
|
||||||
/* M2.5x:进设置页扫描一次 TF 卡 MeowOS/fonts/<family>/,刷新「字体文件」下拉。
|
/* M2.6:进设置页扫描一次 TF 卡 MeowOS/fonts/<family>/<weight>/,刷新「字体文件」下拉。
|
||||||
* 进设置时卡通常在位;此步只枚举/校验,不加载(加载由 meow_font_ensure 按 font.file 决定)。 */
|
* 进设置时卡通常在位;此步只枚举/校验,不加载(加载由 meow_font_ensure 按 font.file 决定)。 */
|
||||||
meow_font_scan_presets();
|
meow_font_scan_presets();
|
||||||
|
|
||||||
@@ -6369,11 +6538,20 @@ static void run_settings_page(void)
|
|||||||
} else if (r->ctrl == SC_ENUM || r->ctrl == SC_PRESET) {
|
} else if (r->ctrl == SC_ENUM || r->ctrl == SC_PRESET) {
|
||||||
/* 单次点击即开选择器子对话框 */
|
/* 单次点击即开选择器子对话框 */
|
||||||
after_modal = 1; /* 关闭后整帧重绘(还原被 dim 的状态栏) */
|
after_modal = 1; /* 关闭后整帧重绘(还原被 dim 的状态栏) */
|
||||||
int pick = s_dropdown(r);
|
if (s_row_is_font(r)) {
|
||||||
if (pick >= 0) {
|
/* M2.6 字重:字体行走「family→字重」两级子菜单 */
|
||||||
if (r->ctrl == SC_ENUM) meow_settings_set_enum(r->key, pick);
|
char fval[MEOW_SET_MAX_LEN];
|
||||||
else meow_settings_set_str(r->key, s_preset_value(r, pick));
|
if (s_font_dropdown(r, fval, sizeof fval)) {
|
||||||
s_commit(r, 1);
|
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 +
|
/* M2.38 Bug3:s_dropdown 返回后彻底排空本页 tfd +
|
||||||
* 重置触摸基线。根因:input 子系统把事件广播到所有
|
* 重置触摸基线。根因:input 子系统把事件广播到所有
|
||||||
@@ -7429,17 +7607,18 @@ int main(int argc, char **argv)
|
|||||||
* menu open so edits apply without a daemon restart) */
|
* menu open so edits apply without a daemon restart) */
|
||||||
settings_load(); /* M2.11:读系统设置(audio.volume 等),供音量/状态栏使用 */
|
settings_load(); /* M2.11:读系统设置(audio.volume 等),供音量/状态栏使用 */
|
||||||
|
|
||||||
/* M2.5x:启动扫描一次 TF 卡 MeowOS/fonts/<family>/,填充「字体文件」下拉 preset。
|
/* M2.6:启动扫描一次 TF 卡 MeowOS/fonts/<family>/<weight>/,填充「字体文件」下拉 preset。
|
||||||
* 无卡/无字库目录 → 仅默认字体(等价现状)。进设置页时 run_settings_page 会再扫一次。 */
|
* 无卡/无字库目录 → 仅默认字体(等价现状)。进设置页时 run_settings_page 会再扫一次。 */
|
||||||
meow_font_scan_presets();
|
meow_font_scan_presets();
|
||||||
|
|
||||||
if (fontscan_root) {
|
if (fontscan_root) {
|
||||||
/* M2.5x 测试钩子:扫描指定卡根下的 MeowOS/fonts/<family>/ 并打印 preset(qemu/宿主可测)。
|
/* M2.6 测试钩子:扫描指定卡根下的 MeowOS/fonts/<family>/<weight>/ 并打印 preset(qemu/宿主可测)。
|
||||||
* 用于验证 font.json 解析/三件套/缺件跳过/sha 校验 + preset 动态化逻辑。 */
|
* 用于验证 font.json 解析(含 weight)/三件套/缺件跳过/sha 校验 + preset 动态化逻辑。 */
|
||||||
meow_font_scan_presets_root(fontscan_root);
|
meow_font_scan_presets_root(fontscan_root);
|
||||||
printf("fonts=%d\n", font_presets_n);
|
printf("fonts=%d\n", font_presets_n);
|
||||||
for (int i = 0; i < font_presets_n; i++)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ static const meow_setting_def g_defs[] = {
|
|||||||
/* M2.38:设置页滑动方向切换(0=标准:手指上滑→列表往下翻;1=反向) */
|
/* M2.38:设置页滑动方向切换(0=标准:手指上滑→列表往下翻;1=反向) */
|
||||||
{ "input.swipe_invert", SET_BOOL, "0", 0, 0, NULL, NULL },
|
{ "input.swipe_invert", SET_BOOL, "0", 0, 0, NULL, NULL },
|
||||||
/* 字体/主题 */
|
/* 字体/主题 */
|
||||||
{ "font.file", SET_STR, "MeowOS/fonts/default/font.png", 0,0, NULL, NULL },
|
{ "font.file", SET_STR, "MeowOS/fonts/default/regular/font.png", 0,0, NULL, NULL },
|
||||||
/* M2.5x 多字体:6 个字符类别各一字体(SET_STR,值=字体 family 相对路径,缺省空=跟随全局 font.file)。
|
/* M2.5x 多字体:6 个字符类别各一字体(SET_STR,值=字体 family 相对路径,缺省空=跟随全局 font.file)。
|
||||||
* 仅 display.multi_font=1 时生效;同一 family 去重;上限 4 套不同字体(超出 WARN)。 */
|
* 仅 display.multi_font=1 时生效;同一 family 去重;上限 4 套不同字体(超出 WARN)。 */
|
||||||
{ "font.class_cjk", SET_STR, "", 0,0, NULL, NULL },
|
{ "font.class_cjk", SET_STR, "", 0,0, NULL, NULL },
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
test_font_weight.py — MeowOS 字重结构(M2.6)宿主导出单测。
|
||||||
|
|
||||||
|
验证 launcher 侧 font 扫描/解析的两块(MEOWOS_SPEC_REF §5.2 字重结构 + §5.7.1 扫描):
|
||||||
|
(1) 两级扫描:<卡根>/MeowOS/fonts/<family>/<weight>/ 每 (family,weight) 一套,生成
|
||||||
|
preset(值 = MeowOS/fonts/<family>/<weight>/font.png,fam/wt 单列)。
|
||||||
|
(2) font.json weight 解析:label = `<name> <Weight>(common≈4k)`(如 `MiSans Regular(common≈4k)`);
|
||||||
|
缺 weight 回退 regular;缺三件套/损坏 → 跳过;旧式一级布局(font.png 直接在 <family>/)
|
||||||
|
→ 不产出(迁移后需重扫)。
|
||||||
|
|
||||||
|
方法:构造一个假卡根(含合法多 weight family / 缺件 family / 旧式文件 / 顶层 stray),
|
||||||
|
用 qemu-mipsel 跑真实 meow_launcherd --fontscan <root>,断言 stdout 的 preset 列表。
|
||||||
|
|
||||||
|
用法(在 patch_v2/meow_launcherd 下):
|
||||||
|
先 build_launcher.sh 生成 meow_launcherd(mipsel 静态 ELF),再:
|
||||||
|
python3 test_font_weight.py
|
||||||
|
依赖 qemu-mipsel(qemu-user)。退出码 0=全过,非 0=断言失败。
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
BIN = os.path.join(ROOT, "meow_launcherd")
|
||||||
|
FIX_ROOT = tempfile.mkdtemp(prefix="mfscan_", dir="/tmp")
|
||||||
|
FONTS = os.path.join(FIX_ROOT, "MeowOS", "fonts")
|
||||||
|
|
||||||
|
|
||||||
|
def write_font(fam, wt, name, weight="regular", charset="common"):
|
||||||
|
"""写一套 <fam>/<wt>/font.png+font.idx+font.json(合法三件套;png/idx 为占位字节)。"""
|
||||||
|
d = os.path.join(FONTS, fam, wt)
|
||||||
|
os.makedirs(d, exist_ok=True)
|
||||||
|
with open(os.path.join(d, "font.png"), "wb") as f:
|
||||||
|
f.write(b"\x89PNG\r\n\x1a\nPLACEHOLDER")
|
||||||
|
with open(os.path.join(d, "font.idx"), "wb") as f:
|
||||||
|
f.write(b"MFNI\x02\x00PLACEHOLDER")
|
||||||
|
meta = {
|
||||||
|
"family": fam, "name": name, "weight": weight, "glyph_count": 4110,
|
||||||
|
"charset": charset, "size_list": [20, 28], "bit_depth": 4,
|
||||||
|
"sha256_png": "0" * 64, "sha256_idx": "0" * 64,
|
||||||
|
}
|
||||||
|
with open(os.path.join(d, "font.json"), "w", encoding="utf-8") as f:
|
||||||
|
json.dump(meta, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def run_scan():
|
||||||
|
"""返回 (ret, stdout)。"""
|
||||||
|
r = subprocess.run(["qemu-mipsel", BIN, "--fontscan", FIX_ROOT],
|
||||||
|
capture_output=True, text=True, timeout=120)
|
||||||
|
return r.returncode, r.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not os.path.exists(BIN):
|
||||||
|
print(f"FAIL: 未找到 {BIN},请先跑 build_launcher.sh"); return 2
|
||||||
|
if shutil.which("qemu-mipsel") is None:
|
||||||
|
print("FAIL: 缺 qemu-mipsel"); return 2
|
||||||
|
|
||||||
|
# ---- 构造卡根 ----
|
||||||
|
write_font("misans", "regular", "MiSans", weight="regular")
|
||||||
|
write_font("misans", "bold", "MiSans", weight="bold")
|
||||||
|
write_font("meslo", "regular", "Meslo LG S Mono", weight="regular")
|
||||||
|
write_font("default", "regular", "font", weight="regular")
|
||||||
|
# 缺件:无 font.idx → 应跳过
|
||||||
|
broken = os.path.join(FONTS, "broken", "regular")
|
||||||
|
os.makedirs(broken, exist_ok=True)
|
||||||
|
with open(os.path.join(broken, "font.png"), "wb") as f:
|
||||||
|
f.write(b"PNG")
|
||||||
|
with open(os.path.join(broken, "font.json"), "w") as f:
|
||||||
|
json.dump({"family": "broken", "name": "Broken", "weight": "regular"}, f)
|
||||||
|
# 旧式一级布局:font.png 直接在 <family>/ 下(无 weight 级)→ 迁移后应不产出
|
||||||
|
old = os.path.join(FONTS, "legacy")
|
||||||
|
os.makedirs(old, exist_ok=True)
|
||||||
|
with open(os.path.join(old, "font.png"), "wb") as f:
|
||||||
|
f.write(b"PNG")
|
||||||
|
# 顶层 stray 文件(非目录)→ 应忽略
|
||||||
|
with open(os.path.join(FONTS, "stray.png"), "wb") as f:
|
||||||
|
f.write(b"PNG")
|
||||||
|
|
||||||
|
rc, out = run_scan()
|
||||||
|
if rc != 0:
|
||||||
|
print(f"FAIL: qemu --fontscan 返回 {rc}\n{out}"); return 1
|
||||||
|
if os.path.exists(os.path.join(os.path.dirname(FIX_ROOT), "meow_boot.log")):
|
||||||
|
pass # launcher 日志在 /tmp,忽略
|
||||||
|
|
||||||
|
# ---- stdout -> dict ----
|
||||||
|
presets = {}
|
||||||
|
famwt = {}
|
||||||
|
for line in out.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("fonts="):
|
||||||
|
continue
|
||||||
|
if line and line[0].isdigit():
|
||||||
|
# " 0 Label -> path (fam=.. wt=..)" 但行首 i 数字后无空格分隔可靠断言,用 split
|
||||||
|
parts = line.split(" -> ")
|
||||||
|
if len(parts) != 2:
|
||||||
|
continue
|
||||||
|
idx = int(parts[0].split()[0])
|
||||||
|
rest, famwt_part = parts[1].split(" (fam=", 1)
|
||||||
|
path = rest.strip()
|
||||||
|
fam = famwt_part.split(" wt=")[0]
|
||||||
|
wt = famwt_part.split(" wt=")[1].rstrip(")")
|
||||||
|
presets[path] = idx
|
||||||
|
famwt[path] = (fam, wt)
|
||||||
|
elif line.startswith("fonts="):
|
||||||
|
n = int(line.split("=")[1])
|
||||||
|
if n != len(presets):
|
||||||
|
print(f"FAIL: fonts={n} 但解析到 {len(presets)} 条\n{out}"); return 1
|
||||||
|
|
||||||
|
fails = 0
|
||||||
|
def check(cond, msg):
|
||||||
|
nonlocal fails
|
||||||
|
if not cond:
|
||||||
|
fails += 1
|
||||||
|
print(f"FAIL: {msg}")
|
||||||
|
else:
|
||||||
|
print(f"ok : {msg}")
|
||||||
|
|
||||||
|
p1 = "MeowOS/fonts/misans/regular/font.png"
|
||||||
|
p2 = "MeowOS/fonts/misans/bold/font.png"
|
||||||
|
p3 = "MeowOS/fonts/meslo/regular/font.png"
|
||||||
|
p0 = "MeowOS/fonts/default/regular/font.png"
|
||||||
|
|
||||||
|
# 两级扫描产出(默认 + 3 套,共 4)
|
||||||
|
check(len(presets) == 4, f"扫描产出 4 套(实际 {len(presets)})")
|
||||||
|
check(p0 in presets and presets[p0] == 0, "索引 0 = default/regular")
|
||||||
|
check(p1 in presets, "misans/regular 进列表")
|
||||||
|
check(p2 in presets, "misans/bold 进列表")
|
||||||
|
check(p3 in presets, "meslo/regular 进列表")
|
||||||
|
check(famwt.get(p1) == ("misans", "regular"), "fam/wt(misans/regular) 系家族+字重")
|
||||||
|
check(famwt.get(p2) == ("misans", "bold"), "fam/wt(misans/bold) 系家族+字重")
|
||||||
|
# 缺件 / 旧式一级 / 顶层文件 → 不产出
|
||||||
|
check(all("broken" not in k for k in presets), "缺 font.idx 的 broken/regular 被跳过")
|
||||||
|
check(all("legacy" not in k for k in presets), "旧式一级布局 legacy(无 weight 级)被忽略(迁移后需重扫)")
|
||||||
|
check(all("stray" not in k for k in presets), "顶层 stray.png(非目录)被忽略")
|
||||||
|
# weight 解析 → 显示名 `Name Weight(common≈4k)`
|
||||||
|
labels = {os.path.basename(os.path.dirname(os.path.dirname(p))):
|
||||||
|
p for p in presets}
|
||||||
|
# 用 stdout 里的 Label 校验(weight 大写 + 档位标记)
|
||||||
|
out_has = "MiSans Regular(common≈4k)" in out
|
||||||
|
out_has_bold = "MiSans Bold(common≈4k)" in out
|
||||||
|
check(out_has, "label 含 `MiSans Regular(common≈4k)`(weight 解析+大小标记)")
|
||||||
|
check(out_has_bold, "label 含 `MiSans Bold(common≈4k)`(bold 字重解析)")
|
||||||
|
|
||||||
|
print(f"\n{'RESULT: PASS' if fails == 0 else 'RESULT: FAIL'} ({fails} failures)")
|
||||||
|
return 1 if fails else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
+17
-16
@@ -29,12 +29,12 @@
|
|||||||
#define FBIO_WAITFORVSYNC 0x4620
|
#define FBIO_WAITFORVSYNC 0x4620
|
||||||
|
|
||||||
#define FB_DEV "/dev/fb0"
|
#define FB_DEV "/dev/fb0"
|
||||||
#define MEOW_FONT_PNG_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.png"
|
#define MEOW_FONT_PNG_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.png"
|
||||||
#define MEOW_FONT_IDX_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.idx"
|
#define MEOW_FONT_IDX_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.idx"
|
||||||
#define MEOW_FONT_PNG_SYS "/usr/share/meowos/font/font.png"
|
#define MEOW_FONT_PNG_SYS "/usr/share/meowos/font/font.png"
|
||||||
#define MEOW_FONT_IDX_SYS "/usr/share/meowos/font/font.idx"
|
#define MEOW_FONT_IDX_SYS "/usr/share/meowos/font/font.idx"
|
||||||
/* 默认字库(卡 MeowOS/fonts/default)与固件内置之外,launcher 会把「当前生效字体」
|
/* 默认字库(卡 MeowOS/fonts/default/regular,M2.6 字重结构)与固件内置之外,launcher 会把
|
||||||
* 经环境变量下发给 app(见 meow_launcherd.c launch_app / meow_font_effective_paths):
|
* 「当前生效字体」经环境变量下发给 app(见 meow_launcherd.c launch_app / meow_font_effective_paths):
|
||||||
* MEOW_FONT_PNG / MEOW_FONT_IDX = 绝对路径;MEOW_FONT_APP = <family> 或 "default"。
|
* MEOW_FONT_PNG / MEOW_FONT_IDX = 绝对路径;MEOW_FONT_APP = <family> 或 "default"。
|
||||||
* SDK load_font 优先读 env(匹配系统 font.file),无则回退卡默认 → 固件。 */
|
* SDK load_font 优先读 env(匹配系统 font.file),无则回退卡默认 → 固件。 */
|
||||||
|
|
||||||
@@ -361,18 +361,19 @@ void meow_ui_swipe_hint(int x)
|
|||||||
* 有状态栏(非全屏)→ y = 状态栏高度;收合(collapsed)→ y=提示线下缘
|
* 有状态栏(非全屏)→ y = 状态栏高度;收合(collapsed)→ y=提示线下缘
|
||||||
* (MUI_SBAR_COLLAPSE_H,与提示线不重叠);无状态栏(全屏/隐藏)→ y=0。 */
|
* (MUI_SBAR_COLLAPSE_H,与提示线不重叠);无状态栏(全屏/隐藏)→ y=0。 */
|
||||||
int hy = meow_ui_statusbar_height();
|
int hy = meow_ui_statusbar_height();
|
||||||
if (x > 0) {
|
/* x<0 视为清空(条长 0);条长上限=屏宽。 */
|
||||||
if (x > g_xres) x = g_xres;
|
if (x < 0) x = 0;
|
||||||
/* 往回滑(新长度 < 上次)时,先清掉「上次画到旧长度、但本次超出的部分」,
|
if (x > g_xres) x = g_xres;
|
||||||
* 否则条只增不减(用户要求:右滑变长、左滑回缩实时可见)。 */
|
/* 只按「新旧长度的差」增量绘制,避免整段重涂背景色:
|
||||||
if (g_hint_last > x) meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg);
|
* - 伸长:仅补画新增段(旧段已是 accent,不再用 bg 重涂——否则 app 底色≠theme bg
|
||||||
meow_ui_fill_rect(0, hy, x, MUI_HINT_H, th->accent);
|
* 时会在旧段留下 bg 色块,就是残留残条);
|
||||||
g_hint_last = x;
|
* - 回缩/清空:仅把「超出新长度」的尾部填回背景色(实时回缩)。 */
|
||||||
} else if (g_hint_last > 0) {
|
if (x > g_hint_last) {
|
||||||
/* 手指回原点/抬手未达标/抬手达标:清掉此前画的条(填回背景色) */
|
meow_ui_fill_rect(g_hint_last, hy, x - g_hint_last, MUI_HINT_H, th->accent);
|
||||||
meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg);
|
} else if (x < g_hint_last) {
|
||||||
g_hint_last = 0;
|
meow_ui_fill_rect(x, hy, g_hint_last - x, MUI_HINT_H, th->bg);
|
||||||
}
|
}
|
||||||
|
g_hint_last = x;
|
||||||
/* 不发 flush:由调用方 meow_ui_flush_region(0,0,宽,6) 只刷顶部条 */
|
/* 不发 flush:由调用方 meow_ui_flush_region(0,0,宽,6) 只刷顶部条 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,7 +705,7 @@ int meow_font_set_tier(int tier)
|
|||||||
/* 字库加载优先级(M2.x):
|
/* 字库加载优先级(M2.x):
|
||||||
* 1) 环境变量 MEOW_FONT_PNG/MEOW_FONT_IDX(launcher 注入的「当前生效字体」= 系统 font.file)
|
* 1) 环境变量 MEOW_FONT_PNG/MEOW_FONT_IDX(launcher 注入的「当前生效字体」= 系统 font.file)
|
||||||
* —— app 未自带字库(§5.7 load_handle 未实现)时跟随系统 font.file;
|
* —— app 未自带字库(§5.7 load_handle 未实现)时跟随系统 font.file;
|
||||||
* 2) 卡默认 MeowOS/fonts/default/font.png(不再硬编码 MeowAPPs/fonts);
|
* 2) 卡默认 MeowOS/fonts/default/regular/font.png(M2.6 字重结构;不再硬编码 MeowAPPs/fonts);
|
||||||
* 3) 固件 /usr/share/meowos/font/font.png。
|
* 3) 固件 /usr/share/meowos/font/font.png。
|
||||||
* 全失败 → g_mfont_ready=0,text_draw 回退 5x7 点阵(系统不死)。 */
|
* 全失败 → g_mfont_ready=0,text_draw 回退 5x7 点阵(系统不死)。 */
|
||||||
static int load_font(void)
|
static int load_font(void)
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ static int t_press_valid = 0; /* BTN_TOUCH=1 已见(本次按下的 gate
|
|||||||
static int t_dx = 0, t_dy = 0; /* 按下原点(BTN_TOUCH 后首个有效 ABS 锁定) */
|
static int t_dx = 0, t_dy = 0; /* 按下原点(BTN_TOUCH 后首个有效 ABS 锁定) */
|
||||||
static int t_cx = 0, t_cy = 0; /* 当前坐标(-1=本次尚未见到有效坐标) */
|
static int t_cx = 0, t_cy = 0; /* 当前坐标(-1=本次尚未见到有效坐标) */
|
||||||
static int t_origin_valid = 0; /* 原点是否已锁定 */
|
static int t_origin_valid = 0; /* 原点是否已锁定 */
|
||||||
|
static int t_gmax_dx = 0; /* 本手势「最大右移位移」(t_cx-t_dx 的最大值,条长上限) */
|
||||||
|
|
||||||
/* 按下边沿:只标记按下,绝不用传入坐标立即锁原点(避免用上一手势残留坐标当原点)。
|
/* 按下边沿:只标记按下,绝不用传入坐标立即锁原点(避免用上一手势残留坐标当原点)。
|
||||||
* t_cx/t_cy 归 -1 —— 本次手势以「无坐标」起步,只接受随后到来的有效 ABS。 */
|
* t_cx/t_cy 归 -1 —— 本次手势以「无坐标」起步,只接受随后到来的有效 ABS。 */
|
||||||
@@ -136,6 +137,7 @@ static void touch_press(int x, int y)
|
|||||||
t_origin_valid = 0;
|
t_origin_valid = 0;
|
||||||
t_cx = -1; t_cy = -1;
|
t_cx = -1; t_cy = -1;
|
||||||
t_dx = 0; t_dy = 0;
|
t_dx = 0; t_dy = 0;
|
||||||
|
t_gmax_dx = 0; /* 新手势:最大右移位移清零(条长从 0 计) */
|
||||||
}
|
}
|
||||||
/* 由 ABS_X/ABS_Y 驱动:若本次按下且原点未锁且坐标有效 → 锁原点;再更新当前坐标。 */
|
/* 由 ABS_X/ABS_Y 驱动:若本次按下且原点未锁且坐标有效 → 锁原点;再更新当前坐标。 */
|
||||||
static void touch_update(int x, int y)
|
static void touch_update(int x, int y)
|
||||||
@@ -145,13 +147,17 @@ static void touch_update(int x, int y)
|
|||||||
}
|
}
|
||||||
if (x >= 0) t_cx = x;
|
if (x >= 0) t_cx = x;
|
||||||
if (y >= 0) t_cy = y;
|
if (y >= 0) t_cy = y;
|
||||||
|
if (t_down && t_origin_valid) {
|
||||||
|
int dx = t_cx - t_dx;
|
||||||
|
if (dx > t_gmax_dx) t_gmax_dx = dx; /* 记录本手势已达的最大右移位移(条长上限) */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
static void touch_release(void)
|
static void touch_release(void)
|
||||||
{
|
{
|
||||||
if (!t_down) return;
|
if (!t_down) return;
|
||||||
t_down = 0; t_press_valid = 0;
|
t_down = 0; t_press_valid = 0;
|
||||||
/* 本次按下从未锁到有效坐标(幽灵触摸/坐标未就绪):丢弃,且清空全部轨迹。 */
|
/* 本次按下从未锁到有效坐标(幽灵触摸/坐标未就绪):丢弃,且清空全部轨迹。 */
|
||||||
if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; return; }
|
if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; t_gmax_dx = 0; return; }
|
||||||
t_origin_valid = 0;
|
t_origin_valid = 0;
|
||||||
int mdx = t_cx - t_dx, mdy = t_cy - t_dy;
|
int mdx = t_cx - t_dx, mdy = t_cy - t_dy;
|
||||||
int adx = mdx < 0 ? -mdx : mdx, ady = mdy < 0 ? -mdy : mdy;
|
int adx = mdx < 0 ? -mdx : mdx, ady = mdy < 0 ? -mdy : mdy;
|
||||||
@@ -163,7 +169,8 @@ static void touch_release(void)
|
|||||||
* 被手势级清空,绝不继承上一手势坐标,确保:
|
* 被手势级清空,绝不继承上一手势坐标,确保:
|
||||||
* ① 中部右滑不会误判成左缘返回(t_dx>=EDGE_W);
|
* ① 中部右滑不会误判成左缘返回(t_dx>=EDGE_W);
|
||||||
* ② 先点中部再侧滑不返回(原点锁定在中部,>=EDGE_W),符合「必须从左缘起划」;
|
* ② 先点中部再侧滑不返回(原点锁定在中部,>=EDGE_W),符合「必须从左缘起划」;
|
||||||
* ③ 右移严格正向(t_cx-t_dx>=0),杜绝「向左划也出条/误返回」。
|
* ③ 返回判定要求右移严格正向(t_cx-t_dx>=0 且 ≥SWIPE_THRESH),杜绝「向左划也误返回」;
|
||||||
|
* 左划过程仅让进度条实时回缩,不额外生长(过程见上方 MEOW_EV_SWIPE_PROGRESS)。
|
||||||
* SDK 统一上报为 MEOW_SWIPE_LEFT(设计"左滑=返回"契约,hello 用 on_swipe(LEFT) 退出)。
|
* SDK 统一上报为 MEOW_SWIPE_LEFT(设计"左滑=返回"契约,hello 用 on_swipe(LEFT) 退出)。
|
||||||
*
|
*
|
||||||
* 屏幕中部滑动不是返回手势:中部右滑→RIGHT(内容导航),中部左滑→非返回(落 TAP,
|
* 屏幕中部滑动不是返回手势:中部右滑→RIGHT(内容导航),中部左滑→非返回(落 TAP,
|
||||||
@@ -192,7 +199,7 @@ static void touch_release(void)
|
|||||||
push_ev(MEOW_EV_TAP, t_cx, t_cy);
|
push_ev(MEOW_EV_TAP, t_cx, t_cy);
|
||||||
}
|
}
|
||||||
/* 手势级干净起点:清空全部轨迹,下一手势从零开始,杜绝污染无自愈。 */
|
/* 手势级干净起点:清空全部轨迹,下一手势从零开始,杜绝污染无自愈。 */
|
||||||
t_cx = t_cy = -1; t_dx = t_dy = 0;
|
t_cx = t_cy = -1; t_dx = t_dy = 0; t_gmax_dx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 输入线程:poll 所有 event 设备 + 解析 ---- */
|
/* ---- 输入线程:poll 所有 event 设备 + 解析 ---- */
|
||||||
@@ -224,12 +231,22 @@ static void *input_thread(void *arg)
|
|||||||
if (code == ABS_X || code == ABS_MT_POSITION_X) {
|
if (code == ABS_X || code == ABS_MT_POSITION_X) {
|
||||||
if (t_down) touch_update(val, t_cy); /* 锁原点+更新当前(t_down 时才驱动) */
|
if (t_down) touch_update(val, t_cy); /* 锁原点+更新当前(t_down 时才驱动) */
|
||||||
else t_cx = val;
|
else t_cx = val;
|
||||||
/* 左滑进度实时上报(视觉条):原点确实在左缘(t_dx<EDGE_W) 且 向右移动
|
/* 左滑进度实时上报(视觉条)。抬手触发仍由 touch_release 判定;这里只报过程。
|
||||||
* (t_cx>t_dx)。抬手触发仍由 touch_release 判定;这里只报过程。
|
* 修复要点(左缘返回滑条 bug):
|
||||||
* t_dx 是本次按下后首个有效 ABS 锁定的真实按下点(非残留),且每次抬手已
|
* ① 双向上报(不再要求 t_cx>t_dx):向右→条伸长、向左→条实时回缩,
|
||||||
* 清空,杜绝「中部点一下出条 / 向左划也出条」。 */
|
* 摒弃「只在返回瞬间闪末帧 / 反向才出条」;
|
||||||
if (t_down && t_origin_valid && t_dx >= 0 && t_dx < EDGE_W && t_cx > t_dx)
|
* ② 仅左缘起手(t_dx<EDGE_W)才上报并被绘制——屏幕中部起手绝不触发条/返回;
|
||||||
push_ev(MEOW_EV_SWIPE_PROGRESS, t_dx, t_cx);
|
* ③ 条长 = 本手势右移位移 clamped 到「本次手势最大右移位移」(t_gmax_dx),
|
||||||
|
* 条的最大 x 恒等于本次手势达过的最大右移距离,反复反向滑不会放大它。
|
||||||
|
* 负数(左滑越过原点)→ 条长 0(清空)。
|
||||||
|
* 事件载荷:a=按下X(t_dx),b=条长 len(clamp 后,≥0)。 */
|
||||||
|
if (t_down && t_origin_valid && t_dx >= 0 && t_dx < EDGE_W) {
|
||||||
|
int dx = t_cx - t_dx;
|
||||||
|
if (dx > t_gmax_dx) t_gmax_dx = dx;
|
||||||
|
int len = dx < 0 ? 0 : dx;
|
||||||
|
if (len > t_gmax_dx) len = t_gmax_dx;
|
||||||
|
push_ev(MEOW_EV_SWIPE_PROGRESS, t_dx, len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) {
|
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) {
|
||||||
if (t_down) touch_update(t_cx, val); else t_cy = val;
|
if (t_down) touch_update(t_cx, val); else t_cy = val;
|
||||||
@@ -282,9 +299,12 @@ void meow_ui_run(const meow_ui_events *ev)
|
|||||||
meow_input_ev e;
|
meow_input_ev e;
|
||||||
int got = 0;
|
int got = 0;
|
||||||
while (meow_input_pop(&e)) {
|
while (meow_input_pop(&e)) {
|
||||||
|
if (g_exit) break; /* 已请求退出:不再分发剩余事件,杜绝「退出后又触发」的偶发再进/退 */
|
||||||
got = 1;
|
got = 1;
|
||||||
int in_startup = (now_ms() < startup_cooldown_until); /* M2.22(C) */
|
int in_startup = (now_ms() < startup_cooldown_until); /* M2.22(C) */
|
||||||
if (e.type == MEOW_EV_TAP) {
|
if (e.type == MEOW_EV_TAP) {
|
||||||
|
if (in_startup) continue; /* 启动窗口也吞 TAP:旧手势残留点按(如上一次左滑抬起)
|
||||||
|
* 在进入瞬间命中 UI 按钮(如全屏按钮)会误触发——丢弃。 */
|
||||||
/* 抬手未达标:清视觉条 */
|
/* 抬手未达标:清视觉条 */
|
||||||
meow_ui_swipe_hint(-1);
|
meow_ui_swipe_hint(-1);
|
||||||
meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6);
|
meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6);
|
||||||
@@ -315,16 +335,17 @@ void meow_ui_run(const meow_ui_events *ev)
|
|||||||
meow_ui_sys_notif_pull();
|
meow_ui_sys_notif_pull();
|
||||||
} else if (e.type == MEOW_EV_SWIPE_PROGRESS) {
|
} else if (e.type == MEOW_EV_SWIPE_PROGRESS) {
|
||||||
if (in_startup) continue; /* 启动窗口进度条静音 */
|
if (in_startup) continue; /* 启动窗口进度条静音 */
|
||||||
int x0 = e.a, xc = e.b, dx = xc - x0; /* dx=当前X-按下X */
|
/* e.a=按下X(t_dx),e.b=条长 len(输入层已 clamp 到「本手势最大右移位移」且≥0)。
|
||||||
/* SDK 自动画顶部条:长度=手指当前 X(=距左缘距离,向内划长度随位移增长)。
|
* SDK 自动画顶部条:从 x=0 画到 len(右移位移),向右伸长、向左实时回缩。
|
||||||
* M2.5x/A-13:全屏/收合下「左缘返回手势保留」,顶部动态提示条(meow_ui_swipe_hint)
|
* M2.5x/A-13:全屏/收合下「左缘返回手势保留」,顶部动态提示条仍需绘制——不再以
|
||||||
* 仍需绘制——不再以 meow_ui_sys_statusbar_active()(全屏/隐藏=0)开关,否则全屏
|
* meow_ui_sys_statusbar_active()(全屏/隐藏=0)开关,否则全屏时滑条完全不显示。
|
||||||
* 时滑条完全不显示。条位 y=meow_ui_statusbar_height():非全屏=状态栏下沿、
|
* 条位 y=meow_ui_statusbar_height():非全屏=状态栏下沿、全屏=0、收合=提示线下缘
|
||||||
* 全屏=0、收合=提示线下缘(MUI_SBAR_COLLAPSE_H),与提示线不重叠加。
|
* (MUI_SBAR_COLLAPSE_H),与提示线不重叠加。APP 若注册 on_swipe_progress 仍可自绘
|
||||||
* APP 若注册 on_swipe_progress 仍可自绘(系统条照常画)。 */
|
* (系统条照常画);递交给 app 的是 (len-x_start) 使 x_start+dx==len,与系统条同长。 */
|
||||||
meow_ui_swipe_hint(xc);
|
int len = e.b;
|
||||||
|
meow_ui_swipe_hint(len);
|
||||||
if (g_events_set && g_events.on_swipe_progress)
|
if (g_events_set && g_events.on_swipe_progress)
|
||||||
g_events.on_swipe_progress(x0, dx);
|
g_events.on_swipe_progress(e.a, len - e.a);
|
||||||
meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6);
|
meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user