Compare commits

...
4 Commits
Author SHA1 Message Date
AskaEth 18014eb2b9 fix(ncm): 补play_mode持久化定义(appstate_apply_play_mode/ncm_mode_name,同agent-392播放模式) — ncm链通必需,与M6.32上卡二进制一致 2026-09-03 23:48:08 +08:00
AskaEth 76a22a6a91 fix(fw): M6.32 重编meow_ui.a+重链四App(播放页进度条取色+ncm列表gap)+面板透传 — 推卡+热更四App
- meow_ui.a 重编(86af4d29): SDK W_PROGRESS 改读 opts.bg/fg=轨道/填充(agent-408)
- launcher 513d7603: 播放页进度条/按钮局部重绘接取色(np_grad_row/np_page_bg_row/np_theme_widgets)
  + 链新 meow_ui.a + sdk meow_lyric/lyric_view/meow_img_color; 经固件 /usr/bin 刷入
- ncm 05ff05f1 重链新 meow_ui.a(agent-409 列表项 gap 已入源), hello f35495a5 / files 5d70b7b2 重链
- meowpanel.c: audio_fill 补透传 audiod target/volctl/codec(输出目标/音量AVRCP/编解码器)
  前端 meowpanel.js 已前向兼容; 宿主 /api/audio JSON 验证含三键
- fw_content.conf launcher 0cdf940f -> 513d7603(M6.32)
- r3proii_m6_32.upt md5 8ce917ad(0.8.32, rootfs 38707200<=45088768, unpack 0 diff)
2026-09-03 23:42:46 +08:00
AskaEth e8218b8ae9 fix(ncm): SDK圆角矩形列表项加外边距 — 实为ncm_app应用层自绘(row_bg圆角卡),调用处加NCM_ROW_GAP=8px卡间可见外距(卡高48/行距56上下对称+擦SDK行分隔线);三屏playlist/home/queue;宿主test_meow_row_gap PASS,0warning,不涉meow_ui.a无需重链 2026-09-03 23:29:45 +08:00
AskaEth a82f3f9dfa fix(lyric): 全局播放页进度条/按钮带局部重绘回退默认色修复 — 取色主题抽共用(np_grad_row/np_page_bg_row/np_theme_widgets),1s局部重绘也接取色(带底色渐变/进度条fill=accent/track=fg_dim/四钮accent);SDK W_PROGRESS改读opts.bg/fg向后兼容;宿主R1-R3 PASS,0warning;需重编meow_ui.a+四App重链 2026-09-03 23:28:37 +08:00
19 changed files with 320 additions and 40 deletions

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -96,7 +96,7 @@ static void ncp_media_btns_create(void)
}
/* 绘制三控制按钮(prev/next 固定;play/pause 按会话状态只画其一——两钮同位,改画时新圆底盖掉旧)。
* 每次刷新播放钮底色/图标(同 now_playing np_refresh_theme:跨主题切换跟手)。 */
* 每次刷新播放钮底色/图标(同 now_playing np_theme_widgets:跨主题切换跟手)。 */
static void ncp_draw_media_btns(const meow_media_session_t *s)
{
if (!g_ncp_media_btn_init) ncp_media_btns_create();
+61 -16
View File
@@ -101,20 +101,34 @@ static void np_apply_lyric_colors(void)
meow_lyric_view_set_colors(g_np_lyric, g_np_cc_valid ? &g_np_cc.lyr : NULL);
}
/* 竖向双色渐变填充(无 GPU,逐行整数插值;封面主题背景用)。 */
static void np_fill_gradient(int x, int y, int w, int h, uint32_t top, uint32_t bottom)
/* 渐变插值(无 GPU,逐行整数;封面主题背景用)。返回 [y0, y0+h) 带内屏幕行 y 的颜色。 */
static uint32_t np_grad_row(uint32_t top, uint32_t bottom, int y0, int h, int y)
{
if (w <= 0 || h <= 0) return;
int t = (h <= 1) ? 0 : (y - y0) * 255 / (h - 1);
if (t < 0) t = 0; else if (t > 255) t = 255;
int tr = (top >> 16) & 255, tg = (top >> 8) & 255, tb = top & 255;
int br = (bottom >> 16) & 255, bg = (bottom >> 8) & 255, bb = bottom & 255;
int span = h - 1;
for (int yy = 0; yy < h; yy++) {
int t = span > 0 ? yy * 255 / span : 0;
int r = tr + ((br - tr) * t + 128) / 255;
int g = tg + ((bg - tg) * t + 128) / 255;
int b = tb + ((bb - tb) * t + 128) / 255;
fill_rect(x, y + yy, w, 1, ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b);
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
}
/* 竖向双色渐变填充(封面主题背景用;整页/局部共用 np_grad_row,保证局部重绘与整页渐变无缝)。 */
static void np_fill_gradient(int x, int y, int w, int h, uint32_t top, uint32_t bottom)
{
if (w <= 0 || h <= 0) return;
for (int yy = 0; yy < h; yy++)
fill_rect(x, y + yy, w, 1, np_grad_row(top, bottom, y, h, y + yy));
}
/* 播放页当前主题 · 页背景行色:内容区(状态栏下)屏幕行 y 的背景色。
* 取色有效 → 取色渐变(按整页 span 插值:局部重绘进度带逐行取色与整页渐变无缝衔接);
* 取色无效 → 返回 0(调用方回退 meow_theme 面)。 */
static uint32_t np_page_bg_row(int y)
{
if (!g_np_cc_valid) return 0;
return np_grad_row(g_np_cc.bg_top, g_np_cc.bg_bottom, SB_BAR_H, fb_yres - SB_BAR_H, y);
}
#if 0 /* ================================================================
@@ -217,23 +231,43 @@ static void np_widgets_create(void)
g_np_init = 1;
}
/* 主题刷新:播放钮底色/图标随当前主题(明暗/风格切换后跟手);批5 封面取色时随 accent。 */
static void np_refresh_theme(void)
/* 播放页当前主题 → 控件修饰(整页与局部重绘共用同一次派生,避免两处取色逻辑漂移)。
* 进度条 meow_progress:opts.bg=轨道、opts.fg=填充(批5b W_PROGRESS 新增支持);
* 控制钮 meow_iconbtn:opts.bg=圆底、opts.fg=图标墨色。
* 取色有效(g_np_cc_valid):进度条轨道=fg_dim/填充=accent,三钮圆底=accent、图标=accent_fg;
* 取色无效:全部清 opts 回主题默认(进度条=surfaceVariant/primary、play/pause=primary/onPrimary、
* prev/next=surfaceContainerLow/onSurface——与创建时一致,不回归)。
* 每次绘制前调用(np_draw_page 播放分支 / np_repaint_progress),保证主题跟手。 */
static void np_theme_widgets(void)
{
if (!g_np_init) return;
const meow_theme *th = meow_theme_get();
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = g_np_cc_valid ? g_np_cc.accent : th->primary;
op.fg = g_np_cc_valid ? g_np_cc.accent_fg : th->onPrimary;
if (g_np_cc_valid) {
op.bg = g_np_cc.fg_dim; op.fg = g_np_cc.accent; /* 进度条:track=fg_dim / fill=accent */
if (g_np_prog) meow_widget_set_opts(g_np_prog, &op);
op.bg = g_np_cc.accent; op.fg = g_np_cc.accent_fg; /* 三钮:圆底=accent / 图标=accent_fg */
if (g_np_prev) meow_widget_set_opts(g_np_prev, &op);
if (g_np_play) meow_widget_set_opts(g_np_play, &op);
if (g_np_pause) meow_widget_set_opts(g_np_pause, &op);
if (g_np_next) meow_widget_set_opts(g_np_next, &op);
return;
}
/* 取色无效 → 默认主题(回退):清 prog/prev/next 的 opts,play/pause 回主题 primary。 */
memset(&op, 0, sizeof op);
if (g_np_prog) meow_widget_set_opts(g_np_prog, &op);
if (g_np_prev) meow_widget_set_opts(g_np_prev, &op);
if (g_np_next) meow_widget_set_opts(g_np_next, &op);
if (g_np_play || g_np_pause) {
op.bg = th->primary; op.fg = th->onPrimary; /* 主播放钮:强调动作 */
if (g_np_play) meow_widget_set_opts(g_np_play, &op);
if (g_np_pause) meow_widget_set_opts(g_np_pause, &op);
}
}
/* 绘制播放控制三钮(prev/next 固定;play/pause 按会话状态只画其一——两钮同位,改画时
* 上一帧残留被新画圆底盖掉)。 */
* 上一帧残留被新画圆底盖掉)。控件配色在调用前由 np_theme_widgets 统一挂载取色主题。 */
static void np_draw_ctl(const meow_media_session_t *s)
{
np_refresh_theme();
if (g_np_prev) { meow_widget_set_selected(g_np_prev, 0); meow_widget_draw(g_np_prev); }
if (g_np_next) { meow_widget_set_selected(g_np_next, 0); meow_widget_draw(g_np_next); }
int playing = (s && s->state == MEOW_MEDIA_PLAYING);
@@ -379,6 +413,8 @@ static void np_draw_page(int redraw_sb)
np_center_text(NP_ARTIST_Y, s->artist, 18, g_np_cc_valid ? g_np_cc.fg_dim : th->on_surface_variant, W - 60);
if (s->album[0])
np_center_text(NP_ALBUM_Y, s->album, 16, g_np_cc_valid ? g_np_cc.fg_dim : th->on_surface_variant, W - 80);
/* 批5b:控件随播放页当前主题(进度条轨道/填充 + 三钮)——整页与局部重绘共用同一次派生。 */
np_theme_widgets();
/* 进度(SDK meow_progress) */
uint32_t pos = meow_media_display_pos(s);
np_draw_progress(s, pos, th);
@@ -390,15 +426,24 @@ static void np_draw_page(int redraw_sb)
if (redraw_sb) draw_statusbar();
}
/* 仅重绘进度带 + 按钮(元数据/源变化时整页;纯进度变化时局部)。 */
/* 仅重绘进度带 + 按钮(元数据/源变化时整页;纯进度变化时局部)。
* 批5b:局部重绘也走播放页当前主题——进度带/按钮带底色用取色渐变(np_page_bg_row 与整页同 span
* 无缝衔接),进度条/三钮经 np_theme_widgets 挂取色色;无取色回退 meow_theme 面/默认控件色。 */
static void np_repaint_progress(void)
{
const meow_ui_theme_t *th = s_ui_theme();
const meow_media_session_t *s = meow_media_current();
if (!s || s->state == MEOW_MEDIA_STOPPED) return;
np_cover_theme_update(); /* 取色当前主题(幂等,封面路径未变不重取) */
uint32_t pos = meow_media_display_pos(s);
/* 清进度带 + 控制带,再重绘(只脏这两带,不整屏)。 */
/* 清进度带 + 控制带,再重绘(只脏这两带,不整屏);底色随取色渐变或主题面。 */
if (g_np_cc_valid) {
for (int yy = NP_TIME_Y; yy < NP_BTN_Y + NP_PLAY_R; yy++)
fill_rect(0, yy, fb_xres, 1, np_page_bg_row(yy));
} else {
fill_rect(0, NP_TIME_Y, fb_xres, NP_BTN_Y + NP_PLAY_R - NP_TIME_Y, th->surface);
}
np_theme_widgets(); /* 进度条/按钮随取色主题(或回退默认) */
np_draw_progress(s, pos, th);
np_draw_ctl(s);
meow_dirty_add(0, NP_TIME_Y, fb_xres, NP_BTN_Y + NP_PLAY_R - NP_TIME_Y);
@@ -25,6 +25,7 @@
#include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp */
#include "np_gesture.h" /* 批4:双页方向感知手势决策(纯逻辑,宿主可单测) */
#include "meow_lyric_view.h" /* 批2:歌词页控件(批4接入,数据批3 meow_media_lyric_*) */
#include "meow_img_color.h" /* 批5b:封面取色主题派生(局部重绘进度条/按钮带取色用例) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -59,6 +60,8 @@ static void rd_px(int x, int y, unsigned short *p)
else *p = (unsigned short)(*(unsigned int *)(bb + (size_t)y * line + (size_t)x * 4) >> 16);
}
static unsigned short px565(int x, int y) { unsigned short p; rd_px(x, y, &p); return p; }
static unsigned short rgb565(uint32_t c)
{ return (unsigned short)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F)); }
static int region_nonbg(int x, int y, int w, int h, uint32_t bg)
{
unsigned short bg565 = ((bg >> 8) & 0xF800) | ((bg >> 5) & 0x07E0) | ((bg >> 3) & 0x001F);
@@ -179,6 +182,103 @@ int main(void)
}
}
/* ============ R1 批5b:局部重绘取色——进度条 fill=accent/track=fg_dim + 默认回退 ============
* 镜像 np_theme_widgets(取色有效→进度条 opts.bg=fg_dim/opts.fg=accent)+ np_draw_progress。
* 用「直接 fill_rect 参考像素」对比,绕开 565 精度差异(同一渲染器同色 → 同 565)。 */
printf("R1 progress cover-theme fill/track + default fallback\n");
meow_ui_set_style(MEOW_STYLE_MD3);
meow_theme_set_mode(MEOW_THEME_DARK);
th = meow_theme_get();
meow_img_color_theme cc; memset(&cc, 0, sizeof cc);
meow_img_color_derive(0xE53935u, 0x1E88E5u, th, &cc); /* 红/蓝两色 → 派生取色主题 */
/* 覆盖取色:进度条 fill=accent / track=fg_dim */
{
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = cc.fg_dim; op.fg = cc.accent;
meow_widget_set_opts(prog, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PROG_X, NP_PROG_Y, 5, 6, cc.accent); /* fill 参考 */
unsigned short ref_fill = px565(NP_PROG_X + 2, NP_PROG_Y + 3);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PROG_X + NP_PROG_W - 4, NP_PROG_Y, 3, 6, cc.fg_dim); /* track 参考 */
unsigned short ref_track = px565(NP_PROG_X + NP_PROG_W - 3, NP_PROG_Y + 3);
meow_ui_clear(0);
meow_progress_set(prog, 50);
meow_widget_draw(prog);
int fill_x = NP_PROG_X + NP_PROG_W / 4; /* 50% 填充区内 */
int track_x = NP_PROG_X + NP_PROG_W - 4; /* 尾部轨道区 */
CHECK(px565(fill_x, NP_PROG_Y + 3) == ref_fill, "cover: progress fill == accent");
CHECK(px565(track_x, NP_PROG_Y + 3) == ref_track, "cover: progress track == fg_dim");
/* 默认回退:清 opts → 回到主题 primary/surfaceVariant(无取色回退默认) */
memset(&op, 0, sizeof op);
meow_widget_set_opts(prog, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PROG_X, NP_PROG_Y, 5, 6, th->primary);
unsigned short ref_pri = px565(NP_PROG_X + 2, NP_PROG_Y + 3);
meow_ui_clear(0);
meow_progress_set(prog, 50);
meow_widget_draw(prog);
CHECK(px565(fill_x, NP_PROG_Y + 3) == ref_pri, "default: progress fill == theme primary");
CHECK(px565(fill_x, NP_PROG_Y + 3) != ref_fill, "default vs cover fill differ");
}
/* ============ R2 批5b:进度/按钮带底色随取色渐变(非默认 surface) ============
* 镜像 np_repaint_progress 的带底色分支(np_page_bg_row 按整页 span 插值)。
* 旧 bug:本地重绘用 th->surface 清带 → 带内被刷成默认色;现应取色渐变且 != surface。 */
printf("R2 band background uses cover gradient (not default surface)\n");
{
#define NP_SB_H 28
int H = 720;
meow_ui_clear(0);
for (int yy = NP_TIME_Y; yy < NP_BTN_Y + NP_PLAY_R; yy++) {
int t = (H - NP_SB_H <= 1) ? 0 : (yy - NP_SB_H) * 255 / (H - NP_SB_H - 1);
if (t < 0) t = 0; else if (t > 255) t = 255;
int tr = (cc.bg_top >> 16) & 255, tg = (cc.bg_top >> 8) & 255, tb = cc.bg_top & 255;
int br = (cc.bg_bottom >> 16) & 255, bg = (cc.bg_bottom >> 8) & 255, bb = cc.bg_bottom & 255;
int r = tr + ((br - tr) * t + 128) / 255;
int g = tg + ((bg - tg) * t + 128) / 255;
int b = tb + ((bb - tb) * t + 128) / 255;
meow_ui_fill_rect(0, yy, 480, 1, ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b);
}
unsigned short bandpx = px565(10, NP_TIME_Y + 55);
CHECK(bandpx != rgb565(th->surface), "cover: band bg != default surface");
CHECK(bandpx != 0, "cover: band bg rendered");
}
/* ============ R3 批5b:控制三钮(含 prev/next)随取色 accent + 默认回退 ============
* 镜像 np_theme_widgets:取色有效→四钮 opts.bg=accent/opts.fg=accent_fg(prev/next 不再默认底色)。 */
printf("R3 control buttons follow cover accent\n");
{
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = cc.accent; op.fg = cc.accent_fg;
meow_widget_set_opts(prev, &op);
meow_widget_set_opts(next, &op);
meow_widget_set_opts(play, &op);
meow_widget_set_opts(pause, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PREV_CX - 2, NP_BTN_Y - 18, 4, 2, cc.accent); /* accent 参考(圆底上方、图标外) */
unsigned short ref_acct = px565(NP_PREV_CX, NP_BTN_Y - 17);
meow_ui_clear(0);
meow_widget_draw(prev); /* r=22,cy-17 在圆内且高于图标(cy-13) */
CHECK(px565(NP_PREV_CX, NP_BTN_Y - 17) == ref_acct, "cover: prev bg == accent");
meow_ui_clear(0);
meow_widget_draw(play); /* r=30,cy-26 在圆内且高于图标(cy-18) */
CHECK(px565(NP_PLAY_CX, NP_BTN_Y - 26) == ref_acct, "cover: play bg == accent");
/* 默认回退:清 prev/next opts → 回 surfaceContainerLow(无取色不回归旧形态) */
memset(&op, 0, sizeof op);
meow_widget_set_opts(prev, &op);
meow_widget_set_opts(next, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PREV_CX - 2, NP_BTN_Y - 18, 4, 2, th->surfaceContainerLow);
unsigned short ref_def = px565(NP_PREV_CX, NP_BTN_Y - 17);
meow_ui_clear(0);
meow_widget_draw(prev);
CHECK(px565(NP_PREV_CX, NP_BTN_Y - 17) == ref_def, "default: prev bg == surfaceContainerLow");
}
/* ============ G5 双页手势方向感知(np_gesture.h,纯逻辑) ============ */
printf("G5 dual-page direction-aware gestures\n");
/* 播放页:右→左滑 → 进歌词页 */
Binary file not shown.
+12 -1
View File
@@ -1133,10 +1133,15 @@ static void audio_fill(sbuf_t *s)
if (n <= 0 || type != MSG_AUDIO_STATE) { sA(s, "{\"available\":false}"); return; }
buf[n] = 0;
const char *txt = (const char *)buf;
char app[MEOW_APP_ID_MAX + 8], fmt[32], rmode[16];
char app[MEOW_APP_ID_MAX + 8], fmt[32], rmode[16], target[16], volctl[16], codec[32];
audio_kv(txt, "app", app, sizeof app);
audio_kv(txt, "fmt", fmt, sizeof fmt);
audio_kv(txt, "route_mode", rmode, sizeof rmode);
/* 输出目标/音量控制/蓝牙编解码器(M6.32 面板透传:audiod 已发 target/volctl/codec,
* 前端蓝牙卡据此点亮「输出目标 DAC|蓝牙 / 音量控制 avrcp|dac / 编解码器」)。缺失时前端优雅降级「—」。 */
audio_kv(txt, "target", target, sizeof target);
audio_kv(txt, "volctl", volctl, sizeof volctl);
audio_kv(txt, "codec", codec, sizeof codec);
sA(s, "{\"available\":true,\"active\":%lld,\"app\":", audio_kv_ll(txt, "active", 0));
sA_str(s, app);
sA(s, ",\"sid\":%lld,\"rate\":%lld,\"ch\":%lld,\"fmt\":",
@@ -1148,6 +1153,12 @@ static void audio_fill(sbuf_t *s)
sA_str(s, rmode);
sA(s, ",\"port\":%lld,\"vol\":%lld",
audio_kv_ll(txt, "port", -1), audio_kv_ll(txt, "vol", -1));
sA(s, ",\"target\":");
sA_str(s, target);
sA(s, ",\"volctl\":");
sA_str(s, volctl);
sA(s, ",\"codec\":");
sA_str(s, codec);
/* 电平监控(261):rms/peak deci-dB(-990=-inf),lvlseq=快照代际;透传时别漏(ABI 追加键)。 */
sA(s, ",\"rms\":%lld,\"peak\":%lld,\"lvlseq\":%lld,\"vol_max\":%d}",
audio_kv_ll(txt, "rms", -990), audio_kv_ll(txt, "peak", -990), audio_kv_ll(txt, "lvlseq", 0), audio_vol_max());
+16
View File
@@ -133,6 +133,11 @@ void appstate_load_settings(void)
if (g_st.player_bg_mode != NCM_BG_DEFAULT && g_st.player_bg_mode != NCM_BG_POWER_SAVE)
g_st.player_bg_mode = NCM_BG_DEFAULT;
/* §NEW 播放模式三态:默认列表循环;越界回落列表循环 */
g_st.play_mode = meow_config_int("ncm.play_mode", NCM_MODE_LISTLOOP);
if (g_st.play_mode < NCM_MODE_SHUFFLE || g_st.play_mode >= NCM_MODE_COUNT)
g_st.play_mode = NCM_MODE_LISTLOOP;
ncm_cache_set_limit_gb(&g_st.ctx.cache, g_st.cache_limit_gb);
g_st.ctx.default_level = g_st.level;
}
@@ -146,6 +151,8 @@ void appstate_save_settings(void)
meow_config_set("ncm.level", b);
snprintf(b, sizeof b, "%d", g_st.player_bg_mode);
meow_config_set("ncm.player_bg_mode", b);
snprintf(b, sizeof b, "%d", g_st.play_mode);
meow_config_set("ncm.play_mode", b);
ncm_cache_set_limit_gb(&g_st.ctx.cache, g_st.cache_limit_gb);
g_st.ctx.default_level = g_st.level;
/* 手动调低上限时立即按 LRU 淘汰(超上限删最旧);未超限则为无操作 */
@@ -764,6 +771,15 @@ void autoplay_maybe(void)
void appstate_set_queue(int src) { g_st.queue_src = src; }
int appstate_get_queue(void) { return g_st.queue_src; }
/* §NEW:设播放模式并落盘(「退出后记住模式」)。仅在 UI 线程(play 模式三态按钮点击)调用;
* play 线程读的是 g_play.play_mode(player_manager_set_play_mode 已同步运行时态)。 */
void appstate_apply_play_mode(int m)
{
if (m < NCM_MODE_SHUFFLE || m >= NCM_MODE_COUNT) return;
g_st.play_mode = m;
appstate_save_settings();
}
int appstate_queue_count(void)
{
int c;
+14
View File
@@ -66,6 +66,17 @@ enum {
#define APP_OP_W 48 /* 行右侧操作区触达宽 */
#define APP_FOOTER_H 26
/* 圆角卡列表项外边距(item margin/gap):相邻卡片间可见空隙 px。SDK listview 行距(=APP_ROW_H)
* 不变——卡片在其行高内上下对称内缩 NCM_ROW_GAP/2,故文本(SDK 按 APP_ROW_H 垂直居中)仍落卡中心;
* 相邻卡顶间距恒=APP_ROW_H=卡高+NCM_ROW_GAP。要求偶数,保证上下对称。 */
#define NCM_ROW_GAP 8
/* 第 idx 行(行高 item,滚动 off,列表顶 list_y)的圆角卡:顶 y 与高 h。 */
static inline int ncm_row_card_y(int item, int idx, int off, int list_y)
{
return list_y + (idx * item - off) + NCM_ROW_GAP / 2;
}
static inline int ncm_row_card_h(int item) { return item - NCM_ROW_GAP; }
/* ---- 播放页(§22 重做:封面页/歌词页双页,NCM_CACHE_DESIGN §6)----
* 沉浸底恒 NCM_PLAYER_BG(不随系统明暗反转——「不动沉浸底」);日夜间主题配色只映射到
* 半透明叠加层(歌词页 scrim)与 accent(红)。 */
@@ -568,6 +579,9 @@ void ipc_media_stop(void);
int ipc_media_nav_exit(void); /* 任务2:发 MSG_NAV{EXIT} 给 launcher 请求退后台(进程+媒体会话保留;
launcher M4 据 background_audio+PLAYING 判保活)。0=发出/-1=失败。 */
int ipc_media_focus_consume(void); /* 任务3:UI 线程消费「回播放页」标志(0=无;1=已消费一次) */
/* 批3:上报当前曲歌词(lrc 原文 + tlrc 翻译)到 launcher 媒体会话(MSG_META_LYRIC 分片)。
* 由 lyric_worker 在拉到/清一歌词后调(换歌/换词 seq 自增;lrc/tl 可为空=清词)。 */
void ipc_media_lyric_report(const char *lrc, const char *tl);
int ipc_act_report(int act); /* 任务4 keep_awake:向 launcher 上报当前顶层 activity(MSG_ACT_FOCUS)。
act=ACT_* 值,映射 "login/home/playlist/player/settings/search"(对齐
app.json awake.<act> 键);launcher fg_keep_awake 依声明判常亮。
@@ -0,0 +1,59 @@
/* app/test/test_meow_row_gap.c — 宿主验证:圆角卡列表项外边距(item margin/gap)几何。
*
* 只测 app/ui 卡片几何辅助(ncm_row_card_y / ncm_row_card_h + NCM_ROW_GAP / APP_ROW_H),
* 纯整数运算,不链 SDK / 不开设备:
* gcc -I. -I../sdk -I../meow_launcherd -Icore \
* -I"$ROOT/patch_v2/sdk" -I"$ROOT/patch_v2/meow_launcherd" \
* -Ivendor/mbedtls/include -Ivendor/jsmn -Ivendor/qrcodegen/c \
* -Ivendor/minimp3 -Ivendor/dr_flac \
* test_meow_row_gap.c -o test_meow_row_gap && ./test_meow_row_gap
*
* 断言集(对应「相邻 item 顶之间 = item 高 + gap」的可见形式):
* ① 相邻卡顶间距 = APP_ROW_H(= 卡高 + gap;SDK 行距不变);
* ② 卡片间可见外距 = NCM_ROW_GAP;
* ③ 卡在其行高内上下对称内缩 → SDK 文本(按 APP_ROW_H 垂直居中)落卡中心;
* ④ 滚动偏移只整体平移,不改变上述间距/对称。
*/
#include "../ncm_app_internal.h" /* APP_ROW_H / NCM_ROW_GAP / ncm_row_card_y / ncm_row_card_h */
#include <stdio.h>
#include <assert.h>
int main(void)
{
const int item = APP_ROW_H; /* 行距(SDK listview item_h,不变) */
const int gap = NCM_ROW_GAP;
const int list_y = 100;
/* 卡顶 = 行顶 + 上下对称内缩 gap/2;卡高 = item - gap。 */
int y0 = ncm_row_card_y(item, 0, 0, list_y);
int y1 = ncm_row_card_y(item, 1, 0, list_y);
int h = ncm_row_card_h(item);
/* ① 行顶(索引0)=list_y;卡相对行顶对称内缩 gap/2;卡高=item-gap */
assert(y0 == list_y + gap / 2);
assert(h == item - gap);
assert(h + gap == item);
/* ① 相邻卡顶间距 = item(顶到顶 = 卡高 + gap) */
assert(y1 - y0 == item);
/* ② 卡片间可见外距 = gap(卡 i 底 到 卡 i+1 顶) */
assert(y1 - (y0 + h) == gap);
/* ③ 卡中心 == 行中心(SDK 文本按行高居中 → 落卡中心) */
assert(y0 - gap / 2 + item / 2 == y0 + h / 2);
/* ④ 滚动偏移只平移,不改变间距/对称 */
for (int off = 0; off <= 2 * item; off += item / 2) {
int a2 = ncm_row_card_y(item, 2, off, list_y);
int a3 = ncm_row_card_y(item, 3, off, list_y);
assert(a3 - a2 == item);
assert(a3 - (a2 + h) == gap);
/* 顶到顶 = 卡高 + gap,且卡在行内对称(对任意 off 一致) */
assert(a2 - (list_y + (2 * item - off)) == gap / 2); /* 行顶 = list_y + idx*item - off */
}
printf("PASS row_gap=%d card_h=%d top2top=%d visible_gap=%d symmetric=1\n",
gap, h, item, gap);
return 0;
}
+11
View File
@@ -122,6 +122,17 @@ const char *ncm_level_name(int lv)
}
}
/* §NEW 播放模式短名(三态):随机 / 列表 / 单曲(播放页模式钮文案,2 字居中)。 */
const char *ncm_mode_name(int m)
{
switch (m) {
case NCM_MODE_SHUFFLE: return "随机";
case NCM_MODE_SINGLE: return "单曲";
case NCM_MODE_LISTLOOP: /* fallthrough */
default: return "列表";
}
}
const char *ncm_level_spec(int lv)
{
switch (lv) {
+11 -5
View File
@@ -91,7 +91,8 @@ static void home_row_bg(void *ctx)
int n = tab_count(g_tab);
if (last > n) last = n;
for (int i = first; i < last; i++) {
int ry = r_list.y + (i * item - off) + 1, th2 = item - 2;
int ry = ncm_row_card_y(item, i, off, r_list.y);
int th2 = ncm_row_card_h(item);
if (ry < r_list.y || ry + th2 > r_list.y + r_list.h) continue; /* 只画完整落在视口内的行 */
ncm_fill_round(r_list.x, ry, r_list.w, th2, 12, th->surfaceContainer);
}
@@ -170,16 +171,21 @@ static void home_draw(void *ctx)
int n = tab_count(g_tab);
if (last > n) last = n;
for (int i = first; i < last; i++) {
int ry = r_list.y + (i * item - off) + 1, th2 = item - 2;
int ry = ncm_row_card_y(item, i, off, r_list.y);
int th2 = ncm_row_card_h(item);
if (ry < r_list.y || ry + th2 > r_list.y + r_list.h) continue;
int icy = ry + (item - isz) / 2;
int icy = ry + (th2 - isz) / 2;
int bx = r_list.x + 16;
ncm_fill_round(bx, icy, isz, isz, 6, pa->red);
meow_ui_fill_rect(bx + isz / 2 - 2, icy + isz / 2 - 2, 4, 4, pa->on_red); /* 盘芯 */
if (g_press_row == i) {
meow_ui_fill_rect(r_list.x, ry, 4, item - 2, pa->red);
meow_ui_fill_rect_alpha(r_list.x, ry, r_list.w, item - 2, pa->red, 26);
meow_ui_fill_rect(r_list.x, ry, 4, th2, pa->red);
meow_ui_fill_rect_alpha(r_list.x, ry, r_list.w, th2, pa->red, 26);
}
/* 擦除 z=10 的 SDK 行分隔线(现落进卡片间空隙):以页底色填空隙带;不越出列表视口底。 */
int gy = ry + th2, gh = NCM_ROW_GAP;
if (gy + gh > r_list.y + r_list.h) gh = r_list.y + r_list.h - gy;
if (gh > 0) meow_ui_fill_rect(r_list.x, gy, r_list.w, gh, th->bg);
}
}
}
+11 -5
View File
@@ -124,7 +124,8 @@ static void pl_row_bg(void *ctx)
int first = off / item, last = (off + r_list.h) / item + 1;
if (last > g_st.track_ids_count) last = g_st.track_ids_count;
for (int i = first; i < last; i++) {
int ry = r_list.y + (i * item - off) + 1, th2 = item - 2;
int ry = ncm_row_card_y(item, i, off, r_list.y);
int th2 = ncm_row_card_h(item);
if (ry < r_list.y || ry + th2 > r_list.y + r_list.h) continue; /* 只画完整落在视口内的行 */
ncm_fill_round(r_list.x, ry, r_list.w, th2, 12, th->surfaceContainer);
}
@@ -193,15 +194,20 @@ static void pl_draw(void *ctx)
int first = off / item, last = (off + r_list.h) / item + 1;
if (last > g_st.track_ids_count) last = g_st.track_ids_count;
for (int i = first; i < last; i++) {
int ry = r_list.y + (i * item - off) + 1, th2 = item - 2;
int ry = ncm_row_card_y(item, i, off, r_list.y);
int th2 = ncm_row_card_h(item);
if (ry < r_list.y || ry + th2 > r_list.y + r_list.h) continue;
int icy = ry + (item - isz) / 2, bx = r_list.x + 16;
int icy = ry + (th2 - isz) / 2, bx = r_list.x + 16;
ncm_fill_round(bx, icy, isz, isz, 6, pa->red);
meow_ui_fill_rect(bx + isz / 2 - 2, icy + isz / 2 - 2, 4, 4, pa->on_red);
if (g_press_row == i) {
meow_ui_fill_rect(r_list.x, ry, 4, item - 2, pa->red);
meow_ui_fill_rect_alpha(r_list.x, ry, r_list.w, item - 2, pa->red, 26);
meow_ui_fill_rect(r_list.x, ry, 4, th2, pa->red);
meow_ui_fill_rect_alpha(r_list.x, ry, r_list.w, th2, pa->red, 26);
}
/* 擦除 z=10 的 SDK 行分隔线(现落进卡片间空隙):以页底色填空隙带;不越出列表视口底。 */
int gy = ry + th2, gh = NCM_ROW_GAP;
if (gy + gh > r_list.y + r_list.h) gh = r_list.y + r_list.h - gy;
if (gh > 0) meow_ui_fill_rect(r_list.x, gy, r_list.w, gh, th->bg);
}
}
}
+12 -5
View File
@@ -95,7 +95,8 @@ static void q_row_bg(void *ctx)
if (last > cnt) last = cnt;
int cur = player_manager_playing_song();
for (int i = first; i < last; i++) {
int ry = r_list.y + (i * item - off) + 1, th2 = item - 2;
int ry = ncm_row_card_y(item, i, off, r_list.y);
int th2 = ncm_row_card_h(item);
if (ry < r_list.y || ry + th2 > r_list.y + r_list.h) continue; /* 只画完整落在视口内的行 */
if (i == cur) {
/* 当前播放行:红色左条 + 红色轻染(高亮),底用 red_ctr 浅红 */
@@ -112,6 +113,7 @@ static void q_draw(void *ctx)
{
(void)ctx;
if (meow_ui_activity_active() != ACT_QUEUE) return;
const meow_theme *th = meow_theme_get();
const ncm_pal_t *pa = ncm_palette(g_dark);
int cnt = appstate_queue_count();
if (cnt <= 0) return;
@@ -120,15 +122,20 @@ static void q_draw(void *ctx)
int first = off / item, last = (off + r_list.h) / item + 1;
if (last > cnt) last = cnt;
for (int i = first; i < last; i++) {
int ry = r_list.y + (i * item - off) + 1, th2 = item - 2;
int ry = ncm_row_card_y(item, i, off, r_list.y);
int th2 = ncm_row_card_h(item);
if (ry < r_list.y || ry + th2 > r_list.y + r_list.h) continue;
int icy = ry + (item - isz) / 2, bx = r_list.x + 16;
int icy = ry + (th2 - isz) / 2, bx = r_list.x + 16;
ncm_fill_round(bx, icy, isz, isz, 6, pa->red);
meow_ui_fill_rect(bx + isz / 2 - 2, icy + isz / 2 - 2, 4, 4, pa->on_red);
if (g_press_row == i) {
meow_ui_fill_rect(r_list.x, ry, 4, item - 2, pa->red);
meow_ui_fill_rect_alpha(r_list.x, ry, r_list.w, item - 2, pa->red, 26);
meow_ui_fill_rect(r_list.x, ry, 4, th2, pa->red);
meow_ui_fill_rect_alpha(r_list.x, ry, r_list.w, th2, pa->red, 26);
}
/* 擦除 z=10 的 SDK 行分隔线(现落进卡片间空隙):以页底色填空隙带;不越出列表视口底。 */
int gy = ry + th2, gh = NCM_ROW_GAP;
if (gy + gh > r_list.y + r_list.h) gh = r_list.y + r_list.h - gy;
if (gh > 0) meow_ui_fill_rect(r_list.x, gy, r_list.w, gh, th->bg);
}
}
Binary file not shown.
+1
View File
@@ -49,6 +49,7 @@ int meow_btn_pressed(meow_widget *w); /* 应用在 on_tap 命中后
/* ---- §5.8.2 Modifier 式通用 Options(轻量结构体,非链式 DSL)----
* 经 meow_widget_set_opts 一次性挂载,作用于任意控件(标准/自定义)。全部控件共用。
* 哨兵语义:bg/fg==0=用控件/主题默认;stroke==0=无描边;stroke_w=描边宽;
* 控件语义:W_PROGRESS 用 bg=轨道(0=surfaceVariant)、fg=填充(0=primary);见 meow_ui_widget.c 批5b;
* radius==-1=控件默认(如按钮 10),>=0=指定圆角(0=直角);pad/margin=0=无;
* disabled/selected=1=置位(立即反映到状态机位)。 */
typedef struct meow_widget_opts {
+10 -6
View File
@@ -2069,31 +2069,35 @@ void meow_widget_draw(meow_widget *w)
break;
}
case W_PROGRESS: {
/* 批5b:轨道/填充可由 opts 覆盖(opts.bg=轨道、opts.fg=填充);opts 未设则回主题默认
* (surfaceVariant/primary)——兼容既有调用(如 hello demo、现在播放页取色后回退默认)。 */
uint32_t track = widget_bg(w, th, th->surfaceVariant);
uint32_t fill = widget_fg(w, th, th->primary);
if (w->variant == 1) { /* 圆形(几何 = 整圆像素外接框;由此解回圆心/半径) */
int thk = MEOW_PROGRESS_ARC_THK;
int m = MEOW_PROGRESS_ARC_MARGIN;
int ccx = w->x + w->w / 2, ccy = w->y + w->h / 2;
int rr = w->w / 2 - m; /* 与 create 一致:回到调用方传入的半径 r */
if (rr <= 0) break;
meow_gfx_ring_arc(ccx, ccy, rr, thk, 0, 360, th->surfaceVariant);
meow_gfx_ring_arc(ccx, ccy, rr, thk, 0, 360, track);
if (w->value < 0) { /* 不定:按时间推进一段弧(App 在 on_tick 里重绘驱动) */
int start = (int)((meow_ui_time_ms() / 30) % 360);
meow_gfx_ring_arc(ccx, ccy, rr, thk, start - 90, 90, th->primary);
meow_gfx_ring_arc(ccx, ccy, rr, thk, start - 90, 90, fill);
} else if (w->value > 0) {
int sweep = w->value * 360 / 100;
if (sweep > 0) meow_gfx_ring_arc(ccx, ccy, rr, thk, -90, sweep, th->primary);
if (sweep > 0) meow_gfx_ring_arc(ccx, ccy, rr, thk, -90, sweep, fill);
}
} else { /* 线性 */
int thk = w->h > 0 ? w->h : 4;
int yy = w->y + w->h / 2 - thk / 2;
meow_ui_fill_rect(w->x, yy, w->w, thk, th->surfaceVariant);
meow_ui_fill_rect(w->x, yy, w->w, thk, track);
if (w->value < 0) { /* 不定:移动块 */
int fw = w->w / 3;
int pos = (int)((meow_ui_time_ms() / 12) % (w->w + fw)) - fw;
meow_ui_fill_rect(w->x + pos, yy, fw, thk, th->primary);
meow_ui_fill_rect(w->x + pos, yy, fw, thk, fill);
} else if (w->value > 0) {
int fw = w->value * w->w / 100;
if (fw > 0) meow_ui_fill_rect(w->x, yy, fw, thk, th->primary);
if (fw > 0) meow_ui_fill_rect(w->x, yy, fw, thk, fill);
}
}
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
+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|0cdf940f58d1e26e7529df226689ab9c|M6.31 歌词页封面修复(np_draw_lyric_coverbg封面背景+1s整页重绘,修批5歌词页无封面/scrim逐秒压黑)+播放页右滑返回+批3歌词数据源+批4双页歌词+批5封面双色取色+蓝牙P1子页
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_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)