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。
236 lines
11 KiB
C
236 lines
11 KiB
C
/* 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 <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)
|
||
|
||
/* ---- 与 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 };
|
||
/* SDK 图标:2 回HiBy=ARROW_BACK、5 取消=CLOSE;其余 -1=自定义画布(pm_icons 专有) */
|
||
static const int pm_sdk_icon[PM_NUM] = {
|
||
-1, -1, MEOW_ICON_ARROW_BACK, -1, -1, MEOW_ICON_CLOSE
|
||
};
|
||
|
||
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), "iconbtn center hits");
|
||
CHECK(!meow_widget_hit(btn[2], 175 - PM_BTN_SIZE / 2, 390 - PM_BTN_SIZE / 2),
|
||
"iconbtn corner (dist~70>50) MISSES");
|
||
CHECK(meow_widget_hit(btn[0], 175, 250), "custom center hits");
|
||
CHECK(!meow_widget_hit(btn[0], 175 - PM_BTN_SIZE / 2, 250 - PM_BTN_SIZE / 2),
|
||
"custom 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");
|
||
/* iconbtn 选中态 */
|
||
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, "iconbtn 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;
|
||
}
|