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

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

No files matched your search

+3 -1
View File
@@ -68,10 +68,12 @@
| `exec` | ✓ | 应用可执行文件绝对路径(必须是**静态** MIPS32r2 ELF,soft/hard-float 均可) | | `exec` | ✓ | 应用可执行文件绝对路径(必须是**静态** MIPS32r2 ELF,soft/hard-float 均可) |
| `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] |
| `fonts` | — | 字库档位声明:`"full"` / `"gbk"` / `"common"`(app 需大字库时可声明 `"full"`,默认小字库 `common`≈4k;launcher 据此为该 app 加载大字库 `gbk`≈20k)。**注意**:与 `"fonts":["<handle>"]`(应用自带字库句柄数组)**同名二义**,见 MEOWOS_SPEC_REF §7 A-14 [源:用户 2026-08-30] |
- 目录扫描深度 1 级:只枚举 `MeowAPPs/` 下的直接子目录 - 目录扫描深度 1 级:只枚举 `MeowAPPs/` 下的直接子目录
- 应用计数上限 **8**(超出忽略;单屏 8 行) - 应用计数上限 **8**(超出忽略;单屏 8 行)
- app.json 解析器极简(launcher 内实现):仅匹配 `"name":`、`"exec":`、`"package":` 后的引号字符串(及扩展 icon/framework/immersive/route./`awake.<id>`(keep_awake)/service.),不做 JSON 校验(多行/单行均可)[源:审计 2026-08-30] - app.json 解析器极简(launcher 内实现):仅匹配 `"name":`、`"exec":`、`"package":` 后的引号字符串(及扩展 icon/framework/immersive/route./`awake.<id>`(keep_awake)/service.),**`fullscreen`(本次实现)与 `fonts`(档位串) 已解析**,不做 JSON 校验(多行/单行均可)[源:审计 2026-08-30][源:用户 2026-08-30]
- 列表刷新时机:每次菜单打开时重新扫描(TF 卡热插拔后立即可见) - 列表刷新时机:每次菜单打开时重新扫描(TF 卡热插拔后立即可见)
### 2.2 边界情况行为(launcher 实现,全部真机验证) ### 2.2 边界情况行为(launcher 实现,全部真机验证)
+3 -1
View File
@@ -36,7 +36,7 @@ target_activity(可选, 来自 hints) / progress(可选 int)
## 5. 存储分层(用户已定 2026-08-29) ## 5. 存储分层(用户已定 2026-08-29)
- **关键配置优先板载**:设置类(notify.* 配额等)存板载 `/usr/data/meow/settings.conf`(B2a),确保**无卡时系统正常工作**。 - **关键配置优先板载**:设置类(notify.* 配额等)存板载 `/usr/data/meow/settings.conf`(B2a),确保**无卡时系统正常工作**。
- **实时通知 → 本地内存**(板载,动态分配/ring)。 - **实时通知 → 本地内存**(板载,动态分配/ring)。
- **超时/转储 → TF 卡** `MeowOS/notifications/`(**系统约定:系统文件统一放 MeowOS/ 下**,非 MeowData/——MeowData 是应用专属数据根)(转储区,如 notifications/<ts>.json 或 appended 日志)。 - **超时/转储 → TF 卡** `MeowOS/notifications/`(**系统约定:系统文件统一放 MeowOS/ 下**,非 MeowData/——MeowData 是应用专属数据根)(转储区,**appended 日志行 CSV**,见 §8)。
- **无 TF 卡可运行**(本地内存态即可,不依赖卡;TF 卡在时才转储)。转储/日志类是可降级数据(无卡用 /tmp 回退),配置类永远板载。 - **无 TF 卡可运行**(本地内存态即可,不依赖卡;TF 卡在时才转储)。转储/日志类是可降级数据(无卡用 /tmp 回退),配置类永远板载。
- **本地数据清理策略**: - **本地数据清理策略**:
- 关机/重启前 → 清理本地内存通知数据(正常关机路径)。 - 关机/重启前 → 清理本地内存通知数据(正常关机路径)。
@@ -60,6 +60,8 @@ target_activity(可选, 来自 hints) / progress(可选 int)
- **正常电源事件(关机/重启)触发清除板载通知数据之前**:先转储所有未上卡的通知,再清除板载。 - **正常电源事件(关机/重启)触发清除板载通知数据之前**:先转储所有未上卡的通知,再清除板载。
- **异常关机**:开机时清理板载残留通知数据。 - **异常关机**:开机时清理板载残留通知数据。
- TF 卡转储区 `MeowOS/notifications/`:保留**最新 10MB**(ring/截断),**不被电源事件清理**(持久留存)。 - TF 卡转储区 `MeowOS/notifications/`:保留**最新 10MB**(ring/截断),**不被电源事件清理**(持久留存)。
- **转储格式(定案 #6,M2.5x)**:`MeowOS/notifications/` 写为 **appended 日志行**(每条通知一行,CSV 风格:`id,app_id,分类,标题,时间,…`),**按总大小滚动截断**(保留最新 **10MB**)。与既有「10s 转储」衔接:创建 10s 后即按此格式追加一行 `[源:用户 2026-08-30]`。
- **启动时清板载 + 清内存通知表**:开机(含异常关机后)清理板载残留通知数据,并**清空内存通知表**(在既有「异常关机:开机时清理板载残留」基础上**补充内存表一并清**);正常关机/重启前先转储未上卡通知再清板载(沿用既有规则)`[源:用户 2026-08-30]`。
- **无 TF 卡**:系统可运行(内存态通知即可),不依赖卡。 - **无 TF 卡**:系统可运行(内存态通知即可),不依赖卡。
## 9. 通知中心 UI(阶段 B) ## 9. 通知中心 UI(阶段 B)
+3
View File
@@ -42,6 +42,7 @@
| | `font.class_latin` | path | *(空=跟全局)* | 多字体:A-Za-z 所用字体 | | | `font.class_latin` | path | *(空=跟全局)* | 多字体:A-Za-z 所用字体 |
| | `font.class_latin_sym` | path | *(空=跟全局)* | 多字体:半角标点 所用字体 | | | `font.class_latin_sym` | path | *(空=跟全局)* | 多字体:半角标点 所用字体 |
| | `font.class_other` | path | *(空=跟全局)* | 多字体:其他字符 所用字体 | | | `font.class_other` | path | *(空=跟全局)* | 多字体:其他字符 所用字体 |
| | `font.load_full` | int 0/1 | 0 | **加载大字库(M2.5x)**:1=**强制全系统用大字库(`gbk`≈20k 档)**,覆盖 app 未声明/覆盖默认 `font.file`;0=默认小字库(`common`≈4k),仅 app 声明 `fonts:"full"` 才大字库 [源:用户 2026-08-30] |
| | `font.size.title` / `font.size.body` | int | 28/20 | 标题/正文字号 | | | `font.size.title` / `font.size.body` | int | 28/20 | 标题/正文字号 |
| | `ui.style` | enum | `md3` | `md3`(Material You)/`miui` | | | `ui.style` | enum | `md3` | `md3`(Material You)/`miui` |
| **通知** | `notify.center_enabled` | bool | true | 下拉通知中心开关 | | **通知** | `notify.center_enabled` | bool | true | 下拉通知中心开关 |
@@ -59,6 +60,8 @@
| | `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]。
> 新增设置项时:在清单加一行 + 存储 schema + API 常量/枚举 + 实体 UI 控件 + web 面板控件(四端同步)。 > 新增设置项时:在清单加一行 + 存储 schema + API 常量/枚举 + 实体 UI 控件 + web 面板控件(四端同步)。
## 2. 统一存储(单一数据源) ## 2. 统一存储(单一数据源)
+20 -3
View File
@@ -96,6 +96,7 @@
- 控件:`meow_list_source = int (*)(int idx,char *label,int len,void *ctx)`([D2] 定案)`[meow_ui.h]` - 控件:`meow_list_source = int (*)(int idx,char *label,int len,void *ctx)`([D2] 定案)`[meow_ui.h]`
- 刷新率:`meow_ui_set_refresh_rate(60/30/15)`/`meow_ui_set_refresh_always_max(1)`/`meow_ui_effective_rate()`/`meow_ui_frames_presented()` `[meow_ui.h §7.1]` - 刷新率:`meow_ui_set_refresh_rate(60/30/15)`/`meow_ui_set_refresh_always_max(1)`/`meow_ui_effective_rate()`/`meow_ui_frames_presented()` `[meow_ui.h §7.1]`
- 状态栏:`meow_ui_set_statusbar(1/0)`/`meow_ui_statusbar_height()`(非全屏=28,全屏=0)/`meow_ui_set_fullscreen(on)`;状态栏高 **MUI_SBAR_H=28**(用户定 40→28,字号随之为 20)`[MIGRATION_AC §11.1][meow_ui.h]` - 状态栏:`meow_ui_set_statusbar(1/0)`/`meow_ui_statusbar_height()`(非全屏=28,全屏=0)/`meow_ui_set_fullscreen(on)`;状态栏高 **MUI_SBAR_H=28**(用户定 40→28,字号随之为 20)`[MIGRATION_AC §11.1][meow_ui.h]`
- 全屏/状态栏收合(M2.5x):launcher 启动 app/切 activity 据 `app.json fullscreen`/`route.<id>.fullscreen` 预先设 SDK 全屏(env **`MEOW_FULLSCREEN`** 注入);进入全屏 = 状态栏**向上收出屏顶、只留一条提示线**(新增 `meow_ui_statusbar_collapsed()` 语义,区分「**收合=状态栏缩为提示线**」vs「**隐藏=高度0**」),带自然过渡动画;全屏下**保留左缘返回手势 + 顶部提示线,顶部下滑仍开通知中心(顶部热区缩小)** `[源:用户 2026-08-30]`
- 日志:`MEOW_LOG_DEBUG=0/INFO/WARN/ERROR`;`meow_log(lvl,fmt,...)`/`meow_log_set_level`/`meow_log_get_level` `[meow_ui.h §9]` - 日志:`MEOW_LOG_DEBUG=0/INFO/WARN/ERROR`;`meow_log(lvl,fmt,...)`/`meow_log_set_level`/`meow_log_get_level` `[meow_ui.h §9]`
- 通知:见 §4。 - 通知:见 §4。
@@ -144,6 +145,7 @@
- **左缘返回手势**:手指从**屏幕左缘 ≤24px** 向内(右)划 **≥90px**,**抬手(release 边沿)才触发** `MEOW_SWIPE_LEFT`("左滑=返回"契约);过程以 `on_swipe_progress` 实时上报 + SDK 自动画顶部进度条 `meow_ui_swipe_hint(x)`(5px 高,top,theme accent 色);x<=0 清条。**中部左/右滑不是返回**(中部右滑→SWIPE_RIGHT,中部左滑→落 TAP)`[MEOW_APP_SDK §6][GESTURE_BACK §2.2/§3]` - **左缘返回手势**:手指从**屏幕左缘 ≤24px** 向内(右)划 **≥90px**,**抬手(release 边沿)才触发** `MEOW_SWIPE_LEFT`("左滑=返回"契约);过程以 `on_swipe_progress` 实时上报 + SDK 自动画顶部进度条 `meow_ui_swipe_hint(x)`(5px 高,top,theme accent 色);x<=0 清条。**中部左/右滑不是返回**(中部右滑→SWIPE_RIGHT,中部左滑→落 TAP)`[MEOW_APP_SDK §6][GESTURE_BACK §2.2/§3]`
- 偏差:原「左缘+向左 mdx<-90」物理不可达 → 取「左缘向内(右)划 ≥90px」,事件仍报 `MEOW_SWIPE_LEFT` `[GESTURE_BACK §3]` - 偏差:原「左缘+向左 mdx<-90」物理不可达 → 取「左缘向内(右)划 ≥90px」,事件仍报 `MEOW_SWIPE_LEFT` `[GESTURE_BACK §3]`
- **全局状态栏**:SDK 内置,默认显示(时钟/电量/WiFi/蓝牙/音量),1s 轻刷,z 最顶;仅 APP 声明全屏/`fullscreen:1` 才隐藏;非全屏 APP 须用 `meow_ui_statusbar_height()` 做 inset `[meow_ui.h §3.1][MIGRATION_AC §3]` - **全局状态栏**:SDK 内置,默认显示(时钟/电量/WiFi/蓝牙/音量),1s 轻刷,z 最顶;仅 APP 声明全屏/`fullscreen:1` 才隐藏;非全屏 APP 须用 `meow_ui_statusbar_height()` 做 inset `[meow_ui.h §3.1][MIGRATION_AC §3]`
- **全屏机制(M2.5x,`app.json fullscreen` 即 app 级 + `route.<id>.fullscreen` 即 activity 级)**:launcher 启动 app/切 activity 时据声明**预先设 SDK 全屏**(env `MEOW_FULLSCREEN`)。进入/退出全屏:**状态栏向上收出屏顶、只留一条提示线高度**(非全隐藏),带上收/恢复的**自然过渡动画**。**全屏下保留**:左缘返回手势 + 顶部动态伸缩提示线;顶部下滑仍可展开通知中心,但**顶部热区缩小**(不影响全屏 app)`[源:用户 2026-08-30]`(细化既有「全屏=隐藏状态栏/隐藏返回键」的表述,见 §7 A-13)
- 沉浸:`app.json immersive:1` → launcher 写 `/tmp/meowapp_state=1` → 隐藏虚拟返回键,手势归 SDK `[APPS §3.5][GESTURE_BACK §2.4]` - 沉浸:`app.json immersive:1` → launcher 写 `/tmp/meowapp_state=1` → 隐藏虚拟返回键,手势归 SDK `[APPS §3.5][GESTURE_BACK §2.4]`
### 5.2 主题 ### 5.2 主题
@@ -152,17 +154,24 @@
- 主界面背景:深色用 background(可选色)、浅色用 surface(白) `[DEV_NOTES #13]` - 主界面背景:深色用 background(可选色)、浅色用 surface(白) `[DEV_NOTES #13]`
- **TF 卡多字体库(M2.5x)**: - **TF 卡多字体库(M2.5x)**:
- 目录 **`<卡根>/MeowOS/fonts/<family>/`**(每套一个子目录),内含三件套:`font.png`(预渲染灰度 PNG,bit depth 4|8)+ `font.idx`(MFNI v2 索引),二者均由 `font/gen/gen_font.py` 生成;`font.json` 为元数据+完整性校验。**缺任一件 → 该套不进列表 + 日志**。 - 目录 **`<卡根>/MeowOS/fonts/<family>/`**(每套一个子目录),内含三件套:`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`(信息用)/`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`(下拉显示名,缺则回退目录名)/`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/font.png`)指向的字体**存在且能 load → 用它**;否则回退卡默认 `MeowOS/fonts/default/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/`**(`font.png`+`font.idx`,可选 `font.json`),不再用 `MeowAPPs/fonts/font.png`;设备旧 `MeowAPPs/fonts/` **保留**(勿删,避免破坏老约定/已有文件),仅新引用一律指向 `MeowOS/fonts/default`。
- **`font.file` 变更即 unload+reload 生效**(设置页回调);拔卡 → 回退固件内置。 - **`font.file` 变更即 unload+reload 生效**(设置页回调);拔卡 → 回退固件内置。
- **preset 动态化**:启动/进设置页扫描一次 `MeowOS/fonts/*/`,重建「字体文件」下拉(默认字体 + ≤16 套卡内字体;上限防爆)。扫描仅此二时机,勿放热路径。 - **preset 动态化**:启动/进设置页扫描一次 `MeowOS/fonts/*/`,重建「字体文件」下拉(默认字体 + ≤16 套卡内字体;上限防爆)。扫描仅此二时机,勿放热路径。
- **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>/`、导出打包)。
- **多字体(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]`。
- 档位枚举:`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`(现行仅 `--extra` 思路升级为**档位选择**,**本次新增**);每个 `MeowOS/fonts/<family>/font.json` 标 `charset` + `glyph_count`(覆盖声明/信息用)。
- **默认用小字库(`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`),让用户知道所选字库量级。
- 判缺字:SDK `meow_font_has_glyph(cp)` 预判当前字库是否含该码位(缺字 → app 回退/提示)。
### 5.3 通知配额/转储 ### 5.3 通知配额/转储
- **每 App 最多 4 类型;每类型活动 ≤4;媒体单例(每 App 仅 1 条,可多 App)**;系统事件抽象为 App(`app_id=system`) 走同一配额 `[NOTIFICATION_SYSTEM §7][meow_ui.h][DEV_NOTES #11]` - **每 App 最多 4 类型;每类型活动 ≤4;媒体单例(每 App 仅 1 条,可多 App)**;系统事件抽象为 App(`app_id=system`) 走同一配额 `[NOTIFICATION_SYSTEM §7][meow_ui.h][DEV_NOTES #11]`
- 通知创建 **10s 后自动转储到 TF `MeowOS/notifications/`**(保留最新 10MB ring,不被电源事件清);正常关机先转储再清板载;无卡可运行 `[NOTIFICATION_SYSTEM §8]` - 通知创建 **10s 后自动转储到 TF `MeowOS/notifications/`**(保留最新 10MB ring,不被电源事件清);正常关机先转储再清板载;无卡可运行 `[NOTIFICATION_SYSTEM §8]`
- **通知 TF 转储(定案 #6,M2.5x)**:转储格式 = **appended 日志行**(每条通知一行,CSV 风格:`id,app_id,分类,标题,时间,…`)+ **按总大小滚动截断**(保留最新 **10MB**)。**启动时清板载通知 + 清内存通知表**(开机清残留/内存表);正常关机/重启前**先转储未上卡通知**再清板载(沿用既有规则)`[源:用户 2026-08-30]`
### 5.4 配置版本单点 / 刷新率 / 息屏 ### 5.4 配置版本单点 / 刷新率 / 息屏
- /etc/meowos_version 版本单点(见 §1.1)`[DEV_NOTES #4]` - /etc/meowos_version 版本单点(见 §1.1)`[DEV_NOTES #4]`
@@ -175,7 +184,7 @@
- `<pkg>/` = 程序主体+app.json+资源;`MeowData/<pkg>/{data,cache}` = 数据;首次启动自动建 data/cache 并经 env 注入,exFAT 无软链 `[APPS 附录 §1/§2]` - `<pkg>/` = 程序主体+app.json+资源;`MeowData/<pkg>/{data,cache}` = 数据;首次启动自动建 data/cache 并经 env 注入,exFAT 无软链 `[APPS 附录 §1/§2]`
- `.mpk` = tar.gz(app.json + meow 二进制 + 资源),**不含 data/cache**;导入校验 app.json/哈希→解压→数据 init→登入列表 `[APPS 附录 §3]` - `.mpk` = tar.gz(app.json + meow 二进制 + 资源),**不含 data/cache**;导入校验 app.json/哈希→解压→数据 init→登入列表 `[APPS 附录 §3]`
- 包管理 **CLI `nyai`**(静态 MIPS,部署 `MeowOS/opt/bin/nyai`):install/remove/list/info/search/index/export/upgrade/scan;源 `MeowOS/opt/nyai/sources.list`;索引 `Packages`(Package/Version/Filename/Size/SHA256/Depends/Description)`[APPS 附录 §5]` - 包管理 **CLI `nyai`**(静态 MIPS,部署 `MeowOS/opt/bin/nyai`):install/remove/list/info/search/index/export/upgrade/scan;源 `MeowOS/opt/nyai/sources.list`;索引 `Packages`(Package/Version/Filename/Size/SHA256/Depends/Description)`[APPS 附录 §5]`
- 应用数上限 **8**(单屏 8 行);扫描深度 1 级;`app.json` 字段:name(仅 ASCII)/exec(静态 MIPS32r2 ELF)/version/author + **`package`(反域名权威 app_id;==目录名;缺省回退目录名)** + 扩展 `framework`/`immersive`/`icon`/`route.<id>`/`awake.<id>`(keep_awake)/`service.<svc>`/`fullscreen`(**未解析**,见矛盾 §A-12) `[APPS §2][MEOW_APP_SDK §9][FRAMEWORK_DESIGN §2][源:审计 2026-08-30]` - 应用数上限 **8**(单屏 8 行);扫描深度 1 级;`app.json` 字段:name(仅 ASCII)/exec(静态 MIPS32r2 ELF)/version/author + **`package`(反域名权威 app_id;==目录名;缺省回退目录名)** + 扩展 `framework`/`immersive`/`icon`/`route.<id>`/`awake.<id>`(keep_awake)/`service.<svc>`/`fullscreen`(app 级;`route.<id>.fullscreen`=activity 级;**本次实现**:launcher 启动/切 activity 据声明预设 SDK 全屏,见 §5.1/§3.1/§A-12)/`fonts`(字体档位 `"full"|"gbk"|"common"`,需大字库时声明,见 §5.7.8) `[APPS §2][MEOW_APP_SDK §9][FRAMEWORK_DESIGN §2][源:审计 2026-08-30][源:用户 2026-08-30]`
### 5.6 冷启动充电屏 ### 5.6 冷启动充电屏
- 触发(**三者同时**):冷启动(`poweroff` 写 `/data/meow_cold_boot=1`;`reboot` 写 0)+ 在充电(AC online=1 或 battery status=Charging)+(开机瞬间一次性快照,判定后不回头)`[CHARGING_SCREEN §0]` - 触发(**三者同时**):冷启动(`poweroff` 写 `/data/meow_cold_boot=1`;`reboot` 写 0)+ 在充电(AC online=1 或 battery status=Charging)+(开机瞬间一次性快照,判定后不回头)`[CHARGING_SCREEN §0]`
@@ -287,6 +296,12 @@
- **launcher env 注入**(launch_app,app 跟随系统字体):`MEOW_MULTI_FONT=0/1`;`MEOW_FONT_PNG/IDX`(全局,保留);6 类 `MEOW_FONT_CLS_<类>_PNG/IDX`(命中则覆盖全局;类值为空=跟随全局不注入)。SDK `meow_ui_gfx` 读 env 构建同一映射;无 env/关 → 单字体。 - **launcher env 注入**(launch_app,app 跟随系统字体):`MEOW_MULTI_FONT=0/1`;`MEOW_FONT_PNG/IDX`(全局,保留);6 类 `MEOW_FONT_CLS_<类>_PNG/IDX`(命中则覆盖全局;类值为空=跟随全局不注入)。SDK `meow_ui_gfx` 读 env 构建同一映射;无 env/关 → 单字体。
- **持久化**:类别字体设置在 settings.conf(板载,无卡也可存),经 `meow_settings_*` 读写,随设置页变更即重配。 - **持久化**:类别字体设置在 settings.conf(板载,无卡也可存),经 `meow_settings_*` 读写,随设置页变更即重配。
- **内存**(推断解码 atlas = `plane_w×plane_h×(depth 4→0.5 / 8→1.0)` 字节;每套另加 glyph 数组 ≈ glyph_count×16B、idx 头保留约 128KB,idx 原始缓冲载毕即释)。实测仓库字库(depth 4):misans(CJK)≈2.44MiB、source_han_sans(CJK)≈2.53MiB、meslo(Latin)≈1.68MiB、google_sans(Latin)≈1.12MiB;按用户估算口径(**depth 8**):CJK ≈ 5-6MB、Latin ≈ 0.3-0.5MB(后者为窄覆盖 Latin 专库)。**典型「1CJK+1Latin」增量**:全局=CJK,另加一套 Latin → 增加 ≈0.3-1.7MB(depth 8 约 0.5MB;实测 depth4 latin 1.1-1.7MB);全局=Latin 另加一套 CJK → 增加 ≈2.4-2.5MB(depth 8 约 5-6MB)。`display.multi_font=0` 时仅全局一套,无任何额外装载。 - **内存**(推断解码 atlas = `plane_w×plane_h×(depth 4→0.5 / 8→1.0)` 字节;每套另加 glyph 数组 ≈ glyph_count×16B、idx 头保留约 128KB,idx 原始缓冲载毕即释)。实测仓库字库(depth 4):misans(CJK)≈2.44MiB、source_han_sans(CJK)≈2.53MiB、meslo(Latin)≈1.68MiB、google_sans(Latin)≈1.12MiB;按用户估算口径(**depth 8**):CJK ≈ 5-6MB、Latin ≈ 0.3-0.5MB(后者为窄覆盖 Latin 专库)。**典型「1CJK+1Latin」增量**:全局=CJK,另加一套 Latin → 增加 ≈0.3-1.7MB(depth 8 约 0.5MB;实测 depth4 latin 1.1-1.7MB);全局=Latin 另加一套 CJK → 增加 ≈2.4-2.5MB(depth 8 约 5-6MB)。`display.multi_font=0` 时仅全局一套,无任何额外装载。
- **5.7.8 字体档位分层 / 按需大字库(app 声明 + 系统开关)** `[源:用户 2026-08-30]`
- **口径**:默认用**小字库 `common`(≈4k 码位)**;当 app 声明需要**大字库**(如小说阅读器,`app.json` 声明 `"fonts":"full"` 或档位 `"gbk"`/`"common"`)时,才为该 app 加载**大字库 `gbk`(≈20k+)**。档位与 `gen_font.py --charset`、`font.json charset/glyph_count` 见 §5.2「字体档位/大字库」。
- **app 级 / activity 级**:app 级以 `app.json "fonts":"<tier>"` 声明;activity 级(如需)以 `route.<id>.fonts` 声明(沿用 `route.<id>` 扩展口径)。launcher 启动 app/切 activity 时据声明选加载大/小字库,并经 env 注入(`MEOW_FONT_PNG/IDX` 指向所选档位字库;新增 `MEOW_FONT_TIER` 标注档位)。
- **系统设置「加载大字库」**(`font.load_full`,默认 0):开 → **强制全系统用大字库(gbk 档)**,覆盖 app 未声明 + 覆盖默认 `font.file`;关(默认)→ 默认小字库,app 声明才大字库。
- **SDK 判缺字**:`meow_font_has_glyph(cp)` 预判当前字库是否含该码位 → app 据此决定回退/提示(避免 `common` 小字库下静默豆腐或缺字)。
- **⚠️ 与 §5.7.3 冲突**:§5.7.3 `"fonts":[<handle>…]` 为**应用自带字库句柄数组**;本小节 `"fonts":"full"` 为**字库档位字符串**。同名键两种语义,需统一解析口径(见 §7 A-14)。
--- ---
@@ -310,5 +325,7 @@
- **A-9 SDK 通知转储**:NOTIFICATION_SYSTEM §8「创建 10s 后自动转储到 TF」;meow_ui.h 现仅「活动表+meow_log 打日志,真实投递 UI/D-Bus 为后续」。→ 10s 转储/TF 尚未实现,属"已定未做"。 - **A-9 SDK 通知转储**:NOTIFICATION_SYSTEM §8「创建 10s 后自动转储到 TF」;meow_ui.h 现仅「活动表+meow_log 打日志,真实投递 UI/D-Bus 为后续」。→ 10s 转储/TF 尚未实现,属"已定未做"。
- **A-10 版本来源细解**:DEV_NOTES #4「/etc/meowos_version 唯一来源;MEOWOS_VERSION 宏仅 dev 占位永不动」;APPS.md §4.6「MEOWOS_VERSION 宏=单一来源,build 时 sed 写入 etc/meowos_version」。→ 两者都强调"写入同源",但"谁是源头"措辞矛盾;实践上 build 脚本 echo 生成 /etc/meowos_version,宏作屏显 dev 占位。 - **A-10 版本来源细解**:DEV_NOTES #4「/etc/meowos_version 唯一来源;MEOWOS_VERSION 宏仅 dev 占位永不动」;APPS.md §4.6「MEOWOS_VERSION 宏=单一来源,build 时 sed 写入 etc/meowos_version」。→ 两者都强调"写入同源",但"谁是源头"措辞矛盾;实践上 build 脚本 echo 生成 /etc/meowos_version,宏作屏显 dev 占位。
- **A-11 `MeowOS/opt/` vs `MeowOpt/`**:§1.2 树中 `MeowOS/opt/{bin,nyai,sources.list},lib/,tools/`(nyai 包管 + 工具)与新增顶层 `MeowOpt/`(板载外部工具/二进制,如 mdnsd)并存,命名仅大小写之差、语义可能重叠。→ **两者是否为同一用途/是否需归并待确认**;当前按各自文档记录,勿混用。 - **A-11 `MeowOS/opt/` vs `MeowOpt/`**:§1.2 树中 `MeowOS/opt/{bin,nyai,sources.list},lib/,tools/`(nyai 包管 + 工具)与新增顶层 `MeowOpt/`(板载外部工具/二进制,如 mdnsd)并存,命名仅大小写之差、语义可能重叠。→ **两者是否为同一用途/是否需归并待确认**;当前按各自文档记录,勿混用。
- **A-12 `fullscreen` 字段未解析**:§5.5 与 MEOW_APP_SDK §9 将 `fullscreen` 列为 app.json 字段,但 `meow_parse_app_json()` **并未读取该键**(实现只解析 name/exec/icon/package/framework/immersive/route./awake./service.)。→ 文档与实现不一致,属"已定未做";若要用需在解析器补读并处理。 - **A-12 `fullscreen` 字段(已定 → 本次实现)**:§5.5 与 MEOW_APP_SDK §9 将 `fullscreen` 列为 app.json 字段,但 `meow_parse_app_json()` **此前并未读取该键**(实现只解析 name/exec/icon/package/framework/immersive/route./awake./service.),属"已定未做"。→ **本次实现**:launcher 启动 app/切 activity 据 `app.json fullscreen`/`route.<id>.fullscreen` 预设 SDK 全屏(env `MEOW_FULLSCREEN`),见 §5.1/§3.1。注意全屏语义由「隐藏状态栏+隐藏返回键」细化为「状态栏收合至提示线 + 保留左缘返回+顶部提示线+下滑通知中心(热区缩小)」(见 A-13)。
- **A-13 全屏语义细化(状态栏「隐藏」→「收合至提示线」)**:既有 §3.1/§5.1 描述全屏=隐藏状态栏(`meow_ui_statusbar_height()`=0、隐藏返回键=沉浸);本次定案细化为**进入/退出全屏:状态栏向上收出屏顶、只留一条提示线高度**,**全屏下保留**左缘返回手势 + 顶部动态伸缩提示线,且**顶部下滑仍可开通知中心(顶部热区缩小)**。→ 需在 SDK 端明确「收合(collapsed)」状态(新增 `meow_ui_statusbar_collapsed()` 等)与「隐藏」的差异,UI 依此绘制提示线。
- **A-14 `fonts` 键二义**:§5.7.3 `"fonts":[<handle>…]`=应用自带字库句柄数组;§5.2/§5.7.8 `"fonts":"full"|"gbk"|"common"`=字库档位字符串。→ 同名键两种语义,解析器需区分(字符串→档位 / 数组→句柄);若避免歧义可改用专键如 `"font_tier"`(**未拍板**)。
`[各源标注见上]` `[各源标注见上]`
Binary file not shown.
+102 -15
View File
@@ -915,6 +915,20 @@ static int meow_font_effective_paths(char *out_png, int plen, char *out_idx, int
return -1; return -1;
} }
/* 【tier 选择入口,font 域占位】按字体档位取「当前生效字体」png/idx,供 launch_app 注入
* MEOW_FONT_PNG/IDX/APP(配合 MEOW_FONT_TIER)。基座只实现 tier=COMMON(=meow_font_effective_paths);
* tier>0 的大字库(GBK/Full)绝对路径由后续 font 代理据此演进填充(约定 MeowOS/fonts/<family>/
* <tier>/…,或 settings 键 font.tier_gbk / font.tier_full —— 见 MEOW_APP_SDK.md 字体分层)。
* 未实现前暂回退全局字体。返回 0 成功,-1 无可用。 */
static int meow_font_effective_paths_tiered(int tier, char *out_png, int plen,
char *out_idx, int ilen, char *out_family, int flen)
{
if (tier == MEOW_FONT_TIER_COMMON || tier < MEOW_FONT_TIER_COMMON)
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
/* TODO(font 代理):按 tier 解析大字库绝对路径(GBK/Full);暂回退全局(tier0)。 */
return meow_font_effective_paths(out_png, plen, out_idx, ilen, out_family, flen);
}
/* M2.5x 多字体:从 settings 读 display.multi_font + 6 类 font.class_*,把各自相对路径按卡根 /* M2.5x 多字体:从 settings 读 display.multi_font + 6 类 font.class_*,把各自相对路径按卡根
* 解析为绝对路径,去重装载到 meow_font 槽位(同 family 去重 & ≤4 套上限由 * 解析为绝对路径,去重装载到 meow_font 槽位(同 family 去重 & ≤4 套上限由
* meow_font_config_multifont 统一处理)。仅当全局字库已装载(mfont_ready / slot0)时生效; * meow_font_config_multifont 统一处理)。仅当全局字库已装载(mfont_ready / slot0)时生效;
@@ -1308,6 +1322,7 @@ struct meow_route {
char act[16]; /* route 键后缀,如 "nowplaying" */ char act[16]; /* route 键后缀,如 "nowplaying" */
char chain[MEOW_CHAIN_MAX]; /* 如 "home;search;nowplaying" */ char chain[MEOW_CHAIN_MAX]; /* 如 "home;search;nowplaying" */
int keep_awake; /* M2.17:该 activity 常亮(app.json awake.<id>=1 / route.<id>.keep_awake=1) */ int keep_awake; /* M2.17:该 activity 常亮(app.json awake.<id>=1 / route.<id>.keep_awake=1) */
int fullscreen; /* 活动级全屏声名(app.json route.<id>.fullscreen=1;M3 活动切换时消费) */
}; };
/* 服务行:service.<svc>=mode:restart:max:window_ms(M1 仅解析存储,M4 保活) */ /* 服务行:service.<svc>=mode:restart:max:window_ms(M1 仅解析存储,M4 保活) */
@@ -1328,6 +1343,8 @@ struct app {
/* ---- 阶段 D · M1:app.json 路由/框架/服务/图标(scan_apps 解析)---- */ /* ---- 阶段 D · M1:app.json 路由/框架/服务/图标(scan_apps 解析)---- */
char framework; /* '1'=framework=true(看门狗返回发 SIGUSR1) */ char framework; /* '1'=framework=true(看门狗返回发 SIGUSR1) */
char immersive; /* '1'=immersive=true(隐藏虚拟返回键,返回手势归 SDK) */ char immersive; /* '1'=immersive=true(隐藏虚拟返回键,返回手势归 SDK) */
char fullscreen; /* '1'=app.json fullscreen=true(注入 MEOW_FULLSCREEN=1,SDK 预先设全屏) */
char fonts; /* 字库档:0=默认4K常用 1=大字库GBK 2=Full(app.json "fonts";注入 MEOW_FONT_TIER) */
char icon[129]; /* icon= 路径 */ char icon[129]; /* icon= 路径 */
int nroute; /* route.<id> 行数 */ int nroute; /* route.<id> 行数 */
struct meow_route routes[MEOW_MAX_ROUTES]; struct meow_route routes[MEOW_MAX_ROUTES];
@@ -1805,6 +1822,8 @@ static void meow_parse_app_json(FILE *f, struct app *a)
char name[17] = "", exec[129] = "", icon[129] = ""; char name[17] = "", exec[129] = "", icon[129] = "";
char package[MEOW_APP_ID_MAX] = ""; char package[MEOW_APP_ID_MAX] = "";
int fram = 0, immersive = 0; int fram = 0, immersive = 0;
int fullscreen = 0; /* app.json fullscreen=<true|1>(注入 MEOW_FULLSCREEN env,SDK 预先设全屏) */
int fonts = 0, fonts_set = 0; /* app.json "fonts":<档>(0=默认4K常用 1=大字库GBK 2=Full;缺省 0) */
struct app tmp; struct app tmp;
memset(&tmp, 0, sizeof tmp); memset(&tmp, 0, sizeof tmp);
while (fgets(buf, sizeof buf, f)) { while (fgets(buf, sizeof buf, f)) {
@@ -1816,8 +1835,8 @@ static void meow_parse_app_json(FILE *f, struct app *a)
char *rp = strstr(buf, "route."); char *rp = strstr(buf, "route.");
if (rp && tmp.nroute < MEOW_MAX_ROUTES) { if (rp && tmp.nroute < MEOW_MAX_ROUTES) {
rp += 6; /* 跳过 "route." */ rp += 6; /* 跳过 "route." */
int idl = 0; char id[16] = ""; int idl = 0; char id[32] = ""; /* 读满整键(识别 ".fullscreen" 后缀,cap 31) */
while (*rp && *rp != '"' && *rp != ':' && *rp != '=' && idl < 15) while (*rp && *rp != '"' && *rp != ':' && *rp != '=' && idl < 31)
id[idl++] = *rp++; id[idl++] = *rp++;
id[idl] = 0; id[idl] = 0;
if (idl > 0) { if (idl > 0) {
@@ -1826,16 +1845,30 @@ static void meow_parse_app_json(FILE *f, struct app *a)
if (*vp == ':' || *vp == '=') vp++; if (*vp == ':' || *vp == '=') vp++;
while (*vp == ' ' || *vp == '\t') vp++; while (*vp == ' ' || *vp == '\t') vp++;
if (*vp == '"') vp++; if (*vp == '"') vp++;
int vl = 0; char chain[MEOW_CHAIN_MAX] = ""; /* route.<id>.fullscreen=<on>:活动级全屏声明(M1 顺带;M3 活动切换时消费)。
while (*vp && *vp != '"' && *vp != ',' && *vp != '\n' && * 命中既有 route.<id> 置 fullscreen;不新建 route(避免 id 被截断误建 "…fulls")。 */
*vp != '\r' && vl < MEOW_CHAIN_MAX - 1) if (idl >= 11 && !strcmp(id + idl - 11, ".fullscreen")) {
chain[vl++] = *vp++; id[idl - 11] = 0; /* 去掉 ".fullscreen" 还原 act */
chain[vl] = 0; char v[8] = ""; int k = 0;
while (vl > 0 && (chain[vl - 1] == ' ' || chain[vl - 1] == '\t')) while (*vp && *vp != '"' && *vp != ',' && *vp != '\n' &&
chain[--vl] = 0; *vp != '\r' && k < 7) v[k++] = *vp++;
strncpy(tmp.routes[tmp.nroute].act, id, 15); v[k] = 0;
strncpy(tmp.routes[tmp.nroute].chain, chain, MEOW_CHAIN_MAX - 1); int on = (!strcmp(v, "1") || !strcmp(v, "true") || !strcmp(v, "yes"));
tmp.nroute++; for (int i = 0; i < tmp.nroute; i++)
if (!strcmp(tmp.routes[i].act, id)) { tmp.routes[i].fullscreen = on; break; }
} else {
int vl = 0; char chain[MEOW_CHAIN_MAX] = "";
while (*vp && *vp != '"' && *vp != ',' && *vp != '\n' &&
*vp != '\r' && vl < MEOW_CHAIN_MAX - 1)
chain[vl++] = *vp++;
chain[vl] = 0;
while (vl > 0 && (chain[vl - 1] == ' ' || chain[vl - 1] == '\t'))
chain[--vl] = 0;
strncpy(tmp.routes[tmp.nroute].act, id, 15);
tmp.routes[tmp.nroute].act[15] = 0; /* 显式收尾(原依赖 id 读长 ≤15) */
strncpy(tmp.routes[tmp.nroute].chain, chain, MEOW_CHAIN_MAX - 1);
tmp.nroute++;
}
} }
} }
/* awake.<id>=1(M2.17 keep_awake:该 activity 常亮,超时不自动息屏)。 /* awake.<id>=1(M2.17 keep_awake:该 activity 常亮,超时不自动息屏)。
@@ -1881,6 +1914,29 @@ static void meow_parse_app_json(FILE *f, struct app *a)
immersive = 1; immersive = 1;
} }
} }
/* fullscreen=true(app 级:注入 MEOW_FULLSCREEN=1,SDK 端 meow_ui_init 预先设全屏)。
* 与 immersive 是两回事:fullscreen 隐藏状态栏/沉浸,immersive 关虚拟返回键。 */
if (!fullscreen && strstr(buf, "fullscreen")) {
char v[8] = "";
if (json_str(buf, "fullscreen", v, sizeof v) > 0) {
if (!strcmp(v, "true") || !strcmp(v, "1") || !strcmp(v, "yes"))
fullscreen = 1;
}
}
/* "fonts":<档>(字库档:0=默认4K常用 1=大字库GBK 2=Full;可选,缺省 0)。
* launcher 注入 MEOW_FONT_TIER;大字库装载由后续 font 代理实现。 */
if (!fonts_set && strstr(buf, "fonts")) {
char v[8] = "";
if (json_str(buf, "fonts", v, sizeof v) > 0) {
if (!strcmp(v, "common") || !strcmp(v, "0")) fonts = 0;
else if (!strcmp(v, "gbk") || !strcmp(v, "1")) fonts = 1;
else if (!strcmp(v, "full") || !strcmp(v, "2")) fonts = 2;
else fonts = atoi(v); /* 数字兜底(越界收敛到 [0,2]) */
if (fonts < 0) fonts = 0;
if (fonts > 2) fonts = 2;
fonts_set = 1;
}
}
/* service.<svc>=mode:restart:max:window_ms(M1 解析存储,保活 M4) */ /* service.<svc>=mode:restart:max:window_ms(M1 解析存储,保活 M4) */
char *sp = strstr(buf, "service."); char *sp = strstr(buf, "service.");
if (sp && tmp.nsvc < MEOW_MAX_SVCS) { if (sp && tmp.nsvc < MEOW_MAX_SVCS) {
@@ -1911,6 +1967,8 @@ static void meow_parse_app_json(FILE *f, struct app *a)
strncpy(a->package, package, sizeof a->package - 1); strncpy(a->package, package, sizeof a->package - 1);
a->framework = fram ? '1' : 0; a->framework = fram ? '1' : 0;
a->immersive = immersive ? '1' : 0; a->immersive = immersive ? '1' : 0;
a->fullscreen = fullscreen ? '1' : 0; /* 注入 MEOW_FULLSCREEN env */
a->fonts = (char)(fonts < 0 ? 0 : (fonts > 2 ? 2 : fonts)); /* 注入 MEOW_FONT_TIER env */
strncpy(a->icon, icon, 128); strncpy(a->icon, icon, 128);
a->nroute = tmp.nroute; a->nroute = tmp.nroute;
if (tmp.nroute) memcpy(a->routes, tmp.routes, sizeof(a->routes[0]) * (size_t)tmp.nroute); if (tmp.nroute) memcpy(a->routes, tmp.routes, sizeof(a->routes[0]) * (size_t)tmp.nroute);
@@ -2263,6 +2321,19 @@ static void settings_sync_cache_listener(const char *key)
else if (!strcmp(key, "power.forceoff_ms")) { int v; if (meow_settings_get_int("power.forceoff_ms", &v) == 0) power_forceoff_ms = v; } else if (!strcmp(key, "power.forceoff_ms")) { int v; if (meow_settings_get_int("power.forceoff_ms", &v) == 0) power_forceoff_ms = v; }
} }
/* ---- 通知 dump 回调占位(通知#6 转储 TF 卡;接线/占位,不做 TF 写)----
* meow_notif 在 TTL 到点(meow_notif_tick,§8 10s)调用此回调归档一条通知,随后释放槽。
* 基座只把回调挂上来(见 settings_load 的 meow_notif_set_dump_cb);真实 TF 落盘
* (按 NOTIFICATION_SYSTEM.md §8 / TF_CARD_LAYOUT.md 写 <卡>/MeowOS/notify/...)由后续
* 通知代理在回调体内实现。注意 meow_notif.c 的 archive_one 自身已 log 一行 "dump: ...",
* 故本回调保持轻量(避免重复刷屏),落盘逻辑填在下方 TODO 处。 */
static void notif_dump_tf_placeholder(const meow_notif_t *n)
{
(void)n;
/* TODO(通知代理, M2c):在此写 TF 卡 <MeowOS>/notify/<date>/<id>.json 等;
* 基座不实现,仅保留挂接点(触发时机=meow_notif_tick 内 10s 到点/止机转储)。 */
}
/* 启动加载(M2.11/M2.24):统一由 meow_settings 后端读 settings.conf; /* 启动加载(M2.11/M2.24):统一由 meow_settings 后端读 settings.conf;
* 此处仅注册监听(一次性)+ 把 backend 当前值同步到缓存全局。重载(应用期 * 此处仅注册监听(一次性)+ 把 backend 当前值同步到缓存全局。重载(应用期
* watchdog 可能改音量)等价于 meow_settings_init() + 手动同步。 */ * watchdog 可能改音量)等价于 meow_settings_init() + 手动同步。 */
@@ -2271,6 +2342,7 @@ static void settings_load(void)
{ {
meow_settings_init(); /* 幂等:注册默认 + 读 settings.conf */ meow_settings_init(); /* 幂等:注册默认 + 读 settings.conf */
meow_notif_init(); /* M2a:通知存储初始化(幂等,读 notify.* 配额) */ meow_notif_init(); /* M2a:通知存储初始化(幂等,读 notify.* 配额) */
meow_notif_set_dump_cb(notif_dump_tf_placeholder); /* 基座(通知#6):挂 dump 回调(TF 转储占位,M2c 落盘由后续代理实现) */
if (!settings_cache_registered) { if (!settings_cache_registered) {
meow_settings_add_listener(settings_sync_cache_listener); meow_settings_add_listener(settings_sync_cache_listener);
settings_cache_registered = 1; settings_cache_registered = 1;
@@ -6732,8 +6804,10 @@ static int launch_app(struct app *a)
char env_app[MEOW_APP_ID_MAX + 16], env_lvl[48]; char env_app[MEOW_APP_ID_MAX + 16], env_lvl[48];
char env_fpng[400], env_fidx[400], env_fapp[96]; char env_fpng[400], env_fidx[400], env_fapp[96];
char env_mf[24]; char env_mf[24];
char env_tier[24];
char env_fs[20];
char env_cls[MEOW_FCLS_COUNT][2][400]; char env_cls[MEOW_FCLS_COUNT][2][400];
char *envp[32]; /* PATH,HOME,log_dir,log_app,log_lvl,font 全局(3),多字体(1),class(≤12),data,cache */ char *envp[40]; /* PATH,HOME,log_dir,log_app,log_lvl,font 全局(3),多字体(1),tier(1),class(≤12),fullscreen(1),data,cache */
int ne = 0; int ne = 0;
const char *cp = getenv("PATH"); const char *cp = getenv("PATH");
snprintf(env_path, sizeof env_path, "PATH=%s", cp ? cp : "/usr/bin:/bin"); snprintf(env_path, sizeof env_path, "PATH=%s", cp ? cp : "/usr/bin:/bin");
@@ -6772,8 +6846,13 @@ static int launch_app(struct app *a)
meow_settings_get_bool("display.multi_font", &mf_on); meow_settings_get_bool("display.multi_font", &mf_on);
snprintf(env_mf, sizeof env_mf, "MEOW_MULTI_FONT=%d", mf_on ? 1 : 0); snprintf(env_mf, sizeof env_mf, "MEOW_MULTI_FONT=%d", mf_on ? 1 : 0);
envp[ne++] = env_mf; envp[ne++] = env_mf;
have_global = (meow_font_effective_paths(fpng, sizeof fpng, fidx, sizeof fidx, /* M2.x(基座):字库档 MEOW_FONT_TIER=<0|1|2> —— 按 app.json "fonts" 声明注入;
fam, sizeof fam) == 0); * 0=默认4K常用(未声明/缺省),1=大字库GBK,2=Full。SDK meow_ui_gfx 读入存档,
* 真正按档装载大字库由后续 font 代理实现(见 meow_font_effective_paths_tiered)。 */
snprintf(env_tier, sizeof env_tier, "MEOW_FONT_TIER=%d", a->fonts);
envp[ne++] = env_tier;
have_global = (meow_font_effective_paths_tiered(a->fonts, fpng, sizeof fpng,
fidx, sizeof fidx, fam, sizeof fam) == 0);
if (have_global) { if (have_global) {
snprintf(env_fpng, sizeof env_fpng, "MEOW_FONT_PNG=%s", fpng); snprintf(env_fpng, sizeof env_fpng, "MEOW_FONT_PNG=%s", fpng);
snprintf(env_fidx, sizeof env_fidx, "MEOW_FONT_IDX=%s", fidx); snprintf(env_fidx, sizeof env_fidx, "MEOW_FONT_IDX=%s", fidx);
@@ -6801,6 +6880,14 @@ static int launch_app(struct app *a)
envp[ne++] = env_cls[c][1]; envp[ne++] = env_cls[c][1];
} }
} }
/* 全屏声明:app.json "fullscreen"(app 级)→ 注入 MEOW_FULLSCREEN=1,SDK 端
* meow_ui_init 据此预先设全屏。基座只接 app 级;活动级 route.<id>.fullscreen
* (meow_route.fullscreen)在 M3 活动切换时由后续代理消费(此处暂不下发)。
* 未声明则不注入(SDK 默认非全屏)。 */
if (a->fullscreen) {
snprintf(env_fs, sizeof env_fs, "MEOW_FULLSCREEN=1");
envp[ne++] = env_fs;
}
char pkg[64]; char pkg[64];
if (meow_pkg_name(a->exec, pkg, sizeof pkg) == 0) { if (meow_pkg_name(a->exec, pkg, sizeof pkg) == 0) {
snprintf(env_data, sizeof env_data, "MEOW_DATA_DIR=/usr/data/mnt/sd_0/MeowData/%s/data", pkg); snprintf(env_data, sizeof env_data, "MEOW_DATA_DIR=/usr/data/mnt/sd_0/MeowData/%s/data", pkg);
+39
View File
@@ -175,6 +175,24 @@ void meow_ui_set_fullscreen(int on);
* 全屏/隐藏时返回 0。 */ * 全屏/隐藏时返回 0。 */
int meow_ui_statusbar_height(void); int meow_ui_statusbar_height(void);
/* ---- 状态栏「上收出屏只留提示线」(collapsed,草案/待定案——本批仅加声明)----
* 供全屏调用:把状态栏内容上移出屏顶、只留一条「提示线」高度(可下拉还原)。
* 与 meow_ui_set_fullscreen(1) 的关系:fullscreen 隐藏整条状态栏(沉浸,inset=0);
* collapsed 仍视状态栏为「激活」,左缘返回手势(SWIPE_LEFT)与顶部伸缩提示线
* (meow_ui_swipe_hint)依旧生效,只是其内容上收、仅留提示线——用于「不完全沉浸」,
* 保留一处可下拉拉出状态栏的入口。顶部下滑热区 meow_ui_statusbar_hotzone() 在全屏时缩小。
* 实现位置:meow_ui_gfx.c(基座让 on 生效:true→只画提示线/内容上移);逐帧过渡动画
* (上收/恢复插值)为待定案,后续代理可经 meow_ui_statusbar_animate(ms) 补齐。 */
void meow_ui_statusbar_set_collapsed(int on); /* on=1 上收(内容上移出屏、只留提示线);on=0 恢复 */
int meow_ui_statusbar_collapsed(void); /* 当前是否处于「上收」目标(1=是) */
void meow_ui_statusbar_animate(int ms); /* TBD:上收/恢复过渡时长(ms);占位,后续代理实现插值 */
/* 顶部下滑热区(下拉展开状态栏/通知)高度(px)。
* 非全屏=整条状态栏可视高度;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。
* 返回「可用于起始下拉」的热区高度;由输入层/后续代理据此判定下拉起点。 */
int meow_ui_statusbar_hotzone(void);
#define MEOW_SBAR_HOTZONE_FS 4 /* 全屏时缩小后的顶部热区高度(px,草案值) */
/* ============================================================ */ /* ============================================================ */
/* §7 动画/时间(B1 最小可用) */ /* §7 动画/时间(B1 最小可用) */
/* ============================================================ */ /* ============================================================ */
@@ -309,6 +327,27 @@ int meow_notify_has_active(void); /* 1=尚有活动通知,0=无 *
const char *meow_notify_urgency_str(meow_notify_urgency u); const char *meow_notify_urgency_str(meow_notify_urgency u);
const char *meow_notify_category_str(meow_notify_category cat); const char *meow_notify_category_str(meow_notify_category cat);
/* ============================================================ */
/* §10 字体分层 / 大字库(草案/待定案 —— 本批仅加声明/枚举/宏,不实现) */
/* ============================================================ */
/* 字体「档位」概念:launcher 按 app.json 声明的字库档("fonts":<档>)向 app 注入
* MEOW_FONT_TIER=<0|1|2>;SDK(meow_ui_gfx / 后续 meow_font 大字库加载)据档选库:
* MEOW_FONT_TIER_COMMON=0 默认 4K 常用字库;
* MEOW_FONT_TIER_GBK =1 大字库 GBK;
* MEOW_FONT_TIER_FULL =2 Full(全字符集)。
* 后续 font 域代理据此在 meow_font.c / gen_font.py 实现大字库生成与按档装载。
* 既有 meow_font_* 符号(class_of/has_glyph/load_slot/config_multifont…)保留不动;
* 大字库装载以 set_tier/tier 作为入口,或由后续代理按 meow_font_config_multifont 扩展。 */
#define MEOW_FONT_TIER_COMMON 0
#define MEOW_FONT_TIER_GBK 1
#define MEOW_FONT_TIER_FULL 2
/* 设当前字体档(0/1/2,越界 clamp 到合法值),返回生效档。
* 基座在 meow_ui_gfx.c 定义(只存档 + 触发重载请求);真正按档装载大字库由后续 font 代理实现。 */
int meow_font_set_tier(int tier);
/* 当前字体档(初始 = MEOW_FONT_TIER_COMMON;meow_ui_init 时读 MEOW_FONT_TIER env 对齐)。 */
int meow_font_tier(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+127 -5
View File
@@ -63,6 +63,24 @@ static int g_dirty_full = 1;
static char *g_dump_path = NULL; static char *g_dump_path = NULL;
/* ---- 状态栏「上收出屏只留提示线」(collapsed,基座)----
* g_sbar_collapsed = 目标态(1=上收);g_sbar_collapse_h = 当前实际上收偏移
* (0..MUI_SBAR_H:0=完全展开,MUI_SBAR_H=内容全部上移出屏、仅留提示线)。
* 基座让 meow_ui_statusbar_set_collapsed(on) 立即生效(true→只画提示线/内容上移);
* 过渡(每帧插值)留待后续代理或经 meow_ui_statusbar_animate(ms) 启用的简易线性逼近。 */
#define MUI_SBAR_HINT_H 6 /* 上收状态下顶部提示线高(px) */
static int g_sbar_collapsed = 0; /* 目标:1=上收 */
static int g_sbar_collapse_h = 0; /* 当前上收偏移(0..MUI_SBAR_H) */
static int g_sbar_collapse_anim_ms = 0; /* 过渡时长(0=瞬切);meow_ui_statusbar_animate 设置 */
static int g_sbar_collapse_from_h = 0; /* 本次过渡起点偏移 */
static unsigned long long g_sbar_collapse_start_ms = 0; /* 本次过渡起始(monotonic ms) */
/* ---- 字体档位(MEOW_FONT_TIER,基座存档 + 公共 get/set)----
* launcher 按 app.json "fonts":<档> 注入 MEOW_FONT_TIER=<0|1|2>;SDK 读入并以
* meow_font_set_tier/tier 暴露。真正按档装载大字库(GBK/Full)由后续 font 代理在
* meow_font.c / meow_gfx_load_multifont 挂钩实现——此处仅记录档位不换库。 */
static int g_font_tier = MEOW_FONT_TIER_COMMON;
/* ---- 帧率调度(A-1/A-2:从 launcher M2.16 迁入,刷新率调度归口 SDK)---- /* ---- 帧率调度(A-1/A-2:从 launcher M2.16 迁入,刷新率调度归口 SDK)----
* meow_ui_flush()/present() 的 fps 档位与自适应降档 governor。launcher 不再自带 * meow_ui_flush()/present() 的 fps 档位与自适应降档 governor。launcher 不再自带
* 一套帧率门控;launcher 与 app 统一走这里 → 全局观感/刷新率一致。 * 一套帧率门控;launcher 与 app 统一走这里 → 全局观感/刷新率一致。
@@ -499,6 +517,71 @@ static void dump_ppm(void)
fclose(f); fclose(f);
} }
/* ================= 状态栏「上收出屏只留提示线」(collapsed,基座) ================= */
/* 每帧推进上收/恢复:向目标偏移(collapsed→MUI_SBAR_H、否则→0)线性逼近;
* 无过渡时长(g_sbar_collapse_anim_ms<=0)= 一步到位(基座默认)。动画插值骨架在此,
* 后续代理可换缓动/每帧步进。 */
static void statusbar_collapse_step(void)
{
int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0;
if (g_sbar_collapse_h == tgt) return;
if (g_sbar_collapse_anim_ms <= 0) { g_sbar_collapse_h = tgt; return; }
unsigned long long now = now_ms();
unsigned long long el = now - g_sbar_collapse_start_ms;
int span = g_sbar_collapse_anim_ms;
if ((int)el >= span) { g_sbar_collapse_h = tgt; return; }
int delta = tgt - g_sbar_collapse_from_h;
g_sbar_collapse_h = g_sbar_collapse_from_h +
(int)((long long)delta * (long long)el / (long long)span);
}
/* 上收中是否需要逐帧重画(动画未达目标位置时置位,强制每帧推进)。 */
static int statusbar_collapse_in_transition(void)
{
int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0;
return g_sbar_collapse_h != tgt;
}
/* 上收状态栏:内容整体上移出屏(不画时钟/电量),仅在顶部画一条「提示线」。
* 与 meow_ui_swipe_hint(左滑返回进度条,5px,动态)不同:此线是「状态栏可下拉还原」的
* 常驻提示(主题强调色下边缘)。原状态栏整块清回主题背景,避免残影(app inset 之内)。 */
static void statusbar_collapsed_render(int h)
{
(void)h;
const meow_theme *th = meow_theme_get();
int w = meow_ui_width();
/* h 越大内容越贴顶;基座:折叠态整块清底 + 顶部提示线(提示线下缘=主题 accent) */
meow_ui_fill_rect(0, 0, w, MUI_SBAR_H, th->bg);
meow_ui_fill_rect(0, MUI_SBAR_HINT_H - 1, w, 1, th->accent);
}
/* ---- 公共 API(基座实现;动画插值骨架见 statusbar_collapse_step)---- */
void meow_ui_statusbar_set_collapsed(int on)
{
int tgt = on ? 1 : 0;
if (tgt == g_sbar_collapsed) return;
g_sbar_collapsed = tgt;
g_sbar_collapse_from_h = g_sbar_collapse_h;
g_sbar_collapse_start_ms = now_ms();
dirty_full(); /* 顶栏显隐/上收变化 → 全帧重画 */
}
int meow_ui_statusbar_collapsed(void) { return g_sbar_collapsed; }
void meow_ui_statusbar_animate(int ms)
{
g_sbar_collapse_anim_ms = (ms > 0) ? ms : 0; /* 0=瞬切(基座默认) */
}
int meow_ui_statusbar_hotzone(void)
{
/* 非全屏=整条状态栏可视;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。
* 上收(collapsed、非全屏)时热区=提示线下缘高度。全屏态由 meow_ui_sys 维护。 */
if (meow_ui_sys_statusbar_active())
return g_sbar_collapsed ? MUI_SBAR_HINT_H : MUI_SBAR_H;
return MEOW_SBAR_HOTZONE_FS;
}
/* 顶部状态栏条是否被本次脏区触及(含整帧):触及或数据已变(force)才需要重画顶条。 */ /* 顶部状态栏条是否被本次脏区触及(含整帧):触及或数据已变(force)才需要重画顶条。 */
static int statusbar_touched(void) static int statusbar_touched(void)
{ {
@@ -519,13 +602,20 @@ static void present(void)
if (!g_refresh_always_max) { if (!g_refresh_always_max) {
if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return; if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return;
} }
/* 全局状态栏(A-0):上屏前把状态栏画进 back_buf 顶部 MUI_SBAR_H 行(z 最顶)。 /* 全局状态栏(A-0)+ 上收(collapsed)基座:上屏前把状态栏画进 back_buf 顶部 MUI_SBAR_H 行。
* 全屏/隐藏时 active()==0 不画(沉浸),app 内容可占满整屏。触发条件: * 全屏/隐藏时 active()==0 不画(沉浸),app 内容可占满整屏。触发条件:
* 数据已变(1s 轻刷/显隐切换) 或 顶条被 app 内容触及(含整帧清+绘,未 inset 时被顶条覆盖)。 * 数据已变(1s 轻刷/显隐切换) / 顶条被 app 内容触及 / 上收动画未到位(逐帧推进)。
* 无需每帧重画——app 局部刷新不触及顶条时,back_buf/screen 顶条保持不变,省一次 blit。 */ * 展开态仍走 meow_ui_sys_statusbar_render()(meow_ui_sys.c 不改,留给后续代理);
* 上收折叠态(collapse_h>=MUI_SBAR_H)由基座只画提示线(内容上移出屏)。 */
if (meow_ui_sys_statusbar_active() && if (meow_ui_sys_statusbar_active() &&
(meow_ui_sys_statusbar_force() || statusbar_touched())) (meow_ui_sys_statusbar_force() || statusbar_touched() ||
meow_ui_sys_statusbar_render(); statusbar_collapse_in_transition())) {
statusbar_collapse_step();
if (g_sbar_collapse_h >= MUI_SBAR_H)
statusbar_collapsed_render(g_sbar_collapse_h);
else
meow_ui_sys_statusbar_render();
}
if (g_fb_mem) { if (g_fb_mem) {
ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */ ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */
unsigned long long tbg = now_ms(); unsigned long long tbg = now_ms();
@@ -575,6 +665,23 @@ unsigned long long meow_ui_frames_presented(void) { return g_frames_presented; }
int meow_gfx_is_device(void) { return g_fb_mem != NULL; } int meow_gfx_is_device(void) { return g_fb_mem != NULL; }
int meow_gfx_mfont_ready(void) { return g_mfont_ready; } int meow_gfx_mfont_ready(void) { return g_mfont_ready; }
/* ---- 字体档位(公共 API,基座定义)----
* 注意:meow_font_tier()/meow_font_set_tier() 已在 meow_ui_gfx.c 定义(符号唯一),
* 后续 font 代理勿在 meow_font.c 重复定义;可在 meow_font.c 实现「按档装载大字库」并在
* meow_font_set_tier 的钩子处唤起(见下)。 */
int meow_font_tier(void) { return g_font_tier; }
int meow_font_set_tier(int tier)
{
if (tier < MEOW_FONT_TIER_COMMON) tier = MEOW_FONT_TIER_COMMON;
if (tier > MEOW_FONT_TIER_FULL) tier = MEOW_FONT_TIER_FULL;
if (tier == g_font_tier) return g_font_tier;
g_font_tier = tier;
/* TBD(基座):真正按档装载大字库(meow_font_load_tier 或 config_multifont 扩展)由
* 后续 font 代理在 meow_font.c 实现;此处仅记录档位 + 标记重载(下一轮 load 读 tier)。 */
return g_font_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)
@@ -584,6 +691,16 @@ int meow_gfx_mfont_ready(void) { return g_mfont_ready; }
* 全失败 → g_mfont_ready=0,text_draw 回退 5x7 点阵(系统不死)。 */ * 全失败 → g_mfont_ready=0,text_draw 回退 5x7 点阵(系统不死)。 */
static int load_font(void) static int load_font(void)
{ {
/* 字体档位(MEOW_FONT_TIER env,由 launcher 按 app.json "fonts" 注入):
* 0=默认4K常用 1=大字库GBK 2=Full。基座只读入存档 + 对齐公共 get/set;
* 真正按档装载大字库(GBK/Full 的字库路径/面)由后续 font 代理在 meow_font.c /
* meow_gfx_load_multifont 挂钩实现——此处仍先装载默认(tier0)字库,档位仅记录。 */
const char *etr = getenv("MEOW_FONT_TIER");
if (etr && etr[0]) {
g_font_tier = atoi(etr);
if (g_font_tier < MEOW_FONT_TIER_COMMON) g_font_tier = MEOW_FONT_TIER_COMMON;
if (g_font_tier > MEOW_FONT_TIER_FULL) g_font_tier = MEOW_FONT_TIER_FULL;
}
const char *ep = getenv("MEOW_FONT_PNG"); const char *ep = getenv("MEOW_FONT_PNG");
const char *ei = getenv("MEOW_FONT_IDX"); const char *ei = getenv("MEOW_FONT_IDX");
if (ep && ep[0] && ei && ei[0] && meow_font_load(ep, ei) == 0) return 1; if (ep && ep[0] && ei && ei[0] && meow_font_load(ep, ei) == 0) return 1;
@@ -677,6 +794,11 @@ int meow_ui_init(const meow_ui_opts *opts)
g_init_ok = 1; g_init_ok = 1;
dirty_full(); dirty_full();
meow_ui_sys_init(); /* A-0:系统状态读取器首刷(电量/WiFi/蓝牙/音量),状态栏就绪 */ meow_ui_sys_init(); /* A-0:系统状态读取器首刷(电量/WiFi/蓝牙/音量),状态栏就绪 */
/* 全屏预置(基座):launcher 按 app.json "fullscreen" 注入 MEOW_FULLSCREEN=1,SDK 据此
* 预先设全屏(隐藏整条状态栏/沉浸,statusbar_active()==0、statusbar_height()==0)。
* 未注入/非"1"=非全屏(默认)。放在 sys_init 后使首帧即为全屏态。 */
const char *efs = getenv("MEOW_FULLSCREEN");
if (efs && efs[0] == '1') meow_ui_set_fullscreen(1);
return 0; return 0;
} }