460 lines
24 KiB
C
460 lines
24 KiB
C
/* now_playing.c — 阶段D附录B:Now Playing 大页(主界面横划进入;左缘右滑返回)。
|
||
*
|
||
* 定案(用户):Now Playing = 通知中心媒体卡片 + 主界面横划进入的播放页,两者都做。
|
||
* 本文件实现后者:封面大图 + 元数据 + 进度 + 控制按钮 + 歌词占位;返回手势回主界面。
|
||
* 数据源:meow_media 会话(最近活跃媒体源);空态=占位。参照 SDK §5.2 横向分页模板的
|
||
* 手势分流(左缘返回 / 顶部下滑通知中心 / 中部横滑内容)——本页只接管「中部/右缘向左滑进、
|
||
* 左缘右滑返回」,与全局手势不冲突。
|
||
*
|
||
* 性能:封面解码缓存(按 artwork 路径变化重载,不每帧解码);整页 1s 刷新进度(墙钟推进),
|
||
* 元数据/源变化立即整页重画;交互只重绘按钮/进度带。
|
||
*/
|
||
#include "meow_launcher_internal.h"
|
||
|
||
|
||
#define NP_COVER 300 /* 封面边长(方,居中) */
|
||
#define NP_COVER_X ((fb_xres - NP_COVER) / 2)
|
||
#define NP_COVER_Y 60
|
||
#define NP_TITLE_Y 412
|
||
#define NP_ARTIST_Y 450
|
||
#define NP_ALBUM_Y 480
|
||
#define NP_PROG_X 40
|
||
#define NP_PROG_W (fb_xres - 80)
|
||
#define NP_PROG_Y 518
|
||
#define NP_TIME_Y 492
|
||
#define NP_BTN_Y 568 /* 控制按钮中心 y */
|
||
#define NP_PLAY_CX (fb_xres / 2)
|
||
#define NP_PREV_CX (NP_PLAY_CX - 78)
|
||
#define NP_NEXT_CX (NP_PLAY_CX + 78)
|
||
#define NP_PLAY_R 30
|
||
#define NP_PREV_R 22
|
||
#define NP_LYRIC_Y 640
|
||
|
||
static meow_img *g_np_cover = NULL;
|
||
static char g_np_cover_path[200] = "";
|
||
|
||
/* 封面解码缓存(artwork 路径变化才重载)。 */
|
||
static void np_cover_load(const meow_media_session_t *s)
|
||
{
|
||
if (!s) return;
|
||
if (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[200];
|
||
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);
|
||
}
|
||
|
||
#if 0 /* ================================================================
|
||
* M7.0b 退役:Now Playing 手绘控制钮/进度条改走 SDK 控件(meow_iconbtn +
|
||
* meow_progress),命中/状态/进度全由 SDK 接管;保留作回退位(删除 #if 0 行恢复旧手绘)。
|
||
* ================================================================ */
|
||
/* 实心三角(dir=+1 右向 / -1 左向)。 */
|
||
static void np_tri(int cx, int cy, int size, int dir, uint32_t col)
|
||
{
|
||
int half = size / 2;
|
||
for (int dy = -half; dy <= half; dy++) {
|
||
int w = half + 1 - (dy < 0 ? -dy : dy);
|
||
if (w <= 0) continue;
|
||
int x0 = cx + (dir > 0 ? -w / 2 : w / 2 - w);
|
||
fill_rect(x0, cy + dy, w, 1, col);
|
||
}
|
||
}
|
||
|
||
/* 圆形控制按钮(kind:0=prev 1=play 2=next 3=pause)。 */
|
||
static void np_btn(int cx, int cy, int r, int kind, const meow_ui_theme_t *th)
|
||
{
|
||
meow_gfx_fill_round(cx - r, cy - r, r * 2, r * 2, r, th->primary);
|
||
uint32_t fg = 0xFFFFFF;
|
||
if (kind == 0) {
|
||
np_tri(cx + 5, cy, r - 5, -1, fg);
|
||
fill_rect(cx - 4, cy - (r - 7), 4, (r - 7) * 2, fg);
|
||
} else if (kind == 1) {
|
||
np_tri(cx + 3, cy, r - 3, 1, fg);
|
||
} else if (kind == 2) {
|
||
np_tri(cx - 5, cy, r - 5, 1, fg);
|
||
fill_rect(cx + 2, cy - (r - 7), 4, (r - 7) * 2, fg);
|
||
} else {
|
||
int bw = (r - 8) / 2; if (bw < 2) bw = 2;
|
||
fill_rect(cx - 8, cy - (r - 7), bw, (r - 7) * 2, fg);
|
||
fill_rect(cx + 8 - bw, cy - (r - 7), bw, (r - 7) * 2, fg);
|
||
}
|
||
}
|
||
#endif /* M7.0b 退役:Now Playing 旧手绘控制钮 */
|
||
|
||
/* 居中 + 按宽截断(UTF-8 安全),防超长标题溢出屏外。(M7.0b 保留:封面/歌名/艺术家仍 SD) */
|
||
static void np_center_text(int y, const char *s, int size, uint32_t color, int maxw)
|
||
{
|
||
if (!s) s = "";
|
||
int W = fb_xres;
|
||
if (ui_width(s, size) <= maxw) {
|
||
ui_draw((W - ui_width(s, size)) / 2, y, s, color, size);
|
||
return;
|
||
}
|
||
/* 近似截断:实际按字节长度比例切(title/artist 罕见超长,够用不裂屏) */
|
||
char buf[160];
|
||
int keep = (int)(strlen(s) * maxw / (ui_width(s, size) + 1));
|
||
if (keep < 0) keep = 0;
|
||
if (keep > (int)sizeof buf - 4) keep = (int)sizeof buf - 4;
|
||
memcpy(buf, s, (size_t)keep); buf[keep] = 0;
|
||
strcat(buf, "...");
|
||
ui_draw((W - ui_width(buf, size)) / 2, y, buf, color, size);
|
||
}
|
||
|
||
/* ==================================================================
|
||
* M7.0b Now Playing 控件化(launcher 无 meow_ui_run,手动驱动 SDK 控件):
|
||
* - 进度条 → meow_progress(线性,SDK 主题 track/fill);
|
||
* - 播放控制三钮 → meow_iconbtn(PREV / PLAY_ARROW|PAUSE / NEXT;按下=PAUSE、暂停=PLAY_ARROW,
|
||
* 同位置叠两钮、按当前状态只画其一);
|
||
* - 命中走 meow_widget_hit(圆形,owner==哨兵 + enabled 闸门)→ meow_media_playctl 分发;
|
||
* - 封面(meow_img contain)与歌名/艺术家(meow_font)不动(本就 SD),仅文案走 meow_str。
|
||
* 控件 lazy 创建一次(owner=NP_SENTINEL_ACT),跨多次进入复用;run_now_playing() 进入 push
|
||
* 哨兵、退出 pop(owner 闸门使控件命中生效)。
|
||
* ================================================================== */
|
||
#define NP_SENTINEL_ACT 0x51 /* Now Playing 哨兵 activity */
|
||
|
||
static meow_widget *g_np_prog = NULL;
|
||
static meow_widget *g_np_prev = NULL, *g_np_play = NULL, *g_np_pause = NULL, *g_np_next = NULL;
|
||
static int g_np_init = 0;
|
||
|
||
static void np_widgets_create(void)
|
||
{
|
||
meow_ui_activity_push(NP_SENTINEL_ACT);
|
||
g_np_prog = meow_progress_create(NP_PROG_X, NP_PROG_Y, NP_PROG_W, 6, 0);
|
||
g_np_prev = meow_iconbtn_create(NP_PREV_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R,
|
||
NP_PREV_R * 2, MEOW_ICON_SKIP_PREV);
|
||
g_np_play = meow_iconbtn_create(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R,
|
||
NP_PLAY_R * 2, MEOW_ICON_PLAY_ARROW);
|
||
g_np_pause = meow_iconbtn_create(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R,
|
||
NP_PLAY_R * 2, MEOW_ICON_PAUSE);
|
||
g_np_next = meow_iconbtn_create(NP_NEXT_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R,
|
||
NP_PREV_R * 2, MEOW_ICON_SKIP_NEXT);
|
||
/* 主播放钮保持「强调动作」观感:底色=primary、图标=onPrimary;其余两钮默认 surfaceContainerLow。 */
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_widget_opts op; memset(&op, 0, sizeof op);
|
||
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);
|
||
meow_ui_activity_pop();
|
||
g_np_init = 1;
|
||
}
|
||
|
||
/* 主题刷新:播放钮底色/图标随当前主题(明暗/风格切换后跟手)。 */
|
||
static void np_refresh_theme(void)
|
||
{
|
||
if (!g_np_init) return;
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_widget_opts op; memset(&op, 0, sizeof op);
|
||
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 按会话状态只画其一——两钮同位,改画时
|
||
* 上一帧残留被新画圆底盖掉)。 */
|
||
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);
|
||
meow_widget *pb = playing ? g_np_pause : g_np_play;
|
||
if (pb) { meow_widget_set_selected(pb, 0); meow_widget_draw(pb); }
|
||
}
|
||
|
||
/* 进度带(进度条 + 时间)绘制;pos = 当前显示位置。进度条走 SDK meow_progress。 */
|
||
static void np_draw_progress(const meow_media_session_t *s, uint32_t pos, const meow_ui_theme_t *th)
|
||
{
|
||
int pct = (s->dur_ms > 0) ? (int)((uint64_t)pos * 100 / s->dur_ms) : 0;
|
||
if (pct > 100) pct = 100;
|
||
if (g_np_prog) { meow_progress_set(g_np_prog, pct); meow_widget_draw(g_np_prog); }
|
||
/* 时间(左=当前,右=总长;MM:SS) */
|
||
char l[16], rbuf[16];
|
||
unsigned lm = pos / 60000u, ls = (pos % 60000u) / 1000u;
|
||
unsigned dm = (s->dur_ms ? s->dur_ms : 0) / 60000u, ds = (s->dur_ms ? s->dur_ms : 0) % 60000u / 1000u;
|
||
snprintf(l, sizeof l, "%u:%02u", lm, ls);
|
||
snprintf(rbuf, sizeof rbuf, "%u:%02u", dm, ds);
|
||
ui_draw(NP_PROG_X, NP_TIME_Y, l, th->on_surface_variant, 16);
|
||
ui_draw(NP_PROG_X + NP_PROG_W - ui_width(rbuf, 16), NP_TIME_Y, rbuf, th->on_surface_variant, 16);
|
||
}
|
||
|
||
/* 整页绘制(空态占位)。redraw_sb=1 时喂/标脏状态栏(打开/主题变化),否则保留上次顶条。 */
|
||
static void np_draw_page(int redraw_sb)
|
||
{
|
||
const meow_ui_theme_t *th = s_ui_theme();
|
||
int W = fb_xres, H = fb_yres;
|
||
if (!g_np_init) np_widgets_create(); /* M7.0b:lazy 建控件(跨进入复用) */
|
||
if (redraw_sb) fill_rect(0, 0, W, SB_BAR_H, th->surface);
|
||
fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface);
|
||
const meow_media_session_t *s = meow_media_current();
|
||
if (!s || s->state == MEOW_MEDIA_STOPPED) {
|
||
/* 空态占位(M7.0b 文案走 meow_str,未知 id 回退中文) */
|
||
const char *t1 = meow_str_zh("np.not_playing", "未在播放");
|
||
ui_draw((W - ui_width(t1, 26)) / 2, 300, t1, th->on_surface_variant, 26);
|
||
const char *t2 = meow_str_zh("np.open_app_hint", "打开播放 App 以显示正在播放");
|
||
ui_draw((W - ui_width(t2, 18)) / 2, 348, t2, th->on_surface_variant, 18);
|
||
const char *t3 = meow_str_zh("np.edge_back", "左缘右滑返回");
|
||
ui_draw((W - ui_width(t3, 16)) / 2, 396, t3, th->on_surface_variant, 16);
|
||
if (redraw_sb) draw_statusbar();
|
||
return;
|
||
}
|
||
np_cover_load(s);
|
||
/* 封面:解码缓存 + 圆角裁方;无封面回退主题色占位 + 首字 */
|
||
if (g_np_cover) {
|
||
meow_gfx_fill_round(NP_COVER_X - 6, NP_COVER_Y - 6, NP_COVER + 12, NP_COVER + 12, 18, th->surface_container);
|
||
meow_ui_img_cover(NP_COVER_X, NP_COVER_Y, NP_COVER, g_np_cover);
|
||
} else {
|
||
meow_gfx_fill_round(NP_COVER_X - 6, NP_COVER_Y - 6, NP_COVER + 12, NP_COVER + 12, 18, th->surface_container);
|
||
meow_gfx_fill_round(NP_COVER_X, NP_COVER_Y, NP_COVER, NP_COVER, 14, th->primary);
|
||
const char *t = s->title[0] ? s->title : s->app_id;
|
||
int cw = ui_width(t, 22);
|
||
ui_draw(NP_COVER_X + (NP_COVER - cw) / 2, NP_COVER_Y + (NP_COVER - meow_font_line_height(22)) / 2,
|
||
t, meow_theme_get()->onPrimary, 22);
|
||
}
|
||
/* 元数据 */
|
||
np_center_text(NP_TITLE_Y, s->title, 26, th->on_surface, W - 40);
|
||
np_center_text(NP_ARTIST_Y, s->artist, 18, th->on_surface_variant, W - 60);
|
||
if (s->album[0])
|
||
np_center_text(NP_ALBUM_Y, s->album, 16, th->on_surface_variant, W - 80);
|
||
/* 进度(SDK meow_progress) */
|
||
uint32_t pos = meow_media_display_pos(s);
|
||
np_draw_progress(s, pos, th);
|
||
/* 控制按钮(SDK meow_iconbtn,命中/状态走 SDK) */
|
||
np_draw_ctl(s);
|
||
/* 歌词占位(M7.0b 文案走 meow_str) */
|
||
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();
|
||
}
|
||
|
||
/* 仅重绘进度带 + 按钮(元数据/源变化时整页;纯进度变化时局部)。 */
|
||
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;
|
||
uint32_t pos = meow_media_display_pos(s);
|
||
/* 清进度带 + 控制带,再重绘(只脏这两带,不整屏)。 */
|
||
fill_rect(0, NP_TIME_Y, fb_xres, NP_BTN_Y + NP_PLAY_R - NP_TIME_Y, th->surface);
|
||
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);
|
||
meow_ui_flush();
|
||
}
|
||
|
||
/* 控制按钮命中:返回 cmd(1-4),0=未命中。走 SDK 圆形命中(owner==哨兵 + enabled 闸门)。
|
||
* 播放/暂停两钮同位:命中任一(同矩形)→ 按会话状态分派 play(1)/pause(2)。 */
|
||
static int np_btn_tap(int tx, int ty)
|
||
{
|
||
const meow_media_session_t *s = meow_media_current();
|
||
if (g_np_prev && meow_widget_hit(g_np_prev, tx, ty)) return 3;
|
||
if (g_np_play && meow_widget_hit(g_np_play, tx, ty))
|
||
return (s && s->state == MEOW_MEDIA_PLAYING) ? 2 : 1;
|
||
if (g_np_next && meow_widget_hit(g_np_next, tx, ty)) return 4;
|
||
return 0;
|
||
}
|
||
|
||
/* 进度 seek 命中(拖动/点按进度带):返回 seek_ms(>0),0=未中(或 dur=0)。 */
|
||
static int64_t np_progress_seek(int tx, int ty)
|
||
{
|
||
const meow_media_session_t *s = meow_media_current();
|
||
if (!s || !s->dur_ms) return 0;
|
||
if (tx >= NP_PROG_X && tx <= NP_PROG_X + NP_PROG_W &&
|
||
ty >= NP_PROG_Y - 14 && ty <= NP_PROG_Y + 20) {
|
||
int frac = (tx - NP_PROG_X) * 1000 / NP_PROG_W;
|
||
return (int64_t)s->dur_ms * frac / 1000;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void run_now_playing(struct app *apps, int napps)
|
||
{
|
||
int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK);
|
||
if (tfd >= 0) { unsigned char ev[16]; while (read(tfd, ev, sizeof ev) == 16) {} }
|
||
unsigned long long touch_cooldown_until = now_ms() + 300;
|
||
struct keypoll kp_keys;
|
||
kp_open(&kp_keys, DEV_KEYS);
|
||
struct keypoll kp_power; /* M2.92:NP 页接管电源键(修「页内电源键无反应」) */
|
||
kp_open(&kp_power, DEV_POWER);
|
||
struct kd_state kd_play;
|
||
memset(&kd_play, 0, sizeof kd_play);
|
||
/* 读播放键按住态(用于边沿) */
|
||
int m_prev_play = 0;
|
||
for (int w = 0; w < KEYBUF_WORDS; w++)
|
||
if (kp_keys.cur[w] & (1UL << (KEY_PLAYPAUSE % 32))) m_prev_play = 1;
|
||
int np_prev_power = 0; /* 电源键边沿跟踪 */
|
||
for (int w = 0; w < KEYBUF_WORDS; w++)
|
||
if (kp_power.cur[w] & (1UL << (KEY_POWER % 32))) np_prev_power = 1;
|
||
unsigned long long np_last_input = now_ms(); /* 自动息屏计时(M2.92:NP 页也遵守 display.screen_off_timeout) */
|
||
|
||
int td = 0, td_ok = 0, tdx = -1, tdy = -1, tlx = -1, tly = -1;
|
||
unsigned long long last_prog = now_ms();
|
||
unsigned long long np_last_status_tick = now_ms(); /* M6.20:播放页状态栏时钟 1s 刷新(主界面同语义) */
|
||
int need_redraw = 1;
|
||
int np_exit = 0; /* M6.22:返回手势命中 → 置位退出外层主循环。触摸内的 break 只跳出内层
|
||
* 读事件 while,不退出外层 while(!sigterm_flag),故必须靠标志跳出(根因:返回「没反应」)。 */
|
||
char last_title[128] = "";
|
||
uint32_t last_state = 0xFFFFFFFFu;
|
||
char last_app[MEOW_APP_ID_MAX] = "";
|
||
|
||
meow_ui_activity_push(NP_SENTINEL_ACT); /* M7.0b:进入 push 哨兵(与函数尾 449 行 pop 配对)。
|
||
* 控件 owner 一直钉哨兵,命中要走 meow_widget_hit 的
|
||
* owner==active 闸门——漏 push 则 active()!=0x51,
|
||
* 三钮 meow_widget_hit 恒 false(「播放页三键无效」根因)。 */
|
||
np_draw_page(1);
|
||
meow_dirty_full();
|
||
meow_ui_flush();
|
||
|
||
while (!sigterm_flag) {
|
||
if (np_exit) break; /* M6.22:返回手势已命中 → 退出本页,走尾部收尾回主界面 */
|
||
meow_ipc_process();
|
||
meow_media_tick();
|
||
unsigned long long now = now_ms();
|
||
|
||
/* 触摸 */
|
||
if (tfd >= 0 && meow_screen_on) {
|
||
struct pollfd pfd = { tfd, POLLIN, 0 };
|
||
if (poll(&pfd, 1, 0) > 0) {
|
||
unsigned char ev[16];
|
||
while (read(tfd, ev, sizeof ev) == 16) {
|
||
uint16_t type = *(uint16_t *)(ev + 8);
|
||
uint16_t code = *(uint16_t *)(ev + 10);
|
||
int32_t val = *(int32_t *)(ev + 12);
|
||
if (type == EV_ABS) {
|
||
if (code == ABS_X || code == ABS_MT_POSITION_X) tlx = val;
|
||
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) tly = val;
|
||
if (td && !td_ok && tlx >= 0 && tly >= 0) { tdx = tlx; tdy = tly; td_ok = 1; }
|
||
} else if (type == EV_KEY && code == BTN_TOUCH) {
|
||
if (val && !td) {
|
||
if (now_ms() >= touch_cooldown_until) { td = 1; td_ok = 0; tlx = tly = -1;
|
||
np_last_input = now_ms(); }
|
||
} else if (!val && td) {
|
||
td = 0;
|
||
if (td_ok) {
|
||
int dx = tlx - tdx, dy = tly - tdy;
|
||
int adx = dx < 0 ? -dx : dx, ady = dy < 0 ? -dy : dy;
|
||
/* M6.21 诊断:打印该次抬手手势的完整输入与各判定结果(真机定位
|
||
* 「返回滑不出」根因——状态栏/通知中心已生效,惟返回未触发)。 */
|
||
int np_is_top = mnp_top_swipe_down(tdx, tdy, dx, dy, SB_BAR_H, 65);
|
||
int np_edge = meow_edge_back(tdx, dx, dy);
|
||
int np_anyright = (dx >= 40 && adx > ady);
|
||
logmsg("np up: down=(%d,%d) d=(%d,%d) adx=%d ady=%d top=%d edge=%d anyright=%d",
|
||
tdx, tdy, dx, dy, adx, ady, np_is_top, np_edge, np_anyright);
|
||
/* 顶部热区下滑 → 打开通知中心(与主界面同语义;mnp_top_swipe_down
|
||
* 内部已排除左缘 down_x<24,与下方返回手势互斥)。run_notif_center
|
||
* 为阻塞模态,返回后整页重画播放页。 */
|
||
if (np_is_top) {
|
||
notif_click_t click;
|
||
memset(&click, 0, sizeof click);
|
||
click.app_idx = -1;
|
||
click.status = NOTIF_CLICK_STATUS_NONE;
|
||
run_notif_center(apps, napps, &click, 0, 0, 0);
|
||
need_redraw = 1;
|
||
meow_dirty_full();
|
||
meow_ui_flush();
|
||
}
|
||
/* 左缘右滑返回(全局手势语义),或任意横向右滑返回——与主界面
|
||
* 「左滑进入 Now Playing」对称。返回阈值 65→40 放宽(防用户右滑
|
||
* 幅度不足 65 判不中);播放页无横向分页内容、无进度条拖动
|
||
* (进度=点按,幅度<60 走 tap 分支),故任意位置横向右滑恒判返回。 */
|
||
else if (np_edge || np_anyright) {
|
||
logmsg("np: BACK break (edge=%d anyright=%d dx=%d)", np_edge, np_anyright, dx);
|
||
np_exit = 1;
|
||
break; /* 仅跳出内层读事件 while;外层主循环靠 np_exit 退出 */
|
||
}
|
||
if (adx < 60 && ady < 60) {
|
||
int cmd = np_btn_tap(tlx, tly);
|
||
if (cmd) { meow_media_playctl((uint32_t)cmd, 0); need_redraw = 1; }
|
||
else {
|
||
int64_t seek = np_progress_seek(tlx, tly);
|
||
if (seek > 0) meow_media_playctl(5, seek);
|
||
}
|
||
}
|
||
tdx = tdy = -1;
|
||
} else { tdx = tdy = -1; }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 播放键边沿 → 播放/暂停切换 */
|
||
kp_poll(&kp_keys, NULL, NULL);
|
||
int play = 0;
|
||
for (int w = 0; w < KEYBUF_WORDS; w++)
|
||
if (kp_keys.cur[w] & (1UL << (KEY_PLAYPAUSE % 32))) play = 1;
|
||
if (play && !m_prev_play) {
|
||
const meow_media_session_t *s = meow_media_current();
|
||
if (s) meow_media_playctl((s->state == MEOW_MEDIA_PLAYING) ? 2u : 1u, 0);
|
||
np_last_input = now_ms();
|
||
}
|
||
m_prev_play = play;
|
||
|
||
/* M2.92:NP 页电源键接管——单击边沿 = 息屏/亮屏(与主界面语义一致;
|
||
* 修「NP 页电源键无反应」。3s 电源菜单仍由 watchdog→/tmp 标记→主循环消费,
|
||
* NP 退出后生效(可接受偏差,记日志)。 */
|
||
kp_poll(&kp_power, NULL, NULL);
|
||
int pwr = 0;
|
||
for (int w = 0; w < KEYBUF_WORDS; w++)
|
||
if (kp_power.cur[w] & (1UL << (KEY_POWER % 32))) pwr = 1;
|
||
if (pwr && !np_prev_power) {
|
||
np_last_input = now_ms();
|
||
if (meow_screen_on) { meow_panel_off(); logmsg("nowplaying: power tap -> panel off"); }
|
||
else { meow_panel_on(); need_redraw = 1; logmsg("nowplaying: power tap -> panel on"); }
|
||
}
|
||
np_prev_power = pwr;
|
||
|
||
/* M2.92:NP 页自动息屏(与主界面同超时;页面无触摸/按键计时器不走主循环) */
|
||
if (screen_off_timeout > 0 && meow_screen_on &&
|
||
now_ms() - np_last_input >= (unsigned long long)screen_off_timeout * 1000) {
|
||
meow_panel_off();
|
||
meow_auto_slept = 1;
|
||
logmsg("nowplaying: idle %ds -> auto panel off", screen_off_timeout);
|
||
}
|
||
/* 息屏期只低频轮询按键(唤醒用),不重画不读触摸(FBIOBLANK 已挂起 goodix) */
|
||
if (!meow_screen_on) { usleep(100 * 1000); continue; }
|
||
|
||
/* 媒体变化检测(元数据/源/状态):立即整页重画 */
|
||
const meow_media_session_t *s = meow_media_current();
|
||
int meta_changed = !s || strcmp(last_app, s->app_id) ||
|
||
strcmp(last_title, s->title) || last_state != s->state;
|
||
if (meta_changed) {
|
||
if (s) {
|
||
snprintf(last_app, sizeof last_app, "%s", s->app_id);
|
||
snprintf(last_title, sizeof last_title, "%s", s->title);
|
||
last_state = s->state;
|
||
} else { last_app[0] = 0; last_title[0] = 0; last_state = 0xFFFFFFFFu; }
|
||
need_redraw = 1;
|
||
}
|
||
/* M2.92 修死循环:空态/停止态只在「变化」时画一次(meta_changed 已覆盖
|
||
* NULL↔会话切换),不再每 10ms 整页重画(旧行为 = 100% CPU 忙刷 → 卡死观感)。 */
|
||
|
||
if (need_redraw) {
|
||
np_draw_page(0);
|
||
meow_dirty_add(0, SB_BAR_H, fb_xres, fb_yres - SB_BAR_H);
|
||
meow_ui_flush();
|
||
need_redraw = 0;
|
||
last_prog = now;
|
||
} else if (s && (now - last_prog >= 1000)) {
|
||
/* 进度推进:仅重绘进度带 + 按钮 */
|
||
last_prog = now;
|
||
np_repaint_progress();
|
||
}
|
||
/* M6.20:播放页状态栏时钟 1s 刷新(与主界面/子页同语义;draw_clock 喂 SDK 全局
|
||
* 状态栏 + 标脏顶条,SDK present() 重绘——否则时间冻结在进入播放页那一刻)。 */
|
||
if (now - np_last_status_tick >= 1000) {
|
||
np_last_status_tick = now;
|
||
draw_clock();
|
||
}
|
||
usleep(10000);
|
||
}
|
||
meow_ui_activity_pop(); /* M7.0b:退出 Now Playing,回主 activity(控件 owner 仍钉哨兵,复用) */
|
||
if (tfd >= 0) close(tfd);
|
||
kp_close(&kp_keys);
|
||
kp_close(&kp_power);
|
||
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
|
||
g_np_cover_path[0] = 0;
|
||
}
|