/* test_power_menu_widgets.c — M7.0b 电源菜单控件化宿主验证。 * * 复现 show_power_menu() 所建控件集(pm_widgets_create:meow_card + 6 圆钮,BACK/CANCEL 走 * meow_iconbtn、POWER/RESTART/UPGRADE/LOCK 走 meow_widget_custom_create 圆形画布),验证: * P1 圆形命中:圆心命中、角部不命中(区别于矩形命中); * P2 owner==哨兵 + enabled 双闸门(非 active / 禁用不命中); * P3 选中/按压态像素变化(SDK 状态层,selected/pressed 改侧像素); * P4 主题四态(MD3/MIUIx × 明暗)下卡片+圆钮渲染非背景像素 + meow_theme_export 九字段映射。 * * 几何常量与 power_menu.c(pm_cy/pm_cx=util.c 同源)/meow_launcher_internal.h 镜像; * 控件创建方式与 pm_widgets_create 同(owner=哨兵 0x50,back/cancel 用 MEOW_ICON_*)。 * * 编译(在 patch_v2/meow_launcherd 下): * gcc -Wall -Wextra -o test_power_menu_widgets test_power_menu_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_power_menu_widgets */ #include "meow_ui.h" #include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp + meow_gfx_fill_circle + meow_widget_rect */ #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) /* ---- 与 power_menu.c / meow_launcher_internal.h 同源几何(镜像) ---- */ #define PM_NUM 6 #define PM_R 50 /* 圆钮半径(直径 100) */ #define PM_BTN_SIZE (PM_R * 2) #define PM_CARD_X0 40 #define PM_CARD_Y0 115 #define PM_CARD_W 400 #define PM_CARD_H 505 #define PM_CARD_R 26 #define PM_SENTINEL 0x50 /* pm_cx[] = util.c 同源;pm_cy[] = power_menu.c 同源 */ static const int pm_cx[PM_NUM] = { 175, 305, 175, 305, 175, 305 }; static const int pm_cy[PM_NUM] = { 250, 250, 390, 390, 530, 530 }; /* M7.0b 曾将 2 回HiBy / 5 取消 走 SDK sb_icons(meow_iconbtn),实测 meow_iconbtn 把 32×32 位图 * 按 0.6*size 放大到 ~60px 致锯齿;定案回退 pm_icons 专有 56×56 1:1(恢复 M6.8 尺寸)。 * 镜像:全部 -1 = 自定义画布(pm_icons 专有)。 */ static const int pm_sdk_icon[PM_NUM] = { -1, -1, -1, -1, -1, -1 }; static meow_widget *card = NULL, *btn[PM_NUM] = {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; } /* 页面底色(meow_theme surface)。区域含「非页面底色」像素计数 = 控件渲染判据。 */ 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; } /* 按主题混合(镜像 power_menu.c pm_blend:base 向 top 混 pct%,24-bit 每通道)。 */ static uint32_t pm_blend(uint32_t base, uint32_t top, int pct) { int br = (base >> 16) & 0xFF, bg = (base >> 8) & 0xFF, bb = base & 0xFF; int tr = (top >> 16) & 0xFF, tg = (top >> 8) & 0xFF, tb = top & 0xFF; int r = (br * (100 - pct) + tr * pct) / 100; int g = (bg * (100 - pct) + tg * pct) / 100; int b = (bb * (100 - pct) + tb * pct) / 100; return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; } /* 自定义画布(镜像 pm_render_cb:selected 圆底 + 禁用/按压自叠 + 内圆标记) */ static void cr_render(meow_widget *w, const meow_theme *th, void *ctx) { (void)ctx; int rc[4]; meow_widget_rect(w, rc); int cx = rc[0] + rc[2] / 2, cy = rc[1] + rc[3] / 2; int r = (rc[2] < rc[3] ? rc[2] : rc[3]) / 2; unsigned st = meow_widget_state(w); int sel = (st & MEOW_WS_SELECTED) != 0; int disabled = (st & MEOW_WS_ENABLED) == 0; int pressed = (st & MEOW_WS_PRESSED) != 0; uint32_t base = sel ? th->primaryContainer : th->surfaceContainer; uint32_t mark = sel ? th->onPrimaryContainer : th->onSurface; if (disabled) base = pm_blend(base, th->onSurface, 38); else if (pressed) base = pm_blend(base, th->onSurface, sel ? 24 : 12); meow_gfx_fill_circle(cx, cy, r, base); meow_gfx_fill_circle(cx, cy, r / 2, mark); } /* 自定义画布命中(镜像 pm_hit_cb:圆形) */ static int cr_hit(meow_widget *w, int x, int y, void *ctx) { (void)ctx; int rc[4]; meow_widget_rect(w, rc); int cx = rc[0] + rc[2] / 2, cy = rc[1] + rc[3] / 2; int r = (rc[2] < rc[3] ? rc[2] : rc[3]) / 2; int dx = x - cx, dy = y - cy; return dx * dx + dy * dy <= r * r; } static void mk_widgets(void) { meow_ui_activity_push(PM_SENTINEL); card = meow_card_create(PM_CARD_X0, PM_CARD_Y0, PM_CARD_W, PM_CARD_H); for (int i = 0; i < PM_NUM; i++) { int x = pm_cx[i] - PM_BTN_SIZE / 2, y = pm_cy[i] - PM_BTN_SIZE / 2; if (pm_sdk_icon[i] >= 0) btn[i] = meow_iconbtn_create(x, y, PM_BTN_SIZE, pm_sdk_icon[i]); else btn[i] = meow_widget_custom_create(x, y, PM_BTN_SIZE, PM_BTN_SIZE, cr_render, cr_hit, (void *)(intptr_t)i); } /* iconbtn 底色=surfaceContainer(与卡片 Low 拉开对比;镜像 pm_refresh_theme) */ const meow_theme *th = meow_theme_get(); meow_widget_opts op; memset(&op, 0, sizeof op); op.bg = th->surfaceContainer; for (int i = 0; i < PM_NUM; i++) if (btn[i] && pm_sdk_icon[i] >= 0) meow_widget_set_opts(btn[i], &op); meow_ui_activity_pop(); } /* 重绘整卡(按钮选中全清) */ static void draw_all(int sel) { if (card) meow_widget_draw(card); for (int i = 0; i < PM_NUM; i++) { if (!btn[i]) continue; meow_widget_set_selected(btn[i], i == sel); meow_widget_draw(btn[i]); } } int main(void) { setbuf(stdout, NULL); setenv("MEOW_UI_DUMP", "/tmp/powermenu.ppm", 1); meow_ui_opts o = { 480, 720 }; assert(meow_ui_init(&o) == 0); /* ============ P1 圆形命中(back=iconbtn idx2, cx=175,cy=390;custom=idx0, cx=175,cy=250) ============ */ printf("P1 circular hit\n"); meow_ui_activity_push(PM_SENTINEL); mk_widgets(); CHECK(meow_widget_hit(btn[2], 175, 390), "custom(back) center hits"); CHECK(!meow_widget_hit(btn[2], 175 - PM_BTN_SIZE / 2, 390 - PM_BTN_SIZE / 2), "custom(back) corner (dist~70>50) MISSES"); CHECK(meow_widget_hit(btn[0], 175, 250), "custom(power) center hits"); CHECK(!meow_widget_hit(btn[0], 175 - PM_BTN_SIZE / 2, 250 - PM_BTN_SIZE / 2), "custom(power) corner MISSES"); /* ============ P2 owner/enabled 双闸门 ============ */ printf("P2 owner/enabled gate\n"); meow_ui_activity_push(8); /* 非 active(owner=0x50≠8) */ CHECK(!meow_widget_hit(btn[2], 175, 390), "non-active owner -> MISS"); meow_ui_activity_pop(); /* 回 0x50 */ CHECK(meow_widget_hit(btn[2], 175, 390), "back to sentinel -> hits"); meow_widget_set_enabled(btn[2], 0); CHECK(!meow_widget_hit(btn[2], 175, 390), "disabled -> MISS"); meow_widget_set_enabled(btn[2], 1); CHECK(meow_widget_hit(btn[2], 175, 390), "enabled -> hits"); /* ============ P3 选中/按压态像素变化 ============ */ printf("P3 selected/pressed state (SDK state layer)\n"); meow_ui_clear(0x000000); draw_all(-1); /* 全部非选中 */ unsigned short id0 = px565(175, 250); /* custom idx0 圆心 */ meow_widget_set_selected(btn[0], 1); meow_widget_draw(btn[0]); unsigned short sel0 = px565(175, 250); CHECK(sel0 != id0, "custom selected -> centre pixel changes"); meow_widget_set_selected(btn[0], 0); meow_widget_set_state(btn[0], MEOW_WS_PRESSED, 1); meow_widget_draw(btn[0]); unsigned short pr0 = px565(175 + 38, 250); /* 内圆 r=25 外的底色环带(按压态连底色变) */ meow_widget_set_state(btn[0], MEOW_WS_PRESSED, 0); meow_widget_draw(btn[0]); unsigned short pr0b = px565(175 + 38, 250); CHECK(pr0 != pr0b, "custom pressed -> bg tint pixel changes"); /* 选中态(自定义画布:selected=primaryContainer 圆底变化) */ draw_all(-1); unsigned short ic0 = px565(175 + PM_BTN_SIZE / 4, 390); meow_widget_set_selected(btn[2], 1); meow_widget_draw(btn[2]); unsigned short ic1 = px565(175 + PM_BTN_SIZE / 4, 390); CHECK(ic1 != ic0, "custom(back) selected -> circle-bg pixel changes"); /* ============ P4 主题四态(MD3/MIUIx × 明暗):卡片+圆钮渲染 + export 九字段 ============ */ printf("P4 theme 4-state render + meow_theme_export\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]); const meow_theme *th = meow_theme_get(); meow_theme ex; meow_theme_export(&ex); /* export == 当前解析生效主题(单一权威)+ 九字段映射到 launcher 便利结构 */ CHECK(ex.surface == th->surface && ex.surfaceVariant == th->surfaceVariant && ex.surfaceContainer == th->surfaceContainer && ex.primary == th->primary && ex.onSurface == th->onSurface && ex.onSurfaceVariant == th->onSurfaceVariant && ex.outline == th->outline && ex.error == th->error && ex.surfaceTint == th->surfaceTint, "export == meow_theme_get (9-role)"); meow_widget_set_opts(btn[2], &(meow_widget_opts){ .bg = th->surfaceContainer }); meow_ui_clear(0); draw_all(-1); int cardpx = region_nonbg(60, 130, 200, 40, th->surface); int btnpx = region_nonbg(pm_cx[0] - PM_BTN_SIZE / 2, pm_cy[0] - PM_BTN_SIZE / 2, PM_BTN_SIZE, PM_BTN_SIZE, th->surface); char nm[80]; snprintf(nm, sizeof nm, "style=%s mode=%s card renders", styles[si] ? "MIUIx" : "MD3", modes[mi] ? "dark" : "light"); CHECK(cardpx > 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; }