/* test_now_playing_widgets.c — M7.0b Now Playing 控件化宿主验证。 * * 复现 run_now_playing() 所建控件集(np_widgets_create:meow_progress 进度条 + meow_iconbtn * 三钮,play/pause 同位双钮按状态只画其一),验证: * N1 进度条:value clamp 0-100 + track/fill 渲染(填充分界像素不同); * N2 三钮圆形命中:圆心命中、角部不命中; * N3 play/pause 同位切换:两钮图标位图不同 → 中心像素不同(画 PAUSE vs PLAY_ARROW); * N4 主题四态(MD3/MIUIx × 明暗)下进度条+三钮渲染非背景像素。 * * 几何常量与 now_playing.c 同源(镜像);控件创建方式与 np_widgets_create 同。 * 编译(在 patch_v2/meow_launcherd 下): * gcc -Wall -Wextra -o test_now_playing_widgets test_now_playing_widgets.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 -I../sdk -I. -DMEOW_UI_HOST_TEST -pthread -lm * 运行:./test_now_playing_widgets */ #include "meow_ui.h" #include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp */ #include #include #include #include static int g_fail = 0; #define CHECK(cond, name) do { \ if (cond) printf(" PASS %s\n", name); \ else { printf(" FAIL %s\n", name); g_fail++; } } while (0) /* ---- 与 now_playing.c 同源几何(镜像) ---- */ #define NP_PROG_X 40 #define NP_PROG_W (480 - 80) /* 400 */ #define NP_PROG_Y 518 #define NP_TIME_Y 492 #define NP_BTN_Y 568 #define NP_PLAY_CX 240 #define NP_PREV_CX (NP_PLAY_CX - 78) /* 162 */ #define NP_NEXT_CX (NP_PLAY_CX + 78) /* 318 */ #define NP_PLAY_R 30 #define NP_PREV_R 22 #define NP_SENTINEL 0x51 static meow_widget *prog = NULL, *prev = NULL, *play = NULL, *pause = NULL, *next = NULL; static void rd_px(int x, int y, unsigned short *p) { unsigned char *bb = meow_ui_back_buf(); int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp(); if (!bb || x < 0 || y < 0) { *p = 0; return; } if (bpp == 16) *p = *(unsigned short *)(bb + (size_t)y * line + (size_t)x * 2); 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 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); int cnt = 0; for (int dy = 0; dy < h; dy++) for (int dx = 0; dx < w; dx++) if (px565(x + dx, y + dy) != bg565) cnt++; return cnt; } static void mk_widgets(void) { meow_ui_activity_push(NP_SENTINEL); prog = meow_progress_create(NP_PROG_X, NP_PROG_Y, NP_PROG_W, 6, 0); prev = meow_iconbtn_create(NP_PREV_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R, NP_PREV_R * 2, MEOW_ICON_SKIP_PREV); play = meow_iconbtn_create(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R, NP_PLAY_R * 2, MEOW_ICON_PLAY_ARROW); pause = meow_iconbtn_create(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R, NP_PLAY_R * 2, MEOW_ICON_PAUSE); 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(镜像 np_widgets_create) */ const meow_theme *th = meow_theme_get(); meow_widget_opts op; memset(&op, 0, sizeof op); op.bg = th->primary; op.fg = th->onPrimary; meow_widget_set_opts(play, &op); meow_widget_set_opts(pause, &op); meow_ui_activity_pop(); } int main(void) { setbuf(stdout, NULL); setenv("MEOW_UI_DUMP", "/tmp/nowplaying.ppm", 1); meow_ui_opts o = { 480, 720 }; assert(meow_ui_init(&o) == 0); meow_ui_activity_push(NP_SENTINEL); mk_widgets(); const meow_theme *th = meow_theme_get(); meow_ui_set_style(MEOW_STYLE_MD3); meow_theme_set_mode(MEOW_THEME_DARK); th = meow_theme_get(); /* ============ N1 进度条 clamp + track/fill 渲染 ============ */ printf("N1 progress value + track/fill\n"); meow_widget_set_opts(prog, &(meow_widget_opts){ .bg = 0 }); meow_ui_clear(0); meow_progress_set(prog, 150); /* clamp → 100 */ meow_widget_draw(prog); unsigned short fill_end = px565(NP_PROG_X + NP_PROG_W - 2, NP_PROG_Y + 3); unsigned short track_mid = px565(NP_PROG_X + NP_PROG_W / 2, NP_PROG_Y + 3); CHECK(fill_end != 0 && track_mid != 0, "value 150 clamp 100 -> full fill renders"); meow_progress_set(prog, 50); meow_widget_draw(prog); /* 50% 填充区 [40,240) 内像素应为主色;240 之后的轨道区应为轨道色(surfaceVariant) */ unsigned short infill = px565(NP_PROG_X + NP_PROG_W / 4, NP_PROG_Y + 3); /* x=140,填充区 */ unsigned short empty = px565(NP_PROG_X + NP_PROG_W - 4, NP_PROG_Y + 3); /* x=436,轨道区 */ CHECK(infill != empty, "value 50 -> fill region differs from empty tail"); /* ============ N2 三钮圆形命中 ============ */ printf("N2 circular hit\n"); CHECK(meow_widget_hit(prev, NP_PREV_CX, NP_BTN_Y), "prev center hits"); CHECK(!meow_widget_hit(prev, NP_PREV_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R), "prev corner MISSES"); CHECK(meow_widget_hit(play, NP_PLAY_CX, NP_BTN_Y), "play center hits"); CHECK(meow_widget_hit(pause, NP_PLAY_CX, NP_BTN_Y), "pause(overlay) center hits"); CHECK(!meow_widget_hit(play, NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R), "play corner MISSES"); CHECK(meow_widget_hit(next, NP_NEXT_CX, NP_BTN_Y), "next center hits"); /* ============ N3 play/pause 同位切换(两钮位图不同 → 像素不同) ============ */ printf("N3 play/pause overlay icon switch\n"); meow_ui_clear(0); meow_widget_draw(play); meow_ui_clear(0); /* 只画 play */ meow_widget_draw(play); unsigned short p_play = px565(NP_PLAY_CX, NP_BTN_Y); /* 中心=图标墨色 */ meow_ui_clear(0); meow_widget_draw(pause); unsigned short p_pause = px565(NP_PLAY_CX, NP_BTN_Y); CHECK(p_play != p_pause, "play vs pause icon differs at centre (overlay switch works)"); /* 圆心区域非页面底色(画出来了) */ meow_ui_clear(0); meow_widget_draw(pause); CHECK(region_nonbg(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R, NP_PLAY_R * 2, NP_PLAY_R * 2, th->surface) > 0, "pause button renders non-bg in circle"); /* ============ N4 主题四态:进度条+三钮渲染非背景 ============ */ printf("N4 theme 4-state render\n"); const int styles[2] = { MEOW_STYLE_MD3, MEOW_STYLE_MIUIX }; const int modes[2] = { MEOW_THEME_LIGHT, MEOW_THEME_DARK }; for (int si = 0; si < 2; si++) { for (int mi = 0; mi < 2; mi++) { meow_ui_set_style(styles[si]); meow_theme_set_mode((meow_theme_mode)modes[mi]); th = meow_theme_get(); meow_theme ex; meow_theme_export(&ex); CHECK(ex.primary == th->primary && ex.surface == th->surface && ex.surfaceVariant == th->surfaceVariant, "export == get (spot roles)"); meow_ui_clear(0); meow_progress_set(prog, 40); meow_widget_draw(prog); meow_widget_draw(prev); meow_widget_draw(pause); meow_widget_draw(next); char nm[80]; int progpx = region_nonbg(NP_PROG_X, NP_PROG_Y, NP_PROG_W, 6, th->surface); int btnpx = region_nonbg(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R, NP_PLAY_R * 2, NP_PLAY_R * 2, th->surface); snprintf(nm, sizeof nm, "style=%s mode=%s prog+btn render", styles[si] ? "MIUIx" : "MD3", modes[mi] ? "dark" : "light"); CHECK(progpx > 0 && btnpx > 0, nm); } } meow_ui_activity_pop(); meow_ui_shutdown(); printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail); return g_fail; }