M7 整体 MD3 重写试点(M7.0 定两隔离页,只动 patch_v2/meow_launcherd/): - 电源菜单 power_menu.c:圆角卡→meow_card;6 圆钮→meow_iconbtn(回HiBy=ARROW_BACK、 取消=CLOSE;sb_icons 表在位图)+ meow_widget_custom_create(关机/重启/升级/锁定=pm_icons 专有,圆形命中自定义);命中/选中/按压态全走 SDK(meow_widget_hit/set_state/draw), 触摸按下置 PRESSED、抬起清(pm_blend 按主题混合状态层);py 手动驱动控件(哨兵 activity 0x50 push/pop,owner 闸门使命中生效)。 - Now Playing now_playing.c:进度条→meow_progress;播放控制三钮→meow_iconbtn(SKIP_PREV / PLAY_ARROW|PAUSE同位双钮按状态只画其一/ SKIP_NEXT);命中走 meow_widget_hit;封面 (meow_img contain) 与歌名/艺术家 (meow_font) 保留;哨兵 activity 0x51 push/pop。 - 主题薄壳 font_theme.c:s_ui_theme() 改读 SDK meow_theme_export()(单一权威,9 字段映射 §8.1.5),退役 launcher 自持 4 套静态色板(#if 0 回退位)。 - i18n 抽取:两页硬编码中文案改 meow_str(新增 meow_str_zh 辅助:SDK 内嵌表无 launcher 域串 时回退中文默认,TF 覆盖表/后续并入 SDK 内嵌后自动换语)。 - 会话骨架不动:物理键语义(单击息屏/3s 菜单/8s 强关/左缘返回/横划 NP)与两页进入退出/事件流 未改,只替换「怎么画/怎么命中」;旧绘制函数 #if 0 留回退位。 - 验证:新增 test_power_menu_widgets / test_now_playing_widgets 宿主测试(圆形命中/owner+enabled 闸门/选中按压态像素/主题四态 MD3+MIUIx×明暗 渲染 + meow_theme_export 九字段)全 PASS; launcher 全量宿主回归(lock_screen/overlay_gate/utf8_name/media/depends/ams_start/bgplay/ settings_brightness/netmgr_ipc/vol_ramp_jack/notif_panel/notif_tf/lock_render + SDK theme_export/ iconbtn)全 PASS;交叉 -Wall -Wextra 0 warning。新 meow_launcherd md5 94a33b7b。
176 lines
8.4 KiB
C
176 lines
8.4 KiB
C
/* 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 <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <assert.h>
|
||
|
||
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;
|
||
}
|