feat(sdk): 中文拼音输入法落地 (agent-229, 设计稿 IME_PINYIN_DESIGN.md) — 静态表词库(403音节+329词,rodata ~20.5KB,零堆)+引擎(meow_pinyin.c,音节切分DFS+联想)+SDK集成(MEOW_IME_PINYIN尾部追加零ABI破坏,组合条+候选栏+翻页+中英切换,英文零回归)

meow_ui.a→a7a9043e; hello/files/ncm重链已部署; test_pinyin 5项全过+test_ime_fix零回归
This commit is contained in:
MeowOS committed 2026-08-31 18:34:51 +08:00
1 parent f34818037f
commit e9b6b690c1
16 files changed
+2261 -11

No files matched your search

+125
View File
@@ -0,0 +1,125 @@
/* test_pinyin.c — 中文拼音 IME 宿主验证(480x720 离屏,直接 handle_tap 确定语义)。
* ① 拼音组合:type "nihao" → 组合串="nihao"、字段=""(组合不上屏、on_change 不触发);
* 点候选(你好)→ 字段="你好"、组合清空、on_change="你好";
* ② 候选翻页:type "shi" → nc>6;点 ▶ 翻页末首候选变化、点 ◀ 回;
* ③ UTF-8 删字段:上屏"你好"后点退格 → 字段="你"(按 codepoint 回退,不出现乱码);
* ④ 中/英切换:拼音点 [拼/英] → ASCII;ASCII 点顶条「中」热区 → 拼音;
* ⑤ 空格/回车上屏首候选:type "ni" + 空格 → 字段="你"。
*
* 编译(宿主 gcc,同 test_ime_fix):
* gcc -Wall -Wextra -o test_pinyin test_pinyin.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_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_ui_media.c \
* meow_notify_ipc.c stb_image.c -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "meow_pinyin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* 拼音键位(build_kb PINYIN:行 y=504/552/600/648,GCOL=48,行左缘 x=2;键 44×36):
* row1 y=504 "qwertyuiop";row2 y=552 "asdfghjkl"+[拼/英]@col9;
* row3 y=600 "zxcvbnm"+[←退格]@col7;row4 y=648 [?123]+[空格]+[OK]。 */
#define KEY_XY(col, y) (((2 + (col) * 48) + 22), ((y) + 18))
static void t(char ch) {
/* 按字符找其键中心坐标(行/列换算) */
int row = 0, col = 0;
const char *rows[3] = { "qwertyuiop", "asdfghjkl", "zxcvbnm" };
for (int r = 0; r < 3; r++) {
const char *p = strchr(rows[r], ch);
if (p) { row = r; col = (int)(p - rows[r]); break; }
}
int x = 2 + col * 48 + 22;
int y = 504 + row * 48 + 18;
meow_ime_handle_tap(x, y);
}
static char g_echo[128] = "";
static void ime_cb(const char *t, void *ctx) { (void)ctx; snprintf(g_echo, sizeof g_echo, "%s", t); }
static void tap(int x, int y) { meow_ime_handle_tap(x, y); }
static meow_ime_ctx g_c;
static void open_py(void) {
memset(&g_c, 0, sizeof g_c);
g_c.mode = MEOW_IME_PINYIN; g_c.maxlen = 64; g_c.on_change = ime_cb;
memset(g_echo, 0, sizeof g_echo);
meow_ui_text_input_async(&g_c);
assert(meow_ime_active() == 1);
}
static void close_py(void) { meow_ime_close(0); assert(meow_ime_active() == 0); }
int main(void) {
setenv("MEOW_UI_DUMP", "/tmp/pinyin.ppm", 1);
meow_ui_opts o = { 480, 720 };
assert(meow_ui_init(&o) == 0);
/* ① 拼音组合 + 点上屏 */
open_py();
t('n'); t('i'); t('h'); t('a'); t('o');
assert(strcmp(meow_ime_composition(), "nihao") == 0);
assert(meow_ime_text()[0] == 0); /* 组合不上屏 */
assert(g_echo[0] == 0); /* 未上屏不回调 */
/* 点候选 0(你好,x=44 起;宿主 stub width=strlen*6) */
tap(60, 480);
assert(strcmp(meow_ime_text(), "你好") == 0);
assert(strcmp(g_echo, "你好") == 0);
assert(meow_ime_composition()[0] == 0); /* 组合已清 */
close_py();
printf("PASS pinyin: nihao -> 你好 (compose+commit+on_change)\n"); fflush(stdout);
/* ② 候选翻页(shi 候选 >6) */
open_py();
t('s'); t('h'); t('i');
char all[32][64];
int n = py_predict("shi", all, 32);
assert(n > 6);
assert(strcmp(all[0], "是") == 0);
/* 点 ▶(下一页)→ 页内首候选应为 all[6](页 0 首候选是 all[0]) */
tap(457, 480); /* ▶:页 0 → 页 1 */
tap(60, 480); /* 点页 1 候选 0(x=44 起) */
assert(strcmp(meow_ime_text(), all[6]) == 0);
close_py();
printf("PASS pagination: shi page_next then commit page1[0] == all[6]\n"); fflush(stdout);
/* ③ UTF-8 删字段:上屏"你好"后点退格 → "你" */
open_py();
t('n'); t('i'); t('h'); t('a'); t('o');
tap(60, 480); /* 上屏"你好" */
assert(strcmp(meow_ime_text(), "你好") == 0);
tap(410, 618); /* ←退格(row3 col7 宽键中心) */
assert(strcmp(meow_ime_text(), "你") == 0);
assert(strcmp(g_echo, "你") == 0);
close_py();
printf("PASS utf8 field backspace: 你好 -> 你\n"); fflush(stdout);
/* ④ 中/英切换 */
open_py();
tap(456, 570); /* [拼/英] row2 col9 → ASCII */
assert(meow_ime_active() == 1);
/* 顶条「中」热区(430..474, KB_TOP..KB_TOP+42)→ 切回拼音;组合已被丢弃 */
tap(452, 438);
assert(meow_ime_active() == 1);
t('n'); t('i');
assert(strcmp(meow_ime_composition(), "ni") == 0);
tap(456, 570); /* 又回 ASCII */
assert(strcmp(meow_ime_composition(), "") == 0); /* 组合态已丢弃 */
close_py();
printf("PASS zh/en switch (pinyin->ascii->pinyin, combo dropped)\n"); fflush(stdout);
/* ⑤ 空格上屏首候选(ni → 你) */
open_py();
t('n'); t('i');
tap(194, 666); /* 空格 */
assert(strcmp(meow_ime_text(), "你") == 0);
assert(strcmp(g_echo, "你") == 0);
close_py();
printf("PASS space commits first candidate: ni -> 你\n"); fflush(stdout);
printf("ALL PASS\n");
meow_ui_shutdown();
return 0;
}