Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e68313981 | ||
|
|
6eccb17f9b | ||
|
|
62ec405dca | ||
|
|
b988d64653 |
No files matched your search
@@ -3,7 +3,7 @@
|
|||||||
> 把 HiBy R3 Pro II 便携播放器(君正 X1600,MIPS32r2 / mipsel / musl 静态软浮点,480×720,无 GPU 软渲染)变成一个可扩展的「第二系统 + 应用框架」。
|
> 把 HiBy R3 Pro II 便携播放器(君正 X1600,MIPS32r2 / mipsel / musl 静态软浮点,480×720,无 GPU 软渲染)变成一个可扩展的「第二系统 + 应用框架」。
|
||||||
> MeowOS 与官方 HibyOS 共存于同一台设备:开机由引导选单二选一;启动后 MeowOS 作为主控层,提供自己的桌面 UI、应用框架、服务管理与系统组件。
|
> MeowOS 与官方 HibyOS 共存于同一台设备:开机由引导选单二选一;启动后 MeowOS 作为主控层,提供自己的桌面 UI、应用框架、服务管理与系统组件。
|
||||||
>
|
>
|
||||||
> 版本:**M6.9(0.8.9,已自刷验收)**;**M6.10 打包中**(launcher `9302913b`+svc_perf `65f2059f`+crash `f02c4f45`,fw_content 已收编未构建)。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.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、包管理、媒体会话、独占音频服务、网页面板、流媒体客户端的完整第三方系统**。
|
||||||
>
|
>
|
||||||
> 许可证:Apache-2.0(见 [`LICENSE`](LICENSE))。本仓库为非官方第三方项目。
|
> 许可证:Apache-2.0(见 [`LICENSE`](LICENSE))。本仓库为非官方第三方项目。
|
||||||
> 主仓**公开可克隆**:`https://git.yeij.top/AskaEth/MeowOS-For-R3P2.git`(维护者推送走
|
> 主仓**公开可克隆**:`https://git.yeij.top/AskaEth/MeowOS-For-R3P2.git`(维护者推送走
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
- **双系统共存**:官方 HibyOS 原样保留;开机引导选单决定本次进入哪个系统(MeowOS / HibyOS),两者互斥运行,任一时刻只跑一个。
|
- **双系统共存**:官方 HibyOS 原样保留;开机引导选单决定本次进入哪个系统(MeowOS / HibyOS),两者互斥运行,任一时刻只跑一个。
|
||||||
- **应用框架**:一套 C 应用 SDK(`meow_ui`),第三方 App 用统一控件 / 输入 / 主题 / 字体 / 通知 API 开发,无需自读帧缓冲、自读输入、自管返回——把每个 App 都要踩一遍的坑收口到框架里。
|
- **应用框架**:一套 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)。
|
- **系统组件**:状态栏、通知中心、电源菜单、全局左缘返回手势、主界面(应用列表,**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 大页**;媒体键路由到最近活跃媒体会话。
|
- **媒体会话 / Now Playing**:`MSG_META`/`MSG_PLAYCTL`(0x72/0x73)媒体会话(最近活跃源,30s 超时回收)+ 通知中心**媒体卡片** + 主界面左滑 **Now Playing 大页**(播放页⇄歌词页双页,方向感知手势:播放页右→左进歌词/左缘右滑回桌面、歌词页左→右回播放);媒体键路由到最近活跃媒体会话。
|
||||||
- **应用生态**:**流媒体客户端 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 待真机取证))。**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)。
|
- **应用生态**:**流媒体客户端 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。
|
- **进程隔离**:系统 UI 渲染进程与 App 进程相互独立,App 卡死不会冻结整个 UI。
|
||||||
- **包管理**:`nyai`(固件内置 `/usr/bin/nyai`)支持 .mpk 安装/升级/导出/列表/索引,走 **version_code 安卓式版本规则**(缺 code 拒装、防降级)+ 签名分级(有签必验、`os.meow.*` 无签拒装、第三方直装告警)+ 导出过滤与体检。
|
- **包管理**:`nyai`(固件内置 `/usr/bin/nyai`)支持 .mpk 安装/升级/导出/列表/索引,走 **version_code 安卓式版本规则**(缺 code 拒装、防降级)+ 签名分级(有签必验、`os.meow.*` 无签拒装、第三方直装告警)+ 导出过滤与体检。
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#define NP_NEXT_CX (NP_PLAY_CX + 78)
|
#define NP_NEXT_CX (NP_PLAY_CX + 78)
|
||||||
#define NP_PLAY_R 30
|
#define NP_PLAY_R 30
|
||||||
#define NP_PREV_R 22
|
#define NP_PREV_R 22
|
||||||
#define NP_LYRIC_Y 640
|
|
||||||
|
|
||||||
static meow_img *g_np_cover = NULL;
|
static meow_img *g_np_cover = NULL;
|
||||||
static char g_np_cover_path[200] = "";
|
static char g_np_cover_path[200] = "";
|
||||||
@@ -53,15 +52,17 @@ static int np_lyric_width(const char *u, int size, void *ctx)
|
|||||||
return ui_width(u, size);
|
return ui_width(u, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 封面解码缓存(artwork 路径变化才重载)。 */
|
/* 封面解码缓存(artwork 路径变化才重载;路径未变但上次加载失败→允许重试)。
|
||||||
|
* M6.33 修「artwork 空→复原」封面不重载:原「路径未变即跳过」在「空 artwork 清空封面后再
|
||||||
|
* 以同路径复原」或「首载失败同路径重报」时封面永不恢复(g_np_cover==NULL 时即使路径未变也重载)。 */
|
||||||
static void np_cover_load(const meow_media_session_t *s)
|
static void np_cover_load(const meow_media_session_t *s)
|
||||||
{
|
{
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
if (strcmp(g_np_cover_path, s->artwork) == 0) return;
|
if (g_np_cover && strcmp(g_np_cover_path, s->artwork) == 0) return; /* 已载且未变 → 跳过 */
|
||||||
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
|
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
|
||||||
snprintf(g_np_cover_path, sizeof g_np_cover_path, "%s", s->artwork);
|
snprintf(g_np_cover_path, sizeof g_np_cover_path, "%s", s->artwork);
|
||||||
if (!s->artwork[0]) return;
|
if (!s->artwork[0]) return;
|
||||||
char path[200];
|
char path[240]; /* 相对 artwork 加前缀最坏 191+19=210 < 240,防 -Wformat-truncation 实际截断 */
|
||||||
if (s->artwork[0] == '/') snprintf(path, sizeof path, "%s", s->artwork);
|
if (s->artwork[0] == '/') snprintf(path, sizeof path, "%s", s->artwork);
|
||||||
else snprintf(path, sizeof path, "/usr/data/mnt/sd_0/%s", s->artwork);
|
else snprintf(path, sizeof path, "/usr/data/mnt/sd_0/%s", s->artwork);
|
||||||
g_np_cover = meow_img_load_file(path);
|
g_np_cover = meow_img_load_file(path);
|
||||||
@@ -347,7 +348,10 @@ static void np_draw_lyric_page(int redraw_sb)
|
|||||||
if (redraw_sb) fill_rect(0, 0, W, SB_BAR_H, th->surface);
|
if (redraw_sb) fill_rect(0, 0, W, SB_BAR_H, th->surface);
|
||||||
/* 批5:封面取色背景(主→次渐变)或主题面;并注入歌词页 set_colors。
|
/* 批5:封面取色背景(主→次渐变)或主题面;并注入歌词页 set_colors。
|
||||||
* 批6:有封面时以封面作整页背景(衬在 lyric_view 半透明 scrim 之下),取色只做附加;
|
* 批6:有封面时以封面作整页背景(衬在 lyric_view 半透明 scrim 之下),取色只做附加;
|
||||||
* 无封面回退渐变/主题面。 */
|
* 无封面回退渐变/主题面。
|
||||||
|
* M6.33:先 np_cover_load(artwork 空→复原/换源时重载封面)再取色——顺序不能反,
|
||||||
|
* 取色读 g_np_cover,须在封面载入后(否则歌词页在 artwork 复原后仍无封面)。 */
|
||||||
|
np_cover_load(meow_media_current());
|
||||||
np_cover_theme_update();
|
np_cover_theme_update();
|
||||||
np_apply_lyric_colors();
|
np_apply_lyric_colors();
|
||||||
if (g_np_cover)
|
if (g_np_cover)
|
||||||
@@ -420,9 +424,8 @@ static void np_draw_page(int redraw_sb)
|
|||||||
np_draw_progress(s, pos, th);
|
np_draw_progress(s, pos, th);
|
||||||
/* 控制按钮(SDK meow_iconbtn,命中/状态走 SDK) */
|
/* 控制按钮(SDK meow_iconbtn,命中/状态走 SDK) */
|
||||||
np_draw_ctl(s);
|
np_draw_ctl(s);
|
||||||
/* 歌词占位(M7.0b 文案走 meow_str) */
|
/* 播放页底部干净(M6.33:歌词占位已移除——已有独立歌词页,占位多余;
|
||||||
const char *ly = meow_str_zh("np.lyric_placeholder", "歌词(占位)");
|
* 歌词页无歌词时的空态由歌词页自身承载,播放页不再显示歌词占位文案)。 */
|
||||||
ui_draw((W - ui_width(ly, 16)) / 2, NP_LYRIC_Y, ly, th->on_surface_variant, 16);
|
|
||||||
if (redraw_sb) draw_statusbar();
|
if (redraw_sb) draw_statusbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,6 +507,7 @@ void run_now_playing(struct app *apps, int napps)
|
|||||||
char last_title[128] = "";
|
char last_title[128] = "";
|
||||||
uint32_t last_state = 0xFFFFFFFFu;
|
uint32_t last_state = 0xFFFFFFFFu;
|
||||||
char last_app[MEOW_APP_ID_MAX] = "";
|
char last_app[MEOW_APP_ID_MAX] = "";
|
||||||
|
char last_art[200] = ""; /* M6.33:上次已刷新封面 artwork 路径(artwork 变化也触发整页重绘) */
|
||||||
|
|
||||||
meow_ui_activity_push(NP_SENTINEL_ACT); /* M7.0b:进入 push 哨兵(与函数尾 pop 配对)。
|
meow_ui_activity_push(NP_SENTINEL_ACT); /* M7.0b:进入 push 哨兵(与函数尾 pop 配对)。
|
||||||
* 控件 owner 一直钉哨兵,命中要走 meow_widget_hit 的
|
* 控件 owner 一直钉哨兵,命中要走 meow_widget_hit 的
|
||||||
@@ -644,16 +648,20 @@ void run_now_playing(struct app *apps, int napps)
|
|||||||
/* 息屏期只低频轮询按键(唤醒用),不重画不读触摸(FBIOBLANK 已挂起 goodix) */
|
/* 息屏期只低频轮询按键(唤醒用),不重画不读触摸(FBIOBLANK 已挂起 goodix) */
|
||||||
if (!meow_screen_on) { usleep(100 * 1000); continue; }
|
if (!meow_screen_on) { usleep(100 * 1000); continue; }
|
||||||
|
|
||||||
/* 媒体变化检测(元数据/源/状态):立即整页重画 */
|
/* 媒体变化检测(元数据/源/状态/artwork):立即整页重画。
|
||||||
|
* M6.33 补 artwork:ncm 状态切换可能先报空 artwork 再复原(标题/源/状态未变),
|
||||||
|
* 原检测漏 artwork → 封面清空后不再触发整页重绘 → 播放页/歌词页封面永不恢复。 */
|
||||||
const meow_media_session_t *s = meow_media_current();
|
const meow_media_session_t *s = meow_media_current();
|
||||||
int meta_changed = !s || strcmp(last_app, s->app_id) ||
|
int meta_changed = !s || strcmp(last_app, s->app_id) ||
|
||||||
strcmp(last_title, s->title) || last_state != s->state;
|
strcmp(last_title, s->title) || strcmp(last_art, s->artwork) ||
|
||||||
|
last_state != s->state;
|
||||||
if (meta_changed) {
|
if (meta_changed) {
|
||||||
if (s) {
|
if (s) {
|
||||||
snprintf(last_app, sizeof last_app, "%s", s->app_id);
|
snprintf(last_app, sizeof last_app, "%s", s->app_id);
|
||||||
snprintf(last_title, sizeof last_title, "%s", s->title);
|
snprintf(last_title, sizeof last_title, "%s", s->title);
|
||||||
|
snprintf(last_art, sizeof last_art, "%s", s->artwork);
|
||||||
last_state = s->state;
|
last_state = s->state;
|
||||||
} else { last_app[0] = 0; last_title[0] = 0; last_state = 0xFFFFFFFFu; }
|
} else { last_app[0] = 0; last_title[0] = 0; last_art[0] = 0; last_state = 0xFFFFFFFFu; }
|
||||||
need_redraw = 1;
|
need_redraw = 1;
|
||||||
}
|
}
|
||||||
/* M2.92 修死循环:空态/停止态只在「变化」时画一次(meta_changed 已覆盖
|
/* M2.92 修死循环:空态/停止态只在「变化」时画一次(meta_changed 已覆盖
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/* test_np_cover.c — M6.33 播放页/歌词页封面 lifecycle 宿主验证。
|
||||||
|
*
|
||||||
|
* 背景:ncm 状态切换可能先上报空 artwork(媒体会话 artwork 短暂为空/换源),随后复原;
|
||||||
|
* launcher 侧封面渲染在 now_playing.c(静态 np_cover_load / run_now_playing 的 meta_changed)。
|
||||||
|
* 本测试验证两处 M6.33 修复的语义:
|
||||||
|
* np_cover_load 缓存决策:artwork 变化重载;artwork 为空→清封面(回退主题占位);
|
||||||
|
* 同路径但封面为空(此前加载失败/空清空后)→ 允许重试重载(旧逻辑「路径未变即跳过」会卡死)。
|
||||||
|
* run_now_playing meta_changed:补 artwork 追踪,artwork-only 变化也触发整页重绘。
|
||||||
|
*
|
||||||
|
* 说明:np_cover_load 为 now_playing.c 内 static(launcher TU,宿主不可链),故本测试按项目
|
||||||
|
* mirror 惯例(同 test_now_playing_widgets 镜像几何)逐字镜像其决策,用**真实 SDK 图片加载**(
|
||||||
|
* meow_img_load_file/meow_img_free + stb_image)驱动真实 BMP,验证 artwork 生命周期。
|
||||||
|
*
|
||||||
|
* 编译(在 patch_v2/meow_launcherd 下;链法同 test_now_playing_widgets + meow_img_color):
|
||||||
|
* gcc -Wall -Wextra -o test_np_cover test_np_cover.c \
|
||||||
|
* ../sdk/test_host_stub.c ../sdk/meow_ui_gfx.c ../sdk/meow_ui_sys.c \
|
||||||
|
* ../sdk/meow_ui_theme.c ../sdk/meow_ui_vol.c ../sdk/meow_ui_log.c \
|
||||||
|
* ../sdk/meow_ui_widget.c ../sdk/meow_ui_input.c ../sdk/meow_ui_activity.c \
|
||||||
|
* ../sdk/meow_ui_dialog.c ../sdk/meow_ui_ime.c ../sdk/meow_ui_str.c ../sdk/meow_pinyin.c \
|
||||||
|
* ../sdk/meow_ui_draw.c ../sdk/meow_ui_view.c ../sdk/meow_ui_box.c ../sdk/meow_ui_anim.c \
|
||||||
|
* ../sdk/meow_ui_scroll.c ../sdk/meow_ui_img.c ../sdk/meow_ui_media.c \
|
||||||
|
* ../sdk/meow_notify_ipc.c ../sdk/stb_image.c ../sdk/meow_lyric.c ../sdk/meow_lyric_view.c \
|
||||||
|
* ../sdk/meow_img_color.c \
|
||||||
|
* -I../sdk -I. -DMEOW_UI_HOST_TEST -pthread -lm
|
||||||
|
* 运行:./test_np_cover
|
||||||
|
*/
|
||||||
|
#include "meow_ui.h"
|
||||||
|
#include "meow_media.h" /* meow_media_session_t(取 artwork 字段) */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* 局部补全 meow_img(与 meow_ui_img.c / meow_img_color.c 布局一致):读取已加载封面尺寸。 */
|
||||||
|
struct meow_img { int w, h; unsigned char *rgba; };
|
||||||
|
static int img_w(meow_img *im) { return im ? ((struct meow_img *)im)->w : 0; }
|
||||||
|
static int img_h(meow_img *im) { return im ? ((struct meow_img *)im)->h : 0; }
|
||||||
|
|
||||||
|
static int g_fail = 0;
|
||||||
|
#define CHECK(cond, name) do { \
|
||||||
|
if (cond) printf(" PASS %s\n", name); \
|
||||||
|
else { printf(" FAIL %s (%s:%d)\n", name, __FILE__, __LINE__); g_fail++; } } while (0)
|
||||||
|
|
||||||
|
/* ================= M6.33 镜像 np_cover_load(now_playing.c 逐字语义) ================= */
|
||||||
|
static meow_img *g_np_cover = NULL;
|
||||||
|
static char g_np_cover_path[200] = "";
|
||||||
|
|
||||||
|
static void np_cover_load(const meow_media_session_t *s)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
if (g_np_cover && strcmp(g_np_cover_path, s->artwork) == 0) return; /* 已载且未变 → 跳过 */
|
||||||
|
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
|
||||||
|
snprintf(g_np_cover_path, sizeof g_np_cover_path, "%s", s->artwork);
|
||||||
|
if (!s->artwork[0]) return;
|
||||||
|
char path[240];
|
||||||
|
if (s->artwork[0] == '/') snprintf(path, sizeof path, "%s", s->artwork);
|
||||||
|
else snprintf(path, sizeof path, "/usr/data/mnt/sd_0/%s", s->artwork);
|
||||||
|
g_np_cover = meow_img_load_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= M6.33 镜像 run_now_playing meta_changed(补 artwork 追踪) ========= */
|
||||||
|
static int meta_changed_needs_redraw(const char *last_app, const char *last_title,
|
||||||
|
uint32_t last_state, const char *last_art,
|
||||||
|
const meow_media_session_t *s)
|
||||||
|
{
|
||||||
|
if (!s) return 1; /* 源消失 → 空态重画 */
|
||||||
|
return strcmp(last_app, s->app_id) || strcmp(last_title, s->title) ||
|
||||||
|
strcmp(last_art, s->artwork) || last_state != s->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ 小工具:写 24-bit 无压缩 BMP 到 path(stb_image 可读),纯色填充 ============
|
||||||
|
* 结构:BITMAPFILEHEADER(14) + BITMAPINFOHEADER(40) + 像素(bottom-up,BGR,行对齐 4B)。 */
|
||||||
|
static void write_bmp(const char *path, int w, int h, int r, int g, int b)
|
||||||
|
{
|
||||||
|
int rowPitch = (w * 3 + 3) & ~3;
|
||||||
|
int imgSize = rowPitch * h;
|
||||||
|
int fileSize = 54 + imgSize;
|
||||||
|
unsigned char hdr[54];
|
||||||
|
memset(hdr, 0, sizeof hdr);
|
||||||
|
hdr[0] = 'B'; hdr[1] = 'M';
|
||||||
|
hdr[2] = (unsigned char)(fileSize & 0xFF);
|
||||||
|
hdr[3] = (unsigned char)((fileSize >> 8) & 0xFF);
|
||||||
|
hdr[4] = (unsigned char)((fileSize >> 16) & 0xFF);
|
||||||
|
hdr[5] = (unsigned char)((fileSize >> 24) & 0xFF);
|
||||||
|
hdr[10] = 54;
|
||||||
|
hdr[14] = 40;
|
||||||
|
hdr[18] = (unsigned char)(w & 0xFF);
|
||||||
|
hdr[19] = (unsigned char)((w >> 8) & 0xFF);
|
||||||
|
hdr[22] = (unsigned char)(h & 0xFF);
|
||||||
|
hdr[23] = (unsigned char)((h >> 8) & 0xFF);
|
||||||
|
hdr[24] = (unsigned char)((h >> 16) & 0xFF);
|
||||||
|
hdr[25] = (unsigned char)((h >> 24) & 0xFF);
|
||||||
|
hdr[26] = 1; hdr[28] = 24; /* planes=1, bpp=24 */
|
||||||
|
hdr[34] = (unsigned char)(imgSize & 0xFF);
|
||||||
|
hdr[35] = (unsigned char)((imgSize >> 8) & 0xFF);
|
||||||
|
hdr[36] = (unsigned char)((imgSize >> 16) & 0xFF);
|
||||||
|
hdr[37] = (unsigned char)((imgSize >> 24) & 0xFF);
|
||||||
|
FILE *f = fopen(path, "wb");
|
||||||
|
if (!f) { printf(" !! cannot write %s\n", path); exit(1); }
|
||||||
|
fwrite(hdr, 1, sizeof hdr, f);
|
||||||
|
unsigned char *row = (unsigned char *)malloc((size_t)rowPitch);
|
||||||
|
memset(row, 0, (size_t)rowPitch);
|
||||||
|
for (int x = 0; x < w; x++) { row[x * 3 + 0] = (unsigned char)b; row[x * 3 + 1] = (unsigned char)g; row[x * 3 + 2] = (unsigned char)r; }
|
||||||
|
for (int y = 0; y < h; y++) fwrite(row, 1, (size_t)rowPitch, f);
|
||||||
|
free(row);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
setenv("MEOW_UI_DUMP", "/tmp/npcover.ppm", 1);
|
||||||
|
meow_ui_opts o = { 480, 720 };
|
||||||
|
if (meow_ui_init(&o) != 0) { printf(" FAIL meow_ui_init\n"); return 1; }
|
||||||
|
meow_ui_activity_push(0x57);
|
||||||
|
|
||||||
|
/* 两张真实封面文件:A=蓝,B=红 */
|
||||||
|
const char *pa = "/tmp/np_cover_a.bmp", *pb = "/tmp/np_cover_b.bmp";
|
||||||
|
const char *pgone = "/tmp/np_cover_missing.bmp"; /* 先不存在 → 加载失败 */
|
||||||
|
write_bmp(pa, 16, 16, 0x40, 0x80, 0xC0); /* 蓝 16x16 */
|
||||||
|
write_bmp(pb, 12, 12, 0xC0, 0x40, 0x40); /* 红 12x12(尺寸不同,便于断言「已替换」) */
|
||||||
|
|
||||||
|
meow_media_session_t s; memset(&s, 0, sizeof s);
|
||||||
|
s.used = 1; s.state = MEOW_MEDIA_PLAYING;
|
||||||
|
strcpy(s.app_id, "ncm"); strcpy(s.title, "song"); strcpy(s.artist, "artist");
|
||||||
|
|
||||||
|
/* ============ C1 artwork 有 → 封面载入(播放页/歌词页共用 g_np_cover) ============ */
|
||||||
|
printf("C1 artwork present -> cover loads\n");
|
||||||
|
memset(g_np_cover_path, 0, sizeof g_np_cover_path);
|
||||||
|
strcpy(s.artwork, pa);
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover != NULL, "artwork present -> cover loaded (non-NULL)");
|
||||||
|
CHECK(img_w(g_np_cover) == 16 && img_h(g_np_cover) == 16, "cover is 16x16");
|
||||||
|
/* 同路径未变 → 缓存命中,不重载(指针不变) */
|
||||||
|
meow_img *keep = g_np_cover;
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover == keep, "same path unchanged -> cache hit (no reload)");
|
||||||
|
|
||||||
|
/* ============ C2 artwork 空 → 封面清空(回退主题占位) ============ */
|
||||||
|
printf("C2 artwork empty -> cover cleared (theme placeholder)\n");
|
||||||
|
memset(s.artwork, 0, sizeof s.artwork);
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover == NULL, "artwork empty -> cover NULL (theme placeholder)");
|
||||||
|
CHECK(g_np_cover_path[0] == 0, "empty -> cached path reset to \"\"");
|
||||||
|
|
||||||
|
/* ============ C3 artwork 从空复原(同路径)→ 封面重载(M6.33 修复) ============ */
|
||||||
|
printf("C3 artwork restored (same path) -> cover reloads (M6.33)\n");
|
||||||
|
strcpy(s.artwork, pa);
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover != NULL, "restore from empty -> cover reloaded");
|
||||||
|
CHECK(img_w(g_np_cover) == 16, "reloaded cover intact");
|
||||||
|
|
||||||
|
/* ============ C4 换源(A→B 另一路径)→ 封面替换 ============ */
|
||||||
|
printf("C4 artwork switch (A->B) -> cover replaced\n");
|
||||||
|
strcpy(s.artwork, pb);
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover != NULL, "switch to B -> cover loaded");
|
||||||
|
/* 用尺寸(而非指针身份,malloc 会复用地址)证明确实换成 B */
|
||||||
|
CHECK(img_w(g_np_cover) == 12 && img_h(g_np_cover) == 12, "cover replaced by B (12x12)");
|
||||||
|
CHECK(strcmp(g_np_cover_path, pb) == 0, "cache path updated to B");
|
||||||
|
|
||||||
|
/* ============ C5 首载失败同路径重报 → 重试成功(M6.33 修复) ============ */
|
||||||
|
printf("C5 first load fails then same-path re-report -> retry succeeds (M6.33)\n");
|
||||||
|
memset(g_np_cover_path, 0, sizeof g_np_cover_path);
|
||||||
|
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
|
||||||
|
/* 缺失文件首载失败:cover NULL, cache path=missing */
|
||||||
|
strcpy(s.artwork, pgone);
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover == NULL, "nonexistent path -> cover NULL");
|
||||||
|
CHECK(strcmp(g_np_cover_path, pgone) == 0, "cache path = missing (recorded)");
|
||||||
|
/* 文件此刻出现,仍同路径重报 → 旧逻辑「路径未变即跳过」卡死;新逻辑 g_np_cover==NULL 重试 */
|
||||||
|
write_bmp(pgone, 8, 8, 0x20, 0xE0, 0x20); /* 绿 */
|
||||||
|
np_cover_load(&s);
|
||||||
|
CHECK(g_np_cover != NULL, "same path + cover NULL -> reload succeeds");
|
||||||
|
CHECK(img_w(g_np_cover) == 8, "retried cover is 8x8");
|
||||||
|
remove(pgone);
|
||||||
|
|
||||||
|
/* ============ C6 meta_changed 补 artwork:artwork-only 变化也触发整页重绘 ============ */
|
||||||
|
printf("C6 meta_changed tracks artwork (M6.33)\n");
|
||||||
|
memset(&s, 0, sizeof s); s.used = 1; s.state = MEOW_MEDIA_PLAYING;
|
||||||
|
strcpy(s.app_id, "ncm"); strcpy(s.title, "song"); strcpy(s.artwork, pa);
|
||||||
|
CHECK(meta_changed_needs_redraw("ncm", "song", MEOW_MEDIA_PLAYING, pa, &s) == 0,
|
||||||
|
"no change -> no redraw");
|
||||||
|
/* 仅 artwork 变化(app/title/state 全同)→ 旧逻辑=0(不重绘→封面卡空);新逻辑=1 */
|
||||||
|
CHECK(meta_changed_needs_redraw("ncm", "song", MEOW_MEDIA_PLAYING, "", &s) == 1,
|
||||||
|
"artwork-only change -> redraw (was missed before M6.33)");
|
||||||
|
CHECK(meta_changed_needs_redraw("ncm", "song", MEOW_MEDIA_PLAYING, pb, &s) == 1,
|
||||||
|
"artwork path switch -> redraw");
|
||||||
|
CHECK(meta_changed_needs_redraw("ncm", "song", MEOW_MEDIA_PLAYING, pa, &s) == 0,
|
||||||
|
"same art same title -> no redraw");
|
||||||
|
|
||||||
|
/* 清理 */
|
||||||
|
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
|
||||||
|
remove(pa); remove(pb);
|
||||||
|
|
||||||
|
meow_ui_activity_pop();
|
||||||
|
meow_ui_shutdown();
|
||||||
|
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
|
||||||
|
return g_fail;
|
||||||
|
}
|
||||||
@@ -156,20 +156,46 @@ void player_manager_set_quality(int level, int cache_gb)
|
|||||||
pthread_mutex_unlock(&g_play.mu);
|
pthread_mutex_unlock(&g_play.mu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 同曲重入护栏的「同曲」判据(任务:跨歌单切换后,同一队列下标指向的是**另一首歌**)。
|
||||||
|
* 裸下标(song_idx==cur_song)在换歌单(播放队列内容变化)后不再成立——实测真机日志
|
||||||
|
* 「play: re-enter current idx=0 state=2 -> no restart」:A 停在 idx=0,切到 B 后点 idx=0
|
||||||
|
* 被当「点正在播的曲」吞掉 → 新歌永不起播、播放页停在上首+暂停;恢复播放(旧曲播完自动顺延
|
||||||
|
* 到不同下标)后才正常切换。故改以「候选曲 id == 正在播快照 id」判同曲:确证同曲才免重播
|
||||||
|
* 聚焦;候选未物化/队列已换(id 不符)→ 非确证,按新播放意图起播(正确方向)。
|
||||||
|
* 候选经 appstate_song_copy/appstate_search_song 做**廉价窗/数组拷贝**,绝不触网络
|
||||||
|
* (占位行未物化即返回 0,交下方正常起播)。不持 g_play.mu 调用(内部自锁读快照 id)。 */
|
||||||
|
static int play_is_same_song(int song_idx)
|
||||||
|
{
|
||||||
|
ncm_song_t cand;
|
||||||
|
int have = (appstate_get_queue() == NCM_QUEUE_SEARCH)
|
||||||
|
? (appstate_search_song(song_idx, &cand) == NCM_OK)
|
||||||
|
: (appstate_song_copy(song_idx, &cand) == NCM_OK);
|
||||||
|
if (!have || !cand.id[0]) return 0; /* 未物化:不能确证同曲 → 交下方正常起播 */
|
||||||
|
pthread_mutex_lock(&g_play.mu);
|
||||||
|
int same = g_play.play_song_valid && g_play.play_song.id[0] &&
|
||||||
|
strcmp(cand.id, g_play.play_song.id) == 0;
|
||||||
|
pthread_mutex_unlock(&g_play.mu);
|
||||||
|
return same;
|
||||||
|
}
|
||||||
|
|
||||||
void player_manager_play(int song_idx)
|
void player_manager_play(int song_idx)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&g_play.mu);
|
pthread_mutex_lock(&g_play.mu);
|
||||||
/* 任务3:点「正在播的曲」(同 idx 且 PLAYING/PAUSED,非切档重播)→ 只聚焦不重播:
|
int is_cur = (song_idx == g_play.cur_song);
|
||||||
* 不 seq++、不置 BUFFER——旧曲继续出声/停原状态,UI 由调用方 nav 到播放页。
|
int pending = g_pending_replay;
|
||||||
* 注意:set_quality 已置 g_pending_replay=1(切音质后重播同曲须换档,P12),此护栏放行。
|
int st = g_play.state;
|
||||||
* 非 PLAYING/PAUSED(STOPPED/另一曲)不受影响,照旧起播。 */
|
|
||||||
if (song_idx == g_play.cur_song && !g_pending_replay &&
|
|
||||||
(g_play.state == NCM_PC_PLAYING || g_play.state == NCM_PC_PAUSED)) {
|
|
||||||
pthread_mutex_unlock(&g_play.mu);
|
pthread_mutex_unlock(&g_play.mu);
|
||||||
meow_log(MEOW_LOG_INFO, "play: re-enter current idx=%d state=%d -> no restart",
|
/* 任务3:点「正在播的曲」→ 只聚焦不重播;**但以曲目身份(id)而非裸下标判定**——跨歌单
|
||||||
song_idx, g_play.state);
|
* 切换后同一队列下标已指向另一首歌(真实日志 `play: re-enter current idx=0` 误吞跨歌单
|
||||||
|
* 点歌),故在 PLAYING/PAUSED 且同下标时再校验候选曲 == 正在播快照:同曲才免重播,
|
||||||
|
* 不同曲(队列已换)/未物化 → 照常起播。注意:set_quality 已置 g_pending_replay=1
|
||||||
|
* (切音质后重播同曲须换档,P12),此护栏放行。非 PLAYING/PAUSED 不受影响,照旧起播。 */
|
||||||
|
if (is_cur && !pending && (st == NCM_PC_PLAYING || st == NCM_PC_PAUSED) &&
|
||||||
|
play_is_same_song(song_idx)) {
|
||||||
|
meow_log(MEOW_LOG_INFO, "play: re-enter current idx=%d -> no restart", song_idx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
pthread_mutex_lock(&g_play.mu);
|
||||||
g_pending_replay = 0; /* 消费掉(新播放意图落地,后续同曲 tap 恢复「只聚焦」) */
|
g_pending_replay = 0; /* 消费掉(新播放意图落地,后续同曲 tap 恢复「只聚焦」) */
|
||||||
g_play.cur_song = song_idx;
|
g_play.cur_song = song_idx;
|
||||||
g_play.cmd = 0;
|
g_play.cmd = 0;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h> /* unlink(covswitch PPM 清理) */
|
||||||
|
|
||||||
ncm_app_state_t g_st;
|
ncm_app_state_t g_st;
|
||||||
ncm_play_run_t g_play = { .mu = PTHREAD_MUTEX_INITIALIZER,
|
ncm_play_run_t g_play = { .mu = PTHREAD_MUTEX_INITIALIZER,
|
||||||
@@ -83,6 +84,36 @@ static void mock_qr(void)
|
|||||||
g_st.login_state = NCM_LOGIN_PENDING;
|
g_st.login_state = NCM_LOGIN_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bug1 回归(covswitch):写一张 8x8 纯色 24-bit BMP,供 cover_host_inject 注入两张不同封面
|
||||||
|
* (stb_image 明确支持 BMP;PPM 在宿主 stb 版本回 NULL → 注入失败)。 */
|
||||||
|
static int cov_make_bmp(const char *path, unsigned char r, unsigned char g, unsigned char b)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
int w = 8, h = 8, row = w * 3, img = row * h;
|
||||||
|
unsigned char hdr[54];
|
||||||
|
memset(hdr, 0, sizeof hdr);
|
||||||
|
hdr[0] = 'B'; hdr[1] = 'M';
|
||||||
|
uint32_t fsz = (uint32_t)(54 + img);
|
||||||
|
hdr[2] = (unsigned char)(fsz & 0xFF); hdr[3] = (unsigned char)((fsz >> 8) & 0xFF);
|
||||||
|
hdr[4] = (unsigned char)((fsz >> 16) & 0xFF); hdr[5] = (unsigned char)((fsz >> 24) & 0xFF);
|
||||||
|
hdr[10] = 54; /* 像素数据偏移 */
|
||||||
|
hdr[14] = 40; /* BITMAPINFOHEADER 大小 */
|
||||||
|
uint32_t ww = (uint32_t)w, hh = (uint32_t)h;
|
||||||
|
hdr[18] = (unsigned char)(ww & 0xFF); hdr[19] = (unsigned char)((ww >> 8) & 0xFF);
|
||||||
|
hdr[20] = (unsigned char)((ww >> 16) & 0xFF); hdr[21] = (unsigned char)((ww >> 24) & 0xFF);
|
||||||
|
hdr[22] = (unsigned char)(hh & 0xFF); hdr[23] = (unsigned char)((hh >> 8) & 0xFF);
|
||||||
|
hdr[24] = (unsigned char)((hh >> 16) & 0xFF); hdr[25] = (unsigned char)((hh >> 24) & 0xFF);
|
||||||
|
hdr[26] = 1; hdr[28] = 24; /* planes=1, bpp=24 */
|
||||||
|
hdr[34] = (unsigned char)(img & 0xFF); hdr[35] = (unsigned char)((img >> 8) & 0xFF);
|
||||||
|
hdr[36] = (unsigned char)((img >> 16) & 0xFF); hdr[37] = (unsigned char)((img >> 24) & 0xFF);
|
||||||
|
if (fwrite(hdr, 1, 54, f) != 54) { fclose(f); return -1; }
|
||||||
|
for (int y = 0; y < h; y++) /* 自底向上 BGR */
|
||||||
|
for (int x = 0; x < w; x++) { fputc(b, f); fputc(g, f); fputc(r, f); }
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int page_id(const char *name)
|
static int page_id(const char *name)
|
||||||
{
|
{
|
||||||
if (strcmp(name, "login") == 0) return ACT_LOGIN;
|
if (strcmp(name, "login") == 0) return ACT_LOGIN;
|
||||||
@@ -98,6 +129,7 @@ static int page_id(const char *name)
|
|||||||
if (strcmp(name, "plsplay") == 0) return ACT_PLAYLIST; /* mini 栏目检(非播放页+播放态) */
|
if (strcmp(name, "plsplay") == 0) return ACT_PLAYLIST; /* mini 栏目检(非播放页+播放态) */
|
||||||
if (strcmp(name, "homeplay") == 0) return ACT_HOME; /* mini 栏目检(首页+播放态) */
|
if (strcmp(name, "homeplay") == 0) return ACT_HOME; /* mini 栏目检(首页+播放态) */
|
||||||
if (strcmp(name, "barhit") == 0) return ACT_PLAYLIST; /* mini 栏命中自检 */
|
if (strcmp(name, "barhit") == 0) return ACT_PLAYLIST; /* mini 栏命中自检 */
|
||||||
|
if (strcmp(name, "covswitch") == 0) return ACT_PLAYLIST;/* 歌单头块封面随全局封面刷新(Bug1) */
|
||||||
if (strcmp(name, "settings") == 0) return ACT_SETTINGS;
|
if (strcmp(name, "settings") == 0) return ACT_SETTINGS;
|
||||||
if (strcmp(name, "search") == 0) return ACT_SEARCH;
|
if (strcmp(name, "search") == 0) return ACT_SEARCH;
|
||||||
if (strcmp(name, "searchhist") == 0) return ACT_SEARCH; /* §23 空态=历史列表 */
|
if (strcmp(name, "searchhist") == 0) return ACT_SEARCH; /* §23 空态=历史列表 */
|
||||||
@@ -149,7 +181,8 @@ int main(int argc, char **argv)
|
|||||||
g_st.login_state = NCM_LOGIN_PENDING;
|
g_st.login_state = NCM_LOGIN_PENDING;
|
||||||
printf("live unikey=%s qr_url=%s size=%d\n", unikey, g_st.qr_url, sz);
|
printf("live unikey=%s qr_url=%s size=%d\n", unikey, g_st.qr_url, sz);
|
||||||
}
|
}
|
||||||
else if (strcmp(name, "playlist") == 0) { mock_playlists(); mock_songs(); }
|
else if (strcmp(name, "playlist") == 0 || strcmp(name, "covswitch") == 0)
|
||||||
|
{ mock_playlists(); mock_songs(); }
|
||||||
else if (strcmp(name, "player") == 0 || strcmp(name, "plcover") == 0 ||
|
else if (strcmp(name, "player") == 0 || strcmp(name, "plcover") == 0 ||
|
||||||
strcmp(name, "pllyric") == 0 || strcmp(name, "gestest") == 0 ||
|
strcmp(name, "pllyric") == 0 || strcmp(name, "gestest") == 0 ||
|
||||||
strcmp(name, "grid") == 0) {
|
strcmp(name, "grid") == 0) {
|
||||||
@@ -265,7 +298,7 @@ int main(int argc, char **argv)
|
|||||||
if (strcmp(name, "login") == 0 || strcmp(name, "loginlive") == 0) v = login_view_create();
|
if (strcmp(name, "login") == 0 || strcmp(name, "loginlive") == 0) v = login_view_create();
|
||||||
else if (strcmp(name, "home") == 0) v = home_view_create();
|
else if (strcmp(name, "home") == 0) v = home_view_create();
|
||||||
else if (strcmp(name, "playlist") == 0 || strcmp(name, "plsplay") == 0 ||
|
else if (strcmp(name, "playlist") == 0 || strcmp(name, "plsplay") == 0 ||
|
||||||
strcmp(name, "barhit") == 0) v = playlist_view_create();
|
strcmp(name, "barhit") == 0 || strcmp(name, "covswitch") == 0) v = playlist_view_create();
|
||||||
else if (page_id(name) == ACT_PLAYER) v = player_view_create(); /* player/lands/playerbuf/playererr/modetest */
|
else if (page_id(name) == ACT_PLAYER) v = player_view_create(); /* player/lands/playerbuf/playererr/modetest */
|
||||||
else if (page_id(name) == ACT_QUEUE) v = queue_view_create(); /* §NEW 播放队列页 */
|
else if (page_id(name) == ACT_QUEUE) v = queue_view_create(); /* §NEW 播放队列页 */
|
||||||
else if (strcmp(name, "settings") == 0) v = settings_view_create();
|
else if (strcmp(name, "settings") == 0) v = settings_view_create();
|
||||||
@@ -314,6 +347,30 @@ int main(int argc, char **argv)
|
|||||||
if (page_id(name) == ACT_PLAYER) {
|
if (page_id(name) == ACT_PLAYER) {
|
||||||
player_tick(meow_ui_time_ms());
|
player_tick(meow_ui_time_ms());
|
||||||
}
|
}
|
||||||
|
if (strcmp(name, "covswitch") == 0) {
|
||||||
|
/* Bug1 回归:歌单页活跃,playlist_tick 须消费 cover_changed(头块封面随全局封面刷新)。
|
||||||
|
* 修复前 playlist_tick 不判 cover_changed/ack → 注入后 cover_changed 恒 1(头块永不重画,
|
||||||
|
* 停留上一张);修复后 tick 判变并 ack → 归 0(下次换封面再变 1,再 ack → 收敛到新封面)。 */
|
||||||
|
int ok = (cov_make_bmp("/tmp/ncm_cov_a.bmp", 220, 40, 40) == 0 &&
|
||||||
|
cov_make_bmp("/tmp/ncm_cov_b.bmp", 40, 90, 220) == 0);
|
||||||
|
if (ok) {
|
||||||
|
cover_host_inject("/tmp/ncm_cov_a.bmp");
|
||||||
|
int c1 = cover_changed(); /* 注入后:新封面待绘制(gen>acked) */
|
||||||
|
playlist_tick(0); /* 修复点:消费 */
|
||||||
|
int c2 = cover_changed(); /* 应 ack 归 0 */
|
||||||
|
cover_host_inject("/tmp/ncm_cov_b.bmp");
|
||||||
|
int c3 = cover_changed(); /* 再注入:又待绘制 */
|
||||||
|
playlist_tick(0);
|
||||||
|
int c4 = cover_changed(); /* 再 ack 归 0 */
|
||||||
|
ok = (c1 && !c2 && c3 && !c4);
|
||||||
|
printf("covswitch: A->changed=%d tick->acked=%d B->changed=%d tickB->acked=%d -> %s\n",
|
||||||
|
c1, c2, c3, c4, ok ? "PASS" : "FAIL");
|
||||||
|
} else {
|
||||||
|
printf("covswitch: init FAIL (bmp write)\n");
|
||||||
|
}
|
||||||
|
if (!ok) return 1;
|
||||||
|
unlink("/tmp/ncm_cov_a.bmp"); unlink("/tmp/ncm_cov_b.bmp");
|
||||||
|
}
|
||||||
if (strcmp(name, "search") == 0 || strcmp(name, "searchtap") == 0) {
|
if (strcmp(name, "search") == 0 || strcmp(name, "searchtap") == 0) {
|
||||||
search_submit(g_st.search_query); /* 宿主:应用预置搜索结果 */
|
search_submit(g_st.search_query); /* 宿主:应用预置搜索结果 */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,13 +743,15 @@ void player_play_song(int idx)
|
|||||||
g_st.queue_src = NCM_QUEUE_PLAYLIST; /* §23:歌单窗口队列 */
|
g_st.queue_src = NCM_QUEUE_PLAYLIST; /* §23:歌单窗口队列 */
|
||||||
/* 黑胶封面用真实专辑图(song_detail 的 al.picUrl,经 ncm_cache 缓存/限幅);
|
/* 黑胶封面用真实专辑图(song_detail 的 al.picUrl,经 ncm_cache 缓存/限幅);
|
||||||
* 滑窗未物化(占位行直点)时回落歌单封面,元数据由播放线程物化后快照接管(§17)。
|
* 滑窗未物化(占位行直点)时回落歌单封面,元数据由播放线程物化后快照接管(§17)。
|
||||||
* §18:g_st.cover_url 只作「歌单封面兜底」。 */
|
* §18:g_st.cover_url 只作「歌单封面兜底」。占位行直点(appstate_song_copy 失败)时
|
||||||
|
* s 未初始化,**不能**把 s.id 传给 cover_request(会带出一段野曲 id 参与兜底判冷却)——
|
||||||
|
* 仅物化成功才带曲目 id,否则 NULL(歌单封面,不带兜底)。 */
|
||||||
ncm_song_t s;
|
ncm_song_t s;
|
||||||
const char *cover = (appstate_song_copy(idx, &s) == 0 && s.al_pic[0])
|
int have_s = (appstate_song_copy(idx, &s) == 0);
|
||||||
? s.al_pic : g_st.cover_url;
|
const char *cover = (have_s && s.al_pic[0]) ? s.al_pic : g_st.cover_url;
|
||||||
player_manager_play(idx);
|
player_manager_play(idx);
|
||||||
appstate_set_playing_song(idx, NULL);
|
appstate_set_playing_song(idx, NULL);
|
||||||
cover_request(cover, s.id); /* 曲目专辑封面:带 id,死链时 song/detail 兜底(REPORT §NEW 任务1) */
|
cover_request(cover, have_s ? s.id : NULL);
|
||||||
g_ppage = 0; /* 新曲 → 回封面页 */
|
g_ppage = 0; /* 新曲 → 回封面页 */
|
||||||
meow_view_page_push(ACT_PLAYER);
|
meow_view_page_push(ACT_PLAYER);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,6 +386,14 @@ void playlist_refresh(void)
|
|||||||
void playlist_tick(uint64_t now)
|
void playlist_tick(uint64_t now)
|
||||||
{
|
{
|
||||||
(void)now;
|
(void)now;
|
||||||
|
/* 歌单头块封面跨页同步:全局单封面(与播放页/mini 栏共享)由 cover worker 异步换入,
|
||||||
|
* 本页须判 cover_changed 并重画头块封面带——否则切歌单后(或从播放页回退时)头块永远
|
||||||
|
* 停留上一张(歌单封面不随切换刷新,实机 Bug1:切到 B 顶图仍是 A 的)。仅在播放页 tick
|
||||||
|
* 消费 cover_ack 是不够的(该页 inactive 不跑);此处 play 页活跃时消费并失效封面带。 */
|
||||||
|
if (cover_changed()) {
|
||||||
|
cover_ack();
|
||||||
|
meow_ui_invalidate(r_cov.x, r_cov.y, r_cov.w, r_cov.h);
|
||||||
|
}
|
||||||
if (!g_plthr_on) return;
|
if (!g_plthr_on) return;
|
||||||
if (g_plgen == g_placked) return;
|
if (g_plgen == g_placked) return;
|
||||||
g_placked = g_plgen;
|
g_placked = g_plgen;
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
* 修后 seq 变化即重启换档,状态回 PLAYING、新档位下载、eff_level 回填。
|
* 修后 seq 变化即重启换档,状态回 PLAYING、新档位下载、eff_level 回填。
|
||||||
* P13 陈旧 hires.mp3(flac 门禁前静默降级产物,hires 键下存 mp3):song_local_file
|
* P13 陈旧 hires.mp3(flac 门禁前静默降级产物,hires 键下存 mp3):song_local_file
|
||||||
* 迁移重命名到 exhigh 键(song_<id>_exhigh.mp3)+ 本档视为 miss 走下载(§NEW 任务2)。
|
* 迁移重命名到 exhigh 键(song_<id>_exhigh.mp3)+ 本档视为 miss 走下载(§NEW 任务2)。
|
||||||
|
* P14 歌单里点「正在播的同曲」→ 只聚焦不重播(任务3 re-entry,同曲 id 判据)。
|
||||||
|
* P15 跨歌单切换后同一队列下标点歌 → 不被同曲护栏吞,起播(Bug2 回归:真机日志
|
||||||
|
* `play: re-enter current idx=0 state=2 -> no restart`,新歌永不播)。
|
||||||
*
|
*
|
||||||
* 编译(宿主,ncm_app 目录下):
|
* 编译(宿主,ncm_app 目录下):
|
||||||
* gcc -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \
|
* gcc -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \
|
||||||
@@ -61,13 +64,16 @@ void meow_audio_on_preempt(meow_audio_preempt_fn cb, void *ctx) { (void)cb; (voi
|
|||||||
/* core/ncm_common.c 提供 ncm_now_ms(墙钟毫秒);本 sim 不链 ncm_common.o,提供同语义桩。 */
|
/* core/ncm_common.c 提供 ncm_now_ms(墙钟毫秒);本 sim 不链 ncm_common.o,提供同语义桩。 */
|
||||||
int64_t ncm_now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (int64_t)ts.tv_sec*1000 + ts.tv_nsec/1000000; }
|
int64_t ncm_now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (int64_t)ts.tv_sec*1000 + ts.tv_nsec/1000000; }
|
||||||
|
|
||||||
/* ---- appstate stub:窗内全命中,id=1000+idx;fee 可注入(§20 P8) ---- */
|
/* ---- appstate stub:窗内全命中,id=g_song_base+1000+idx;fee 可注入(§20 P8)----
|
||||||
|
* g_song_base 模拟「播放队列内容变化」:P14 跨歌单切换后,同一队列下标指向另一首歌
|
||||||
|
* (base 0→5000,idx=0 由 id=1000 变 id=6000),用于回归 Bug2 的同曲重入护栏。 */
|
||||||
|
static int g_song_base = 0;
|
||||||
static int g_fee = 0;
|
static int g_fee = 0;
|
||||||
int appstate_song_copy(int gidx, ncm_song_t *out)
|
int appstate_song_copy(int gidx, ncm_song_t *out)
|
||||||
{
|
{
|
||||||
if (gidx < 0 || gidx >= g_st.track_ids_count) return -1;
|
if (gidx < 0 || gidx >= g_st.track_ids_count) return -1;
|
||||||
memset(out, 0, sizeof *out);
|
memset(out, 0, sizeof *out);
|
||||||
snprintf(out->id, sizeof out->id, "%d", 1000 + gidx);
|
snprintf(out->id, sizeof out->id, "%d", g_song_base + 1000 + gidx);
|
||||||
snprintf(out->name, sizeof out->name, "Song%d", gidx);
|
snprintf(out->name, sizeof out->name, "Song%d", gidx);
|
||||||
out->duration_ms = 180000;
|
out->duration_ms = 180000;
|
||||||
out->fee = g_fee;
|
out->fee = g_fee;
|
||||||
@@ -77,6 +83,10 @@ int appstate_song_fetch(int gidx, ncm_song_t *out) { return appstate_song_copy(g
|
|||||||
/* §23:player_manager 改走 appstate_queue_*(队列来源)——sim 恒 PLAYLAST 队列 = 窗口 */
|
/* §23:player_manager 改走 appstate_queue_*(队列来源)——sim 恒 PLAYLAST 队列 = 窗口 */
|
||||||
int appstate_queue_count(void) { return g_st.track_ids_count; }
|
int appstate_queue_count(void) { return g_st.track_ids_count; }
|
||||||
int appstate_queue_song(int idx, ncm_song_t *out) { return appstate_song_copy(idx, out); }
|
int appstate_queue_song(int idx, ncm_song_t *out) { return appstate_song_copy(idx, out); }
|
||||||
|
/* P14 相关:player_manager_play 的同曲护栏(play_is_same_song)会读队列来源 + 搜索数组;
|
||||||
|
* sim 恒歌单窗口队列、搜索数组为空。appstate.c 未链入,两符号此处提供桩。 */
|
||||||
|
int appstate_get_queue(void) { return NCM_QUEUE_PLAYLIST; }
|
||||||
|
int appstate_search_song(int i, ncm_song_t *out) { (void)i; (void)out; return NCM_ERR_ARG; }
|
||||||
|
|
||||||
/* ---- 缓存/网络 stub ----
|
/* ---- 缓存/网络 stub ----
|
||||||
* g_cache_ok=1 → lookup 命中(给真实存在的 /tmp 文件,免下载);
|
* g_cache_ok=1 → lookup 命中(给真实存在的 /tmp 文件,免下载);
|
||||||
@@ -650,6 +660,36 @@ int main(void)
|
|||||||
|
|
||||||
usleep(200 * 1000);
|
usleep(200 * 1000);
|
||||||
player_manager_stop_thread();
|
player_manager_stop_thread();
|
||||||
|
|
||||||
|
printf("== P15: 跨歌单切换后同 idx 点歌不被「同曲只聚焦」吞(Bug2 回归)==\n");
|
||||||
|
/* 线程已停 → 纯逻辑判护栏(无竞态)。手工造「歌单 A 的歌停在 idx=0(id=1000)」的
|
||||||
|
* 播放快照态;然后模拟切到歌单 B(g_song_base 0→5000:同一队列下标 idx=0 现指向 id=6000
|
||||||
|
* 的另一首歌——真机日志实锤 `play: re-enter current idx=0 state=2 -> no restart`:新歌
|
||||||
|
* 永不起播、播放页停在上首+暂停)。护栏改按曲目 id 判同曲后,跨歌单同下标点歌必须起播。 */
|
||||||
|
pthread_mutex_lock(&g_play.mu);
|
||||||
|
g_play.cur_song = 0;
|
||||||
|
g_play.state = NCM_PC_PAUSED;
|
||||||
|
g_play.play_song_valid = 1;
|
||||||
|
memset(&g_play.play_song, 0, sizeof g_play.play_song);
|
||||||
|
snprintf(g_play.play_song.id, sizeof g_play.play_song.id, "1000");
|
||||||
|
g_play.seq = 90; g_play.pos_ms = 1000; g_play.err = 0;
|
||||||
|
g_play.dl_done = 0; g_play.dl_total = 0;
|
||||||
|
pthread_mutex_unlock(&g_play.mu);
|
||||||
|
int seq_before = g_play.seq; /* 线程已停,无锁读安全 */
|
||||||
|
|
||||||
|
/* (a) 同歌单点「正在播的同曲」(同下标、同 id)→ 护栏放行:确证同曲 → 只聚焦不重播 */
|
||||||
|
g_song_base = 0;
|
||||||
|
player_manager_play(0);
|
||||||
|
CHECK(player_manager_state() == NCM_PC_PAUSED && player_manager_playing_song() == 0 &&
|
||||||
|
g_play.seq == seq_before,
|
||||||
|
"same-playlist re-tap -> PAUSED/cur=0/seq=%d (expect 不重播)", g_play.seq);
|
||||||
|
|
||||||
|
/* (b) 切到歌单 B:队列内容变化(idx=0 现为 id=6000 的另一首)→ 同 idx 点歌必须起播 */
|
||||||
|
g_song_base = 5000;
|
||||||
|
player_manager_play(0);
|
||||||
|
CHECK(player_manager_state() == NCM_PC_BUFFER && g_play.seq == seq_before + 1,
|
||||||
|
"cross-playlist idx=0 tap -> BUFFER/seq=%d (expect 起播,不被吞)", g_play.seq);
|
||||||
|
|
||||||
printf(g_fail ? "== PLAYER FLOW SIM: FAIL ==\n" : "== PLAYER FLOW SIM: ALL PASS ==\n");
|
printf(g_fail ? "== PLAYER FLOW SIM: FAIL ==\n" : "== PLAYER FLOW SIM: ALL PASS ==\n");
|
||||||
return g_fail;
|
return g_fail;
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
# ---- 用户态/服务守护二进制(usr/bin,全部 chmod 755 静态 MIPS)----
|
# ---- 用户态/服务守护二进制(usr/bin,全部 chmod 755 静态 MIPS)----
|
||||||
patch_v2/bootmenu/meow_bootmenu|/usr/bin/meow_bootmenu|8de51144538d698c3ff82ee6651b8b1a|引导选单 M6.25(开机菜单/第二屏亮度按用户设置恢复:显示前读 settings.conf 写回,方案A)
|
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|513d7603ff4b8203af3927ff88569d21|M6.32 全局播放页进度条/按钮局部重绘回退默认色修复(agent-408取色主题抽共用np_grad_row/np_page_bg_row/np_theme_widgets,1s局部重绘也接取色,进度条fill=accent/track=fg_dim/四钮accent)+重编SDK W_PROGRESS读opts.bg/fg(meow_ui.a 86af4d29)+链sdk meow_lyric/lyric_view/meow_img_color
|
patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|6d2ddb25442a6cdd81cd87a44300f676|M6.33 ①移除播放页底部歌词占位符(已有独立歌词页,删NP_LYRIC_Y+占位块,播放页底部干净) ②封面'空→复原'恢复修复(meta_changed补artwork检测+np_cover_load缓存去重改为空也重载+歌词页补载封面);宿主test_np_cover 14项PASS,0warning;链sdk meow_lyric/lyric_view/meow_img_color
|
||||||
patch_v2/meow_watchdog/meow_watchdog|/usr/bin/meow_watchdog|34ddbf0275526eaf9be988134845c3af|应用看门狗 M6.23(亮屏亮度按用户设置 display.brightness 恢复,修 panel ON 固定60跳变)
|
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)
|
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)
|
mips-repo/bins/meow_mdns/bin/meow_mdns|/usr/bin/meow_mdns|d6b0151bda26768f19952dae1342610d|mDNS 应答器 M2.78(启动竞态+换IP重检 agent-194)
|
||||||
|
|||||||
Reference in new issue
Block a user