meow_ui.a→9a8eb7ef; hello→fc299577; launcher→b33da20a; 宿主5套件+新test_notif_open+qemu分发 全过 出入: SDK符号用meow_ui_前缀防与launcher内同名冲突; 叠加会话真机待验; §10.5文档旧号段已过时
229 lines
9.9 KiB
C
229 lines
9.9 KiB
C
/* test_sdk_fixes.c — M2.71 修复项宿主验证(离屏 480x720,16bpp)。
|
||
* 覆盖:
|
||
* bug4 disabled 按钮圆角外无灰方块(角点 = 页面背景而非叠暗);
|
||
* bug1 圆形进度环带确实画到 = 半径+thk/2 处(bbox 与真实像素一致,漏刷修复的几何前提);
|
||
* bug3 modal(Dialog)open→cancel 后背景还原(scrim 不累积/无残影);
|
||
* bug7 IME 回显实时同步(on_change 随键入触发)+ 关闭后键盘区背景还原;
|
||
* fix1 dialog INPUT 在 ESC/背景(result=0)关闭后文本不丢(M2.71-IME-fix 新语义:回车只提交不关)。
|
||
*
|
||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||
* gcc -Wall -o test_sdk_fixes test_sdk_fixes.c test_host_stub.c \
|
||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||
* meow_ui_draw.c meow_ui_view.c meow_notify_ipc.c \
|
||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_internal.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <assert.h>
|
||
#include <unistd.h>
|
||
#include <pthread.h>
|
||
|
||
static void rd_px(int x, int y, int *r, int *g, int *b)
|
||
{
|
||
unsigned char *bb = meow_ui_back_buf();
|
||
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
|
||
assert(bb && line > 0);
|
||
if (bpp == 16) {
|
||
uint16_t px = *(uint16_t *)(bb + (size_t)y * line + (size_t)x * 2);
|
||
*r = (((px >> 11) & 0x1F) << 3) | ((px >> 11) & 0x1F) >> 2;
|
||
*g = (((px >> 5) & 0x3F) << 2) | ((px >> 5) & 0x3F) >> 4;
|
||
*b = ((px & 0x1F) << 3) | ((px & 0x1F) >> 2);
|
||
} else {
|
||
uint32_t px = *(uint32_t *)(bb + (size_t)y * line + (size_t)x * 4);
|
||
*r = (px >> 16) & 0xFF; *g = (px >> 8) & 0xFF; *b = px & 0xFF;
|
||
}
|
||
}
|
||
static int near(int a, int b) { int d = a - b; if (d < 0) d = -d; return d <= 12; }
|
||
|
||
/* ---- 为阻塞模态/IME 在后台注入事件 ---- */
|
||
static int g_inj_tap_x = 0, g_inj_tap_y = 0, g_inj_delay = 0;
|
||
static int K_DLG_OK_X = 0, K_DLG_OK_Y = 0; /* dialog INPUT 的 OK 按钮中心(运行时算) */
|
||
static void *inject_thread(void *arg)
|
||
{
|
||
(void)arg;
|
||
usleep((useconds_t)g_inj_delay);
|
||
meow_input_inject(MEOW_EV_TAP, g_inj_tap_x, g_inj_tap_y);
|
||
return NULL;
|
||
}
|
||
static void inject_after(int ms, int x, int y)
|
||
{
|
||
pthread_t th; g_inj_tap_x = x; g_inj_tap_y = y; g_inj_delay = ms;
|
||
pthread_create(&th, NULL, inject_thread, NULL);
|
||
pthread_detach(th);
|
||
}
|
||
|
||
static void test_disabled_corner(void)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_clear(th->bg);
|
||
meow_widget *b = meow_btn_create(100, 100, 200, 60, "DISABLED");
|
||
meow_widget_set_enabled(b, 0); /* 禁用:old 用方形 α 盖整 bbox → 圆角外露灰 */
|
||
meow_widget_draw(b);
|
||
meow_ui_flush();
|
||
int r, g, bb;
|
||
rd_px(100, 100, &r, &g, &bb); /* bbox 左上角(圆角外):应为页面背景 */
|
||
printf("[bug4] disabled corner rgb=%02X%02X%02X (bg=%02X%02X%02X) ",
|
||
r, g, bb, (th->bg >> 16) & 0xFF, (th->bg >> 8) & 0xFF, th->bg & 0xFF);
|
||
assert(near(r, (th->bg >> 16) & 0xFF) && near(g, (th->bg >> 8) & 0xFF) && near(bb, th->bg & 0xFF));
|
||
printf("PASS\n");
|
||
meow_widget_free(b);
|
||
}
|
||
|
||
static void test_circular_extent(void)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_clear(th->bg);
|
||
/* r=30, thk=4 → 环外缘 = r+thk/2 = 32;create 几何外接框 = 中心±(r+margin)=±33 */
|
||
int cx = 240, cy = 200, r = 30;
|
||
meow_widget *pg = meow_progress_circular_create(cx, cy, r, 100);
|
||
meow_widget_draw(pg);
|
||
meow_ui_flush();
|
||
int rd, g, b;
|
||
rd_px(cx - 32, cy, &rd, &g, &b); /* 最左环外缘:应被画(非背景) */
|
||
int bg_r = (th->bg >> 16) & 0xFF, bg_g = (th->bg >> 8) & 0xFF, bg_b = th->bg & 0xFF;
|
||
printf("[bug1] ring-left-extent rgb=%02X%02X%02X bg=%02X%02X%02X ", rd, g, b, bg_r, bg_g, bg_b);
|
||
assert(!(near(rd, bg_r) && near(g, bg_g) && near(b, bg_b))); /* 环带已画到外缘 */
|
||
printf("PASS\n"); fflush(stdout);
|
||
meow_widget_free(pg);
|
||
}
|
||
|
||
static void test_modal_restore(void)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_clear(th->bg);
|
||
meow_ui_flush();
|
||
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Sure?");
|
||
/* 两次 open→cancel:背景应两次均还原(scrim 不累积、无残影) */
|
||
for (int i = 0; i < 2; i++) {
|
||
inject_after(30, 1, 1); /* (1,1) 在居中卡片外 → 取消 */
|
||
meow_dialog_show(d);
|
||
int r, g, b;
|
||
rd_px(400, 100, &r, &g, &b); /* 卡片外右上:应 == 背景 */
|
||
printf("[bug3] open%d cancel bg rgb=%02X%02X%02X bg=%02X%02X%02X ", i, r, g, b,
|
||
(th->bg >> 16) & 0xFF, (th->bg >> 8) & 0xFF, th->bg & 0xFF);
|
||
assert(near(r, (th->bg >> 16) & 0xFF) && near(g, (th->bg >> 8) & 0xFF) && near(b, th->bg & 0xFF));
|
||
}
|
||
printf("PASS\n");
|
||
meow_dialog_free(d);
|
||
}
|
||
|
||
/* IME 回显同步回调 */
|
||
static char g_echo[64] = "";
|
||
static void ime_cb(const char *t, void *ctx) { (void)ctx; snprintf(g_echo, sizeof g_echo, "%s", t); }
|
||
|
||
static void test_ime_echo_and_restore(void)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_clear(th->bg);
|
||
meow_ui_flush();
|
||
meow_ime_ctx c; memset(&c, 0, sizeof c);
|
||
c.mode = MEOW_IME_ASCII; c.maxlen = 32; c.on_change = ime_cb;
|
||
/* --- 异步:验『回显/输入框实时同步(on_change 随键入触发)』--- */
|
||
meow_ui_text_input_async(&c);
|
||
assert(meow_ime_active() == 1);
|
||
meow_input_inject(MEOW_EV_TAP, 20, 580); /* ASCII 行3第1键 = 'a'(shift 关 → 小写 'a') */
|
||
meow_ime_handle_tap(20, 580);
|
||
assert(strcmp(g_echo, "a") == 0 && strcmp(meow_ime_text(), "a") == 0);
|
||
printf("[bug7] ime echo live-sync after 'a' -> \"%s\" (shift-off lower) PASS\n", g_echo); fflush(stdout);
|
||
meow_ime_handle_tap(450, 584); /* SHIFT 开 */
|
||
meow_ime_handle_tap(20, 580); /* 'a' → 'A' 大写 */
|
||
assert(strcmp(g_echo, "aA") == 0 && strcmp(meow_ime_text(), "aA") == 0);
|
||
printf("[bug7] shift-on -> 'A' (echo \"%s\") PASS\n", g_echo); fflush(stdout);
|
||
meow_input_inject(MEOW_EV_TAP, 20, 680); /* ESC(X) → 取消 */
|
||
meow_ime_handle_tap(20, 680);
|
||
assert(meow_ime_active() == 0);
|
||
/* --- 阻塞:验『关闭后键盘区背景还原』--- */
|
||
meow_ui_clear(th->bg);
|
||
meow_ui_flush();
|
||
inject_after(30, 20, 680); /* ESC 关闭 */
|
||
int rc = meow_ui_text_input(&c);
|
||
assert(rc == 0); /* 取消 */
|
||
int r, g, b;
|
||
rd_px(240, 600, &r, &g, &b); /* 键盘区内一点:应 == 背景 */
|
||
printf("[bug7] ime close restore px rgb=%02X%02X%02X bg=%02X%02X%02X ", r, g, b,
|
||
(th->bg >> 16) & 0xFF, (th->bg >> 8) & 0xFF, th->bg & 0xFF);
|
||
assert(near(r, (th->bg >> 16) & 0xFF) && near(g, (th->bg >> 8) & 0xFF) && near(b, th->bg & 0xFF));
|
||
printf("PASS\n");
|
||
}
|
||
|
||
/* ---- 回归①:dialog INPUT 在 ESC/背景(result=0)关闭后文本不丢(M2.71-IME-fix 新语义)。
|
||
* 复现 dialog.c dlg_input_ok_center → 复用 dialog 的 OK 按钮中心几何(与 dlg_calc 同一公式)。 */
|
||
static void dialog_input_ok_center(int *ox, int *oy)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
int W = meow_ui_width(), H = meow_ui_height();
|
||
int sb = meow_ui_statusbar_height(); if (sb < 0) sb = 0;
|
||
int pad = 24, btn = 44;
|
||
int tH = meow_ui_text_height(th->font_title);
|
||
int bH = meow_ui_text_height(th->font_body);
|
||
int content = bH + 8; /* INPUT(无 items)→ 1 行正文预览 */
|
||
int dw = 348; if (dw > W - 40) dw = W - 40;
|
||
int dx = (W - dw) / 2;
|
||
int dh = pad + tH + 12 + content + pad + (16 + btn);
|
||
int avail = H - sb - 20;
|
||
if (dh > avail) dh = avail;
|
||
int dy = (H - dh) / 2; if (dy < sb) dy = sb;
|
||
int row_y = dy + pad + tH + 12;
|
||
int btn_y = row_y + content + 16;
|
||
int btn_w = (dw - 2 * pad - 12) / 2;
|
||
int cx = dx + pad + btn_w + 12; /* OK = 右侧填充按钮左缘 */
|
||
*ox = cx + btn_w / 2;
|
||
*oy = btn_y + btn / 2;
|
||
}
|
||
|
||
static void *dlg_input_driver(void *arg)
|
||
{
|
||
(void)arg;
|
||
usleep(40000); /* 等 dialog show 起(遮罩+卡片已绘) */
|
||
meow_input_inject(MEOW_EV_TAP, K_DLG_OK_X, K_DLG_OK_Y); /* 点 OK → 打开阻塞 IME */
|
||
usleep(140000);
|
||
meow_input_inject(MEOW_EV_TAP, 20, 584); /* 行3第1键 'a'(shift 关) */
|
||
usleep(140000);
|
||
meow_input_inject(MEOW_EV_TAP, 20, 680); /* ESC → 关 IME → dialog 随之关 */
|
||
return NULL;
|
||
}
|
||
|
||
static void test_dialog_input_preserve(void)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_clear(th->bg);
|
||
meow_ui_flush();
|
||
meow_ime_ctx ime; memset(&ime, 0, sizeof ime);
|
||
ime.mode = MEOW_IME_ASCII; ime.maxlen = 64;
|
||
strcpy(ime.buf, "");
|
||
meow_dialog *d = meow_dialog_create(MEOW_DLG_INPUT, "Name");
|
||
meow_dialog_set_ime(d, &ime);
|
||
|
||
dialog_input_ok_center(&K_DLG_OK_X, &K_DLG_OK_Y);
|
||
pthread_t thd; pthread_create(&thd, NULL, dlg_input_driver, NULL);
|
||
meow_dialog_show(d);
|
||
pthread_join(thd, NULL);
|
||
|
||
/* ESC 关闭(result=0)→ 文本应保留:App 原始 ctx.buf 应含 'a'(不再丢失),且是取消语义 */
|
||
printf("[fix1] dialog INPUT after ESC: result=%d App.ime.buf=\"%s\" (expect 0, \"a\")\n",
|
||
meow_dialog_result(d), ime.buf);
|
||
assert(meow_dialog_result(d) == 0);
|
||
assert(strcmp(ime.buf, "a") == 0);
|
||
printf("[fix1] dialog INPUT text preserved on ESC PASS\n");
|
||
meow_dialog_free(d);
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
setenv("MEOW_UI_DUMP", "/tmp/fixes.ppm", 1);
|
||
meow_ui_opts o = { 480, 720 };
|
||
assert(meow_ui_init(&o) == 0);
|
||
test_disabled_corner();
|
||
test_circular_extent();
|
||
test_modal_restore();
|
||
test_ime_echo_and_restore();
|
||
test_dialog_input_preserve();
|
||
meow_ui_shutdown();
|
||
printf("ALL PASS\n");
|
||
return 0;
|
||
}
|