/* test_lock_widgets.c — M7.1b 锁屏 UI 控件化宿主验证。 * * 复现 lock_screen_ui.c 所建锁屏键盘控件集(12 键 meow_widget_custom + meow_widget 命中 + * 按压态 MEOW_WS_PRESSED→primary 26% α),验证: * P1 键盘渲染:12 键各画成 surfaceContainer 圆角钮(非背景像素); * P2 键盘命中:meow_widget_hit 在每键中心命中;键值映射(digit/清空/退格)正确;间隙不命中; * P3 主题四态(MD3/MIUIx × 明暗)键盘渲染非背景; * P4 按压态:meow_widget_set_state(MEOW_WS_PRESSED) → 该键像素变化(primary 26% α 叠加); * P5 键值 round-trip:lk_key_value_at 在键中心/间隙分别返回正确键值与 LK_DOWN_NONE。 * * 布局几何/键序与 lock_screen_ui.c 镜像(LK_X0..LK_COLS/LK_ROWS/lk_keys),控件创建方式与 * lk_widgets_build 同(owner=哨兵 0x51,meow_widget_custom_create,render 读 meow_widget_rect 几何)。 * * 编译(在 patch_v2/meow_launcherd 下): * gcc -Wall -Wextra -o test_lock_widgets test_lock_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_lock_widgets */ #include "meow_ui.h" #include "meow_ui_internal.h" /* meow_ui_back_buf / meow_widget_rect / meow_gfx_fill_round[_alpha] */ #include #include #include #include static int g_fail = 0; #define CHECK(cond, ...) do { \ if (cond) { printf(" PASS: " __VA_ARGS__); printf("\n"); } \ else { printf(" FAIL: " __VA_ARGS__); printf("\n"); g_fail++; } } while (0) /* ---- 与 lock_screen_ui.c 同源几何(镜像) ---- */ #define LK_X0 32 #define LK_COLW 130 #define LK_COLG 8 #define LK_Y0 320 #define LK_ROW_H 82 #define LK_ROWG 10 #define LK_ROWS 4 #define LK_COLS 3 #define LK_ACT_CLR (-2) #define LK_ACT_BS (-1) #define LK_SENTINEL 0x51 /* 锁屏控件 owner 哨兵(镜像 lock_screen_ui.c) */ #define LK_DOWN_NONE (-10) static const int lk_keys[LK_ROWS][LK_COLS] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { LK_ACT_CLR, 0, LK_ACT_BS }, }; #define XRES 480 #define YRES 720 static meow_widget *lk_w[LK_ROWS][LK_COLS]; static void lk_rect(int row, int col, int rc[4]) { rc[0] = LK_X0 + col * (LK_COLW + LK_COLG); rc[1] = LK_Y0 + row * (LK_ROW_H + LK_ROWG); rc[2] = LK_COLW; rc[3] = LK_ROW_H; } static void lk_center(int row, int col, int *cx, int *cy) { int rc[4]; lk_rect(row, col, rc); *cx = rc[0] + rc[2] / 2; *cy = rc[1] + rc[3] / 2; } /* ---- 键渲染(镜像 lock_screen_ui.c lk_key_render 的视觉语义)---- */ static void key_render(meow_widget *w, const meow_theme *th, void *ctx) { (void)ctx; int rc[4]; meow_widget_rect(w, rc); int rad = (th->radius > 0) ? th->radius : 10; meow_gfx_fill_round(rc[0], rc[1], rc[2], rc[3], rad, th->surfaceContainer); if (meow_widget_state(w) & MEOW_WS_PRESSED) meow_gfx_fill_round_alpha(rc[0], rc[1], rc[2], rc[3], rad, th->primary, 66); meow_ui_text_center(rc[0] + rc[2] / 2, rc[1] + rc[3] / 2 - meow_ui_text_height(22) / 2, "1", 22, th->onSurface); } static void mk_widgets(void) { meow_ui_activity_push(LK_SENTINEL); for (int r = 0; r < LK_ROWS; r++) for (int c = 0; c < LK_COLS; c++) { int rc[4]; lk_rect(r, c, rc); lk_w[r][c] = meow_widget_custom_create(rc[0], rc[1], rc[2], rc[3], key_render, NULL, (void *)(intptr_t)lk_keys[r][c]); } meow_ui_activity_pop(); } /* 坐标 -> 键值(镜像 lock_screen_ui.c lk_key_value_at)。 */ static int key_value_at(int x, int y) { for (int r = 0; r < LK_ROWS; r++) for (int c = 0; c < LK_COLS; c++) if (lk_w[r][c] && meow_widget_hit(lk_w[r][c], x, y)) return lk_keys[r][c]; return LK_DOWN_NONE; } static void draw_all(void) { const meow_theme *th = meow_theme_get(); meow_ui_fill_rect(0, 0, XRES, YRES, th->surface); for (int r = 0; r < LK_ROWS; r++) for (int c = 0; c < LK_COLS; c++) if (lk_w[r][c]) meow_widget_draw(lk_w[r][c]); } static unsigned short px565(int x, int y) { unsigned char *bb = meow_ui_back_buf(); int pitch = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp(); if (!bb || x < 0 || y < 0 || x >= XRES || y >= YRES) return 0; if (bpp == 16) return *(unsigned short *)(bb + (size_t)y * pitch + (size_t)x * 2); return (unsigned short)(*(unsigned int *)(bb + (size_t)y * pitch + (size_t)x * 4) >> 16); } static unsigned short bg565(uint32_t bg) { return (unsigned short)(((bg >> 8) & 0xF800) | ((bg >> 5) & 0x07E0) | ((bg >> 3) & 0x001F)); } int main(void) { setbuf(stdout, NULL); setenv("MEOW_UI_DUMP", "/tmp/lock_widgets.ppm", 1); meow_ui_opts o = { XRES, YRES }; if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; } mk_widgets(); meow_ui_activity_push(LK_SENTINEL); /* 命中闸门打开(owner==active) */ /* ============ P1 键盘渲染(12 键各画成非背景钮) ============ */ printf("P1 keypad render (12 keys non-bg)\n"); const meow_theme *th = meow_theme_get(); draw_all(); int all_nonbg = 1; for (int r = 0; r < LK_ROWS && all_nonbg; r++) for (int c = 0; c < LK_COLS && all_nonbg; c++) { int cx, cy; lk_center(r, c, &cx, &cy); if (px565(cx, cy) == bg565(th->surface)) all_nonbg = 0; } CHECK(all_nonbg, "each of 12 keys draws non-surface at center"); /* ============ P2 命中(meow_widget_hit + 键值映射) ============ */ printf("P2 keypad hit via meow_widget_hit\n"); int hit_ok = 1, val_ok = 1; for (int r = 0; r < LK_ROWS; r++) for (int c = 0; c < LK_COLS; c++) { int cx, cy; lk_center(r, c, &cx, &cy); if (!meow_widget_hit(lk_w[r][c], cx, cy)) hit_ok = 0; if (key_value_at(cx, cy) != lk_keys[r][c]) val_ok = 0; } CHECK(hit_ok, "meow_widget_hit hits every key center"); CHECK(val_ok, "key_value_at maps every key center to its digit/clear/backspace value"); /* 间隙(两列之间)不命中 */ int gap_x = LK_X0 + LK_COLW + LK_COLG / 2; /* 列0 与列1 之间的间隙中心 */ int gap_y = LK_Y0 + LK_ROW_H / 2; CHECK(meow_widget_hit(lk_w[0][0], gap_x, gap_y) == 0, "gap between columns not hit"); CHECK(key_value_at(gap_x, gap_y) == LK_DOWN_NONE, "gap not mapped to any key"); /* ============ P3 主题四态(MD3/MIUIx × 明暗)键盘渲染非背景 ============ */ printf("P3 theme 4-state keypad render\n"); const int styles[2] = { MEOW_STYLE_MD3, MEOW_STYLE_MIUIX }; const int modes[2] = { MEOW_THEME_LIGHT, MEOW_THEME_DARK }; char nm[96]; 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(); draw_all(); /* 用一个键区的多像素带判定非背景(文字+底) */ int rc[4]; lk_rect(1, 0, rc); int nonbg = 0; for (int dy = 0; dy < rc[3] && !nonbg; dy++) for (int dx = 0; dx < rc[2] && !nonbg; dx++) if (px565(rc[0] + dx, rc[1] + dy) != bg565(th->surface)) nonbg = 1; snprintf(nm, sizeof nm, "style=%s mode=%s keypad renders non-surface", styles[si] ? "MIUIx" : "MD3", modes[mi] ? "dark" : "light"); CHECK(nonbg, "%s", nm); } } meow_ui_set_style(MEOW_STYLE_MD3); meow_theme_set_mode(MEOW_THEME_DARK); /* ============ P4 按压态(MEOW_WS_PRESSED → primary 26% α 像素变化) ============ */ printf("P4 pressed state -> primary 26%% alpha overlay\n"); th = meow_theme_get(); draw_all(); int cx, cy; lk_center(2, 1, &cx, &cy); /* 键 (2,1) = 数字 8 */ unsigned short p0 = px565(cx, cy); meow_widget_set_state(lk_w[2][1], MEOW_WS_PRESSED, 1); draw_all(); unsigned short p1 = px565(cx, cy); meow_widget_set_state(lk_w[2][1], MEOW_WS_PRESSED, 0); CHECK(p1 != p0, "pressed key center pixel changes (primary 26%% overlay)"); draw_all(); unsigned short p2 = px565(cx, cy); CHECK(p2 == p0, "release restores original pixel"); /* ============ P5 键值 round-trip(清空/退格/间隙) ============ */ printf("P5 key-value round-trip (clear/backspace/gap)\n"); int ccx, ccy, bcx, bcy, zcx, zcy; lk_center(3, 0, &ccx, &ccy); /* 清空 */ lk_center(3, 1, &zcx, &zcy); /* 0 */ lk_center(3, 2, &bcx, &bcy); /* 退格 */ CHECK(key_value_at(ccx, ccy) == LK_ACT_CLR, "bottom-left center == CLEAR (-2)"); CHECK(key_value_at(zcx, zcy) == 0, "bottom-mid center == 0"); CHECK(key_value_at(bcx, bcy) == LK_ACT_BS, "bottom-right center == BACKSPACE (-1)"); meow_ui_activity_pop(); meow_ui_shutdown(); printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail); return g_fail; }