From 8c165bb8e6117bd1c41cebd3c00926068f9558f8 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Thu, 3 Sep 2026 22:36:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(lyric/bt):=20=E2=91=A0=E6=A1=8C=E9=9D=A2?= =?UTF-8?q?=E6=AD=8C=E8=AF=8D=E9=A1=B5=E5=B0=81=E9=9D=A2=E4=B8=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=BF=AE=E5=A4=8D(=E6=A0=B9=E5=9B=A0:=E6=AD=8C?= =?UTF-8?q?=E8=AF=8D=E9=A1=B5=E4=BB=8E=E4=B8=8D=E7=94=BB=E5=B0=81=E9=9D=A2?= =?UTF-8?q?+scrim=E9=80=90=E7=A7=92=E5=8F=A0=E6=B6=82=E5=8E=8B=E9=BB=91?= =?UTF-8?q?=E2=86=92=E6=96=B0=E5=A2=9Enp=5Fdraw=5Flyric=5Fcoverbg=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E8=83=8C=E6=99=AF+1s=E6=95=B4=E9=A1=B5=E9=87=8D?= =?UTF-8?q?=E7=BB=98;=E6=92=AD=E6=94=BE=E9=A1=B5=E5=B0=81=E9=9D=A2?= =?UTF-8?q?=E5=AE=BF=E5=AE=9E=E8=AF=81=E9=9D=9E=E6=89=B95=E7=A0=B4?= =?UTF-8?q?=E5=9D=8F)=20=E2=91=A1alsa.conf=20pcm.bluealsa=20DEV=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=AF=86=E5=88=AB(bt=5Fdev.c:audiod=20BT=E6=89=93?= =?UTF-8?q?=E5=BC=80=E6=97=B6bluealsa-cli=E6=A3=80=E6=B5=8Ba2dpsrc?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=B9=82=E7=AD=89=E6=94=B9=E5=86=99DEV=3D?= =?UTF-8?q?=E4=BF=9D=E7=95=99codec/ldac=5Feqmid=E7=AD=89);test=5Fbt=5Fdev?= =?UTF-8?q?=2014/14,0warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- patch_v2/meow_audiod/bt_dev.c | 138 ++++++++++++++++++++++++++ patch_v2/meow_audiod/bt_dev.h | 27 +++++ patch_v2/meow_audiod/build_audiod.sh | 11 +- patch_v2/meow_audiod/meow_audiod.c | 46 +++++++++ patch_v2/meow_audiod/test_bt_dev.c | 116 ++++++++++++++++++++++ patch_v2/meow_launcherd/now_playing.c | 34 +++++-- 6 files changed, 359 insertions(+), 13 deletions(-) create mode 100644 patch_v2/meow_audiod/bt_dev.c create mode 100644 patch_v2/meow_audiod/bt_dev.h create mode 100644 patch_v2/meow_audiod/test_bt_dev.c diff --git a/patch_v2/meow_audiod/bt_dev.c b/patch_v2/meow_audiod/bt_dev.c new file mode 100644 index 0000000..2d1f059 --- /dev/null +++ b/patch_v2/meow_audiod/bt_dev.c @@ -0,0 +1,138 @@ +/* bt_dev.c — 蓝牙 A2DP 设备自识别(纯逻辑实现,见 bt_dev.h 契约)。 + * + * 纯 libc(string / stdlib),无任何 meow_* 调用、无 popen、无文件 I/O —— meow_audiod 负责 + * 读取 /usr/data/alsa.conf、执行 `bluealsa-cli list-pcms` 及写回。宿主单测零链路重。 + */ +#include "bt_dev.h" + +#include +#include +#include + +/* 解析 bluealsa-cli list-pcms 输出,取第一个 a2dpsrc/a2dp_source 端点的设备 MAC。 + * 端点形如 `a2dpsrc/AA:BB:CC:DD:EE:FF`(MAC 在端点 token 内最后一个 '/' 之后到空白前)。 + * 逐个**有界行**匹配(strstr 必须在行内,避免跨换行误把后一行当本行端点——如行 1 + * "profile/aptx" 与行 3 "a2dp_source/..." 的经典误判)。 */ +int bt_dev_detect_mac(const char *listpcms, char *mac, size_t macsz) +{ + if (!listpcms || !mac || macsz < 2) return -1; + mac[0] = 0; + const char *ln = listpcms; + while (ln && *ln) { + const char *nl = strchr(ln, '\n'); + const char *le = nl ? nl : (ln + strlen(ln)); + size_t linelen = (size_t)(le - ln); + char line[256]; + if (linelen >= sizeof line) linelen = sizeof line - 1; + memcpy(line, ln, linelen); line[linelen] = 0; + const char *p = line; + while (*p == ' ' || *p == '\t') p++; + const char *hit = strstr(p, "a2dpsrc"); + if (!hit) hit = strstr(p, "a2dp_source"); + if (hit) { + const char *te = hit; + while (*te && *te != ' ' && *te != '\t' && *te != '\n') te++; + const char *slash = NULL; + for (const char *q = hit; q < te; q++) if (*q == '/') slash = q; + if (slash && (slash + 1) < te) { + const char *start = slash + 1; + size_t len = (size_t)(te - start); + if (len > 0 && len < macsz) { + memcpy(mac, start, len); + mac[len] = 0; + return 0; + } + } + } + if (!nl) break; + ln = nl + 1; + } + return -1; +} + +/* 在单行内把 anchor("DEV=" / "device")后的 MAC 值替换为 mac,保留引号风格。 + * 行未含 anchor → 返回 1(out=原样);含 anchor → 写回新行并返回 0。 */ +static int line_replace_mac(char *out, size_t outsz, const char *line, + const char *anchor, const char *mac) +{ + const char *at = strstr(line, anchor); + if (!at) { snprintf(out, outsz, "%s", line); return 1; } + const char *vp = at + strlen(anchor); + while (*vp == ' ' || *vp == '\t') vp++; + if (*vp == '"') vp++; /* 引号风格:值可带双引号 */ + const char *ve = vp; + while (*ve && *ve != ' ' && *ve != '\t' && *ve != '\n' && + *ve != '"' && *ve != '{' && *ve != '}') ve++; + size_t prefix = (size_t)(vp - line); /* 前段(含开引号,若带) */ + size_t tail = (size_t)(ve - line); /* 值终点(不含闭引号,若带) */ + /* 前段 + mac + 尾段(尾段保留闭引号与其后内容) */ + snprintf(out, outsz, "%.*s%s%s", (int)prefix, line, mac, line + tail); + return 0; +} + +/* 幂等改写 alsa.conf:只把 `pcm.bluealsa` 块内 `DEV=` 与 `device` 后的 MAC 换成 mac。 + * 逐行处理;无 pcm.bluealsa / 无 DEV= / device token → -1;两端已一致 → 1;改了 → 0。 */ +int bt_dev_rewrite(const char *in, size_t in_len, const char *mac, + char **out, size_t *out_len) +{ + if (!in || !mac || !mac[0] || !out || !out_len || in_len == 0) return -1; + if (!strstr(in, "pcm.bluealsa") || + (!strstr(in, "DEV=") && !strstr(in, "device"))) return -1; + + size_t cap = in_len + 128; + char *res = malloc(cap ? cap : 1); + if (!res) return -1; + size_t o = 0, changed = 0; + const char *p = in, *end = in + in_len; + + while (p < end) { + const char *nl = memchr(p, '\n', (size_t)(end - p)); + const char *le = nl ? nl : end; + size_t linelen = (size_t)(le - p); + char line[512], newline[512]; + if (linelen >= sizeof line) linelen = sizeof line - 1; + memcpy(line, p, linelen); line[linelen] = 0; + + const char *k = line; + while (*k == ' ' || *k == '\t') k++; + int anchored = 0; + if (strstr(k, "DEV=")) { + line_replace_mac(newline, sizeof newline, line, "DEV=", mac); + anchored = 1; + } else if (strncmp(k, "device", 6) == 0 && + (k[6] == ' ' || k[6] == '\t' || k[6] == 0)) { + line_replace_mac(newline, sizeof newline, line, "device", mac); + anchored = 1; + } + if (anchored) { + if (strcmp(line, newline) != 0) changed = 1; + size_t n = strlen(newline); + if (o + n + 2 > cap) { + cap = o + n + 256; + char *t = realloc(res, cap); + if (!t) { free(res); return -1; } + res = t; + } + memcpy(res + o, newline, n); o += n; + if (nl) res[o++] = '\n'; + } else { + size_t n = linelen; + if (o + n + 2 > cap) { + cap = o + n + 256; + char *t = realloc(res, cap); + if (!t) { free(res); return -1; } + res = t; + } + memcpy(res + o, p, n); o += n; + if (nl) res[o++] = '\n'; + } + if (!nl) break; + p = nl + 1; + } + + if (!changed) { free(res); return 1; } + res[o] = 0; + *out = res; + *out_len = o; + return 0; +} diff --git a/patch_v2/meow_audiod/bt_dev.h b/patch_v2/meow_audiod/bt_dev.h new file mode 100644 index 0000000..8c6ed76 --- /dev/null +++ b/patch_v2/meow_audiod/bt_dev.h @@ -0,0 +1,27 @@ +/* bt_dev.h — 蓝牙 A2DP 设备自识别(DEV 自动更新纯逻辑,宿主可单测)。 + * + * 背景:/usr/data/alsa.conf 的 `pcm.bluealsa`(plug→bluealsa)需把 DEV/device 钉在当前 + * 连接的 A2DP sink 设备 MAC 上才能正确出声。本模块提供两段纯逻辑(无 meow_* / popen / 文件 + * I/O —— meow_audiod 负责读文件与执行系统命令): + * - bt_dev_detect_mac:从 `bluealsa-cli list-pcms` 输出提取第一个 a2dpsrc 端点的设备 MAC; + * - bt_dev_rewrite:幂等改写 alsa.conf 文本,把 `pcm.bluealsa` 块内 `DEV=` 与 + * `device ` 两处换成目标 MAC(保留引号风格 / codec / 其余 token)。 + * 纯 libc(string / stdlib),独立编译,宿主零链接重。 + */ +#ifndef BT_DEV_H +#define BT_DEV_H + +#include + +/* 解析 bluealsa-cli list-pcms 输出,取第一个 a2dpsrc/a2dp_source 端点的设备 MAC。 + * 返回 0=找到并写入 mac(NUL 结尾),-1=无 a2dpsrc 端点 / 参数非法。macsz 建议 ≥18。 */ +int bt_dev_detect_mac(const char *listpcms, char *mac, size_t macsz); + +/* 幂等改写 alsa.conf 文本(in_len 不含 NUL):把 `pcm.bluealsa` 块内 `DEV=` 与 + * `device ` 两处 MAC 换成 mac(保留引号风格、codec/其它 token 原样)。 + * 返回 0=已改动(*out 与 *out_len 给出新文本,调用方负责 free);1=无需改动(两端已一致); + * -1=无 pcm.bluealsa / 无 DEV= 或 device token / 参数非法(不动)。 */ +int bt_dev_rewrite(const char *in, size_t in_len, const char *mac, + char **out, size_t *out_len); + +#endif /* BT_DEV_H */ diff --git a/patch_v2/meow_audiod/build_audiod.sh b/patch_v2/meow_audiod/build_audiod.sh index 9743c13..1108131 100755 --- a/patch_v2/meow_audiod/build_audiod.sh +++ b/patch_v2/meow_audiod/build_audiod.sh @@ -28,12 +28,15 @@ if [ "$1" = "host" ]; then HOST_CC="${CC_HOST:-cc}" # BT 目标直链宿主 libasound.so.2(无 asoundlib.h,原型内联于 meow_audiod.c;-l: 精确到版本, # 不依赖 unversioned dev 链接)。删 -ldl:BT 不再用 dlopen。 - "$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod_host -lpthread -l:libasound.so.2 + "$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c bt_dev.c -o meow_audiod_host -lpthread -l:libasound.so.2 "$HOST_CC" $INC -Os -Wall -Wextra meow_audio.c -o meow_audio_host -lm "$HOST_CC" $INC -Os -Wall -Wextra test_audiod_host.c -o test_audiod_host -lm + "$HOST_CC" -Os -Wall -Wextra test_bt_dev.c bt_dev.c -o test_bt_dev -lm echo "== host builds ==" - ls -la meow_audiod_host meow_audio_host test_audiod_host - file meow_audiod_host meow_audio_host test_audiod_host + ls -la meow_audiod_host meow_audio_host test_audiod_host test_bt_dev + file meow_audiod_host meow_audio_host test_audiod_host test_bt_dev + echo "== host regression (test_bt_dev: BT DEV auto-detect) ==" + ./test_bt_dev || { echo "!! test_bt_dev FAILED"; exit 1; } echo "== host regression (test_audiod_host) ==" ./test_audiod_host || { echo "!! host regression FAILED"; exit 1; } exit 0 @@ -56,7 +59,7 @@ mkdir -p "$LIBBT" if [ ! -f "$LIBBT/libasound.so.2.0.0" ]; then cp "$ASOUND_SRC" "$LIBBT/libasound.so.2.0.0"; fi ln -sf libasound.so.2.0.0 "$LIBBT/libasound.so" ln -sf libasound.so.2.0.0 "$LIBBT/libasound.so.2" -"$ZIG" cc --target=mipsel-linux-gnueabihf.2.22 $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod \ +"$ZIG" cc --target=mipsel-linux-gnueabihf.2.22 $INC -Os -Wall -Wextra meow_audiod.c bt_dev.c -o meow_audiod \ -lpthread -Llibbt -lasound echo "== meow_audiod (mipsel glibc-dynamic, BT links libasound.so.2) ==" ls -la meow_audiod diff --git a/patch_v2/meow_audiod/meow_audiod.c b/patch_v2/meow_audiod/meow_audiod.c index c12797d..c587879 100644 --- a/patch_v2/meow_audiod/meow_audiod.c +++ b/patch_v2/meow_audiod/meow_audiod.c @@ -58,6 +58,7 @@ #include #include #include "meow_ipc.h" +#include "bt_dev.h" /* P2:DEV 自识别(a2dpsrc 端点 → MAC → alsa.conf 幂等改写) */ #ifdef __linux__ #include @@ -910,6 +911,50 @@ static void bt_apply_codec_conf(void) free(out); } +/* P2 BT DEV 自识别:把 /usr/data/alsa.conf 的 `pcm.bluealsa` DEV/device 自动改为**当前连接的 + * A2DP sink 设备** MAC(bluealsa-cli list-pcms 的 a2dpsrc 端点)。幂等(设备未变不重写)、 + * 无设备/失败跳过;只改 DEV/device 两处、保留 codec/其余 token。BT 打开前调用,实现 + * 「连哪个设备就播哪个」。跟 bt_apply_codec_conf 同构(外科式 token 替换,绝不建结构)。 */ +static void bt_apply_device_conf(void) +{ + char mac[32] = ""; + char *o = popen_read_tmo(1500, "bluealsa-cli list-pcms 2>/dev/null"); + if (!o) { logmsg("bt: dev conf skipped (no bluealsa-cli)"); return; } + if (bt_dev_detect_mac(o, mac, sizeof mac) != 0) { + logmsg("bt: dev conf skipped (no a2dpsrc pcm)"); + return; + } + logmsg("bt: current a2dp dev %s", mac); + const char *path = "/usr/data/alsa.conf"; + FILE *f = fopen(path, "r"); + if (!f) return; + fseek(f, 0, SEEK_END); long sz = ftell(f); + if (sz <= 0 || sz > 64 * 1024) { fclose(f); return; } + fseek(f, 0, SEEK_SET); + char *data = malloc((size_t)sz + 1); + if (!data) { fclose(f); return; } + size_t rl = fread(data, 1, (size_t)sz, f); data[rl] = 0; + fclose(f); + char *outdata = NULL; size_t outlen = 0; + int rc = bt_dev_rewrite(data, rl, mac, &outdata, &outlen); + free(data); + if (rc == 0) { + int fd = open(path, O_WRONLY | O_TRUNC, 0644); + if (fd >= 0) { + if (write(fd, outdata, outlen) < 0) { } + close(fd); + logmsg("bt: dev conf auto-updated %s -> %s", path, mac); + } else { + logmsg("bt: dev conf %s not writable, skip", path); + } + } else if (rc == 1) { + logmsg("bt: dev conf ok (already %s)", mac); + } else { + logmsg("bt: dev conf skip (no pcm.bluealsa/no DEV token)"); + } + if (outdata) free(outdata); +} + /* BT 打开:libasound → snd_pcm_open(pcm.bluealsa/bluealsa, PLAYBACK, NONBLOCK) → * set_params(S16_LE, RW, ch, rate, soft_resample, 100ms)。返回 0=ok。失败置 g_bt_open=0。 */ static int bt_open(void) @@ -1177,6 +1222,7 @@ static int dev_open(void) * 宿主即 FILE/NULL sink 时不走此支——因此宿主可测「目标选择/音量隔离/格式归一」逻辑, * 真实 bluealsa 传输留给真机(见 bt_* 注释)。失败 → 降级丢帧(自感知兜底 §五)。 */ if (g_target == AUDIO_OUT_BT && g_sink == SINK_ALSA) { + bt_apply_device_conf(); /* P2:DEV 幂等自更新到当前连接 A2DP 设备(连哪台播哪台) */ bt_apply_codec_conf(); if (bt_open() == 0) return 0; /* g_bt_open=1;返回 0=逻辑已开(真实句柄在 g_bt_hdl) */ logmsg("bt: open failed (degrade: drop PCM until next OPEN)"); diff --git a/patch_v2/meow_audiod/test_bt_dev.c b/patch_v2/meow_audiod/test_bt_dev.c new file mode 100644 index 0000000..6fa1957 --- /dev/null +++ b/patch_v2/meow_audiod/test_bt_dev.c @@ -0,0 +1,116 @@ +/* test_bt_dev.c — 蓝牙 DEV 自识别宿主验证(纯逻辑,bt_dev.c)。 + * + * 覆盖矩阵(对齐任务B 宿主测试要求): + * D1 bt_dev_detect_mac:a2dpsrc/ 端点 → 提取 MAC(含 a2dp_source、多空行、多设备取第一个、 + * 无 a2dpsrc / 空输出 / NULL → -1)。 + * D2 bt_dev_rewrite:真实 alsa.conf 文本 → DEV=、device 两处 MAC 换成目标,保留 codec/其余 token; + * 同值 → 1(无需改动);无 pcm.bluealsa / 无 DEV= → -1;带引号风格保留。 + * + * 编译(在 patch_v2/meow_audiod 下): + * cc -Wall -Wextra -o test_bt_dev test_bt_dev.c bt_dev.c + * 运行:./test_bt_dev + */ +#include "bt_dev.h" +#include +#include +#include + +static int g_pass = 0, g_fail = 0; +static void check(int cond, const char *name) +{ + if (cond) { printf(" PASS %s\n", name); g_pass++; } + else { printf(" FAIL %s\n", name); g_fail++; } +} + +int main(void) +{ + setbuf(stdout, NULL); + + /* ---- D1 设备列表 → 选中 a2dpsrc 连接设备 ---- */ + { + char mac[32] = ""; + int rc = bt_dev_detect_mac( + "a2dpsrc/E0:42:6D:79:C5:98\na2dpsnk/E0:42:6D:79:C5:98\n", mac, sizeof mac); + check(rc == 0 && strcmp(mac, "E0:42:6D:79:C5:98") == 0, "D1a a2dpsrc/ -> extract MAC"); + } + { + char mac[32] = ""; + int rc = bt_dev_detect_mac( + "profile/aptx\n\na2dp_source/33:DE:2E:21:AD:30\n", mac, sizeof mac); + check(rc == 0 && strcmp(mac, "33:DE:2E:21:AD:30") == 0, "D1b a2dp_source/ -> extract MAC"); + } + { + char mac[32] = ""; + int rc = bt_dev_detect_mac( + "a2dpsrc/AA:BB:CC:DD:EE:FF 0\na2dpsrc/11:22:33:44:55:66 1\n", mac, sizeof mac); + check(rc == 0 && strcmp(mac, "AA:BB:CC:DD:EE:FF") == 0, "D1c multi-device -> first a2dpsrc"); + } + { + char mac[32] = ""; + int rc = bt_dev_detect_mac("\n", mac, sizeof mac); + check(rc == -1, "D1d empty output -> -1"); + } + { + char mac[32] = ""; + int rc = bt_dev_detect_mac("a2dpsnk/11:22:33:44:55:66\n", mac, sizeof mac); + check(rc == -1, "D1e only sink (no a2dpsrc) -> -1"); + } + { + char mac[32] = ""; + int rc = bt_dev_detect_mac(NULL, mac, sizeof mac); + check(rc == -1, "D1f NULL input -> -1"); + } + + /* ---- D2 DEV 串入 / 保留其它 token ---- */ + { + const char *conf = + "pcm.bluealsa:DEV=E0:42:6D:79:C5:98 {\n" + " type plug\n" + " slave {\n" + " pcm {\n" + " type bluealsa\n" + " device E0:42:6D:79:C5:98\n" + " profile \"a2dp\"\n" + " codec \"LDAC\"\n" + " ldac_eqmid \"LDAC_ABR\"\n" + " }\n" + " }\n" + "}\n"; + char *out = NULL; size_t olen = 0; + int rc = bt_dev_rewrite(conf, strlen(conf), "33:DE:2E:21:AD:30", &out, &olen); + check(rc == 0 && out != NULL, "D2a rewrite returns changed"); + check(out && strstr(out, ":DEV=33:DE:2E:21:AD:30 {"), "D2b DEV= updated"); + check(out && strstr(out, "device 33:DE:2E:21:AD:30\n"), "D2c device updated"); + check(out && strstr(out, "codec \"LDAC\"") && strstr(out, "ldac_eqmid \"LDAC_ABR\""), + "D2d codec/other tokens preserved"); + /* 旧 MAC 不再出现 */ + check(out && !strstr(out, "E0:42:6D:79:C5:98"), "D2e old MAC gone"); + free(out); + } + { + const char *conf = + "pcm.bluealsa:DEV=33:DE:2E:21:AD:30 {\n" + " type plug\n slave {\n pcm {\n" + " type bluealsa\n device 33:DE:2E:21:AD:30\n" + " profile \"a2dp\"\n codec \"LDAC\"\n }\n }\n}\n"; + char *out = NULL; size_t olen = 0; + int rc = bt_dev_rewrite(conf, strlen(conf), "33:DE:2E:21:AD:30", &out, &olen); + check(rc == 1 && out == NULL, "D2f same MAC -> unchanged (idempotent)"); + } + { + const char *conf = "pcm.bluealsa {\n type bluealsa\n device \"E0:42:6D:79:C5:98\"\n}\n"; + char *out = NULL; size_t olen = 0; + int rc = bt_dev_rewrite(conf, strlen(conf), "33:DE:2E:21:AD:30", &out, &olen); + check(rc == 0 && out && strstr(out, "device \"33:DE:2E:21:AD:30\""), + "D2g quoted device preserved (no DEV= token)"); + free(out); + } + { + char *out = NULL; size_t olen = 0; + int rc = bt_dev_rewrite("pcm.hw {\n type hw\n}\n", 21, "33:DE:2E:21:AD:30", &out, &olen); + check(rc == -1 && out == NULL, "D2h no pcm.bluealsa -> -1"); + } + + printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail); + return g_fail; +} diff --git a/patch_v2/meow_launcherd/now_playing.c b/patch_v2/meow_launcherd/now_playing.c index 3158410..1759343 100644 --- a/patch_v2/meow_launcherd/now_playing.c +++ b/patch_v2/meow_launcherd/now_playing.c @@ -291,6 +291,19 @@ static void np_lyric_refresh(const meow_media_session_t *s) g_np_lyric_last_valid = valid; } +/* 歌词页封面作整页背景:等比 cover 到 max(W, H-SB_BAR_H) 方块、居中铺满内容区(裁剪溢出 + * 部分),衬在 lyric_view 半透明 scrim 之下形成压暗封面;无封面不画(回退渐变/主题面)。 + * 批6:批5 只铺渐变、从不画封面 → 歌词页无封面;此处补上封面背景,取色只做附加。 */ +static void np_draw_lyric_coverbg(int W, int H) +{ + if (!g_np_cover) return; + int size = W > (H - SB_BAR_H) ? W : (H - SB_BAR_H); + int x = (W - size) / 2; + int y = SB_BAR_H + (H - SB_BAR_H - size) / 2; + if (y < SB_BAR_H) y = SB_BAR_H; + meow_ui_img_cover(x, y, size, g_np_cover); +} + /* 歌词页整页绘制:铺封面取色渐变(或 theme surface)底 → lyric_view(自绘 scrim + 歌词/空态)。 */ static void np_draw_lyric_page(int redraw_sb) { @@ -298,13 +311,17 @@ static void np_draw_lyric_page(int redraw_sb) int W = fb_xres, H = fb_yres; if (!g_np_init) np_widgets_create(); if (redraw_sb) fill_rect(0, 0, W, SB_BAR_H, th->surface); - /* 批5:封面取色背景(主→次渐变)或主题面;并注入歌词页 set_colors。 */ + /* 批5:封面取色背景(主→次渐变)或主题面;并注入歌词页 set_colors。 + * 批6:有封面时以封面作整页背景(衬在 lyric_view 半透明 scrim 之下),取色只做附加; + * 无封面回退渐变/主题面。 */ np_cover_theme_update(); - if (g_np_cc_valid) + np_apply_lyric_colors(); + if (g_np_cover) + np_draw_lyric_coverbg(W, H); + else if (g_np_cc_valid) np_fill_gradient(0, SB_BAR_H, W, H - SB_BAR_H, g_np_cc.bg_top, g_np_cc.bg_bottom); else fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface); - np_apply_lyric_colors(); np_lyric_refresh(meow_media_current()); if (g_np_lyric) meow_widget_draw(meow_lyric_view_widget(g_np_lyric)); @@ -607,12 +624,11 @@ void run_now_playing(struct app *apps, int napps) /* 进度推进(按页分流):播放页=进度带+按钮;歌词页=歌词实时滚动重绘(当前行随 pos)。 */ last_prog = now; if (g_np_page == NP_PAGE_LYRIC) { - np_lyric_refresh(s); - if (g_np_lyric) { - meow_widget_draw(meow_lyric_view_widget(g_np_lyric)); - meow_dirty_add(0, SB_BAR_H, fb_xres, fb_yres - SB_BAR_H); - meow_ui_flush(); - } + /* 批6:整页重绘(封面背景+scrim+歌词),避免 lyric_view 半透明 scrim 每 1s + * 叠涂把封面/背景逐秒压黑(批4 既有「只重绘控件不重铺背景」结构修正)。 */ + np_draw_lyric_page(0); + meow_dirty_add(0, SB_BAR_H, fb_xres, fb_yres - SB_BAR_H); + meow_ui_flush(); } else { np_repaint_progress(); }