meow_ui.a d814d0ed→7e20f10e; hello 0a1c96a5→2ce09466 宿主验证: test_p1p2p3_repro P1c/P2/P3 ALL PASS; test_meow_ui_widgets ALL PASS 无回归; mipsel -Wall -Wextra 0 warning
209 lines
9.1 KiB
C
209 lines
9.1 KiB
C
/* test_ime_fix.c — M2.71-IME-fix:宿主离屏验证三项修复(480x720,16bpp)。
|
||
* ① shift 大小写正确:shift 关→小写('q'),开→大写('Q');
|
||
* ② K_ENTER 只提交不关(meow_ime_active() 仍真),ESC 后关;
|
||
* ③ 阻塞 IME 循环喂 on_tick(动态内容重绘)且 IME 面板仍在最上
|
||
* (On_tick 在 IME 面板区画红色哨兵行,IME 绘制后应盖回面板色,不被 App 覆盖)。
|
||
*
|
||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||
* gcc -Wall -Wextra -o test_ime_fix test_ime_fix.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 \
|
||
* -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>
|
||
|
||
/* ASCII 键盘键位(build_kb:KB_TOP=420 → 首行 y=470;GW=44,GAP=4,GCOL=48,行左缘 x=2):
|
||
* row2 "qwertyuiop" y∈[518,554);row3 "asdfghjkl"+SHIFT y∈[566,602);
|
||
* row5 ESC+SPACE+ENTER y∈[662,698)。 */
|
||
#define K_Q_X 20
|
||
#define K_Q_Y 536
|
||
#define K_SHIFT_X 450
|
||
#define K_SHIFT_Y 584
|
||
#define K_ENTER_X 430
|
||
#define K_ENTER_Y 680
|
||
#define K_ESC_X 20
|
||
#define K_ESC_Y 680
|
||
|
||
/* ---- 像素/配色辅助(离屏 back_buf) ---- */
|
||
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 <= 14; }
|
||
|
||
/* ---- ① 异步 shift 测试(直接 handle_tap,确定语义,无阻塞循环) ---- */
|
||
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_shift(void)
|
||
{
|
||
meow_ime_ctx c; memset(&c, 0, sizeof c);
|
||
c.mode = MEOW_IME_ASCII; c.maxlen = 32; c.on_change = ime_cb;
|
||
meow_ui_text_input_async(&c);
|
||
assert(meow_ime_active() == 1);
|
||
|
||
meow_ime_handle_tap(K_Q_X, K_Q_Y); /* shift 关 → 'q' 小写 */
|
||
assert(strcmp(meow_ime_text(), "q") == 0);
|
||
assert(strcmp(g_echo, "q") == 0);
|
||
assert(meow_ime_text()[0] >= 'a' && meow_ime_text()[0] <= 'z'); /* 明确小写 */
|
||
|
||
meow_ime_handle_tap(K_SHIFT_X, K_SHIFT_Y); /* shift 开 */
|
||
meow_ime_handle_tap(K_Q_X, K_Q_Y); /* 'q' → 'Q' 大写 */
|
||
|
||
assert(strcmp(meow_ime_text(), "qQ") == 0);
|
||
assert(strcmp(g_echo, "qQ") == 0);
|
||
assert(meow_ime_text()[1] >= 'A' && meow_ime_text()[1] <= 'Z'); /* 明确大写 */
|
||
printf("PASS shift: off='q'(lower), on='Q'(upper)\n"); fflush(stdout);
|
||
|
||
meow_ime_handle_tap(K_ESC_X, K_ESC_Y); /* 关闭 */
|
||
meow_ime_close(0); /* 复位(清 g_ime_done/active) */
|
||
assert(meow_ime_active() == 0);
|
||
}
|
||
|
||
/* ---- ②③ 阻塞 IME(经 meow_ui_run 驱动):K_ENTER 不关 / on_tick 喂 / IME 最上 ---- */
|
||
static meow_widget *g_tf;
|
||
static char g_tfbuf[32] = "";
|
||
static int g_tick = 0;
|
||
static int g_sentinel_painted = 0;
|
||
static int g_test_done = 0;
|
||
|
||
static int g_active_after_enter = -1; /* K_ENTER 后 meow_ime_active()(期望 1) */
|
||
static int g_active_after_esc = -1; /* ESC 后 meow_ime_active()(期望 0) */
|
||
static int g_probe_r = -1, g_probe_g = -1, g_probe_b = -1; /* IME 面板区内取样(期望=面板色非哨兵红) */
|
||
static char g_drv_text[64] = ""; /* shift 序列后 meow_ime_text()(期望 "qQ") */
|
||
|
||
/* 把 back_buf 写成 PPM(视觉目检 IME 面板渲染/最上;在 IME 空闲时调用,back_buf 稳定) */
|
||
static void dump_ppm(const char *path)
|
||
{
|
||
unsigned char *bb = meow_ui_back_buf();
|
||
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
|
||
int W = meow_ui_width(), H = meow_ui_height();
|
||
FILE *f = fopen(path, "wb");
|
||
if (!f) return;
|
||
fprintf(f, "P6\n%d %d\n255\n", W, H);
|
||
for (int y = 0; y < H; y++) {
|
||
for (int x = 0; x < W; x++) {
|
||
int r, g, b; rd_px(x, y, &r, &g, &b);
|
||
fputc(r, f); fputc(g, f); fputc(b, f);
|
||
}
|
||
}
|
||
fclose(f);
|
||
}
|
||
|
||
/* App on_tick:模拟 demo 动态重绘。IME 打开时先在 IME 面板区内画一条红色哨兵行,
|
||
* 再由 meow_ime_draw 盖回面板色 → 验证「喂 on_tick 后 IME 仍最上」的绘制顺序。 */
|
||
static void host_tick(void)
|
||
{
|
||
g_tick++;
|
||
if (meow_ime_active() && !g_sentinel_painted) {
|
||
g_sentinel_painted = 1;
|
||
meow_ui_fill_rect(0, 500, 480, 8, 0xFF0000); /* 哨兵行:App 内容拟 IME 面板区内 */
|
||
meow_ui_flush_region(0, 500, 480, 8);
|
||
}
|
||
if (g_test_done) meow_ui_request_exit();
|
||
}
|
||
|
||
/* App on_tap:命中 TextField → 打开**阻塞** IME(同 hello demo 路径) */
|
||
static void host_tap(int x, int y)
|
||
{
|
||
(void)x; (void)y;
|
||
if (g_tf && meow_widget_hit(g_tf, x, y)) meow_textfield_activate(g_tf);
|
||
}
|
||
|
||
static void *driver(void *arg)
|
||
{
|
||
(void)arg;
|
||
usleep(600000); /* 等 meow_ui_run 起动(startup cooldown=500ms) */
|
||
meow_input_inject(MEOW_EV_TAP, 150, 322); /* 点 TextField → 阻塞 IME */
|
||
usleep(160000);
|
||
meow_input_inject(MEOW_EV_TAP, K_Q_X, K_Q_Y); /* shift 关 → 'q' */
|
||
usleep(90000);
|
||
meow_input_inject(MEOW_EV_TAP, K_SHIFT_X, K_SHIFT_Y); /* shift 开 */
|
||
usleep(60000);
|
||
meow_input_inject(MEOW_EV_TAP, K_Q_X, K_Q_Y); /* shift 开 → 'Q' → 缓冲 "qQ" */
|
||
usleep(90000);
|
||
strncpy(g_drv_text, meow_ime_text(), sizeof g_drv_text - 1);
|
||
g_drv_text[sizeof g_drv_text - 1] = 0;
|
||
|
||
meow_input_inject(MEOW_EV_TAP, K_ENTER_X, K_ENTER_Y); /* K_ENTER:只提交不关 */
|
||
usleep(160000);
|
||
g_active_after_enter = meow_ime_active(); /* 期望 1(键盘仍开) */
|
||
|
||
rd_px(240, 500, &g_probe_r, &g_probe_g, &g_probe_b); /* IME 面板区内取样 */
|
||
dump_ppm("/tmp/ime_panel.ppm"); /* 目检:IME 面板渲染 + 盖住哨兵 */
|
||
printf("[drv] text=%s active_after_enter=%d probe=%02X%02X%02X sentineld=%d\n",
|
||
g_drv_text, g_active_after_enter, g_probe_r, g_probe_g, g_probe_b,
|
||
g_sentinel_painted); fflush(stdout);
|
||
|
||
meow_input_inject(MEOW_EV_TAP, K_ESC_X, K_ESC_Y); /* ESC → 关闭 */
|
||
usleep(160000);
|
||
g_active_after_esc = meow_ime_active(); /* 期望 0 */
|
||
g_test_done = 1;
|
||
return NULL;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
setenv("MEOW_UI_DUMP", "/tmp/ime_fix.ppm", 1);
|
||
meow_ui_opts o = { 480, 720 };
|
||
assert(meow_ui_init(&o) == 0);
|
||
const meow_theme *th = meow_theme_get();
|
||
|
||
test_shift(); /* ① */
|
||
|
||
/* ②③ 在 meow_ui_run 里驱动(g_events 仅 meow_ui_run 能设置) */
|
||
g_tf = meow_textfield_create(10, 300, 300, 44, "Type here...", g_tfbuf, 32, MEOW_IME_ASCII);
|
||
meow_ui_events ev; memset(&ev, 0, sizeof ev);
|
||
ev.on_tap = host_tap;
|
||
ev.on_tick = host_tick;
|
||
pthread_t thd; pthread_create(&thd, NULL, driver, NULL);
|
||
meow_ui_run(&ev);
|
||
pthread_join(thd, NULL);
|
||
|
||
/* 断言 ② */
|
||
printf("[assert] active_after_enter=%d (expect 1), active_after_esc=%d (expect 0)\n",
|
||
g_active_after_enter, g_active_after_esc);
|
||
assert(g_active_after_enter == 1); /* K_ENTER 不关 */
|
||
assert(g_active_after_esc == 0); /* ESC 关 */
|
||
|
||
/* 断言 ①(阻塞路径经 on_change 实时同步到字段) */
|
||
printf("[assert] blocking text=%s (expect \"qQ\"), tf=%s (expect \"qQ\")\n",
|
||
g_drv_text, meow_textfield_text(g_tf));
|
||
assert(strcmp(g_drv_text, "qQ") == 0);
|
||
assert(strcmp(meow_textfield_text(g_tf), "qQ") == 0);
|
||
|
||
/* 断言 ③:on_tick 在 IME 打开期间被喂过(哨兵已画)+ IME 面板盖住哨兵 */
|
||
int pr = (th->surfaceContainerHighest >> 16) & 0xFF;
|
||
int pg = (th->surfaceContainerHighest >> 8) & 0xFF;
|
||
int pb = (th->surfaceContainerHighest) & 0xFF;
|
||
printf("[assert] sentinel_painted=%d probe=%02X%02X%02X panel=%02X%02X%02X\n",
|
||
g_sentinel_painted, g_probe_r, g_probe_g, g_probe_b, pr, pg, pb);
|
||
assert(g_sentinel_painted == 1); /* on_tick 在 IME 期间被调(重绘过) */
|
||
assert(g_tick > 0);
|
||
assert(near(g_probe_r, pr) && near(g_probe_g, pg) && near(g_probe_b, pb)); /* IME 面板最上 */
|
||
/* 哨兵红 0xFF0000 与面板色显著不同,确保上面断言有判别力(面板色不应近纯红) */
|
||
assert(!(near(pr, 0xFF) && near(pg, 0x00) && near(pb, 0x00)));
|
||
|
||
printf("ALL PASS\n");
|
||
meow_ui_shutdown();
|
||
return 0;
|
||
}
|