Compare commits

...
7 Commits
Author SHA1 Message Date
AskaEth 4d0a8450e9 fix(fw): M6.36 收编全面MD3对齐+批①叠加/列表外距/后台图标/后台圆角卡+重编meow_ui.a重链四App — 推卡
- meow_ui.a 重编 d02c0f57(0warning): MD3对齐(meow_ui_widget/widgets:容器/胶囊/shape)+批①叠加共享判定+meow_img_pixels+meow_ui_audio
- launcher 0c4f3c10: MD3全面对齐(subpage/settings/bg/MENU列表圆角卡8px外距/按钮胶囊/状态层/shape)+批①叠加/穿透根治(7处转场)+顶栏后台按钮图标+后台管理页圆角卡
- 四App热更: ncm df149a9c / hello f3ac1fc0 / files f37d66c7 / reader b6cbc050(reader四格式冒烟全过)
- 固件 r3proii_m6_36.upt 91ccf411(0.8.36, rootfs 38715392<=45088768, unpack 0 diff)
- netmgrd 9c345b2e/audiod 9f47c8fd/meow_audio 6864ec38 未变; amsd 57e19cde 已M6.35收编
2026-09-04 12:00:59 +08:00
AskaEth b712088daa style(md3): 全面MD3对齐P0-P3 — P0扁平行列表容器化(meow_listview_set_container容器模式:surfaceContainer圆角卡12dp+tonal状态层+去分隔线;sub_page/settings_page/bg_page圆角容器) P1按钮胶囊化+shape令牌(extrasmall/small/medium/large/full) P2状态层补齐 P3子控件(Switch off-track/NavBar pill64×32/Checkbox24/对话框28);宿主test_meow_md3等全过,0warning;meow_ui.a变M6.36需重编+四App重链 2026-09-04 11:49:59 +08:00
AskaEth 3fea237306 docs: 同步至 M6.35/0.8.35(App后台常驻框架+批③后台管理页+面板拆分合并页+MD3对齐在跑+third_party/md-material-web参考+redline纪律;不提网易云明面) 2026-09-04 11:40:00 +08:00
AskaEth 2db87b2c5c style(bt): 后台管理页行改MD3圆角卡+8px外距(bgm_row_card_y/h,卡高48行距56上下对称,删除分隔线,按压限卡内,按钮相对卡定位恒等)+test_bg_row_card PASS,0warning 2026-09-04 11:27:07 +08:00
AskaEth 7a276f609e fix(bt): 批①叠加/穿透根治 — /tmp/meow_notif_active从布尔标志改为内容=前台归属app_id(App用MEOW_LOG_APP比较:前台照常上屏收输入/后台常驻自抑+排空输入/空=全抑制),SDK present+input共用共享判定meow_ui_overlay_suppressed_active,launcher 7处转场改写;宿主test_overlay_suppress(T2c)/notif_gesture/overlay_gate/bg全过,0warning;meow_ui.a变M6.36需重编+重链四App 2026-09-04 04:05:41 +08:00
AskaEth 6bab304660 style(launcher): 主界面列表布局(home_layout=list)圆角卡加8px外距(MENU_LIST_ROW_GAP+menu_row_card_y/h卡上下对称内缩行距不变) — 圆角列表项紧贴修复;其余扁平行(Material标准)未动;test_menu_row_gap PASS,0warning,不动meow_ui.a 2026-09-04 03:53:09 +08:00
AskaEth 491db5069c style(launcher): 主界面顶栏'后台'按钮图标美化 — 去方形primary底改surface_container柔和圆角+矢量双卡层叠图标(后台应用堆叠语义)+小字'后台';命中矩形/入口不变,0warning 2026-09-04 03:51:30 +08:00
19 changed files with 726 additions and 159 deletions

No files matched your search

+8 -4
View File
@@ -3,7 +3,7 @@
> 把 HiBy R3 Pro II 便携播放器(君正 X1600,MIPS32r2 / mipsel / musl 静态软浮点,480×720,无 GPU 软渲染)变成一个可扩展的「第二系统 + 应用框架」。
> MeowOS 与官方 HibyOS 共存于同一台设备:开机由引导选单二选一;启动后 MeowOS 作为主控层,提供自己的桌面 UI、应用框架、服务管理与系统组件。
>
> 版本:**M6.32(0.8.32,已自刷验收)**。M6.19→M6.32 连续十三版推卡自刷验收——**M6.19**:ncm 后台化 UI 叠层/点击穿透+再进 ncm 桌面叠层+播放页新旧 UI 叠层抢刷脏区(软自抑帧移交不完整,恢复边沿强制整帧重画)+PIN 亮屏亮度跳+通知中心封面显歌单封面;**M6.20**:播放页右滑返回+顶部下滑通知中心+状态栏时钟 1s;**M6.21-22**:播放页返回修复(`np_exit` 退出外层主循环)+方向感知手势;**M6.23**:watchdog 亮屏亮度按用户设置恢复;**M6.24**:App 内按电源键锁屏时序;**M6.25**:bootmenu 开机菜单亮度方案 A;**M6.26**:播放页三键命中+媒体卡片体 FOCUS;**M6.27**:netmgrd 流量统计收编(pub_bytes/去 lo)+面板公网流量卡;**M6.28**:收编桌面 Now Playing 双页歌词页+歌词数据源+蓝牙 P1(netmgrd BT_CTL/bt_page);**M6.29**:播放页任意右滑返回桌面;**M6.30**:收编批5 取色+蓝牙 P2(audiod glibc-dynamic)+面板蓝牙卡;**M6.31**:桌面歌词页封面修复+alsa.conf DEV 自动识别;**M6.32**:重编 meow_ui.a+重链四 App(播放页进度条取色+ncm 列表 gap)+面板透传。M6.10→M6.18:M7 launcher 重写第 1-5 期(M6.10-14:虚拟化宫格/设置页卡顿修复/OOM 通用化/自动切歌根治)+(M6.15)meowpanel jv_free 泄漏修复+(M6.16)Hello demo 滚动卡顿修复+(M6.17)歌词 UI+(M6.18)媒体通知封面+封面分辨率。M6.1→M6.9 全链自刷验收——**M6.1**:netmgrd 期3/powermgr 期2/面板 PIN 鉴权+首页仪表盘/WiFi 录入 UI/服务名统一 meow_*;**M6.2**:锁屏 PIN 全量(`system.pin` 同源面板)+VOL_DOWN×3 框架门控+显示名 name[49](amsd/launcher UTF-8 整序列截断);**M6.3**:设置页 5s 修复(309 网络异步步骤机+跳过字体 sha 校验)+310 锁屏三连(输入穿透/解锁状态栏消失/App 死亡残影);**M6.4**:310 二期 **EVIOCGRAB 锁屏独占触控**(未冻结 App 锁屏输入隔离)+主界面应用名截断;**M6.5**:audiod 315 孤儿会话两段式回收(客户端崩溃/OOM 杀/正常退出后会话完整回收,STATE 回干净无流基线);**M6.6**:锁屏 310 三期——App 内手动息屏也触发锁屏 + 锁屏脏区全屏不透明底 + 300ms 重铺;**M6.7**:锁屏页自睡(空闲超时自动灭屏,294 遗留补;0.8.7 补刷吸收 fw_content 事故修复——拆分 watchdog+meow_audio 两行);**M6.8**:310 四轮 锁屏自睡 min(系统息屏,30s);**M6.9**:netmgrd 期4(SDK 自愿通道/forget/RSSI/AAAA 过滤/iptables 评估)+powermgr 期3(policy/标定工具)固化 + 327 M7.0b launcher 试点(电源菜单/Now Playing 控件化+主题薄壳+i18n 抽串);历史:M2.69→M2.96 全链路自刷验收:控件库/主界面 MD3 双布局/M3 导航栈/M5 通知跳转/媒体会话/Now Playing/SDK nav+media 封装/字库启动竞态修复**/M2.84-88**:installed 图标修复+残帧pan加固/物理键EVIOCGKEY双源/签名鉴权 2B/meow_route 音频路由/amsd 二期(picker 契约)/流媒体客户端二期(封面·歌词后台·大歌单分页)/MeowPanel 实时画面+遥控+通知实时化/中文拼音 IME**/M2.89**:开机选单抢屏回归修复+watchdog SIGUSR1 不误杀框架 App+amsd 失联修复+顶部下滑手势分流**/M2.90**:**meow_audiod 独占音频守护**(AUDIOD_DESIGN,meow_route 并入退役)+MIUIx 顶级主题+流媒体客户端文案中文化+面板字体库 tar.gz 导入导出**/M2.91-96**:audiod 开机武装 hbc3000 FPGA(根治 PCM 启动内核死锁)+svc_power 充电态权威链(mp2731)+amsd 注册自愈(`am rescan`)/Now Playing 死循环修复+状态栏充电闪电+插孔消抖(三路)/audiod 长流护栏+ALSA 几何收窄 period=1024×4(根治长流停摆)+watchdog 音量存盘毁设置修复/音量反转四路统一(CS43198 寄存器=衰减量,`dac=255-pct*255/100`)+炸耳安全修复(dac_vol_mute 写 255 真静音);**M6.0-6.1**:网络管理服务 **meow_netmgrd** 期1-3(接口统一层+连接普查三实体+DNS 代理+策略引擎防自锁+WiFi 录入 UI)+功耗管理服务 **meow_powermgr** 期0-2(查看/功耗分解 top/历史/事件/mp2731 实测充电功率)+MeowPanel 增强(4 位 PIN 鉴权+首页仪表盘/电源页/进程服务页)+服务名统一 meow_*(audiod/mdnsd→meow_audiod/meow_mdns))。版本号单点 = 板上 `/etc/meowos_version`,见「构建」。本仓库是一个**自带 SDK、包管理、媒体会话、独占音频服务、网页面板、流媒体客户端的完整第三方系统**。
> 版本:**M6.35(0.8.35,已上卡)**。M6.19→M6.35 连续十七版推卡自刷验收——**M6.19**:ncm 后台化 UI 叠层/点击穿透+再进 ncm 桌面叠层+播放页新旧 UI 叠层抢刷脏区(软自抑帧移交不完整,恢复边沿强制整帧重画)+PIN 亮屏亮度跳+通知中心封面显歌单封面;**M6.20**:播放页右滑返回+顶部下滑通知中心+状态栏时钟 1s;**M6.21-22**:播放页返回修复(`np_exit` 退出外层主循环)+方向感知手势;**M6.23**:watchdog 亮屏亮度按用户设置恢复;**M6.24**:App 内按电源键锁屏时序;**M6.25**:bootmenu 开机菜单亮度方案 A;**M6.26**:播放页三键命中+媒体卡片体 FOCUS;**M6.27**:netmgrd 流量统计收编(pub_bytes/去 lo)+面板公网流量卡;**M6.28**:收编桌面 Now Playing 双页歌词页+歌词数据源+蓝牙 P1(netmgrd BT_CTL/bt_page);**M6.29**:播放页任意右滑返回桌面;**M6.30**:收编批5 取色+蓝牙 P2(audiod glibc-dynamic)+面板蓝牙卡;**M6.31**:桌面歌词页封面修复+alsa.conf DEV 自动识别;**M6.32**:重编 meow_ui.a+重链四 App(播放页进度条取色+ncm 列表 gap)+面板透传;**M6.33**:收编 launcher 播放页占位符移除(已有独立歌词页)+封面「空→复原」恢复修复(test_np_cover 14 项 PASS);**M6.34**:收编**批①后台常驻+宽限期**(全 App 入后台表+30min `MEOW_BG_GRACE_MS` 宽限超时回收+声明常驻/媒体活跃无限期保活+槽满 LRU+app.json `background_keepalive` 三权限位+amsd `EV BG/FG` 上报)+**audiod A3/DSD**(24/32bit 高清 byte-exact+独立 `AUDIO_FMT_DOP` DoP DSD 透传+音量收编 v2 双组写)+**meow_img_pixels 访问器上收**+**面板拆分+屏幕·遥控合并页**(前端三层+左屏右遥控 MD3 SVG 矢量按钮);**M6.35**:收编**批③后台管理页**(桌面顶栏「后台」入口+`bg_page` 后台列表+每行「杀」按钮+顶栏「一键清」,`!framework` 门控不杀框架)+**amsd FG/BG 收编**(`57e19cde`)+**i18n settings/lock 补抽**。M6.35 之后工作区已提交(暂未打包,并入 M6.36):批①叠加/穿透根治(`/tmp/meow_notif_active` 内容=前台归属 app_id+SDK present/input 共享判定+launcher 7 处转场)、主界面顶栏后台按钮图标美化、主界面列表圆角卡 8px 外距、后台管理页行 MD3 圆角卡;**MD3 对齐在跑**(agent-435,参考 `third_party/md-material-web` 最新 M3)。M6.10→M6.18:M7 launcher 重写第 1-5 期(M6.10-14:虚拟化宫格/设置页卡顿修复/OOM 通用化/自动切歌根治)+(M6.15)meowpanel jv_free 泄漏修复+(M6.16)Hello demo 滚动卡顿修复+(M6.17)歌词 UI+(M6.18)媒体通知封面+封面分辨率。M6.1→M6.9 全链自刷验收——**M6.1**:netmgrd 期3/powermgr 期2/面板 PIN 鉴权+首页仪表盘/WiFi 录入 UI/服务名统一 meow_*;**M6.2**:锁屏 PIN 全量(`system.pin` 同源面板)+VOL_DOWN×3 框架门控+显示名 name[49](amsd/launcher UTF-8 整序列截断);**M6.3**:设置页 5s 修复(309 网络异步步骤机+跳过字体 sha 校验)+310 锁屏三连(输入穿透/解锁状态栏消失/App 死亡残影);**M6.4**:310 二期 **EVIOCGRAB 锁屏独占触控**(未冻结 App 锁屏输入隔离)+主界面应用名截断;**M6.5**:audiod 315 孤儿会话两段式回收(客户端崩溃/OOM 杀/正常退出后会话完整回收,STATE 回干净无流基线);**M6.6**:锁屏 310 三期——App 内手动息屏也触发锁屏 + 锁屏脏区全屏不透明底 + 300ms 重铺;**M6.7**:锁屏页自睡(空闲超时自动灭屏,294 遗留补;0.8.7 补刷吸收 fw_content 事故修复——拆分 watchdog+meow_audio 两行);**M6.8**:310 四轮 锁屏自睡 min(系统息屏,30s);**M6.9**:netmgrd 期4(SDK 自愿通道/forget/RSSI/AAAA 过滤/iptables 评估)+powermgr 期3(policy/标定工具)固化 + 327 M7.0b launcher 试点(电源菜单/Now Playing 控件化+主题薄壳+i18n 抽串);历史:M2.69→M2.96 全链路自刷验收:控件库/主界面 MD3 双布局/M3 导航栈/M5 通知跳转/媒体会话/Now Playing/SDK nav+media 封装/字库启动竞态修复**/M2.84-88**:installed 图标修复+残帧pan加固/物理键EVIOCGKEY双源/签名鉴权 2B/meow_route 音频路由/amsd 二期(picker 契约)/流媒体客户端二期(封面·歌词后台·大歌单分页)/MeowPanel 实时画面+遥控+通知实时化/中文拼音 IME**/M2.89**:开机选单抢屏回归修复+watchdog SIGUSR1 不误杀框架 App+amsd 失联修复+顶部下滑手势分流**/M2.90**:**meow_audiod 独占音频守护**(AUDIOD_DESIGN,meow_route 并入退役)+MIUIx 顶级主题+流媒体客户端文案中文化+面板字体库 tar.gz 导入导出**/M2.91-96**:audiod 开机武装 hbc3000 FPGA(根治 PCM 启动内核死锁)+svc_power 充电态权威链(mp2731)+amsd 注册自愈(`am rescan`)/Now Line truncated
>
> 许可证:Apache-2.0(见 [`LICENSE`](LICENSE))。本仓库为非官方第三方项目。
> 主仓**公开可克隆**:`https://git.yeij.top/AskaEth/MeowOS-For-R3P2.git`(维护者推送走
@@ -26,6 +26,7 @@
- **应用框架**:一套 C 应用 SDK(`meow_ui`),第三方 App 用统一控件 / 输入 / 主题 / 字体 / 通知 API 开发,无需自读帧缓冲、自读输入、自管返回——把每个 App 都要踩一遍的坑收口到框架里。
- **系统组件**:状态栏、通知中心、电源菜单、全局左缘返回手势、主界面(应用列表,**MD3/MIUIx 顶级主题×明暗**,卡片宫格/列表双布局)、设置页、服务管理器、mDNS 广播;系统进程总监 `meow_sysui`;专项子服务 `meow_svc_perf`(性能/系统只读聚合,含真实 network)与 `meow_svc_power`(关机/重启/升级/回 HiBy,含升级写保护 + dac 真实音量 + 读级鉴权放宽);**独占音频守护 `meow_audiod`**(M2.90:App 经 SDK `meow_audio_*` 喂 PCM,守护独占 `hw:0,0`——DAC 原生率跟随无 SRC、格式归一、4.4/3.5 路由自动跟随插孔、抢占+EOF 回收、CLI status/tone/route/vol,规范 `devdocs/plan/AUDIOD_DESIGN.md`);系统级崩溃捕获 `meow_crash` + App 崩溃直报;.mpk **Ed25519 签名验签** `meow_verify`(见 §2.9);**网络管理服务 `meow_netmgrd`**(M6.0 期1 / M6.1 期3:接口统一层/连接普查三实体/DNS 代理/策略引擎防自锁,HibyOS 只读)与**功耗管理服务 `meow_powermgr`**(M6.0 期1 / M6.1 期2:功耗查看/分解/历史/事件,HibyOS 只读);MeowPanel 外部管理面板(HTTP/JSON 后端 + Web 前端,支持可选密码鉴权 + 4 位 PIN 访问级鉴权 + 首页仪表盘 + 字体库 tar.gz 导入导出,见 §2.10)。
- **媒体会话 / Now Playing**:`MSG_META`/`MSG_PLAYCTL`(0x72/0x73)媒体会话(最近活跃源,30s 超时回收)+ 通知中心**媒体卡片** + 主界面左滑 **Now Playing 大页**(播放页⇄歌词页双页,方向感知手势:播放页右→左进歌词/左缘右滑回桌面、歌词页左→右回播放);媒体键路由到最近活跃媒体会话。
- **App 后台常驻框架(M6.34 起,`devdocs/plan/APP_BACKGROUND_FRAMEWORK.md`)**:App 返回桌面**不立即杀**——`meow_media_app_resident` 按 App 自身会话判活跃 + `meow_bg_should_keep` 三参(keepalive/媒体活跃/声明)+ app.json **`background_keepalive`** 声明 + amsd `EV BG/FG` 上报;**宽限期**`MEOW_BG_GRACE_MS`(30min)全 App 入后台表,未声明超时回收(SIGTERM→SIGKILL 真退序列),声明常驻/媒体活跃**无限期保活**,槽满 LRU;**批③后台管理页**(M6.35,桌面顶栏「后台」入口):列出每个后台常驻 App(名字+CPU/内存+状态)+ 每行「杀」按钮 + 顶栏「一键清」(`!framework` 门控不杀框架 App),数据源=amsd FG/BG + svc_perf 同口径 /proc 采样。批②底部上滑回桌面+home 指示条待做、批④面板后台列表排队。
- **应用生态**:**流媒体客户端 dev.player.ncm**(`core/`:mbedtls+jsmn+qrcodegen+minimp3 ≈438KB;复古 UI:登录二维码/我创建的·我收藏的双页签/搜索/歌单详情/黑胶播放页/设置,登录 803 已通;二期:专辑封面/歌词后台加载/大歌单分页;三期:歌单真分页/真实专辑封面/歌词锁修复/搜索接拼音 IME 中文搜索/设置页(音质 5 档+缓存上限+清理缓存)/缓存 LRU 淘汰接生产路径/文案全中文;三期续:歌曲对象瘦身 1324B→608B+256 首滑动窗口(任意长度歌单)/封面与缓冲 UX 修复(消除 worker 忙等 100% CPU)/FLAC 无损(dr_flac)+缓存键=歌曲 ID+音质档位/物理播放键 toggle 全链路;2026-09-02 批次:真机四连症状(豆腐/歌词一行/封面加载/音量键 SIGKILL)+返回首页竞态 staging+内存瘦身(封面 8.28MB→0.78MB)+流媒体专项六项(档位保真/封面 4xx 不重试/收藏显隐/缓存命名迁移/topbar 位图图标)+三死因闭环(OOM/误杀/歌词栈溢出)+封面链分诊+风控加固(接口 120s 缓存+游客令牌)+**M6.10 批次**(批B UI/批C 播放后台/338 VIP3 档+多歌手/336 根因——每次 audio_out_open 建短连接致 audiod fd 风暴已修;卡上热更 `bef4b0e1`,内存 40→49.2MB OOM 疑 page cache 待真机取证);**2026-09-03 桌面歌词/取色/蓝牙批**:桌面 Now Playing「播放页⇄歌词页」双页+歌词链路批1-5(SDK `meow_lyric` 通用歌词组件+`meow_lyric_view` 歌词页渲染控件+media 会话歌词数据源 `meow_media_lyric_*`/MSG_META_LYRIC+方向感知手势)+封面双色渐变取色(`meow_img_color`)+ncm 底部 mini 播放栏/播放模式三态+实时播放列表/列表项 8px 外距;**蓝牙 P0/P1/P2**(`aplay -D bluealsa` 真机出声打通/netmgrd BT_CTL 权威收口+launcher 蓝牙子页重做/audiod OUT_BT 输出目标+音量 AVRCP 隔离+alsa.conf DEV 自动识别))。**A2 起音频输出走 `meow_audiod`**(SINK_AUDIOD 默认后端,SDK `meow_audio_*` 客户端,被抢占回调→暂停;旧 ALSA 直开仅 `MEOW_AUDIO_DIRECT=1` 调试);外置**系统文件管理器 os.meow.files**;`meow_ui` SDK 配套结构化列表 `W_LISTVIEW`、通用长按、SDK nav/media 封装、视图框架(可嵌套 view + 事件路由 + 绘制期 clip);App 通知查询/清除 IPC(0x25-27)与**点击通知跳转路由**(route_status)。
- **进程隔离**:系统 UI 渲染进程与 App 进程相互独立,App 卡死不会冻结整个 UI。
- **包管理**:`nyai`(固件内置 `/usr/bin/nyai`)支持 .mpk 安装/升级/导出/列表/索引,走 **version_code 安卓式版本规则**(缺 code 拒装、防降级)+ 签名分级(有签必验、`os.meow.*` 无签拒装、第三方直装告警)+ 导出过滤与体检。
@@ -79,14 +80,15 @@ init (rcS)
### 2.3 launcher —— 系统 UI 渲染宿主
`/usr/bin/meow_launcherd`是 MeowOS 的核心进程,负责渲染**系统级 UI**。**二进制名不变**(`/usr/bin/meow_launcherd`,bootmenu `LAUNCHER_BIN` 指向它),源码 M2.69 起由单文件 `patch_v2/meow_launcherd/meow_launcherd.c`(9778 行)**拆分为多模块**(`patch_v2/meow_launcherd/`,当前含 `main_menu/statusbar/settings_page/power_menu/notif_center/app_launcher/fb_compositor/audio/backlight/net/about/font_theme/keys/util` + 后期新增 `subpage/wifi_page/bt_page`(蓝牙/WiFi 独立子页)、`now_playing`(Now Playing 大页)、`meow_media`(媒体会话,最近活跃源 + 30s 超时回收)+ 共享头 `meow_launcher_internal.h`;`build_launcher.sh` 列模块)——逻辑更清晰、便于并行开发,行为不变。
`/usr/bin/meow_launcherd`是 MeowOS 的核心进程,负责渲染**系统级 UI**。**二进制名不变**(`/usr/bin/meow_launcherd`,bootmenu `LAUNCHER_BIN` 指向它),源码 M2.69 起由单文件 `patch_v2/meow_launcherd/meow_launcherd.c`(9778 行)**拆分为多模块**(`patch_v2/meow_launcherd/`,当前含 `main_menu/statusbar/settings_page/power_menu/notif_center/app_launcher/fb_compositor/audio/backlight/net/about/font_theme/keys/util` + 后期新增 `subpage/wifi_page/bt_page`(蓝牙/WiFi 独立子页)、`now_playing`(Now Playing 大页)、`meow_media`(媒体会话,最近活跃源 + 30s 超时回收)、`bg_page`(批③后台管理页)+ `meow_bgplay`/`meow_bgmgr`(批①后台常驻保活/管理逻辑)+ 共享头 `meow_launcher_internal.h`;`build_launcher.sh` 列模块)——逻辑更清晰、便于并行开发,行为不变。
负责渲染:
- **状态栏(统一系统进程渲染)**:由 launcher 作为**唯一渲染主体**(主界面与 App 前台用**同一个**状态栏实例,App 不画、不做「样式一样的两套实现」);右侧 **MD3 图标 + 变量值**(时钟/电量/WiFi/蓝牙/音量,数据源唯一由 launcher 统一喂/取,两边必然一致);**全屏收合**:状态栏向上收出屏顶、只留提示线,**顶部保留 28px 热区**(仍可下滑通知中心)+ 左缘指示条;随主题 `ui.style` 换图标集。
- 通知中心(顶部下滑展开,launcher 渲染,功能见 §2.6);
- 电源菜单、全局左缘返回手势、全屏提示线;
- 主界面(应用列表)、设置页;
- 主界面(应用列表)、设置页、**后台管理页**(`bg_page`,M6.35:桌面顶栏「后台」入口,列出后台常驻 App + 每行杀 + 一键清);
- **后台常驻框架**(M6.34):App 返回桌面保活 + 宽限期回收(`meow_bgplay`),amsd `EV BG/FG` 上报数据源;
- **锁屏(M6.2 起)**:`lock_screen.c` 4 位 PIN(`system.pin` 同源面板 sha256+salt)+ 息屏唤醒锁 `system.pin_lock_on_wake` + 错 5 锁 5 分钟持久化 + 电源菜单「锁定」(作废一次性绕过标志)+ 设置「安全→锁屏 PIN」页(设/改需旧、清免旧)+ `/tmp/meow_pin_unlock_once` 一次性解锁恢复口;M6.4 起锁屏用 **EVIOCGRAB 独占触控**杜绝输入穿透下层 App。
**渲染与 App 进程隔离**:launcher 是系统进程,App 是独立进程。App 卡死/崩溃只影响该 App,系统 UI(状态栏/返回/通知中心)始终可用。
@@ -195,6 +197,7 @@ init (rcS)
- **面板批2 + 加固(2026-09-02)**:崩溃/日志中心 + 音频路由页 + 音量写经 clamp `vol_max` + 后端补 `reboot_reason`;**JSON 输出 UTF-8 加固**(`sA_str_n` 单遍校验、非法序列换 `?`、IPC 原始 JSON 门禁降级,防任一上游坏字节拖垮整页);**overview 自校验纪律**:`/api/overview` 服务端发出前先自查/严格平衡检查(坏则 dump `/tmp/overview_bad.json` + 500),前端非 JSON 自动回传原始字节到 `/api/debug/report_bad_json`(落 `/tmp/overview_client_bad.txt`);**教训**:后端 `jv_parse` **容忍尾随垃圾**,而浏览器 `JSON.parse` **不容忍**——net 块多写一个 `}` 即「Extra data」破页(batch2 DNS 并入事故),故自校验必须做与浏览器口径对齐的**严格括号/闭合平衡检查**。
- **⚠️ 鉴权默认关**:内网信任模型(`reboot/shutdown/ota/apps/删除/上传` 等危险端点默认无鉴权,**勿把 8080 暴露到不可信网络**);`meow_svc_power` 服务端做 `SO_PEERCRED uid==0` 基础校验。
- **前端**:`patch_v2/meowpanel/web/`(MD3/MIUIx 双主题 + 宝塔式 + 浏览日夜);`/usr/bin/nyai` 装 `.mpk`;launcher 的 `meow_pkg_export` / `nyai export` 导出 tar.gz(busybox tar + gzip,不需 zip)。详见 `patch_v2/meowpanel/README.md`。
- **M6.34 前后端拆分 + 屏幕·遥控合并页(2dc4cad)**:后端 `meowpanel.c`(6372 行)拆为 `src/*.c`(20 个,按 API 域)+ `mp.h`(235 函数原型、20 端点 output byte-identical、0 warning);前端拆 `mp_shared.js`/`mp_pages.js`/`meowpanel.js` 三层;新增**屏幕·遥控合并页**(左=设备实时画面、右=MD3 SVG 矢量按钮无 emoji),替代旧的分离屏幕/遥控页;重编后热更 TF 卡。面板后端热更只写白名单 `MeowOS/opt/MeowPanel/`。
---
@@ -216,6 +219,7 @@ init (rcS)
- **`docs/`(入库)**—— `MEOWOS_SPEC_REF.md` 权威速查总索引。
- **`font/`(入库)**—— 字形系统:`gen/gen_font.py` 生成器、`meow_font.c/.h` 渲染库、`demo/`、`src/`(字体源文件)。
- **`bootlogos/`(入库)**—— 启动 / 关机 logo。
- **`third_party/`(入库)**—— 第三方参考/依赖归拢目录(与 MeowOS 源码 `patch_v2/` 分离、不混入构建):`CNMPlayer`(流媒体客户端功能对照)、`md-material-web`(**最新 Material 3 参考**,MD3 对齐依据,agent-435 按其对所有组件细化)。
- **`LICENSE`** —— Apache-2.0。
- **顶层文档(入库)**—— `APPS.md`、`MEOW_APP_SDK.md`、`FRAMEWORK_DESIGN.md`、`SYSTEM_SETTINGS.md`、`NOTIFICATION_SYSTEM.md`、`TF_CARD_LAYOUT.md`、`DEVELOPMENT_PLAN.md`、`GESTURE_BACK_DESIGN.md`、`KEY_CONTROL_DESIGN.md`、`PERF_SCHEDULER.md`、`MEOWOS_API.md`。
- **本地构建资产(根目录 gitignore;但 `scripts/build/` 的 `README.md`/`build_fw_apps.sh`/`fw_content.conf` 已 `git add -f` 入库)**—— 其余(`fw_apps/` 固件组装产物、官方固件基线 rootfs、交叉工具链、本地开发文档、设备密钥等)不入库。详见「构建」与「部署」。
@@ -309,7 +313,7 @@ patch_v2/hello_app/build_hello.sh # 链 meow_ui.a + libz.a + -lpthread → hel
| `fullscreen` | — | app 级全屏:状态栏**收合至提示线** + 保留左缘返回 / 顶部提示线 / 下滑通知中心(activity 级用 `route.<id>.fullscreen`) |
| `fonts` | — | 字库档位声明 `"full" | "gbk" | "common"`(默认 `common`≈4k) |
其他扩展(launcher 极简解析、不校验 JSON):`icon` / `framework`(导航栈承载)/ `immersive` / `route.<id>`(路由表,`framework=1` 时看门狗返回发 SIGUSR1)/ `awake.<id>`(keep_awake)/ `service.<svc>`。应用数上限 8,扫描深度 1 级。
其他扩展(launcher 极简解析、不校验 JSON):`icon` / `framework`(导航栈承载)/ `immersive` / `route.<id>`(路由表,`framework=1` 时看门狗返回发 SIGUSR1)/ `awake.<id>`(keep_awake)/ `service.<svc>` / **`background_keepalive`**(后台常驻声明,M6.34:`1`=返回桌面**无限期保活**不清;缺省/`0` 走 30min 宽限期回收;播放类 App 若带 `background_audio` 且媒体活跃也无限期保活)。应用数上限 8,扫描深度 1 级。
### 6.2 链接与入口
+4 -4
View File
@@ -1,12 +1,12 @@
# MeowOS 固件内置(板载)二进制/组件清单
> **当前基线:M6.6(0.8.6)**。**本文件由 `scripts/build/fw_content.conf` 生成/手工同步——改固件内容改 conf,别改本文件。**
> **当前基线:M6.35(0.8.35,commit `b23c6d5`,已上卡)**。**本文件由 `scripts/build/fw_content.conf` 生成/手工同步——改固件内容改 conf,别改本文件。**
> 基线 = 官方 squashfs 解包 + 下表组件。打包入口 `scripts/build/build_fw_apps.sh`(见 scripts/build/README.md)。
| 固件内路径 | 仓库源 | 说明 |
|---|---|---|
| `/usr/bin/meow_bootmenu` | `patch_v2/bootmenu/meow_bootmenu` | 引导选单 M2.85(sysui让位 agent-216) |
| `/usr/bin/meow_launcherd` | `patch_v2/meow_launcherd/meow_launcherd` | 主屏 launcher M6.6(M6.4 全量+310 三期:App 内手动息屏也触发锁屏+锁屏脏区全屏不透明底+300ms 重铺) |
| `/usr/bin/meow_launcherd` | `patch_v2/meow_launcherd/meow_launcherd` | 主屏 launcher M6.35(批①后台常驻+宽限期 `f3ae06d/45c6338` + 批③后台管理页按钮化 `211c5a5/5c17422` + i18n settings/lock `c69fb92` + 310 三期锁屏/播放页双页歌词/取色;md5 `9515c9d8`) |
| `/usr/bin/meow_watchdog` | `patch_v2/meow_watchdog/meow_watchdog` | 应用看门狗 M2.97(音量反转 + DS1039 平方锥度 + 插孔消抖) |
| `/usr/bin/meow_svcd` | `patch_v2/meow_svcd/meow_svcd` | 服务管理器 M2.77(+崩溃上报 meow_crash agent-187) |
| `/usr/bin/meow_mdns` | `mips-repo/bins/meow_mdns/bin/meow_mdns` | mDNS 应答器 M2.78(启动竞态+换IP重检 agent-194) |
@@ -16,7 +16,7 @@
| `/usr/bin/meow_verify` | `patch_v2/meow_verify/meow_verify` | II期验签 2B(多公钥+v2签名块 agent-221) |
| `/usr/bin/meow_crash` | `patch_v2/meow_crash/meow_crash` | 系统级崩溃捕获 (agent-187) |
| `/usr/bin/meow_svc_perf` | `patch_v2/meow_svc_perf/meow_svc_perf` | 性能专项服务 M2.75(net_json 补 network 闭合括号,修 /api/system JSON 解析失败) |
| `/usr/bin/meow_audiod` | `patch_v2/meow_audiod/meow_audiod` | 独占音频守护 M6.5(M6.0 全量+315 孤儿会话两段式回收:客户端崩溃/OOM 杀后 STATE 回干净无流基线) |
| `/usr/bin/meow_audiod` | `patch_v2/meow_audiod/meow_audiod` | 独占音频守护 M6.35(M6.0 全量+315 孤儿会话两段式回收+M6.34 A3/DSD:24/32bit 高清 byte-exact+DoP DSD 透传+音量收编 v2 双组写;改 glibc-dynamic 链 libasound;md5 `9f47c8fd`) |
| `/usr/bin/meow_audio` | `patch_v2/meow_audiod/meow_audio` | audiod 用户侧 CLI M2.97(+monitor 实时电平表/文本电平条;status 补 rms/peak) |
| `/usr/bin/meow_powermgr` | `patch_v2/meow_powermgr/meow_powermgr` | 功耗管理专项服务期2 M6.1(期1+history/top/events+mp2731 current_now 实测充电功率+WiFi 未关联档/BT rfkill 细化+系数路径 /data/meow/powermgr/;IPC 0xD0-D7) |
| `/usr/bin/meow_power` | `patch_v2/meow_powermgr/meow_powermgr` | meow_powermgr 的 CLI 形态(同二进制双形态,argv[0]=meow_power 即 CLI:status/estimate/model/history/top/events,policy 期3桩) |
@@ -24,7 +24,7 @@
| `/usr/bin/meow_net` | `patch_v2/meow_netmgrd/meow_netmgrd` | meow_netmgrd 的 CLI 形态(同二进制双形态,argv[0]=meow_net 即 CLI:status/iface/wifi/bt/rfkill/apps/conns/top/dns/policy/help) |
| `/etc/init.d/S70meow_sysui` | `patch_v2/meow_sysui/S70meow_sysui` | sysui 自启 M2.85(launcher.conf enabled=1+迁移) |
| `/etc/init.d/S89meow_svcd` | `patch_v2/meow_svcd/S89meow_svcd` | svcd 自启 M6.2(+amsd.conf 热更 exec→/usr/bin 迁移) |
| `/usr/bin/meow_amsd` | `patch_v2/meow_amsd/meow_amsd` | 活动管理器 M6.2(注册表自愈 3s 快重试+30s 合并重扫+am rescan + busy 识别修复 + 显示名 UTF-8 整序列 name[49] 修面板 overview 非法 UTF-8) |
| `/usr/bin/meow_amsd` | `patch_v2/meow_amsd/meow_amsd` | 活动管理器 M6.35(注册表自愈 3s 快重试+30s 合并重扫+am rescan + busy 识别修复 + 显示名 UTF-8 整序列 name[49] + **批① FG/BG 上报 `EV BG/FG`/handle_bg/handle_fg**(md5 `57e19cde`,替代 M6.34 旧 `026cad52`;amsd FG/BG 供批③后台管理页/`am status` 数据源) |
| `/usr/bin/am` | `patch_v2/meow_amsd/am` | amsd 用户侧CLI(+resolve/picker/rescan) |
| `/etc/init.d/S90meow_bootmenu` | `patch_v2/bootmenu/S90meow_bootmenu` | bootmenu 自启 M2.85(注释同步) |
| `/etc/init.d/S92_03_start_music_player` | `patch_v2/bootmenu/S92_03_start_music_player` | §4.3 兜底 patch(rcS 正常路径被 S90 阻塞不执行) |
+3 -3
View File
@@ -173,7 +173,7 @@
- **功耗管理服务 `meow_powermgr`(期0 调研 + 期1 骨架 M6.0 已落地,期2 M6.1,规范 `devdocs/plan/POWER_MANAGER_DESIGN.md`)**:板载 `/usr/bin/meow_powermgr`,svcd 托管(板载 `services.d/meow_powermgr.conf`,`trigger=boot`;**HibyOS 模式只读降级**——只采样电池,绝不放策略/不碰 wlan0/bt/声卡)。双形态 CLI **`meow_power`**(同二进制 argv[0] 分流)。**期0 事实**(`devdocs/plan/POWER_HW_FACTS.md`):`battery/current_now` **不存在**→实时功率只能 `derived+estimated` 双轨、`cpufreq` **不存在**→省电档无降频点、**背光量程 0-101**(非 255!「亮度跳 100」=满量程、默认 101=满亮的根因)、`axp_battery` 是幻影设备、**mp2731 有 `current_now`**(充电功率可测)、rfkill×4 存在(期3 控制点)、无 thermal/hwmon。**期1**:权威充电态链复用(mp2731-charger/status)+ 经验模型 14 系数(`/data/meow/powermgr/power_model.conf` 可调)+ IPC 0x00D0-D3(QUERY/RESULT/POLICY_GET/POLICY_SET)+ CLI `status/estimate/model`。**期2(M6.1)**:`history`(**30s 环形、4h 不落盘**)+`top`(功耗分解排序,2s)+`events`(充放电/低电/亮灭屏/背光跳变事件流)+模型细化(**WiFi 未关联档**、**BT rfkill/ACL 档**)+ **mp2731 `current_now` 实测充电功率**(`charge_confidence=measured`;放电仍 `estimated`,诚实区分)。系数/状态路径 = **`/data/meow/powermgr/`**(组件持久化目录定案,SPEC_REF §1.1)。IPC 0x00D4-D7(期2 HISTORY/EVENTS)。`[patch_v2/meow_powermgr/]`
- **网络管理服务 `meow_netmgrd`(期1 M6.0 已落地,期2/期3 M6.1,规范 `devdocs/plan/NETMGR_DESIGN.md`)**:板载 `/usr/bin/meow_netmgrd`,svcd 托管(板载 `services.d/meow_netmgrd.conf`,`trigger=boot`);双形态 CLI **`meow_net`**(`status/iface/wifi/bt/rfkill/apps/conns/top/dns/policy/help`)。**HibyOS 模式只读降级**(接口状态可查,绝不下发控制),RSS 预算 ≤1MB。期0 内核事实(`devdocs/plan/NETWORK_HW_FACTS.md`):**无 iptables 用户态/无 cgroup/无 conntrack → 内核级每应用管控不可行**,主杠杆 = **DNS 代理(域名级)+ 连接普查(inet_diag)+ SDK 自愿通道**;**双行 resolv.conf 崩溃零断流**。**期1 = 接口统一层**(iface 枚举+热插拔/WiFi `wpa_cli` 有界收割/BT `hciconfig`+`bluetoothctl`/rfkill/IP 路由 DNS 只读/udhcpc 续租释放仅 MeowOS)。**期2 = 连接普查 + DNS 代理**:2s `/proc/net/{tcp,udp}` 普查(inode→pid→**三实体** `app:<包名>`(查 amsd)/`svc:<服务名>`(查 svcd)/`proc:<comm>` 归因)+ DNS 代理(127.0.0.1:53,双行 resolv.conf+atexit 恢复+5s 自检,真机实测 enabled)+ 事件日志(1min 去重)+ CLI `apps/conns/top/dns`。**期3(M6.1)= 策略引擎**:`/data/meow/netmgrd/net_policy.conf`(组件持久化目录定案,SPEC_REF §1.1)读改写单键+原子写;**防自锁双层**(dropbear/wpa_supplicant/udhcpc/netmgrd/svcd/sysui/init **拒绝置 block** + 策略文件被手工改坏时的加载校验强制复位);**SOCK_DESTROY 真机实测 = no-op(内核 4.4 接受请求但不销毁 socket)→ 连接级管控不可用,仅 DNS 层生效**(`meow_net policy show` 头明示 `enforce=dns_only`);classify_exe 面板白名单 + apps 归因修复 + WiFi `save_config`。日志 `MeowOS/logs/net_<bootts>.log`(无卡降级 /tmp)。`[patch_v2/meow_netmgrd/]`
- **音频硬件事实(sndprobe/tonepcm2 真机实测,2026-08-31)**:播放 PCM = `/dev/snd/pcmC0D0p`(`cs43198-hifi-0`);`pcmC0D1p`=snd-soc-dummy(勿用)。能力:S16_LE/S24_LE、1-2ch、8k-768k、buffer 2048-1048576 帧、period 512-262144。⚠️ **ALSA UAPI 两坑**(流媒体客户端无声根因,勿再踩):①hw_params 必须「全放行」初始化(mask 全 1、interval [0,UINT_MAX],params_any 语义)再收窄——memset 0 的 interval=「只能取 0」求交必空 → HW_PARAMS 恒 EINVAL;②access/format/subformat mask 必须**先清零再单点**——留全 1 会被内核 choose 成 MMAP_INTERLEAVED,ch>1 时 write 恒 EINVAL。**CS43198 音量寄存器=衰减量(官方数据手册 DS1039 实证,用户供)**:PCM_VOLUME_A/B(0x0B/0x0C),0=0dB 满幅最大声、255=-127.5dB≈静音(0.5dB/步;DSD 通道独立 DSD_VOLUME_A/B)。**混音器 L/R Playback Volume 读回恒 0 是死镜像**(官方播放器走 sa_hgl_dma 硬直通不经此控制,HibyOS 对照实测同读 0),勿据此判音量。**音量寄存器=衰减量(0=最响 255=静音,M2.95 真机实测方向)**:四路(launcher/audio.c+audiod+watchdog+SDK vol)统一 `dac=255-pct*255/100`;mute 必须写 255 真静音(M2.96 炸耳修复)。混音器清单:numid=1/2 L/R Vol、3 Mute Output、4 Digital Filter、5 Soft Mute、6 NOS_EN、7 DRE_EN、8 Balance Lineout En、9 Output Port Switch(0 空闲/1 lineout/2 3.5/3 4.4/4 SPDIF;0 危险勿写;**5 = audiod 内部「强制重烧 FPGA」约定值**(M2.91 开机武装同值改写路径),非常规路由值,App/面板勿写)、10 DOP_EN。`[patch_v2/tools/sndprobe.c][tonepcm2.c][APPS §3.7]`
- 后台权限:`MSG_HELLO/MSG_REGISTER.flags` 扩展 `MF_BACKGROUND`/`MF_PLAYBACK`(**已落地 M4**:app.json `background`/`background_audio` 字段 + MSG_REGISTER 校验链;播放中退出转后台保活见 FRAMEWORK_DESIGN §9);`PERF_BACKGROUND`/`PERF_AUDIO_BACKGROUND`(规划)`[FRAMEWORK_DESIGN 附录 A]`
- 后台权限:`MSG_HELLO/MSG_REGISTER.flags` 扩展 `MF_BACKGROUND`/`MF_PLAYBACK`(**已落地 M4**:app.json `background`/`background_audio` 字段 + MSG_REGISTER 校验链;播放中退出转后台保活见 FRAMEWORK_DESIGN §9);`PERF_BACKGROUND`/`PERF_AUDIO_BACKGROUND`(规划)`[FRAMEWORK_DESIGN 附录 A]`。**M6.34+ 追加后台常驻框架**:app.json `background_keepalive`(无限期保活)+ `meow_bg_should_keep` 三参判定 + 宽限期 `MEOW_BG_GRACE_MS`(30min)回收 + amsd `EV BG/FG` 上报(批③后台管理页数据源),设计见 `devdocs/plan/APP_BACKGROUND_FRAMEWORK.md` §7 / `APP_BACKGROUND_KEEPALIVE_P1.md`。
---
@@ -226,7 +226,7 @@
- **⑨ 面板「跟手下拉、半程可回」(launcher 侧,M2.65)**:面板打开/关闭由触发式 180ms 过渡升级为**手指拖动实时跟手**——顶部热区下拉 `progress = 手指位移映射`(复用 `mnp_anim_off`,∈[0,1024],1:1 跟手);松手 `progress≥40%×1024≈410` → 时间轴补到 1024(展开);`<阈值 或 反向拖回` → 回弹到 0 收回(半程取消);关闭同理可跟手。与行左滑(⑦ 横向主导)/列表滚动(竖向溢出)/`meow_edge_back`(左缘)互斥分流。`[NOTIFICATION_SYSTEM §9]`
### 5.4 配置版本单点 / 刷新率 / 息屏
- **现行:M2.96(`/etc/meowos_version` = `0.7.91`,已刷;261 audiod 电平监控已提交、待 M2.97 固件化;M2.95/0.7.90、M2.94/0.7.89、M2.93/0.7.88、M2.92/0.7.87、M2.91/0.7.86、M2.90/0.7.85、M2.89/0.7.84、M2.88/0.7.83、M2.83/0.7.78、M2.82/0.7.77、M2.79/0.7.74、M2.78/0.7.73、M2.77/0.7.72、M2.76/0.7.71、M2.75/0.7.70、M2.74/0.7.69、M2.73/0.7.68、M2.72/0.7.67、M2.71/0.7.66、M2.70/0.7.65、M2.69/0.7.64 …)**;版本单点 = `/etc/meowos_version`(见 §1.1,`build_fw_apps.sh -v` 写入)`[DEV_NOTES #4][fw_content.conf]`
- **现行:M6.35(`/etc/meowos_version` = `0.8.35`,已上卡;M6.34/0.8.34、M6.33/0.8.33、M6.32/0.8.32、M6.31/0.8.31、M6.30/0.8.30、M6.29/0.8.29、M6.28/0.8.28、M6.27/0.8.27、M6.26/0.8.26、M6.25/0.8.25、M6.24/0.8.24、M6.23/0.8.23、M6.22/0.8.22、M6.21/0.8.21、M6.20/0.8.20、M6.19/0.8.19;更早 M6.18-0.8.18 …)**;版本单点 = `/etc/meowos_version`(见 §1.1,`build_fw_apps.sh -v` 写入)`[DEV_NOTES #4][fw_content.conf]`
- 刷新率:`display.refresh_rate` enum 60/30/15(默认 60);`display.refresh_always_max` bool false(关=允许 60→30→15 降档)。governor:连续 12 帧超预算降一档,连续 4 批≈48 帧远低预算升一档 `[SYSTEM_SETTINGS §1][PERF_SCHEDULER][MIGRATION_AC §12.1]`
- 息屏:`display.screen_off_timeout` int 秒 默认 **30**(0=常亮);`display.screen_off_mode` enum `panel`(面板下电+背光0+FBIOPBLANK)/`black`(背光0全黑+双击亮屏) `[SYSTEM_SETTINGS §1]`
- 亮度 clamp:`display.brightness` 0-255 默认 101(clamp ≥ `display.brightness_min`);`display.brightness_min` 默认 **1**(M2.39 由 25 改 1)——**与旧文档 25 冲突见 §A** `[SYSTEM_SETTINGS §1]`
@@ -236,7 +236,7 @@
- `<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]`
- 包管理 **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>`(M4 起服务二进制=App 同目录 `<svc>` 文件)/**`background`/`background_audio`(M4 已落地:无头服务注册权限/后台播放保活)**/`fullscreen`(app 级;`route.<id>.fullscreen`=activity 级;**本次实现**:launcher 启动/切 activity 据声明预设 SDK 全屏,见 §5.1/§3.1/§A-12)/`fonts`(字体档位 `"full"|"gbk"|"common"`,需大字库时声明,见 §5.7.8)/`depends`(**二进制依赖**声明:`"curl,jq:optional"` 或行式 `depends=curl,jq:optional,ffmpeg:required`;comma 分项 `<name>[:<mod>]`,mod=required|req(默认)/optional|opt;launcher 启动前查 `/usr/bin/<name>` 与 `/usr/data/mnt/sd_0/MeowOS/opt/bin/<name>`(X_OK,命一即可),required 缺失阻断启动(「缺依赖」toast)、optional 缺失仅 WARN,**仅启动时检查**;nyai install 同查并提示) `[APPS §2][MEOW_APP_SDK §9][FRAMEWORK_DESIGN §2][源:审计 2026-08-30][源:用户 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>`(M4 起服务二进制=App 同目录 `<svc>` 文件)/**`background`/`background_audio`(M4 已落地:无头服务注册权限/后台播放保活)**/**`background_keepalive`(M6.34 起:后台常驻声明——`1`=返回桌面无限期保活不清;缺省走 30min `MEOW_BG_GRACE_MS` 宽限期回收;与 `background_audio`+媒体活跃可叠加无限期保活)**/`fullscreen`(app 级;`route.<id>.fullscreen`=activity 级;**本次实现**:launcher 启动/切 activity 据声明预设 SDK 全屏,见 §5.1/§3.1/§A-12)/`fonts`(字体档位 `"full"|"gbk"|"common"`,需大字库时声明,见 §5.7.8)/`depends`(**二进制依赖**声明:`"curl,jq:optional"` 或行式 `depends=curl,jq:optional,ffmpeg:required`;comma 分项 `<name>[:<mod>]`,mod=required|req(默认)/optional|opt;launcher 启动前查 `/usr/bin/<name>` 与 `/usr/data/mnt/sd_0/MeowOS/opt/bin/<name>`(X_OK,命一即可),required 缺失阻断启动(「缺依赖」toast)、optional 缺失仅 WARN,**仅启动时检查**;nyai install 同查并提示) `[APPS §2][MEOW_APP_SDK §9][FRAMEWORK_DESIGN §2][源:审计 2026-08-30][源:用户 2026-08-30]`
### 5.6 冷启动充电屏
- 触发(**三者同时**):冷启动(`poweroff` 写 `/data/meow_cold_boot=1`;`reboot` 写 0)+ 在充电(AC online=1 或 battery status=Charging)+(开机瞬间一次性快照,判定后不回头)`[CHARGING_SCREEN §0]`
+55 -28
View File
@@ -1394,6 +1394,22 @@ static void notify_app_crash(const char *pkg, int status)
close(fd);
}
/* 写 §10.5 SDK 自抑标记(/tmp/meow_notif_active)。**内容 = 当前前台屏幕归属者 app_id**;
* 空(传 NULL / "")= 菜单前台 / 叠加层覆盖 / 无前台单元(SDK 判「抑制全部 App」)。
* SDK(meow_ui_gfx.c / meow_ui_input.c 共享判定 meow_ui_overlay_suppressed_active)据内容判
* 本进程是否前台:内容==本进程 app_id → 前台不抑制;内容为空 → 全抑制;内容为其它 app_id
* → 后台常驻/非前台 → 自抑上屏 + 排空输入。
* 批①多 App 后台常驻:取代旧「单标志写 1」——单标志无法区分前台/后台,清标志会连后台 App
* 一起放行(真机「前台 Hello 叠后台 ncm 帧 + 触摸穿透一次交互双响应」的根因)。 */
static void notif_active_set(const char *owner)
{
int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (nfd >= 0) {
if (owner && owner[0]) (void)write(nfd, owner, strlen(owner));
close(nfd);
}
}
/* §10.5:App 前台顶部下滑 → 展开「由 launcher 渲染」的通知中心叠加会话。
* 复用电源菜单对 App 的冻结模板(show_power_menu 的 SIGSTOP/读回/叠加/接管/恢复):
* ① SIGSTOP App → ② 写 /tmp/meow_notif_active(+ /tmp/meow_power_menu_active 让 watchdog 让位)
@@ -1433,9 +1449,9 @@ static int run_app_notif_overlay(struct app *a, pid_t pid)
}
}
/* 让位标记:/tmp/meow_notif_active(§10.5 语义同 powmenu_active)。当前 watchdog 只认
* POWMENU_ACTIVE,故同时写它让 watchdog 对电源长按让位;会话结束统一 unlink。 */
int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (nfd >= 0) { write(nfd, "1", 1); close(nfd); }
* POWMENU_ACTIVE,故同时写它让 watchdog 对电源长按让位;会话结束统一恢复通知中心前台。
* 内容为空=抑制全部 App(叠加层覆盖前台 App + 后台常驻 App 一并自抑)。 */
notif_active_set(""); /* 叠加期:全抑制(面板/锁屏盖在前台 App 上) */
int pfd = open(POWMENU_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (pfd >= 0) { write(pfd, "1", 1); close(pfd); }
@@ -1453,30 +1469,32 @@ static int run_app_notif_overlay(struct app *a, pid_t pid)
int died = g_notif_ctrl_died;
if (died) {
/* App 已死:不 SIGCONT、不恢复死帧;back_buf 交还 run_menu 统一重绘 */
unlink(NOTIF_ACTIVE);
/* App 已死:不 SIGCONT、不恢复死帧;back_buf 交还 run_menu 统一重绘。
* 标记置空(全抑制):菜单前台,后台常驻 App 仍自抑(不再放行帧/输入)。 */
notif_active_set("");
unlink(POWMENU_ACTIVE);
logmsg("notif-overlay: app died during session -> unified exit path");
return 1;
}
/* 恢复 App 帧(run_notif_center 关面板后 back_buf 已还原为背景=App 帧;再拍一次兜底
* 覆盖非动画关闭路径)。媒体保活路径(keep_running):先恢复帧再 unlink 标记——
* App 未被冻结仍在跑,标记多留一拍可让 SDK present 自抑覆盖到恢复帧上屏之后,
* 避免「标记先消失 → App 抢在恢复帧前 present 新帧 → 又被旧帧盖回」的一帧回闪。 */
* 覆盖非动画关闭路径)。媒体保活路径(keep_running):先恢复帧再把标记更新为前台归属
* (a->id)——App 未被冻结仍在跑,标记多留一拍可让 SDK present 自抑覆盖到恢复帧上屏之后,
* 避免「标记先消失 → App 抢在恢复帧前 present 新帧 → 又被旧帧盖回」的一帧回闪。
* 标记翻回 a->id:本 App 恢复前台不抑制,后台常驻 App(如 ncm)随之继续自抑(不叠帧/不穿输入)。 */
pm_capture_app_frame();
meow_ui_flush();
if (!keep_running) {
/* §310 输入穿透定案(与 lock-overlay 同源):App 被 SIGSTOP 冻结过 → 在标记仍存在时
* 先 SIGCONT,让 SDK 输入线程排空冻结期缓冲的 evdev 事件 + 对齐 EVIOCGKEY,再等
* OVERLAY_DRAIN_WINDOW_MS 后 unlink——防关面板瞬间把积累触摸回放成 App 内点击。 */
* OVERLAY_DRAIN_WINDOW_MS 后把标记翻回前台——防关面板瞬间把积累触摸回放成 App 内点击。 */
kill(pid, SIGCONT);
usleep(OVERLAY_DRAIN_WINDOW_MS * 1000);
logmsg("notif-overlay: closed -> SIGCONT app, drain %dms, then unlink mark",
logmsg("notif-overlay: closed -> SIGCONT app, drain %dms, then mark fg",
OVERLAY_DRAIN_WINDOW_MS);
} else {
logmsg("notif-overlay: closed -> app never stopped (media keep-alive), no SIGCONT");
}
unlink(NOTIF_ACTIVE);
notif_active_set(a ? a->id : ""); /* 标记翻回前台归属(非空→本 App 解除自抑) */
unlink(POWMENU_ACTIVE);
return 0;
}
@@ -1504,8 +1522,8 @@ static int run_app_lock_overlay(struct app *a, pid_t pid)
}
}
}
int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (nfd >= 0) { write(nfd, "1", 1); close(nfd); }
/* 标记置空=全抑制(锁屏叠在前台 App + 后台常驻 App 上;解锁后翻回前台归属)。 */
notif_active_set("");
int pfd = open(POWMENU_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (pfd >= 0) { write(pfd, "1", 1); close(pfd); }
@@ -1520,9 +1538,9 @@ static int run_app_lock_overlay(struct app *a, pid_t pid)
g_lock_overlay_pid = 0;
if (lr == LCK_OVERLAY_RET_DIED) {
/* 叠加期 App 已死:不 SIGCONT(死进程)、不恢复死帧;unlink 标记交回 waitpid 循环回收,
* run_menu 统一重绘主屏(含状态栏)。launcher 不崩。 */
unlink(NOTIF_ACTIVE);
/* 叠加期 App 已死:不 SIGCONT(死进程)、不恢复死帧;标记置空(菜单前台,后台常驻 App
* 仍自抑)交回 waitpid 循环回收,run_menu 统一重绘主屏(含状态栏)。launcher 不崩。 */
notif_active_set("");
unlink(POWMENU_ACTIVE);
logmsg("lock-overlay: app pid %d died during lock -> unified exit path", pid);
return 1;
@@ -1532,17 +1550,19 @@ static int run_app_lock_overlay(struct app *a, pid_t pid)
meow_ui_flush();
if (!keep_running) {
/* §310 输入穿透定案(overlay_gate.h):App 被 SIGSTOP 冻结过 → 在 /tmp/meow_notif_active
* 仍未 unlink 时**先 SIGCONT**,让 SDK 输入线程排空冻结期缓冲的 evdev 事件副本 + 对齐
* EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS(> SDK 200ms 节流窗)后 unlink——防解锁瞬间
* 把积累的触摸回放成 App 内点击(锁屏穿透→「启动奇怪界面」→ 状态栏消失的触发源)。 */
* 仍未更新时**先 SIGCONT**,让 SDK 输入线程排空冻结期缓冲的 evdev 事件副本 + 对齐
* EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS(> SDK 200ms 节流窗)后把标记翻回前台归属——
* 防解锁瞬间把积累的触摸回放成 App 内点击(锁屏穿透→「启动奇怪界面」→ 状态栏消失的触发源)。 */
kill(pid, SIGCONT);
usleep(OVERLAY_DRAIN_WINDOW_MS * 1000);
logmsg("lock-overlay: closed (lr=%d) -> SIGCONT app, drain %dms, then unlink mark",
logmsg("lock-overlay: closed (lr=%d) -> SIGCONT app, drain %dms, then mark fg",
lr, OVERLAY_DRAIN_WINDOW_MS);
} else {
logmsg("lock-overlay: closed (lr=%d) -> app never stopped (media keep-alive), no SIGCONT", lr);
}
unlink(NOTIF_ACTIVE);
/* 标记翻回前台归属(a->id):本 App 解除自抑恢复前台;后台常驻 App(如 ncm)随之继续自抑
* (不叠帧 / 不穿输入——修复「锁屏解锁后 ncm 歌单帧叠加在 Hello 上 + 交互双响应」)。 */
notif_active_set(a ? a->id : "");
unlink(POWMENU_ACTIVE);
/* §310 状态栏恢复:解锁后强制 g_sb_mode_last=-1 + 立即重叠加顶条(不等 app 循环 1s 慢刷),
* 消除「解锁回 App 无状态栏」的残留态。overlay_force_sb_resync() = 定案恒真。 */
@@ -1579,9 +1599,9 @@ static int launch_app_chain(struct app *a, const char *chain)
logmsg("launch: %s resume from background (pid=%d)", a->name, (int)pid);
if (chain && chain[0]) logmsg("launch: resume ignores chain=\"%s\"", chain);
if (pick_action[0]) logmsg("launch: resume ignores picker action=%s", pick_action);
/* 回前台:清 §10.5 SDK present 自抑标记(无其他 bg App 且无叠加会话时) */
if (meow_bg_count() == 0 && g_notif_ctrl_pid <= 0)
unlink(NOTIF_ACTIVE);
/* 回前台:标记内容置为前台归属(a->id)——本 App 解除自抑恢复前台;仍驻留后台的其它
* App(如 ncm)随内容≠自身而继续自抑(不叠帧/不穿输入;批①多 App 常驻关键)。 */
notif_active_set(a->id);
drain_input();
meow_nav_begin(a->id, NULL); /* 转后台时导航栈已清;回前台从裸态重建 */
ams_report_fg(a->id); /* 批①:单向 best-effort 上报 amsd 置 AMS_FG */
@@ -1596,6 +1616,10 @@ static int launch_app_chain(struct app *a, const char *chain)
return -1;
}
}
/* 冷启动:标记内容置为前台归属(a->id),让本 App 首帧即不被自抑(后台常驻 App 继续自抑)。
* 旧版此处不写标记:若已有后台常驻 App(ncm)残留「1/空」标记,新前台 App 会被误自抑
* → 主界面点 Hello 渲染卡死(状态栏正常)的根因。 */
notif_active_set(a->id);
meow_nav_begin(a->id, chain); /* M3:冷启动/裸启动都以当前链深初始化导航栈(chain 空→depth 0) */
/* M2.12:启动前准备 MeowData/<pkg>/{data,cache}(幂等;卡不在/失败只 log 不崩)。
@@ -2208,10 +2232,10 @@ static int launch_app_chain(struct app *a, const char *chain)
usleep(50000);
}
}
/* 写 §10.5 SDK 自抑标记:App 进程见 /tmp/meow_notif_active → present 画进
* back_buf 不上屏 + 输入线程排空(=UI 冻结);音频经 audiod socket 天然续播。 */
int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (nfd >= 0) { write(nfd, "1", 1); close(nfd); }
/* 写 §10.5 SDK 自抑标记(内容空=菜单前台全抑制):App 进程见 /tmp/meow_notif_active
* 且内容≠自身 → present 画进 back_buf 不上屏 + 输入线程排空(=UI 冻结,不叠前台/不穿输入);
* 音频经 audiod socket 天然续播。 */
notif_active_set("");
meow_write_app_state(-1);
unlink(MEOW_SBAR_MODE_FILE); /* 顶条模式文件由 App SDK 写;转后台清理复位菜单整条 */
g_sb_mode_last = -1;
@@ -2279,6 +2303,9 @@ static int launch_app_chain(struct app *a, const char *chain)
* 让菜单音量/状态栏与真实 DAC 一致(watchdog 可能已改)。 */
settings_load();
meow_nav_exit(); /* M3:前台应用会话结束,清导航栈 */
/* 回菜单:标记置空(菜单前台全抑制)——若仍驻留后台常驻 App(批①),其继续自抑不叠帧/不穿输入;
* 若已无后台 App,置空标记无害(SDK 无抑制目标)。 */
notif_active_set("");
/* 阶段D附录B:源 App 退出 → 清其媒体会话(如该 App 上报过 META),通知中心/播放页回空态 */
meow_media_clear(a->id);
logmsg("app %s exited status=%d", a->name, WEXITSTATUS(status));
+25 -11
View File
@@ -6,6 +6,8 @@
* 列表(live=1 约 2s 重扫,实时增删):
* - 每后台 App 一行 SUB_ROW:label=名字,sub=「CPU x% · yMB · 状态」,**行最右一个「杀」按钮**
* (sub_page_t.row_draw 行自绘 + row_tap 自定义命中:命中按钮区=kill 该 App;行体不响应)。
* - 行=MD3 圆角卡(meow_gfx_fill_round r=12 surfaceContainer;卡在行高内上下对称内缩
* BGM_CARD_GAP/2,行中心=卡中心,相邻卡 8px 外距——与主界面列表/ ncm 歌单同手法)。
* - 空态「无后台 App」。
* 数据源=amsd FG/BG 注册表(后台常驻集=launcher meow_bg 表,EV BG/FG 上报进 amsd)+ 每进程
* pcpu/rss(meow_svc_perf 同源 /proc 聚合,本页在 launcher 内同口径取)。
@@ -24,6 +26,13 @@
#define BGM_BTN_W 46
#define BGM_BTN_H 34
/* 圆角卡列表项外边距(item margin/gap):相邻卡片间可见空隙 px。SDK listview 行距(=SUB_ITEM_H)
* 不变——卡片在其行高内上下对称内缩 BGM_CARD_GAP/2,故行内内容(按行高垂直居中)仍落卡中心;
* 相邻卡顶间距恒=SUB_ITEM_H=卡高+BGM_CARD_GAP。要求偶数,保证上下对称。 */
#define BGM_CARD_GAP 8
static inline int bgm_row_card_y(int row_top) { return row_top + BGM_CARD_GAP / 2; }
static inline int bgm_row_card_h(int h) { return h - BGM_CARD_GAP; }
typedef struct {
struct app *apps;
int napps;
@@ -177,13 +186,15 @@ static int bgm_rows(void *ctx, sub_row_t *rbuf, int max)
/* kill 通道(bgmgr_kill_all 的函数指针签名:void(*)(const char*)) */
static void bgm_kill_fn(const char *app_id) { meow_bg_terminate(app_id); }
/* ---- 行内杀按钮矩形(绘制与命中共用;行视口矩形 rx,ry,rw,rh → 右侧 46×34 圆角钮)---- */
/* ---- 行内杀按钮矩形(绘制与命中共用;行视口矩形 rx,ry,rw,rh → 右侧 46×34 圆角钮)----
* 卡在行高内上下对称内缩 BGM_CARD_GAP/2(行中心=卡中心);按钮垂直居中于卡内,故其中心
* =卡中心=行中心。命中矩形仍为整行距(row_tap 传整行视口),按钮几何不变 → 卡内缩后命中不破。 */
static void bgm_btn_rect(int rx, int ry, int rw, int rh, int *bx, int *by, int *bw, int *bh)
{
*bw = BGM_BTN_W;
*bh = BGM_BTN_H;
*bx = rx + rw - 12 - BGM_BTN_W;
*by = ry + (rh - BGM_BTN_H) / 2;
*by = bgm_row_card_y(ry) + (bgm_row_card_h(rh) - BGM_BTN_H) / 2;
}
/* ---- 行完全自绘(sub_page_t.row_draw;覆盖默认 renderer)----
@@ -195,29 +206,32 @@ static void bgm_row_draw(void *ctx, int idx, sub_row_t *r,
bgm_ctx_t *b = ctx;
const meow_theme *mt = meow_theme_get();
int i = r->act;
/* MD3 圆角卡:行高内上下对称内缩 BGM_CARD_GAP/2(行中心=卡中心),替代扁平行+底部分隔线。 */
int cy = bgm_row_card_y(y);
int ch = bgm_row_card_h(h);
meow_gfx_fill_round(x, cy, w, ch, 12, mt->surfaceContainer);
if (state & MEOW_LISTVIEW_ST_PRESSED)
meow_gfx_fill_round_alpha(x, cy, w, ch, 12, mt->primary, 26); /* 按压态(实时,限卡内) */
if (i < 0 || i >= b->n) { /* 空态行 */
int th = meow_ui_text_height(18);
meow_ui_text_center(x + w / 2, y + (h - th) / 2,
meow_ui_text_center(x + w / 2, cy + (ch - th) / 2,
r->label ? r->label : meow_str_zh("bgmgr.empty", "无后台 App"),
18, mt->onSurfaceVariant);
return;
}
if (state & MEOW_LISTVIEW_ST_PRESSED)
meow_ui_fill_rect_alpha(x, y, w, h, mt->primary, 26); /* 按压态(实时) */
int th = meow_ui_text_height(20), tsub = meow_ui_text_height(16);
int rx = x + 16; /* 左内边距 */
int ry = y + (h - (th + tsub + 4)) / 2;
int ry = cy + (ch - (th + tsub + 4)) / 2; /* 内容在卡内垂直居中(行中心=卡中心) */
if (r->label) meow_ui_text(rx, ry, r->label, 20, mt->onSurface);
if (r->sub) meow_ui_text(rx, ry + th + 4, r->sub, 16, mt->onSurfaceVariant);
/* 行内杀按钮(圆角框 + 「杀」;error=红=杀) */
/* 行内杀按钮(全形=胶囊(bh/2);「杀」;error=红=杀)——P1 按钮胶囊化 */
int bx, by, bw, bh; bgm_btn_rect(x, y, w, h, &bx, &by, &bw, &bh);
meow_ui_draw_rect_round(bx, by, bw, bh, 10, mt->error);
meow_ui_draw_rect_round(bx, by, bw, bh, bh / 2, mt->error);
int bth = meow_ui_text_height(16);
if (bth > bh) bth = bh;
meow_ui_text_center(bx + bw / 2, by + (bh - bth) / 2, "杀", 16, mt->error);
/* 底部分隔线 */
if (y + h - 1 <= fb_yres)
meow_ui_fill_rect(x, y + h - 1, w, 1, mt->outlineVariant);
}
/* ---- 行内自定义命中(sub_page_t.row_tap;仅按钮区 kill,行体不响应/不误杀)---- */
+23 -6
View File
@@ -96,18 +96,24 @@ static void menu_after_app_exit(int app_ran)
static int menu_title_size(void); /* 前向声明:draw_header_content 用(定义在下方布局帮助块) */
static uint32_t menu_contrast(uint32_t c); /* 前向声明(定义在下方布局帮助块) */
/* 批③:桌面顶栏居右「后台管理」按钮(图标 tile + 字块)。命中区见 BGMR_BTN_*。 */
/* 批③:桌面顶栏居右「后台管理」按钮(柔和圆角底 + 层叠卡片图标 + 小字,替下原先整块
* primary 方块)。命中区见 BGMR_BTN_*(不变)。 */
#define BGMR_BTN_S 46 /* 按钮边长 */
#define BGMR_BTN_X0 (fb_xres - BGMR_BTN_S - 14) /* 右缘留 14px 边距 */
#define BGMR_BTN_Y0 40 /* 顶缘(标题行内,apps 区之上) */
static void menu_draw_bgm_btn(const meow_ui_theme_t *th)
{
int x = BGMR_BTN_X0, y = BGMR_BTN_Y0, s = BGMR_BTN_S;
fill_rect(x, y, s, s, th->primary);
/* 柔和圆角底(surface_container 卡片语义),与主界面卡片/容器视觉统一 */
meow_gfx_fill_round(x, y, s, s, 12, th->surface_container);
/* 层叠卡片图标 = 后台/应用栈:后卡 muted、前卡 primary(前卡后画盖住后卡中部) */
int cx = x + s / 2;
meow_gfx_fill_round(cx - 13, y + 13, 18, 12, 4, th->on_surface_variant); /* 后卡 */
meow_gfx_fill_round(cx - 5, y + 6, 18, 12, 4, th->primary); /* 前卡 */
/* 小字标签(on_surface,落在 surface_container 底上对比足够) */
const char *lab = meow_str_zh("bgmgr.topbtn", "后台");
int tw = meow_ui_text_width(lab, 14);
ui_draw(x + (s - tw) / 2, y + (s - meow_font_line_height(14)) / 2, lab,
menu_contrast(th->primary), 14);
ui_draw(cx - tw / 2, y + 27, lab, th->on_surface, 14);
}
static int menu_hit_bgm_btn(int tx, int ty)
{
@@ -187,6 +193,13 @@ void s_draw_chevron_left(int x, int y, uint32_t color)
#define MENU_LIST_ROW_H 60 /* 列表行高(大图标行) */
#define MENU_LIST_ICON 40 /* 列表图标 ink 高(方) */
#define MENU_LIST_ICON_X 24 /* 列表图标左缘 */
/* 圆角卡列表项外边距(item margin/gap):相邻卡片间可见空隙 px。SDK listview 行距(=MENU_LIST_ROW_H)
* 不变——卡片在其行高内上下对称内缩 MENU_LIST_ROW_GAP/2,故内容(按行高垂直居中)仍落卡中心;
* 相邻卡顶间距恒=MENU_LIST_ROW_H=卡高+MENU_LIST_ROW_GAP。要求偶数,保证上下对称。 */
#define MENU_LIST_ROW_GAP 8
/* 第 idx 行(行顶 row_top,行高 h)的圆角卡:顶 y 与高。卡片上下对称内缩,行中心=卡中心。 */
static inline int menu_row_card_y(int row_top) { return row_top + MENU_LIST_ROW_GAP / 2; }
static inline int menu_row_card_h(int h) { return h - MENU_LIST_ROW_GAP; }
/* 应用图标缓存(进主界面加载一次,不每帧解码;index 对齐 apps[]) */
static meow_img *s_app_icons[MAX_APPS];
@@ -619,10 +632,14 @@ static void menu_draw_list_cell(const struct app *a, meow_img *icon, int x, int
int w, int h, int sel, int pressed,
const meow_ui_theme_t *th)
{
meow_gfx_fill_round(x, y, w, h, 12, th->surface_container);
/* 圆角卡在行高内上下对称内缩(card center = 行中心):给相邻圆角卡之间留出可见外距,
* 消除原来「卡高==行距」导致的紧贴。内容仍按整行高垂直居中(行中心=卡中心)。 */
int cy = menu_row_card_y(y);
int ch = menu_row_card_h(h);
meow_gfx_fill_round(x, cy, w, ch, 12, th->surface_container);
if (sel || pressed) {
int alpha = pressed ? 72 : 40;
meow_gfx_fill_round_alpha(x, y, w, h, 12, th->primary, alpha);
meow_gfx_fill_round_alpha(x, cy, w, ch, 12, th->primary, alpha);
}
int sz = MENU_LIST_ICON;
int icy = y + (h - sz) / 2;
+3 -2
View File
@@ -6,8 +6,9 @@
*
* ① 输入穿透(S1 根因):App 被 SIGSTOP 冻结过 → 关闭时必须在 /tmp/meow_notif_active 仍存在
* 时先 SIGCONT,让 SDK 输入线程(meow_ui_input.c overlay_input_suppressed)排空冻结期缓冲的
* evdev 事件副本 + 对齐 EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS 后 unlink 标记。
* 若反序(unlink->SIGCONT,改动前写法),App 在无隔离态恢复 → 解冻瞬间把积累的触摸回放成
* evdev 事件副本 + 对齐 EVIOCGKEY,再等 OVERLAY_DRAIN_WINDOW_MS 后把标记内容更新为前台归属
* (notif_active_set(a->id),批①内容化标记取代旧 unlink;清标记/置空会连后台常驻 App 一起放行)。
* 若反序(清标记->SIGCONT,改动前写法),App 在无隔离态恢复 → 解冻瞬间把积累的触摸回放成
* App 内点击(锁屏穿透 /「启动奇怪界面」/ 解锁后状态栏消失的触发源)。
* ② 状态栏恢复(S2):解锁后强制 g_sb_mode_last=-1 让 app 循环立即重叠加,不等 1s 慢刷。
* ③ 死亡重绘(S3):锁屏叠加中 App 崩溃(/proc/<pid>/stat==Z)→ 中止锁屏走统一退出路径,
+32 -22
View File
@@ -780,8 +780,8 @@ static void dd_modal_paint(const meow_ui_theme_t *th, int W, int H, int d_x, int
int content_top, int content_bot, int dd_scroll)
{
meow_ui_fill_rect_alpha(0, 0, W, H, 0x000000, 150);
meow_gfx_fill_round(d_x, d_y, d_w, d_h, 16, th->surface_container);
meow_ui_draw_rect_round(d_x, d_y, d_w, d_h, 16, th->outline);
meow_gfx_fill_round(d_x, d_y, d_w, d_h, 28, th->surface_container); /* MD3 extraLarge 28(原 16 偏小) */
meow_ui_draw_rect_round(d_x, d_y, d_w, d_h, 28, th->outline);
if (mfont_ready) ui_draw(d_x + 18, d_y + 8, title, th->on_surface, 20);
else draw_text(d_x + 18, d_y + 8, title, 2, th->on_surface);
for (int i = 0; i < n; i++) {
@@ -1094,6 +1094,11 @@ static int s_font_dropdown(const srow_t *r, char *out_val, int vlen)
#define SET_TRACK_CY 48 /* 滑条 track 中心相对行顶 y(=SET_SLIDER_YOFF+3,与绘制对齐) */
#define SET_SLIDER_LY 12 /* 滑条行 label 顶相对行顶 y(上移给 track 留位) */
#define SET_SWITCH_POOL 12
/* MD3 容器化(P0):设置页行=surface 上 surfaceContainer 圆角卡 + 行间空隙(去分隔线);
* 卡横向外边距=SET_CARD_X(8),卡在行高内上下对称内缩(行中心=卡中心,同主界面/ncm 歌单手法)。 */
#define SET_ROW_CARD_X SET_CARD_X /* 8:卡左缘 = 内容区左缘 */
#define SET_ROW_CARD_G 8 /* 卡纵向间隙(px) */
#define SET_ROW_CARD_R 12 /* 卡圆角(12dp) */
static meow_widget *g_set_lv = NULL; /* meow_listview(虚拟化) */
static meow_scroll *g_set_sc = NULL; /* 滚动物理 */
@@ -1155,24 +1160,30 @@ static void set_lv_draw(int idx, int x, int y, int w, int h, int state, void *ct
uint32_t textcol = sel ? mt->onPrimaryContainer : mt->onSurface;
uint32_t subcol = sel ? mt->onPrimaryContainer : mt->onSurfaceVariant;
/* MD3 容器化(P0):行=surface 上 surfaceContainer 圆角卡;段头=surface 上的 primary 段标签(无卡)。 */
int cx = x + SET_ROW_CARD_X; /* 卡左缘 */
int cw = w - 2 * SET_ROW_CARD_X; /* 卡宽 */
int cy = y + SET_ROW_CARD_G / 2; /* 卡顶(行内对称内缩) */
int ch = SET_ITEM_H - SET_ROW_CARD_G; /* 卡高 */
if (r->ctrl == SC_HEADER) {
meow_ui_fill_rect(x, y, w, SET_ITEM_H, mt->surfaceContainerHigh);
int ty = y + (SET_ITEM_H - thh) / 2;
if (ty >= 0) meow_ui_text(x + 16, ty, s_row_label(r), mt->font_body, mt->primary);
meow_ui_fill_rect(x, y + SET_ITEM_H - 1, w, 1, mt->outlineVariant);
if (ty >= 0) meow_ui_text(cx + 16, ty, s_row_label(r), mt->font_body, mt->primary);
return;
}
if (sel) meow_ui_fill_rect(x, y, w, SET_ITEM_H, mt->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(x, y, w, SET_ITEM_H, mt->primary, 26);
meow_gfx_fill_round(cx, cy, cw, ch, SET_ROW_CARD_R, mt->surfaceContainer);
if (sel) meow_gfx_fill_round(cx, cy, cw, ch, SET_ROW_CARD_R, mt->primaryContainer);
else if (press) meow_gfx_fill_round_alpha(cx, cy, cw, ch, SET_ROW_CARD_R, mt->primary, 26);
int ly = y + (SET_ITEM_H - thh) / 2; /* label 单行垂直居中 */
int rgt = cx + cw - 16; /* 卡内右缘(内容右对齐锚点) */
switch (r->ctrl) {
case SC_SWITCH: {
meow_ui_text(x + 16, ly, s_row_label(r), mt->font_body, textcol);
meow_ui_text(cx + 16, ly, s_row_label(r), mt->font_body, textcol);
meow_widget *sw = set_sw_take();
if (sw) {
int on = 0; meow_settings_get_switch(r->key, &on);
meow_widget_set_rect(sw, x + w - 16 - 56, y + (SET_ITEM_H - 32) / 2, 56, 32);
meow_widget_set_rect(sw, rgt - 56, y + (SET_ITEM_H - 32) / 2, 56, 32);
meow_switch_set(sw, on);
meow_widget_draw(sw);
}
@@ -1180,9 +1191,9 @@ static void set_lv_draw(int idx, int x, int y, int w, int h, int state, void *ct
}
case SC_SLIDER: {
/* label 上移一行,track 放行下方(中心=SET_SLIDER_YOFF+3=48),与拖动命中带对齐 */
meow_ui_text(x + 16, y + SET_SLIDER_LY, s_row_label(r), mt->font_body, textcol);
meow_ui_text(cx + 16, y + SET_SLIDER_LY, s_row_label(r), mt->font_body, textcol);
char vb[32]; s_fmt_value(r, vb, sizeof vb);
meow_ui_text(x + w - 16 - meow_ui_text_width(vb, mt->font_body), y + SET_SLIDER_LY, vb, mt->font_body, subcol);
meow_ui_text(rgt - meow_ui_text_width(vb, mt->font_body), y + SET_SLIDER_LY, vb, mt->font_body, subcol);
int v = 0; meow_settings_get_int(r->key, &v);
/* 息屏时间为档位索引型(秒→索引 等距画);其余 clamp 到 [min,max] */
if (!strcmp(r->key, "display.screen_off_timeout")) v = s_time_tier_idx(v);
@@ -1191,30 +1202,29 @@ static void set_lv_draw(int idx, int x, int y, int w, int h, int state, void *ct
break;
}
case SC_INFO: {
meow_ui_text(x + 16, ly, s_row_label(r), mt->font_body, textcol);
meow_ui_text(cx + 16, ly, s_row_label(r), mt->font_body, textcol);
char vb[48]; s_net_get(r, vb, sizeof vb);
meow_ui_text(x + w - 16 - meow_ui_text_width(vb, mt->font_body), ly, vb, mt->font_body, subcol);
meow_ui_text(rgt - meow_ui_text_width(vb, mt->font_body), ly, vb, mt->font_body, subcol);
break;
}
case SC_LIST: {
meow_ui_text(x + 16, ly, s_row_label(r), mt->font_body, textcol);
meow_ui_text(cx + 16, ly, s_row_label(r), mt->font_body, textcol);
char vb[48]; s_net_get(r, vb, sizeof vb);
int vx = x + w - 16 - 12 - meow_ui_text_width(vb, mt->font_body);
int vx = rgt - 12 - meow_ui_text_width(vb, mt->font_body);
meow_ui_text(vx, ly, vb, mt->font_body, subcol);
s_draw_chevron(x + w - 16, y + SET_ITEM_H / 2, subcol);
s_draw_chevron(rgt, y + SET_ITEM_H / 2, subcol);
break;
}
default: { /* SC_ENUM / SC_PRESET */
meow_ui_text(x + 16, ly, s_row_label(r), mt->font_body, textcol);
meow_ui_text(cx + 16, ly, s_row_label(r), mt->font_body, textcol);
char vb[40]; s_display_value(r, vb, sizeof vb);
int vx = x + w - 16 - meow_ui_text_width(vb, mt->font_body);
int vx = rgt - meow_ui_text_width(vb, mt->font_body);
meow_ui_text(vx, ly, vb, mt->font_body, subcol);
s_draw_chevron(vx - 8, y + SET_ITEM_H / 2, subcol);
break;
}
}
meow_ui_fill_rect(x, y + SET_ITEM_H - 1, w, 1, mt->outlineVariant);
if (dis) meow_ui_fill_rect_alpha(x, y, w, SET_ITEM_H, mt->onSurface, 90);
if (dis) meow_gfx_fill_round_alpha(cx, cy, cw, ch, SET_ROW_CARD_R, mt->onSurface, 90);
}
/* ---- meow_scroll 回调(包 listview)---- */
@@ -1229,7 +1239,7 @@ static void set_redraw_region(void *ctx)
int lvx, lvy, lvw, lvh; set_lv_rect(&lvx, &lvy, &lvw, &lvh);
if (lvh <= 0) return;
const meow_ui_theme_t *th = s_ui_theme();
meow_ui_fill_rect(lvx, lvy, lvw, lvh, th->surface_variant);
meow_ui_fill_rect(lvx, lvy, lvw, lvh, th->surface); /* P0:内容底改 surface(卡在其上抬升) */
if (g_set_lv) { g_set_sw_use = 0; meow_widget_draw(g_set_lv); }
meow_dirty_add(lvx, lvy, lvw, lvh);
meow_ui_flush();
@@ -1274,7 +1284,7 @@ static void set_paint_page(int sel)
if (mfont_ready) ui_draw(24, 50, meow_str_zh("menu.settings", "系统设置"), th->on_surface, 28);
else draw_text(24, 30, "SETTINGS", 3, th->on_surface);
fill_rect(0, 50 + (mfont_ready ? meow_font_line_height(28) : 24) + 2, W, 2, th->outline);
fill_rect(0, SET_CARD_Y, W, H - SET_CARD_Y, th->surface_variant);
fill_rect(0, SET_CARD_Y, W, H - SET_CARD_Y, th->surface); /* P0:内容底 surface,行用圆角卡抬升 */
if (g_set_lv) {
g_set_sw_use = 0;
int p = s_vis_of(sel); if (p >= 0) meow_listview_set_sel(g_set_lv, p);
+4 -1
View File
@@ -619,7 +619,7 @@ static void sp_paint_top_action(void)
if (w <= 0 || h <= 0) return;
const meow_theme *mt = meow_theme_get();
uint32_t col = mt->primary;
meow_ui_draw_rect_round(x, y, w, h, 10, col); /* 圆角描边按钮 */
meow_ui_draw_rect_round(x, y, w, h, h / 2, col); /* 全形=胶囊描边按钮(P1) */
int th = meow_ui_text_height(20);
if (th > h) th = h;
meow_ui_text_center(x + w / 2, y + (h - th) / 2, g_sp->top_action, 20, col);
@@ -658,6 +658,9 @@ static void sp_widgets_create(void)
/* 双行富 item + 无左图标 + 右尾件(SWITCH/ARROW);行高 56 */
meow_listview_set_item_renderer(g_lv, 2, 0, 1);
meow_listview_set_item_h(g_lv, SUB_ITEM_H);
/* MD3(P0):默认 renderer 容器模式——行=surfaceContainer 圆角卡+行间空隙,
* 选中/按压 tonal(primaryContainer/primary α26)、去冗余分隔线;段头=surface 上的 primary 段标签。 */
meow_listview_set_container(g_lv, 1);
}
/* 注:刻意不 set_snap —— SDK meow_scroll 的 snap 以「fling 起点」为吸附目标,快速滑动时会
* 从自然停点往回拉一整行(实测 697→336),UX 不佳;默认 ease-out 惯性停(无 snap)更顺滑。
+106
View File
@@ -0,0 +1,106 @@
/* meow_launcherd/test_bg_row_card.c — 宿主验证:后台管理页(bg_page)MD3 圆角卡行 + 行内杀按钮
* 的几何与命中(纯整数运算,不链 SDK / 不开设备):
* gcc -Wall -Wextra test_bg_row_card.c -o test_bg_row_card && ./test_bg_row_card
*
* 断言集(对应「相邻 item 顶之间 = item 高 + gap」的可见形式):
* ① 卡高 = 行高 - BGM_CARD_GAP;卡在其行高内上下对称内缩 gap/2(行中心=卡中心);
* ② 相邻卡顶间距 = SUB_ITEM_H(= 卡高 + gap;SDK 行距不变),卡片间可见外距 = gap;
* ③ 杀按钮垂直居中于卡(中心=卡中心=行中心),完整落卡内,右缘距行右 12px;
* ④ 命中不破:卡内缩后按钮几何不变(相对整行视口)——按钮区内点按命中(1),
* 行体/卡内空白点按不命中(0);命中矩形仍为整行距;
* ⑤ 滚动偏移只整体平移,不改变上述间距/对称/命中。
*
* 几何公式与 patch_v2/meow_launcherd/bg_page.c 的 bgm_row_card_y/bgm_row_card_h/bgm_btn_rect 同源。
*/
#include <stdio.h>
#include <assert.h>
#define SUB_ITEM_H 56 /* 行距(SDK listview item_h,subpage.c,不变) */
#define BGM_CARD_GAP 8
#define BGM_BTN_W 46
#define BGM_BTN_H 34
/* 与 bg_page.c bgm_row_card_y / bgm_row_card_h 同源(上下对称内缩)。 */
static int bgm_row_card_y(int row_top) { return row_top + BGM_CARD_GAP / 2; }
static int bgm_row_card_h(int h) { return h - BGM_CARD_GAP; }
/* 与 bg_page.c bgm_btn_rect 同源(按钮垂直居中于卡;卡中心=行中心 → 命中不破)。 */
static void bgm_btn_rect(int rx, int ry, int rw, int rh, int *bx, int *by, int *bw, int *bh)
{
*bw = BGM_BTN_W;
*bh = BGM_BTN_H;
*bx = rx + rw - 12 - BGM_BTN_W;
*by = bgm_row_card_y(ry) + (bgm_row_card_h(rh) - BGM_BTN_H) / 2;
}
/* 行顶 = list_y + idx*item - off(SDK listview 布局:行距=item)。 */
static int row_top(int idx, int item, int list_y, int off) { return list_y + idx * item - off; }
/* 复刻 bgm_row_tap 的按钮命中判定(仅几何):命中按钮区=1,行体/卡内空白=0。 */
static int btn_hit(int tx, int ty, int rx, int ry, int rw, int rh)
{
int bx, by, bw, bh; bgm_btn_rect(rx, ry, rw, rh, &bx, &by, &bw, &bh);
if (tx < bx || tx >= bx + bw || ty < by || ty >= by + bh) return 0;
return 1;
}
int main(void)
{
const int item = SUB_ITEM_H;
const int gap = BGM_CARD_GAP;
const int list_y = 100; /* 内容区顶缘(SUB_CONT_TOP 等值,任意值皆可) */
const int rw = 480; /* 行视口宽(fb_xres,480×720) */
const int rx = 0; /* 行视口左缘 */
int y0 = bgm_row_card_y(row_top(0, item, list_y, 0));
int y1 = bgm_row_card_y(row_top(1, item, list_y, 0));
int h = bgm_row_card_h(item);
/* ① 卡相对行顶对称内缩 gap/2;卡高=item-gap;卡高+gap=item */
assert(y0 == list_y + gap / 2);
assert(h == item - gap);
assert(h + gap == item);
/* ① 相邻卡顶间距 = item(顶到顶 = 卡高 + gap) */
assert(y1 - y0 == item);
/* ② 卡片间可见外距 = gap(卡 0 底 到 卡 1 顶) */
assert(y1 - (y0 + h) == gap);
/* ③ 卡中心 == 行中心(内容按行高居中 → 落卡中心) */
assert(y0 - gap / 2 + item / 2 == y0 + h / 2);
/* 下行底到卡底 = gap/2(下对称) */
assert((list_y + item) - (y0 + h) == gap / 2);
/* ③④ 杀按钮:相对整行视口定位,垂直居中于卡(中心=卡中心=行中心),完整落卡内,
* 右缘距行右 12px;滚动偏移只平移,不改变以上(③④⑤合并循环)。 */
for (int off = 0; off <= item; off += item / 2) {
int r0 = row_top(0, item, list_y, off);
int bx, by, bw, bh; bgm_btn_rect(rx, r0, rw, item, &bx, &by, &bw, &bh);
int card_top = bgm_row_card_y(r0);
int card_bot = card_top + bgm_row_card_h(item);
int row_cy = r0 + item / 2;
int card_cy = card_top + bgm_row_card_h(item) / 2;
/* 按钮完整落卡内(含圆角内边距;上下由卡限,左右由行限) */
assert(bx >= rx && bx + bw <= rx + rw);
assert(by >= card_top && by + bh <= card_bot);
/* 按钮中心 = 卡中心 = 行中心(垂直) */
assert(by + bh / 2 == card_cy);
assert(by + bh / 2 == row_cy);
/* 按钮右缘距行右 12px(与绘制一致) */
assert(rx + rw - (bx + bw) == 12);
/* ④ 命中:按钮中心 → 命中(1);卡内空白(左上圆角内) / 行体左缘 → 不命中(0) */
assert(btn_hit(bx + bw / 2, by + bh / 2, rx, r0, rw, item) == 1);
assert(btn_hit(card_top + 2, card_top + 2, rx, r0, rw, item) == 0);
assert(btn_hit(rx + 16, row_cy, rx, r0, rw, item) == 0);
/* 卡上/下外侧(gap 带内)点按 → 不命中(命中仍按整行距,含 gap 带) */
assert(btn_hit(rx + rw / 2, r0 + gap / 2 - 1, rx, r0, rw, item) == 0);
}
printf("PASS bg_row_card_gap=%d card_h=%d top2top=%d visible_gap=%d "
"btn_in_card=1 btn_center==row_center=1 hit_unbroken=1\n",
gap, h, item, gap);
return 0;
}
@@ -0,0 +1,67 @@
/* meow_launcherd/test_menu_row_gap.c — 宿主验证:主界面列表模式圆角卡列表项外边距
* (item margin/gap)几何。纯整数运算,不链 SDK / 不开设备:
* gcc test_menu_row_gap.c -o test_menu_row_gap && ./test_menu_row_gap
*
* 断言集(对应「相邻 item 顶之间 = item 高 + gap」的可见形式):
* ① 相邻卡顶间距 = MENU_LIST_ROW_H(= 卡高 + gap;SDK 行距不变);
* ② 卡片间可见外距 = MENU_LIST_ROW_GAP;
* ③ 卡在其行高内上下对称内缩 → 内容(按行高垂直居中)落卡中心;
* ④ 滚动偏移只整体平移,不改变上述间距/对称。
*
* 几何公式与 patch_v2/meow_launcherd/main_menu.c 的 menu_row_card_y/menu_row_card_h 同源:
* 卡顶 = 行顶 + gap/2;卡高 = 行高 - gap。
*/
#include <stdio.h>
#include <assert.h>
#define MENU_LIST_ROW_H 60 /* 行距(SDK listview item_h,不变) */
#define MENU_LIST_ROW_GAP 8
/* 与 main_menu.c menu_row_card_y / menu_row_card_h 同源(上下对称内缩)。 */
static int menu_row_card_y(int row_top) { return row_top + MENU_LIST_ROW_GAP / 2; }
static int menu_row_card_h(int h) { return h - MENU_LIST_ROW_GAP; }
/* 行顶 = list_y + idx*item(SDK listview 布局:行距=item)。 */
static int row_top(int idx, int item, int list_y, int off) { return list_y + idx * item - off; }
int main(void)
{
const int item = MENU_LIST_ROW_H;
const int gap = MENU_LIST_ROW_GAP;
const int list_y = 100;
int y0 = menu_row_card_y(row_top(0, item, list_y, 0));
int y1 = menu_row_card_y(row_top(1, item, list_y, 0));
int h = menu_row_card_h(item);
/* ① 卡相对行顶对称内缩 gap/2;卡高=item-gap;卡高+gap=item */
assert(y0 == list_y + gap / 2);
assert(h == item - gap);
assert(h + gap == item);
/* ① 相邻卡顶间距 = item(顶到顶 = 卡高 + gap) */
assert(y1 - y0 == item);
/* ② 卡片间可见外距 = gap(卡 0 底 到 卡 1 顶) */
assert(y1 - (y0 + h) == gap);
/* ③ 卡中心 == 行中心(内容按行高居中 → 落卡中心) */
assert(y0 - gap / 2 + item / 2 == y0 + h / 2);
/* 上行底到卡底 = gap/2(下对称) */
assert((list_y + item) - (y0 + h) == gap / 2);
/* ④ 滚动偏移只平移,不改变间距/对称(任意 off) */
for (int off = 0; off <= 2 * item; off += item / 2) {
int r2 = row_top(2, item, list_y, off);
int r3 = row_top(3, item, list_y, off);
int a2 = menu_row_card_y(r2);
int a3 = menu_row_card_y(r3);
assert(a3 - a2 == item);
assert(a3 - (a2 + h) == gap);
assert(a2 - r2 == gap / 2);
assert((r2 + item) - (a2 + h) == gap / 2);
}
printf("PASS menu_row_gap=%d card_h=%d top2top=%d visible_gap=%d symmetric=1\n",
gap, h, item, gap);
return 0;
}
+15
View File
@@ -146,6 +146,16 @@ enum {
meow_widget *meow_btn_create_variant(int x, int y, int w, int h, const char *label, int variant);
int meow_btn_set_variant(meow_widget *w, int variant); /* 越界 clamp;0=成功 */
/* ---- MD3 shape 层级(§5.8.6,Material You 圆角体系,单位 px)----
* 组件按角色落到四档 + 胶囊:按钮=full(全高一半)、卡=large(16)、输入框=small(8)、
* 勾选=extrasmall(4)。theme.radius/radius_card 为风格族默认;App 可经 opts.radius 逐控件覆盖。
* 全高一半用 MEOW_SHAPE_FULL(h) 宏(控件的胶囊圆角)。 */
#define MEOW_SHAPE_EXTRASMALL 4 /* 勾选 Checkbox/Radio */
#define MEOW_SHAPE_SMALL 8 /* 输入框 TextField */
#define MEOW_SHAPE_MEDIUM 12 /* 主题默认 radius(列表卡/通用控件) */
#define MEOW_SHAPE_LARGE 16 /* 卡片/容器(theme.radius_card 默认) */
#define MEOW_SHAPE_FULL(h) ((h) / 2) /* 全形=胶囊(按钮/导航等) */
/* ---- TextField(单行文本输入,接 IME §5.1)---- */
/* placeholder=提示文案;buf=App 持有的文本缓冲(组件不回拷,只绘制/编辑其上);maxlen=含 NUL 预算 */
meow_widget *meow_textfield_create(int x, int y, int w, int h, const char *placeholder,
@@ -384,6 +394,11 @@ int meow_listview_set_item_tier(meow_widget *lv, int lines);
/* 完全自绘:设 draw_item 回调(覆盖默认 renderer);NULL=用默认 renderer。返回 0=成功。 */
int meow_listview_set_item_draw(meow_widget *lv, meow_listview_draw_fn draw,
void *ctx);
/* MD3 容器模式(默认 renderer 可选,P0 对齐):0=扁平满宽行+分隔线(默认,兼容);
* 1=每行外套 surfaceContainer 圆角卡(12dp)+行间空隙,选中=primaryContainer/按压=primary α
* (tonal,非满幅实心),去冗余分隔线;段头=surface 上的 primary 段标签(无卡无分隔线)。
* 注意:仅对默认 renderer(未 set_item_draw)生效;完全自绘行由 App self-draw 自理。 */
int meow_listview_set_container(meow_widget *lv, int on);
/* 空态占位:count==0 时视口内居中画 empty_text(NULL 不画)。返回 0=成功。 */
int meow_listview_set_empty_text(meow_widget *lv, const char *text);
+48 -8
View File
@@ -974,16 +974,33 @@ static int statusbar_touched(void)
return 0;
}
/* ---- §10.5 叠加让位自抑(present 层)----
* /tmp/meow_notif_active 存在 = 通知中心叠加会话进行中(launcher 正在 fb 上渲染面板)。
* App 进程(非系统 UI 渲染者)此时不上屏;200ms 节流缓存存在性(别每帧 stat)。
/* ---- §10.5 叠加让位自抑(present / input 共用判定)----
* /tmp/meow_notif_active = 前台屏幕归属标记,**内容=当前前台 app_id**(空=菜单/叠加层覆盖/
* 无前台单元,即「抑制全部 App」)。App 进程(非系统 UI 渲染者)据此判「我是否前台」:
* - 内容==本进程 app_id(env MEOW_LOG_APP)→ 前台 → 照常上屏 / 收输入;
* - 内容为空 → 菜单前台或被叠加层覆盖 → 全抑制(含自身);
* - 内容为其它 app_id → 后台常驻(批①多 App)/非前台 → 自抑上屏 + 排空输入。
* 200ms 节流缓存存在性+内容(别每帧 open/stat)。
* 计数 g_overlay_suppressed 供宿主单测断言(meow_gfx_overlay_suppressed_count)。 */
static unsigned long long g_overlay_suppressed = 0;
/* §后台化保活-残帧:记录「上一次 present 是否处于叠加自抑态」,用于在抑制→不抑制的恢复边沿
* 强制整帧重画(盖掉 launcher 在后台化期间画进 buffer0 的桌面/主界面帧)。 */
static int g_was_overlay_suppressed = 0;
static int overlay_present_suppressed(void)
/* 本进程 app_id(env MEOW_LOG_APP,launcher 注入)= 判「我是否前台」的身份锚。 */
static const char *overlay_own_app(void)
{
static const char *s = NULL;
if (!s) {
const char *e = getenv("MEOW_LOG_APP");
s = (e && e[0]) ? e : "";
}
return s;
}
/* §批①多 App 后台常驻:present 层与 input 层共用的「是否自抑」判定(单一实现,防两端漂移)。
* 系统 UI(launcher 渲染面板/主界面)恒 0。返回 1=本进程应自抑上屏+排空输入。 */
int meow_ui_overlay_suppressed_active(void)
{
if (meow_ui_sys_render_enabled()) return 0; /* launcher(系统 UI)渲染面板,照常上屏 */
static char s_path[108];
@@ -998,7 +1015,29 @@ static int overlay_present_suppressed(void)
unsigned long long now = now_ms();
if (s_last_chk && now - s_last_chk < MEOW_OVERLAY_CHK_MS) return s_last_res;
s_last_chk = now;
s_last_res = (access(s_path, F_OK) == 0) ? 1 : 0;
{
char owner[48];
int have = 0;
owner[0] = 0;
int fd = open(s_path, O_RDONLY);
if (fd >= 0) {
ssize_t n = read(fd, owner, sizeof(owner) - 1);
close(fd);
if (n > 0) owner[n] = 0;
have = 1; /* 文件存在(无论内容)→ 处于抑制管控 */
} else {
have = 0; /* 文件不存在 → 无抑制 */
}
if (!have) {
s_last_res = 0;
} else if (owner[0] == 0) {
s_last_res = 1; /* 空内容=全抑制(菜单前台/叠加层覆盖) */
} else {
const char *me = overlay_own_app();
/* 内容==本进程 app_id → 前台不抑制;否则(后台常驻/非前台)→ 抑制 */
s_last_res = (strcmp(owner, me) == 0) ? 0 : 1;
}
}
return s_last_res;
}
@@ -1010,9 +1049,10 @@ static void present(void)
if (!(g_dirty_full || g_ndirty > 0)) return;
/* §10.5 叠加让位自抑(仅 App 进程):App 前台播放中唤起通知中心时 launcher 不 SIGSTOP
* (保 PCM 供流,ncm 播放线程同进程),改由 App 进程在此自抑上屏——绘制照常进 back_buf,
* 只是不 present 到 fb,防 App 重绘穿透 launcher 叠加层。系统 UI 进程(launcher 渲染面板)
* 不抑制。抑制期脏区保留累积(超上限自然升级整帧),标记消失后下一次 present 一次上屏。 */
if (overlay_present_suppressed()) { g_overlay_suppressed++; g_was_overlay_suppressed = 1; return; }
* 只是不 present 到 fb,防 App 重绘穿透 launcher 叠加层;后台常驻(批①)App 同判据自抑。
* 系统 UI 进程(launcher 渲染面板)不抑制。抑制期脏区保留累积(超上限自然升级整帧),
* 判定为「回到前台」后下一次 present 一次上屏。 */
if (meow_ui_overlay_suppressed_active()) { g_overlay_suppressed++; g_was_overlay_suppressed = 1; return; }
/* §后台化保活-残帧:抑制→不抑制的**恢复边沿**强制整帧重画。App 被 launcher 转后台保活
* (/tmp/meow_notif_active 存在)期间 present 被自抑,back_buf 仍保留 App 自己的完整帧;
* 但 launcher 已把桌面/主界面整帧画进 buffer0 盖在 App 残帧上。恢复前台时若只 present
+7 -18
View File
@@ -140,28 +140,17 @@ static int meow_input_jack_debounce(void)
return g_jack_changed_at && (now - g_jack_changed_at) < 800;
}
/* ---- §10.5 叠加让位:通知中心叠加会话期输入排空(播放中 App 不被 SIGSTOP 的配套)----
/* ---- §10.5 叠加让位:通知中心叠加会话期 / 批①后台常驻期 输入排空(播放中 App 不被 SIGSTOP 的配套)----
* 媒体播放中的 App 前台唤起通知中心时 launcher 不冻结 App(保 PCM 供流,ncm 播放线程同进程),
* App 输入线程仍在读同一批 evdev 设备(每 fd 各收一份事件副本,与 launcher 互不剥夺)——若不
* 抑制,面板上的一次点击会被 App 再消费一次(如面板媒体卡片「暂停」与 App 同坐标控件双重触发)。
* 故标记存在期:排空本进程 fd 队列并丢弃、EVIOCGKEY 位图静默对齐(不产生边沿语义,系统键动作
* 叠加期由 launcher/watchdog 单侧承担)。200ms 节流缓存存在性(与 present 层同口径,别每圈 stat)。 */
* 抑制,面板上的一次点击会被 App 再消费一次(如面板媒体卡片「暂停」与 App 同坐标控件双重触发);
* 批①后台常驻 App 同理:前台 App(如 Hello)时,后台 ncm 的触摸必须被排空(一次交互只响应前台)。
* 判据统一走 meow_ui_overlay_suppressed_active()(内容=前台 app_id;非前台/空=抑制)。抑制期:
* 排空本进程 fd 队列并丢弃、EVIOCGKEY 位图静默对齐(不产生边沿语义,系统键动作叠加期由
* launcher/watchdog 单侧承担)。200ms 节流缓存由共享判定承担(与 present 层同口径)。 */
static int overlay_input_suppressed(void)
{
static char s_path[108];
static int s_path_init = 0;
static unsigned long long s_last_chk = 0;
static int s_last_res = 0;
if (!s_path_init) {
const char *e = getenv("MEOW_NOTIF_ACTIVE_FILE"); /* 宿主/单测覆写(进程内只解析一次) */
snprintf(s_path, sizeof s_path, "%s", (e && e[0]) ? e : "/tmp/meow_notif_active");
s_path_init = 1;
}
unsigned long long now = now_ms();
if (s_last_chk && now - s_last_chk < 200) return s_last_res;
s_last_chk = now;
s_last_res = (access(s_path, F_OK) == 0) ? 1 : 0;
return s_last_res;
return meow_ui_overlay_suppressed_active();
}
/* ---- 系统按键语义(M2.55:SDK 输入层消化,App 前台也生效)----
+6
View File
@@ -163,6 +163,12 @@ int meow_gfx_is_device(void);
* (仅 App 进程会自抑;宿主单测断言用,设备上无调用方)。 */
unsigned long long meow_gfx_overlay_suppressed_count(void);
/* §批①多 App 后台常驻:本进程是否应自抑上屏+排空输入(present/input 层共用判定)。
* /tmp/meow_notif_active 内容=前台 app_id:内容==本进程 app_id → 前台不抑制(返回 0);
* 内容为空或其它 app_id → 菜单前台/叠加层覆盖/后台常驻 → 自动抑制(返回 1)。
* 系统 UI(launcher)恒 0;200ms 节流缓存。meow_ui_gfx.c 实现,input 层复用。 */
int meow_ui_overlay_suppressed_active(void);
/* 中文/日文字库(mfont)是否已加载(状态栏「蓝牙」CJK 与回退 "BT" 二选一)。 */
int meow_gfx_mfont_ready(void);
+103 -34
View File
@@ -116,6 +116,7 @@ struct meow_widget {
int show_icon; /* 1=画左图标/缩略图(默认 1) */
int show_right;/* 1=画右尾件(默认 1) */
char empty_text[64]; /* 空态占位文案(首字符 0=不画) */
int container; /* MD3 容器模式(默认 renderer):1=行=surfaceContainer 圆角卡+空隙 (P0) */
/* §5.5 左滑删除(列表行横向手势:滑动偏移/删除动画/回弹;calloc 归零,安全)。
* 手势由 App 在 on_touch_move 喂(swipe_begin/move/end);横向小位移判 swipe、纵向滚动优先;
@@ -141,6 +142,12 @@ static uint32_t widget_fg(const meow_widget *w, const meow_theme *th, uint32_t d
{ (void)th; return (w->opts_used && w->opts.fg) ? w->opts.fg : def; }
static int widget_radius(const meow_widget *w, int def)
{ return (w->opts_used && w->opts.radius >= 0) ? w->opts.radius : def; }
/* MD3 全形按钮圆角 = 胶囊(全高一半);opts.radius 显式优先(P1 shape 对齐,替代硬编码 10)。 */
static int widget_btn_radius(const meow_widget *w)
{
if (w->opts_used && w->opts.radius >= 0) return w->opts.radius;
return MEOW_SHAPE_FULL(w->h);
}
static int widget_pady(const meow_widget *w)
{ return (w->opts_used && w->opts.pad_y > 0) ? w->opts.pad_y : 0; }
@@ -1017,6 +1024,11 @@ int meow_list_tap_at(meow_widget *w, int x, int y)
#define MEOW_LISTV_ICON 24 /* 图标盒尺寸(px) */
#define MEOW_LISTV_PAD 16 /* 行内左右 padding(px) */
#define MEOW_LISTV_SUB_GAP 2 /* 标题与副标题间隙(px) */
/* MD3 容器模式(P0):行=surfaceContainer 圆角卡 + 行间空隙(去分隔线);卡图元与主界面/
* ncm 歌单同手法(main_menu.c:639 / playlist.c)。卡在行高内上下对称内缩(行中心=卡中心)。 */
#define MEOW_LISTV_CARD_MARGIN 8 /* 卡横向外边距(px) */
#define MEOW_LISTV_CARD_GAP 8 /* 卡纵向间隙(px) */
#define MEOW_LISTV_CARD_RADIUS 12 /* 卡圆角(px,12dp) */
/* meow_ui_img_scaled 弱引用:W_LISTVIEW 的 MEOW_LIST_ICON_IMG 分支用它绘制图片图标。
* 弱引用让「只用内置符号图标、不解码图片」的 App 不被牵连进 meow_ui_img.o/stb_image.o
@@ -1395,6 +1407,14 @@ int meow_listview_set_item_draw(meow_widget *w, meow_listview_draw_fn draw,
return 0;
}
int meow_listview_set_container(meow_widget *w, int on)
{
if (!w || w->type != W_LISTVIEW) return -1;
w->container = on ? 1 : 0;
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h);
return 0;
}
int meow_listview_set_empty_text(meow_widget *w, const char *text)
{
if (!w || w->type != W_LISTVIEW) return -1;
@@ -1631,7 +1651,7 @@ void meow_widget_draw(meow_widget *w)
uint32_t bg;
if (fill) bg = (hold) ? state_tint(base, th, 24) : (press ? state_tint(base, th, 12) : base);
else bg = 0;
int r = widget_radius(w, 10);
int r = widget_btn_radius(w);
if (fill) meow_gfx_fill_round(w->x, w->y, w->w, w->h, r, bg);
else if (press) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->primary, 12); /* transparent ripple */
/* 内容区受 pad 垂直内缩(水平居中文本,pad_x 不影响中心) */
@@ -1693,11 +1713,15 @@ void meow_widget_draw(meow_widget *w)
break;
}
case W_SWITCH: {
uint32_t bg = w->on ? th->primary : th->onSurfaceVariant;
/* MD3(P3):on-track=primary;off-track=surfaceContainerHighest(原 onSurfaceVariant 过暗);
* on-thumb=onPrimary;off-thumb=surface + outline(原恒白)。 */
uint32_t bg = w->on ? th->primary : th->surfaceContainerHighest;
meow_gfx_fill_round(w->x, w->y, w->w, w->h, w->h / 2, bg);
int knob_r = w->h / 2 - 3;
int kx = w->on ? w->x + w->w - knob_r * 2 - 3 : w->x + 3;
meow_gfx_fill_round(kx, w->y + 3, knob_r * 2, knob_r * 2, knob_r, 0xFFFFFF);
uint32_t knob = w->on ? th->onPrimary : th->surface; /* off-thumb 表面色 */
meow_gfx_fill_round(kx, w->y + 3, knob_r * 2, knob_r * 2, knob_r, knob);
if (!w->on) meow_ui_draw_rect_round(kx, w->y + 3, knob_r * 2, knob_r * 2, knob_r, th->outline);
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, w->h / 2, th->onSurface, 90);
break;
}
@@ -1777,11 +1801,16 @@ void meow_widget_draw(meow_widget *w)
int vx0 = w->x, vy0 = w->y, vy1 = w->y + w->h;
int thh = meow_ui_text_height(th->font_body);
int pad = MEOW_LISTV_PAD, isz = MEOW_LISTV_ICON;
int text_x = vx0 + pad + ((w->show_icon) ? (isz + pad) : 0); /* 无图标则无图标间隙 */
int op_start = (w->show_right) ? (vx0 + (w->w * 2 / 3)) /* 行右 1/3 = 操作区 */
: (vx0 + w->w - pad);
/* 容器模式(P0):行=surfaceContainer 圆角卡,卡横向内缩 cm;内容左缘/操作区/可用宽
* 都在卡内计算(非容器 cm=0 → 与既有行为逐字一致)。 */
int cm = w->container ? MEOW_LISTV_CARD_MARGIN : 0;
int cw = w->w - 2 * cm; /* 内容/卡宽 */
int cx0 = vx0 + cm; /* 内容左缘(卡内) */
int text_x = cx0 + pad + ((w->show_icon) ? (isz + pad) : 0); /* 无图标则无图标间隙 */
int op_start = (w->show_right) ? (cx0 + (cw * 2 / 3)) /* 行右 1/3 = 操作区 */
: (cx0 + cw - pad);
int text_maxw = (w->show_right) ? (op_start - pad - text_x) /* 标题/副标题可用宽(不压操作区) */
: ((vx0 + w->w - pad) - text_x);
: ((cx0 + cw - pad) - text_x);
if (text_maxw < 8) text_maxw = 8;
/* 空态占位(§5.5):count==0 且设了 empty_text → 视口内居中画文案。 */
@@ -1841,26 +1870,36 @@ void meow_widget_draw(meow_widget *w)
meow_str("common.delete"), th->font_body, th->onError);
shift = w->swipe_dx;
}
int rlx = vx0 + shift; /* 行内容左移后的 x */
int rlx = (w->container ? cx0 : vx0) + shift; /* 行内容/卡左缘(随行左移) */
int rtx = text_x + shift; /* 文本 x(随行左移) */
int rop = op_start + shift; /* 操作区分界 x */
int card_y = ry + MEOW_LISTV_CARD_GAP / 2; /* 容器模式:卡顶(行内对称内缩) */
int card_h = item - MEOW_LISTV_CARD_GAP;
if (it->section) { /* 段头占位行(非粘性):surfaceContainerHigh 底 + primary 段名 */
meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->surfaceContainerHigh);
if (it->section) { /* 段头占位行(非粘性):容器=surface 上的 primary 段标签;非容器=surfaceContainerHigh 底 */
if (!w->container) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->surfaceContainerHigh);
int sty = ry + (item - thh) / 2;
if (it->title && sty >= vy0 && sty + thh <= vy1)
listv_text_trunc(rtx, sty, it->title, text_maxw, th->font_body, th->primary);
if (!w->container) {
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
if (!it->enabled)
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
continue;
}
/* 选中/按压背景(primaryContainer / 半透明主题色按压层) */
/* 选中/按压背景(容器=卡内 tonal;非容器=满幅实心,原行为) */
if (w->container) {
meow_gfx_fill_round(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->surfaceContainer);
if (sel) meow_gfx_fill_round(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->primaryContainer);
else if (press) meow_gfx_fill_round_alpha(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->primary, 26);
} else {
if (sel) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->primary, 26);
}
uint32_t textcol = sel ? th->onPrimaryContainer : th->onSurface;
uint32_t subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
@@ -1890,21 +1929,27 @@ void meow_widget_draw(meow_widget *w)
/* 右尾件(show_right=1:箭头/开关/复选/⋯) */
if (w->show_right && it->right != MEOW_LIST_RIGHT_NONE) {
int ax = rlx + w->w - pad - 24; /* 右缘动作槽(约 24px) */
int ax = (cx0 + cw - pad - 24) + shift; /* 右缘动作槽(约 24px;容器=卡内右缘) */
if (ax >= rop)
listv_draw_right(th, ax, ry, item, it->right, it->on, subcol);
}
/* 分隔线(仅落在可见带内才画) */
/* 分隔线(仅非容器画;容器模式去冗余分隔线,行间用空隙替代) */
if (!w->container) {
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
}
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗) */
if (!it->enabled)
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗;容器=卡内淡) */
if (!it->enabled) {
if (w->container)
meow_gfx_fill_round_alpha(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->onSurface, 90);
else
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
}
}
} else {
/* ① src2 模式(既有):每帧按需取行(meow_list2_source),行为不变。
* 行 = 图标(左) + 标题 + 副标题(次要色) + 右侧操作区;选中行 = primaryContainer。 */
@@ -1918,10 +1963,13 @@ void meow_widget_draw(meow_widget *w)
int cl = ry > vy0 ? ry : vy0; /* 可见顶界(裁剪用) */
int cr = (ry + item) < vy1 ? (ry + item) : vy1; /* 可见底界 */
int sel = (i == w->sel);
int press = meow_input_touch_down() &&
meow_input_touch_x() >= vx0 && meow_input_touch_x() < vx0 + w->w &&
meow_input_touch_y() >= ry && meow_input_touch_y() < ry + item;
uint32_t textcol = sel ? th->onPrimaryContainer : th->onSurface;
uint32_t subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
/* 左滑删除(M7.3):本行被滑动 → 右露删除区 + 行 x 左移。非滑动行 shift=0 → 输出不变。 */
int shift = 0, rlx = vx0, rtx = text_x, rop = op_start;
int shift = 0, rlx = (w->container ? cx0 : vx0), rtx = text_x, rop = op_start;
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
@@ -1929,9 +1977,19 @@ void meow_widget_draw(meow_widget *w)
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
shift = w->swipe_dx; rlx = vx0 + shift; rtx = text_x + shift; rop = op_start + shift;
shift = w->swipe_dx; rlx = (w->container ? cx0 : vx0) + shift;
rtx = text_x + shift; rop = op_start + shift;
}
int card_y = ry + MEOW_LISTV_CARD_GAP / 2;
int card_h = item - MEOW_LISTV_CARD_GAP;
if (w->container) {
meow_gfx_fill_round(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->surfaceContainer);
if (sel) meow_gfx_fill_round(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->primaryContainer);
else if (press) meow_gfx_fill_round_alpha(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->primary, 26);
} else {
if (sel) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->primary, 26);
}
/* 图标(左) */
uint32_t iccol = sel ? th->onPrimaryContainer
@@ -1958,21 +2016,27 @@ void meow_widget_draw(meow_widget *w)
/* 右侧操作区 */
if (it.right != MEOW_LIST_RIGHT_NONE) {
int ax = rlx + w->w - pad - 24; /* 右缘动作槽(约 24px) */
int ax = (cx0 + cw - pad - 24) + shift; /* 右缘动作槽(约 24px;容器=卡内右缘) */
if (ax >= rop)
listv_draw_right(th, ax, ry, item, it.right, it.on, subcol);
}
/* 分隔线(仅落在可见带内才画) */
/* 分隔线(仅非容器画;容器模式去冗余分隔线,行间用空隙替代) */
if (!w->container) {
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
}
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗) */
if (!it.enabled)
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗;容器=卡内淡) */
if (!it.enabled) {
if (w->container)
meow_gfx_fill_round_alpha(rlx, card_y, cw, card_h, MEOW_LISTV_CARD_RADIUS, th->onSurface, 90);
else
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
}
}
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
break;
}
@@ -1988,7 +2052,7 @@ void meow_widget_draw(meow_widget *w)
break;
case W_TEXTFIELD: {
/* MD3 输入框:圆角外框(surfaceContainerLowest 底 + outline 描边;focused=primary 2px)+ 提示/文本 + 光标。 */
int r = widget_radius(w, 8);
int r = widget_radius(w, MEOW_SHAPE_SMALL);
int focused = (w->state & MEOW_WS_FOCUSED) != 0;
meow_gfx_fill_round(w->x, w->y, w->w, w->h, r, th->surfaceContainerLowest);
uint32_t bcol = focused ? th->primary : th->outline;
@@ -2045,26 +2109,30 @@ void meow_widget_draw(meow_widget *w)
break;
}
case W_CHECKBOX: {
/* MD3(P3):视觉字 24dp 于控件矩形内居中(触摸目标不变);修正原 34 视觉过大。 */
int sel = (w->state & MEOW_WS_SELECTED) != 0;
int sz = w->w;
int r = widget_radius(w, 4);
int sz = 24;
if (w->w < sz) sz = w->w; /* 控件小于视觉尺寸时跟随 */
int bx = w->x + (w->w - sz) / 2, by = w->y + (w->h - sz) / 2;
int r = widget_radius(w, MEOW_SHAPE_EXTRASMALL);
if (sel) {
meow_gfx_fill_round(w->x, w->y, sz, sz, r, th->primary);
int ax = w->x + 9, ay1 = w->y + 17, ay2 = w->y + 23, bx = w->x + 15, cx = w->x + 25, cy = w->y + 11;
meow_gfx_line(ax, ay1, bx, ay2, 3, th->onPrimary);
meow_gfx_line(bx, ay2, cx, cy, 3, th->onPrimary);
meow_gfx_fill_round(bx, by, sz, sz, r, th->primary);
int ax = bx + 7, ay1 = by + 13, ay2 = by + 17, b2 = bx + 11, cx = bx + 18, cy = by + 7;
meow_gfx_line(ax, ay1, b2, ay2, 2, th->onPrimary);
meow_gfx_line(b2, ay2, cx, cy, 2, th->onPrimary);
} else {
meow_ui_draw_rect_round(w->x, w->y, sz, sz, r, th->outline);
meow_ui_draw_rect_round(bx, by, sz, sz, r, th->outline);
}
if (w->state & MEOW_WS_FOCUSED) meow_ui_draw_rect_round(w->x, w->y, sz, sz, r, th->primary);
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, sz, sz, r, th->onSurface, 90);
if (w->state & MEOW_WS_FOCUSED) meow_ui_draw_rect_round(bx, by, sz, sz, r, th->primary);
if (disabled) meow_gfx_fill_round_alpha(bx, by, sz, sz, r, th->onSurface, 90);
break;
}
case W_RADIO: {
/* MD3(P3):视觉 20dp(环半径 10)于控件内居中;原环半径 12 略大。 */
int sel = (w->state & MEOW_WS_SELECTED) != 0;
int ccx = w->x + w->w / 2, ccy = w->y + w->h / 2;
meow_gfx_ring_arc(ccx, ccy, 12, 2, 0, 360, sel ? th->primary : th->outline);
if (sel) meow_gfx_fill_circle(ccx, ccy, 6, th->primary);
meow_gfx_ring_arc(ccx, ccy, 10, 2, 0, 360, sel ? th->primary : th->outline);
if (sel) meow_gfx_fill_circle(ccx, ccy, 5, th->primary);
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, w->w / 2, th->onSurface, 90);
break;
}
@@ -2154,7 +2222,8 @@ void meow_widget_draw(meow_widget *w)
for (int i = 0; i < w->nitems; i++) {
int ix = w->x + i * iw;
int sel = (i == w->selected);
if (sel) meow_gfx_fill_round(ix + iw / 2 - 24, w->y + 8, 48, 22, 11, th->secondaryContainer);
if (sel) /* MD3 active pill 64×32(原 48×22 偏小);垂直居中于栏 */
meow_gfx_fill_round(ix + iw / 2 - 32, w->y + (w->h - 32) / 2, 64, 32, 16, th->secondaryContainer);
uint32_t col = sel ? th->onSecondaryContainer : th->onSurfaceVariant;
meow_ui_text_center(ix + iw / 2, ay, w->items[i], th->font_body, col);
}
+171
View File
@@ -0,0 +1,171 @@
/* test_meow_md3.c — 宿主验证 MD3(Material You)对齐修复(agent-434 P0/P1/P3):
* P0 listview 容器模式(默认 renderer:行=surfaceContainer 圆角卡 + 行间空隙 + 去分隔线);
* P1 按钮胶囊化(圆角=h/2);
* P3 Switch off-track/off-thumb、NavigationBar active pill 64×32、Checkbox/Radio 视觉尺寸。
* 断言全部「画完读 back_buf 像素」(region_has/px_near),不依赖 /dev/fb0/字库(meow_font 打桩)。
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -Wall -Wextra -o test_meow_md3 test_meow_md3.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
* 运行:./test_meow_md3
*/
#include "meow_ui.h"
#include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static void rd_px(int x, int y, int *r, int *g, int *b)
{
unsigned char *bb = meow_ui_back_buf();
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
if (!bb || x < 0 || y < 0) { *r = *g = *b = 0; return; }
if (bpp == 16) {
uint16_t px = *(uint16_t *)(bb + (size_t)y * line + (size_t)x * 2);
*r = (((px >> 11) & 0x1F) << 3) | ((px >> 11) & 0x1F) >> 2;
*g = (((px >> 5) & 0x3F) << 2) | ((px >> 5) & 0x3F) >> 4;
*b = ((px & 0x1F) << 3) | ((px & 0x1F) >> 2);
} else {
uint32_t px = *(uint32_t *)(bb + (size_t)y * line + (size_t)x * 4);
*r = (px >> 16) & 0xFF; *g = (px >> 8) & 0xFF; *b = px & 0xFF;
}
}
static int near2(int a, int b) { int d = a - b; if (d < 0) d = -d; return d <= 12; }
static int px_near(int x, int y, int r, int g, int b)
{
int rr, gg, bb; rd_px(x, y, &rr, &gg, &bb);
return near2(rr, r) && near2(gg, g) && near2(bb, b);
}
#define UNPK(c, rr, gg, bb) do { (rr) = ((c) >> 16) & 0xFF; (gg) = ((c) >> 8) & 0xFF; (bb) = (c) & 0xFF; } while (0)
/* Listview src2 数据源:2 行普通富 item(无图标、无右尾件,label-only)。 */
typedef struct { const char *title; const char *sub; int right; int enabled; } trow_t;
static trow_t g_rows[2] = {
{ "Row 0", NULL, MEOW_LIST_RIGHT_NONE, 1 },
{ "Row 1", NULL, MEOW_LIST_RIGHT_NONE, 1 },
};
static int trow_src(int idx, meow_list_item *out, void *ctx)
{
(void)ctx;
if (idx < 0 || idx >= 2) return 0;
out->icon_kind = MEOW_LIST_ICON_NONE;
out->title = g_rows[idx].title;
out->subtitle = g_rows[idx].sub;
out->right = g_rows[idx].right;
out->on = 0;
out->enabled = g_rows[idx].enabled;
return 1;
}
int main(void)
{
setenv("MEOW_UI_DUMP", "/tmp/md3.ppm", 1);
meow_ui_opts o = { 480, 720 };
assert(meow_ui_init(&o) == 0);
assert(meow_ui_width() == 480 && meow_ui_height() == 720);
const meow_theme *th = meow_theme_get();
int sf_r, sf_g, sf_b, sc_r, sc_g, sc_b, p_r, p_g, p_b;
int pc_r, pc_g, pc_b, ov_r, ov_g, ov_b, sch_r, sch_g, sch_b, scs_r, scs_g, scs_b;
UNPK(th->surface, sf_r, sf_g, sf_b);
UNPK(th->surfaceContainer, sc_r, sc_g, sc_b);
UNPK(th->primary, p_r, p_g, p_b);
UNPK(th->primaryContainer, pc_r, pc_g, pc_b);
UNPK(th->outline, ov_r, ov_g, ov_b);
UNPK(th->surfaceContainerHighest, sch_r, sch_g, sch_b);
UNPK(th->secondaryContainer, scs_r, scs_g, scs_b);
/* ---- P0 listview 容器模式:行=surfaceContainer 圆角卡 + 空隙,去分隔线 ---- */
meow_widget *lv = meow_listview_create(20, 240, 320, 112, 2, trow_src, NULL, MEOW_LIST_SEL_SINGLE);
assert(lv);
assert(meow_listview_set_item_h(lv, 56) == 56);
assert(meow_listview_set_container(lv, 1) == 0);
meow_ui_clear(th->surface);
meow_widget_draw(lv);
/* 行 0 卡(x=28,y=244,w=304,h=48):中心=surfaceContainer;行 1 同。 */
assert(px_near(180, 268, sc_r, sc_g, sc_b));
assert(px_near(180, 324, sc_r, sc_g, sc_b));
/* 卡间空隙(y=292..300):surface(行间分隔用空隙)。 */
assert(px_near(180, 296, sf_r, sf_g, sf_b));
/* 去分隔线:行边界不再画 outlineVariant(改空隙代替)。 */
assert(!px_near(180, 292, ov_r, ov_g, ov_b));
/* 选中行 0 → primaryContainer(tonal,非满幅实心)。 */
meow_listview_set_sel(lv, 0);
meow_ui_clear(th->surface);
meow_widget_draw(lv);
assert(px_near(180, 268, pc_r, pc_g, pc_b));
assert(px_near(180, 324, sc_r, sc_g, sc_b)); /* 行 1 未选仍卡 */
printf("PASS listview container: rounded card + gap + no-divider + tonal select\n");
/* ---- P1 按钮胶囊化:圆角=h/2(56/2=28)---- */
meow_widget *btn = meow_btn_create_variant(10, 10, 200, 56, "FILLED", MEOW_BTN_FILLED);
assert(btn);
meow_ui_clear(th->surface);
meow_widget_draw(btn);
assert(px_near(110, 22, p_r, p_g, p_b)); /* 上部(避开文字)=primary */
/* 顶缘中心在圆角胶囊内(仍 primary);角落(圆角外)回 surface。 */
assert(px_near(110, 13, p_r, p_g, p_b));
assert(px_near(12, 12, sf_r, sf_g, sf_b)); /* 角落被圆角切掉 */
/* 同高矮按钮(h=40 → 圆角 20):上部仍 primary,角落 surface。 */
meow_widget *btn2 = meow_btn_create_variant(10, 120, 120, 40, "B", MEOW_BTN_FILLED);
assert(btn2);
meow_ui_clear(th->surface);
meow_widget_draw(btn2);
assert(px_near(70, 125, p_r, p_g, p_b));
assert(px_near(12, 122, sf_r, sf_g, sf_b));
printf("PASS button: capsule radius = h/2\n");
/* ---- P3 Switch off-track/off-thumb ---- */
meow_widget *sw = meow_switch_create(20, 300, 0); /* off */
assert(sw);
meow_ui_clear(th->surface);
meow_widget_draw(sw);
assert(px_near(60, 316, sch_r, sch_g, sch_b)); /* off-track=surfaceContainerHighest */
assert(px_near(36, 316, sf_r, sf_g, sf_b)); /* off-thumb=surface(表面色,非恒白) */
meow_switch_set(sw, 1);
meow_ui_clear(th->surface);
meow_widget_draw(sw);
assert(px_near(28, 316, p_r, p_g, p_b)); /* on-track=primary(thumb 左侧) */
printf("PASS switch: off-track surfaceContainerHighest / off-thumb surface\n");
/* ---- P3 NavigationBar active pill 64×32 ---- */
meow_widget *nb = meow_navigationbar_create(0, 600, 480, 80);
assert(nb);
meow_navigationbar_add_item(nb, "A");
meow_navigationbar_add_item(nb, "B");
meow_navigationbar_add_item(nb, "C");
meow_navigationbar_add_item(nb, "D");
meow_navigationbar_set_selected(nb, 0);
meow_ui_clear(th->surface);
meow_widget_draw(nb);
/* item0 pill 中心 (ix+60, y+40)=(60,640) — 64×32 居中于栏;pill 内=secondaryContainer。 */
assert(px_near(60, 640, scs_r, scs_g, scs_b));
/* pill 外(栏上部)= surfaceContainer(navbar 容器底)。 */
assert(px_near(60, 610, sc_r, sc_g, sc_b));
printf("PASS navbar: active pill 64x32 (was 48x22)\n");
/* ---- P3 Checkbox 视觉 24dp(原 34 偏大):勾选框视觉居中,角落回 surface ---- */
meow_widget *ck = meow_checkbox_create(150, 60);
assert(ck);
meow_ui_clear(th->surface);
meow_widget_draw(ck);
/* 未选:24dp 描边框(左缘=150+5=155);框边=outline;内部=surface(空心)。 */
assert(px_near(155, 77, ov_r, ov_g, ov_b)); /* 框左缘 stroke */
assert(px_near(167, 77, sf_r, sf_g, sf_b)); /* 内部空心 */
meow_widget_set_selected(ck, 1);
meow_ui_clear(th->surface);
meow_widget_draw(ck);
/* 选中:24dp 实心 primary 居中(左缘 155..179 顶 65..89);控件矩形角落(视觉 24 外)不填。 */
assert(px_near(150, 60, sf_r, sf_g, sf_b)); /* 视觉 24 外 → surface */
assert(px_near(167, 77, p_r, p_g, p_b)); /* 视觉中心=primary */
printf("PASS checkbox: 24dp visual (was 34)\n");
meow_widget_free(lv); meow_widget_free(btn); meow_widget_free(btn2);
meow_widget_free(sw); meow_widget_free(nb); meow_widget_free(ck);
printf("ALL MD3 PASS\n");
return 0;
}
+45 -17
View File
@@ -1,13 +1,18 @@
/* test_overlay_suppress.c — §10.5 叠加让位 present 自抑宿主验证(离屏 480x720,16bpp)。
*
* 背景:媒体播放中的 App 前台唤起通知中心时 launcher 不 SIGSTOP(保 PCM 供流),改由 SDK
* present 层见 /tmp/meow_notif_active(宿主用 MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏。
* present 层见 /tmp/meow_notif_active(宿主用 MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏;
* 批①后台常驻后标记**内容化**:内容=当前前台 app_id(空=菜单/叠加=全抑制)。本测试同时验证
* 批①多 App 常驻的「前台/后台」判定:内容==本进程 app_id(env MEOW_LOG_APP)→ 前台不抑制;
* 内容为其它 app_id / 空 → 后台常驻/被覆盖 → 自抑。
*
* 覆盖:
* T1 无标记:flush 正常上屏(frames_presented 递增,抑制计数 0);
* T2 有标记:flush 被抑制(只画进 back_buf 不 present:frames 不变、抑制计数递增);
* T2 标记=其它 app_id(后台常驻)→ 抑制(只画进 back_buf 不上屏:frames 不变、计数递增);
* T2b 抑制期第二次 flush 仍被抑制(200ms 节流缓存内行为一致);
* T3 标记消失:下一次 flush 恢复上屏(抑制期累积脏区一次上屏,无需重画);
* T2c 标记=自身 app_id(前台 App)→ 不抑制(恢复上屏,恢复边沿整帧重画;计数不再增长);
* T3 标记=空内容(菜单/叠加全抑制)→ 仍抑制;
* T3b 标记消失 → 恢复上屏(抑制期累积脏区一次上屏,无需重画);
* T4 系统 UI 进程(launcher,system_mode=1)即使有标记也不抑制(面板必须照常上屏)。
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
@@ -27,12 +32,15 @@
#include <fcntl.h>
#define MARKER "/tmp/test_notif_active_sdk"
#define SELF_ID "testapp"
static void marker_write(void)
static void marker_write(const char *content)
{
int fd = open(MARKER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
assert(fd >= 0);
if (write(fd, "1", 1) < 0) assert(0);
if (content && content[0]) {
if (write(fd, content, strlen(content)) < 0) assert(0);
}
close(fd);
}
@@ -40,6 +48,7 @@ int main(void)
{
setenv("MEOW_UI_DUMP", "/tmp/overlay_suppress.ppm", 1);
setenv("MEOW_NOTIF_ACTIVE_FILE", MARKER, 1);
setenv("MEOW_LOG_APP", SELF_ID, 1); /* 本测试进程身份=前台/后台的判定锚 */
unlink(MARKER);
meow_ui_opts o = { 480, 720 };
assert(meow_ui_init(&o) == 0);
@@ -53,15 +62,15 @@ int main(void)
assert(meow_gfx_overlay_suppressed_count() == 0);
printf("T1 PASS: no marker -> presented=1 suppressed=0\n");
/* T2:有标记 → 抑制(等 >200ms 越过存在性节流缓存) */
marker_write();
/* T2:标记=其它 app_id(后台常驻 App)→ 抑制(等 >200ms 越过存在性节流缓存) */
marker_write("ncm");
usleep(250000);
unsigned long long f0 = meow_ui_frames_presented();
meow_ui_clear(0x223344);
meow_ui_flush();
assert(meow_ui_frames_presented() == f0);
assert(meow_gfx_overlay_suppressed_count() == 1);
printf("T2 PASS: marker present -> suppressed (frames frozen at %llu)\n", f0);
printf("T2 PASS: marker=other app -> suppressed (frames frozen at %llu)\n", f0);
/* T2b:抑制期再次 flush 仍抑制(节流缓存内一致) */
meow_ui_clear(0x334455);
@@ -70,24 +79,43 @@ int main(void)
assert(meow_gfx_overlay_suppressed_count() == 2);
printf("T2b PASS: still suppressed within throttle window\n");
/* T3:标记消失 → 恢复上屏(抑制期累积的整帧脏一次上屏,无需重画) */
/* T2c:标记=自身 app_id(前台 App)→ 不抑制(批①前后台判定核心;恢复边沿整帧重画一次上屏) */
marker_write(SELF_ID);
usleep(250000);
meow_ui_clear(0x445566);
meow_ui_flush();
assert(meow_ui_frames_presented() == f0 + 1);
assert(meow_gfx_overlay_suppressed_count() == 2); /* 不再增长(前台不抑制) */
printf("T2c PASS: marker=self -> foreground, present resumed (accumulated dirty flushed)\n");
/* T3:标记=空内容(菜单/叠加层覆盖=全抑制)→ 仍抑制 */
marker_write("");
usleep(250000);
unsigned long long f1 = meow_ui_frames_presented();
meow_ui_clear(0x556677);
meow_ui_flush();
assert(meow_ui_frames_presented() == f1);
assert(meow_gfx_overlay_suppressed_count() == 3);
printf("T3 PASS: empty marker (menu/overlay) -> suppressed\n");
/* T3b:标记消失 → 恢复上屏(抑制期累积的整帧脏一次上屏) */
unlink(MARKER);
usleep(250000);
meow_ui_flush();
assert(meow_ui_frames_presented() == f0 + 1);
assert(meow_gfx_overlay_suppressed_count() == 2);
printf("T3 PASS: marker gone -> present resumed (accumulated dirty flushed)\n");
assert(meow_ui_frames_presented() == f1 + 1);
assert(meow_gfx_overlay_suppressed_count() == 3);
printf("T3b PASS: marker gone -> present resumed (accumulated dirty flushed)\n");
/* T4:系统 UI 进程(launcher)不抑制——通知中心面板本身必须照常上屏 */
meow_ui_sys_set_system_mode(1);
assert(meow_ui_sys_render_enabled() == 1);
marker_write();
marker_write("ncm");
usleep(250000);
unsigned long long f1 = meow_ui_frames_presented();
meow_ui_clear(0x445566);
unsigned long long f2 = meow_ui_frames_presented();
meow_ui_clear(0x667788);
meow_ui_flush();
assert(meow_ui_frames_presented() == f1 + 1);
assert(meow_gfx_overlay_suppressed_count() == 2); /* 不再增长 */
assert(meow_ui_frames_presented() == f2 + 1);
assert(meow_gfx_overlay_suppressed_count() == 3); /* 不再增长 */
unlink(MARKER);
printf("T4 PASS: system-UI process (launcher) never suppressed\n");
+1 -1
View File
@@ -28,7 +28,7 @@
# ---- 用户态/服务守护二进制(usr/bin,全部 chmod 755 静态 MIPS)----
patch_v2/bootmenu/meow_bootmenu|/usr/bin/meow_bootmenu|8de51144538d698c3ff82ee6651b8b1a|引导选单 M6.25(开机菜单/第二屏亮度按用户设置恢复:显示前读 settings.conf 写回,方案A)
patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|9515c9d8fce4bdd997da3873897395f9|M6.35 批③后台管理页(按钮化:每行右'杀'+顶栏'一键清除后台',sub_page_t row_draw/row_tap/top_action/top_action_cb)+i18n(settings/lock剩余中文补抽 meow_str_zh)+批①后台常驻框架+宽限期;链meow_ui.a(sdk meow_lyric/lyric_view/meow_img_color,meow_img_pixels访问器);0warning
patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|0c4f3c109e3c6657542e4822ecf93f51|M6.36 收编批①叠加/穿透根治(共享判定meow_ui_overlay_suppressed_active,launcher 7处转场)+MD3全面对齐(subpage/settings/bg/MENU列表圆角卡8px外距+按钮胶囊+状态层+shape令牌)+顶栏后台按钮图标+后台管理页圆角卡+批①后台常驻框架+批③后台管理页+i18n(settings/lock补抽);链meow_ui.a(sdk meow_lyric/lyric_view/meow_img_color,meow_img_pixels访问器);0warning
patch_v2/meow_watchdog/meow_watchdog|/usr/bin/meow_watchdog|34ddbf0275526eaf9be988134845c3af|应用看门狗 M6.23(亮屏亮度按用户设置 display.brightness 恢复,修 panel ON 固定60跳变)
patch_v2/meow_svcd/meow_svcd|/usr/bin/meow_svcd|605646c4cc93f2baf29071ea6bc870ea|服务管理器 M2.77(+崩溃上报 meow_crash agent-187)
mips-repo/bins/meow_mdns/bin/meow_mdns|/usr/bin/meow_mdns|d6b0151bda26768f19952dae1342610d|mDNS 应答器 M2.78(启动竞态+换IP重检 agent-194)