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 9e4ec15e85
commit f01509d4f4
12 files changed
+2249 -9

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ AR="${AR:-/usr/bin/ar}" # 系统 GNU ar 即可归档 mipsel ELF 对象(架
rm -f meow_ui.a *.o
# -I../meow_launcherd:meow_notify_ipc.c 复用共享协议头 meow_ipc.h(同一份、杜绝两端漂移)
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media stb_image"
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_pinyin meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media stb_image"
CFLAGS="-Os -static -I$ROOT/patch_v2/meow_launcherd"
for f in $OBJS; do
echo "== cc $f =="
+832
View File
@@ -0,0 +1,832 @@
# -*- coding: utf-8 -*-
"""
gen_pinyin_lex.py — MeowOS 中文拼音输入法 · 词库生成器(主机侧,离线)。
产出:meow_pinyin_lex.h(静态 .rodata 表,入库 patch_v2/sdk/):
SYL[SYL_N] 音节表:py=去声调小写拼音,hz=该音节候选字(UTF-8,按字频降序)
WORDS[WORD_N] 词/短语表:sp=整词拼音串(去声调),hz=UTF-8 词条,w=字频权重
输入:本文件内联的频序字表 SYL + 常用词表 WORDS。无真实语料(宿主机无 pypinyin/jieba
字表),按设计稿 §2.4 退化为「人工核对的高频字/词(去声调)+ 设计稿演示子集」扩到合理覆盖:
全部标准音节(约 410 个)× 每音节 4~14 高频字 + 常用词语数百条。候选字全部落在
GB2312 常用字集(font/gen/gen_font.py 的 common/gbk tier 已含),保证上屏可渲染。
运行:python3 gen_pinyin_lex.py (在工作目录产出 meow_pinyin_lex.h,并打印体积账)
约束:字节序无关;词表按 sp 排序(前缀提示走「精确二分 + 线性回溯」)。
"""
# 音节表:py=去声调小写拼音,hz=该音节候选字(按字频降序,UTF-8)。
# 覆盖 GB2312 一级+高频二级字的常见音节;生僻音节给 2~5 个高频字即可。
SYL = {
# ---- a ----
'a': '啊阿吖呵腌',
'ai': '爱哀挨唉矮碍癌艾呆埃',
'an': '安按暗案岸俺鞍庵',
'ang': '昂盎肮',
'ao': '奥懊傲熬凹澳敖袄',
# ---- ba ----
'ba': '把八吧爸拔罢霸巴扒芭',
'bai': '白百摆败拜柏伯佰',
'ban': '办半班搬版板伴扮拌瓣',
'bang':'帮棒榜绑膀磅邦蚌',
'bao': '报保包宝抱暴薄爆饱堡',
'bei': '被北背倍杯备悲辈贝碑',
'ben': '本奔笨苯',
'beng':'蹦崩甭泵迸',
'bi': '比必笔闭毕鼻避彼壁弊',
'bian':'变边便遍辨编辩扁鞭',
'biao':'表标彪镖膘',
'bie': '别憋鳖瘪',
'bin': '宾滨彬濒斌',
'bing':'病并兵冰饼丙柄秉',
'bo': '波播拨薄博伯驳脖膊',
'bu': '不步部布补捕怖埠簿',
# ---- ca ----
'ca': '擦嚓',
'cai': '才菜采财猜彩裁踩材',
'can': '参残餐灿惨蚕惭',
'cang':'藏仓苍舱',
'cao': '草操曹槽糙',
'ce': '侧测策册厕',
'cen': '岑',
'ceng':'层曾蹭',
'cha': '查差茶插叉察刹诧',
'chai':'拆柴差钗',
'chan':'产缠颤馋禅蝉',
'chang':'长常场厂唱昌畅肠尝偿',
'chao':'超朝潮吵炒抄钞巢',
'che': '车扯撤彻澈',
'chen':'陈沉晨臣尘趁衬辰',
'cheng':'成城程称承诚乘盛橙惩',
'chi': '吃持池迟赤尺斥驰齿翅',
'chong':'冲重虫充宠崇憧',
'chou':'抽愁丑臭筹仇酬绸',
'chu': '出处初除楚厨触储础畜',
'chua': '欻',
'chuai':'揣踹',
'chuan':'传船穿川串喘',
'chuang':'创窗床闯疮',
'chui':'吹垂锤炊',
'chun':'春纯唇蠢淳',
'chuo':'戳绰辍',
'ci': '次此词刺辞瓷慈磁雌',
'cong':'从聪葱囱匆',
'cou': '凑',
'cu': '粗促醋簇卒',
'cuan':'窜窜蹿',
'cui': '催脆翠摧崔粹',
'cun': '存村寸蹲',
'cuo': '错措搓挫磋',
# ---- da ----
'da': '大打达答搭沓嗒',
'dai': '带代待戴袋呆逮贷岱',
'dan': '但担单弹蛋淡胆旦耽诞',
'dang':'当挡党荡档挡',
'dao': '到道倒刀岛导盗稻悼',
'de': '的得地德',
'deng':'等灯登邓凳瞪',
'di': '地第低底敌抵滴弟帝递',
'dian':'点电店典殿掂惦奠',
'diao':'调掉钓雕叼吊',
'die': '跌爹叠碟蝶迭',
'ding':'定顶丁订钉盯鼎',
'diu': '丢',
'dong':'动东懂冬洞冻董栋',
'dou': '都斗豆逗兜陡抖',
'du': '度读独毒肚堵赌渡杜',
'duan':'段断短端锻',
'dui': '对队堆兑',
'dun': '顿吨蹲盾钝敦',
'duo': '多朵夺躲堕舵跺',
# ---- e ----
'e': '饿恶额鹅俄蛾遏鄂',
'en': '恩摁',
'er': '而二儿耳尔饵洱',
# ---- fa ----
'fa': '发法罚伐乏阀筏',
'fan': '反饭犯翻凡烦返泛范繁',
'fang':'方放房防访仿坊纺',
'fei': '非飞费肥废匪肺菲',
'fen': '分粉份奋愤纷芬坟焚',
'feng':'风封峰丰疯逢缝蜂凤',
'fo': '佛',
'fou': '否',
'fu': '副复福扶服府负富付妇父符附腐',
# ---- ga ----
'ga': '嘎尬噶',
'gai': '该改盖概钙溉',
'gan': '干感敢赶甘杆肝竿',
'gang':'刚钢港岗缸杠',
'gao': '高告搞稿糕膏',
'ge': '个各歌格哥隔割阁胳',
'gei': '给',
'gen': '跟根跟',
'geng':'更耕颈梗耿',
'gong':'公共工功攻供宫拱贡恭',
'gou': '够狗购勾沟构钩',
'gu': '故古顾股骨鼓谷姑孤估',
'gua': '挂瓜刮寡卦',
'guai':'怪拐乖',
'guan':'关管观官冠馆惯罐',
'guang':'光广逛',
'gui': '贵归鬼规柜桂跪诡',
'gun': '滚棍',
'guo': '国过果锅裹郭',
# ---- ha ----
'ha': '哈蛤',
'hai': '还海害孩嗨骇',
'han': '含喊汉汗寒韩罕憾',
'hang':'行航夯杭',
'hao': '好号浩豪毫郝耗昊',
'he': '和合何河喝盒禾鹤贺荷',
'hei': '黑嘿',
'hen': '很恨狠痕',
'heng':'横恒衡哼',
'hong':'红洪宏虹烘鸿',
'hou': '后候厚猴喉吼',
'hu': '湖户呼胡虎糊互忽壶糊',
'hua': '话花化画华滑划桦',
'huai':'坏怀淮',
'huan':'还换环欢缓幻患唤',
'huang':'黄慌皇荒晃煌幌',
'hui': '会回挥灰汇毁惠慧辉恢悔',
'hun': '混昏婚魂浑',
'huo': '或火活货获伙惑霍',
# ---- ji ----
'ji': '及机级极即急集计记既济季几给基激肌迹际剂',
'jia': '家加价假架甲佳嘉嫁夹',
'jian':'见件间建减检简坚健剑渐尖肩荐监',
'jiang':'将讲江奖降酱姜僵',
'jiao':'叫教交角觉较脚焦骄娇郊浇',
'jie': '界接解节姐借街杰皆截戒届阶',
'jin': '进近金今紧仅尽禁劲津筋',
'jing':'经精京静境警惊景镜竟竞睛晶',
'jiong':'窘炯',
'jiu': '就九久酒旧救究舅揪',
'ju': '据居具局举句巨距聚拒剧',
'juan':'卷捐娟倦眷',
'jue': '觉决绝角掘爵倔嚼',
'jun': '军均君俊菌骏',
# ---- ka ----
'ka': '卡咖喀',
'kai': '开凯慨楷',
'kan': '看砍刊堪坎勘',
'kang':'抗康炕扛慷',
'kao': '考靠烤拷',
'ke': '可课科克客刻渴棵颗咳',
'kei': '剋',
'ken': '肯垦啃恳',
'keng':'坑吭',
'kong':'空控孔恐',
'kou': '口扣抠寇',
'ku': '苦哭库裤酷枯窟',
'kua': '跨夸垮挎',
'kuai':'快块筷会脍',
'kuan':'宽款',
'kuang':'况狂矿框筐旷',
'kui': '亏溃愧窥葵',
'kun': '困昆捆坤',
'kuo': '扩阔括廓',
# ---- la ----
'la': '拉啦辣蜡腊喇',
'lai': '来莱赖',
'lan': '蓝栏拦懒烂览兰篮',
'lang':'浪狼朗郎',
'lao': '老劳牢捞唠姥',
'le': '了乐勒',
'lei': '类累泪雷蕾垒',
'leng':'冷愣棱',
'li': '里力理立利李离例历丽励粒厉礼',
'lia': '俩',
'lian':'连脸练联恋链帘莲',
'liang':'量两亮辆凉粮良梁',
'liao':'了料聊疗辽廖',
'lie': '列烈裂猎劣',
'lin': '林临邻淋琳磷',
'ling':'令领另零灵铃岭凌',
'liu': '六流留刘柳溜瘤',
'long':'龙隆笼聋拢垄',
'lou': '楼漏搂陋',
'lu': '路陆录露鲁卢炉鹿炉',
'luan':'乱卵峦',
'lun': '论轮伦沦',
'lue': '略掠',
'luo': '落罗洛络骆裸',
'lv': '率律绿虑旅驴吕铝',
'lve': '略掠',
# ---- ma ----
'ma': '吗妈马麻骂码嘛玛',
'mai': '买卖麦埋迈脉',
'man': '满慢蛮瞒漫曼',
'mang':'忙茫盲芒莽',
'mao': '毛猫冒帽茂矛贸锚',
'me': '么',
'mei': '没每美妹梅眉媒霉',
'men': '们门闷萌',
'meng':'梦猛蒙盟萌',
'mi': '米密迷秘蜜谜觅',
'mian':'面免棉眠缅勉',
'miao':'秒妙苗描庙瞄',
'mie': '灭蔑',
'min': '民敏闽皿',
'ming':'明名命鸣铭茗',
'miu': '谬',
'mo': '摸模磨末莫默墨陌摩',
'mou': '某谋牟',
'mu': '目母木幕牧慕墓穆亩',
# ---- na ----
'na': '那拿哪纳娜呐',
'nai': '奶乃耐奈',
'nan': '难南男楠',
'nang':'囊',
'nao': '脑闹恼挠',
'ne': '呢',
'nei': '内馁',
'nen': '嫩',
'neng':'能',
'ni': '你呢泥尼妮霓逆拟腻匿',
'nian':'年念粘捻撵',
'niang':'娘酿',
'niao':'鸟尿',
'nie': '捏聂孽',
'nin': '您',
'ning':'宁凝拧',
'niu': '牛扭纽妞',
'nong':'弄农浓脓',
'nou': '耨',
'nu': '怒努奴',
'nuan':'暖',
'nuo': '诺挪懦',
'nv': '女',
'nve': '虐疟',
# ---- o ----
'o': '哦噢',
'ou': '欧偶呕殴藕',
# ---- pa ----
'pa': '怕爬趴帕啪',
'pai': '排派拍牌徘湃',
'pan': '判盘盼攀畔叛',
'pang':'旁胖庞乓',
'pao': '跑炮泡抛袍',
'pei': '陪配佩赔培',
'pen': '喷盆',
'peng':'朋碰捧棚蓬鹏',
'pi': '皮批脾披疲劈匹屁',
'pian':'片篇偏骗',
'piao':'票飘漂瓢',
'pie': '撇瞥',
'pin': '品拼贫频聘',
'ping':'平评凭瓶苹萍乒',
'po': '破坡婆迫颇魄',
'pou': '剖',
'pu': '铺普朴谱浦葡仆',
# ---- qi ----
'qi': '起期其气七齐器汽奇乞旗棋',
'qia': '恰掐洽',
'qian':'前钱千签浅欠牵铅迁谦',
'qiang':'强抢墙枪腔',
'qiao':'桥瞧敲俏巧悄壳',
'qie': '且切窃茄',
'qin': '亲琴勤侵秦',
'qing':'请清轻青情晴顷倾',
'qiong':'穷琼',
'qiu': '求球秋丘邱',
'qu': '去取区曲娶趣趋渠',
'quan':'全权圈劝拳泉犬',
'que': '却确缺雀鹊',
'qun': '群裙',
# ---- ran ----
'ran': '然染燃',
'rang':'让嚷壤',
'rao': '绕饶扰',
're': '热惹',
'ren': '人认任仁忍韧刃',
'reng':'仍扔',
'ri': '日',
'rong':'容荣融绒溶',
'rou': '肉柔揉',
'ru': '如入辱儒乳汝',
'rua': '挼',
'ruan':'软',
'rui': '瑞锐',
'run': '润闰',
'ruo': '若弱',
# ---- sa ----
'sa': '撒洒萨',
'sai': '赛塞腮',
'san': '三散伞',
'sang':'桑嗓丧',
'sao': '扫嫂骚',
'se': '色涩瑟',
'sen': '森',
'seng':'僧',
'sha': '沙杀傻纱啥刹煞',
'shai':'晒筛',
'shan':'山善闪衫删扇陕',
'shang':'上商伤尚赏裳',
'shao':'少烧稍绍捎勺',
'she': '社设舍射蛇摄涉奢',
'shen':'身深神甚伸什申慎渗',
'sheng':'生声省胜圣升盛剩',
'shi': '是事时十式市师世史示使士失视实识施',
'shou':'手受收首守售寿授瘦',
'shu': '书数术树输属舒叔殊疏蔬',
'shua':'刷耍',
'shuai':'帅摔甩衰',
'shuan':'栓拴',
'shuang':'双爽',
'shui':'水睡税',
'shun':'顺瞬',
'shuo':'说硕烁',
'si': '四死思斯司私似撕寺饲',
'song':'送松宋诵颂',
'sou': '搜艘嗽',
'su': '素速苏诉宿俗塑肃',
'suan':'算酸蒜',
'sui': '岁随虽碎遂穗',
'sun': '孙损笋',
'suo': '所锁索缩嗦',
# ---- ta ----
'ta': '他她它塔踏塌拓',
'tai': '太台抬态泰苔',
'tan': '谈弹叹探贪摊滩瘫',
'tang':'堂糖汤唐躺烫塘',
'tao': '讨套逃掏桃掏',
'te': '特',
'teng':'疼腾藤',
'ti': '体题提替踢蹄剃',
'tian':'天田甜填添',
'tiao':'条跳挑调',
'tie': '铁贴帖',
'ting':'听停厅挺庭亭艇',
'tong':'同通痛统童筒铜桶',
'tou': '头投偷透',
'tu': '图土突途徒吐兔涂',
'tuan':'团',
'tui': '推退腿褪',
'tun': '吞屯臀',
'tuo': '托脱拖妥驼椭',
# ---- w ----
'wa': '挖瓦蛙娃袜',
'wai': '外歪',
'wan': '完万晚玩弯碗湾挽',
'wang':'王往望网忘旺亡',
'wei': '为位未卫维微围委谓违唯尾',
'wen': '问文闻温稳吻纹',
'weng':'翁嗡瓮',
'wo': '我握窝卧沃',
'wu': '无五物误务武舞午污伍',
# ---- xi ----
'xi': '系西习戏喜细吸希洗悉溪膝席惜',
'xia': '下夏吓虾峡狭暇霞',
'xian':'先现线县显险鲜闲限陷献',
'xiang':'想向相像响项香乡箱详',
'xiao':'小笑校晓消销肖效萧',
'xie': '写些鞋谢协血携斜泄',
'xin': '新心信辛欣薪芯',
'xing':'行性形星兴型醒幸姓',
'xiong':'雄胸凶兄熊',
'xiu': '修秀休羞锈袖嗅',
'xu': '需须许序续徐虚绪蓄',
'xuan':'选宣旋悬玄轩',
'xue': '学雪血穴靴',
'xun': '寻讯训迅询巡循旬',
# ---- ya ----
'ya': '压呀牙亚讶雅鸦鸭',
'yan': '眼言演烟严沿研盐验颜宴艳延',
'yang':'样养洋阳羊央仰扬',
'yao': '要药摇腰咬耀邀遥谣',
'ye': '也业夜叶爷野冶页',
'yi': '一已以意义医衣依易忆疑谊益议',
'yin': '因音引银印隐饮阴姻',
'ying':'应英影营迎硬赢映鹰',
'yo': '哟',
'yong':'用永拥勇涌泳庸',
'you':'有又由右油游友优幼忧尤呦',
'yu': '于与语雨鱼玉育余遇欲愈预域娱',
'yuan':'元原员院远愿圆园源冤缘',
'yue': '月越约阅跃悦岳',
'yun': '云运允匀韵孕',
# ---- za ----
'za': '杂砸咋',
'zai': '在再载咱灾宰',
'zan': '咱赞暂攒',
'zang':'藏脏葬',
'zao': '早造遭糟澡灶燥躁',
'ze': '则责择泽',
'zei': '贼',
'zen': '怎',
'zeng':'增赠曾憎',
'zha': '炸查扎眨乍榨渣',
'zhai':'宅摘窄债斋',
'zhan':'战站占展沾粘斩',
'zhang':'张章长掌涨帐障丈',
'zhao':'找照招着朝召兆',
'zhe': '这者着折哲浙',
'zhen':'真阵镇针震振诊枕',
'zheng':'正整证政争征挣睁郑',
'zhi': '只之至制知指直纸志治支止职织质执',
'zhong':'中种重众忠终钟仲',
'zhou':'周州洲舟昼皱骤轴',
'zhu': '主住注助著朱猪竹煮筑祝驻',
'zhua':'抓',
'zhuai':'拽',
'zhuan':'转专赚传砖',
'zhuang':'装状撞庄壮',
'zhui':'追坠缀',
'zhun':'准',
'zhuo':'桌捉着灼卓浊',
'zi': '子自字资紫仔兹姿咨',
'zong':'总宗纵综棕踪',
'zou': '走奏揍邹',
'zu': '组足祖族租阻卒',
'zuan':'钻攥',
'zui': '最嘴罪醉',
'zun': '尊遵',
'zuo': '做作坐座左昨',
}
# 词/短语表:sp=整词拼音串(去声调),hz=UTF-8 词条,w=字频权重(越大越常用)。
# 覆盖搜索框/文件名/输入框常见词;多音节词让「短语精确命中 + 前缀整词提示」生效。
WORDS = [
# ---- 二音节常用词(频率降序大致排列) ----
('nihao', '你好', 10000),
('zhongguo', '中国', 9200),
('women', '我们', 9000),
('shijie', '世界', 8600),
('shijian', '时间', 8400),
('xianzai', '现在', 8300),
('keyi', '可以', 8200),
('meiyou', '没有', 8100),
('yijing', '已经', 8000),
('haode', '好的', 7900),
('pengyou', '朋友', 7800),
('gongzuo', '工作', 7700),
('shenghuo', '生活', 7600),
('xuexi', '学习', 7500),
('xihuan', '喜欢', 7400),
('beijing', '北京', 7300),
('shanghai', '上海', 7300),
('yinyue', '音乐', 7200),
('sousuo', '搜索', 7100),
('xiaza', '下载', 7000),
('wenjian', '文件', 7000),
('wenjianjia', '文件夹', 6900),
('shezhi', '设置', 6900),
('yingyong', '应用', 6900),
('tongzhi', '通知', 6800),
('zhongwen', '中文', 6800),
('pinyin', '拼音', 6800),
('meiti', '媒体', 6700),
('bofang', '播放', 6700),
('zanting', '暂停', 6600),
('jixu', '继续', 6600),
('shangyi', '上一', 6600),
('xiayi', '下一', 6600),
('liebiao', '列表', 6550),
('geshou', '歌手', 6500),
('gequ', '歌曲', 6500),
('zhuanji', '专辑', 6450),
('gouwu', '购物', 6400),
('dianhua', '电话', 6400),
('shouji', '手机', 6400),
('diannao', '电脑', 6350),
('dianying', '电影', 6300),
('dianshi', '电视', 6300),
('tushu', '图书', 6250),
('xuexiao', '学校', 6200),
('daxue', '大学', 6200),
('xiaoxue', '小学', 6150),
('gongsi', '公司', 6100),
('laoshi', '老师', 6100),
('tongxue', '同学', 6050),
('jiating', '家庭', 6000),
('baba', '爸爸', 6000),
('mama', '妈妈', 6000),
('gege', '哥哥', 5900),
('jiejie', '姐姐', 5900),
('didi', '弟弟', 5850),
('meimei', '妹妹', 5850),
('pengyoumen', '朋友们', 5800),
('nihaoma', '你好吗', 7000),
('zhongguoren', '中国人', 6200),
('shanghairen', '上海人', 4200),
('beijingren', '北京人', 4200),
('shenme', '什么', 6600),
('weishenme', '为什么', 6500),
('zemme', '怎么', 6500),
('zenmeyang', '怎么样', 6400),
('shenmeshihou', '什么时候', 6300),
('nar', '哪儿', 6200),
('duoshao', '多少', 6400),
('dui', '对', 8200),
('bushi', '不是', 8100),
('shi', '是', 8300),
('yexu', '也许', 6300),
('keshi', '可是', 6200),
('suoyi', '所以', 6300),
('ranhou', '然后', 6200),
('danshi', '但是', 6200),
('haishi', '还是', 6400),
('huozhe', '或者', 6300),
('bingqie', '并且', 6200),
('erqie', '而且', 6300),
('yinwei', '因为', 6500),
('suoyi', '所以', 6300),
('jintian', '今天', 6500),
('mingtian', '明天', 6400),
('zuotian', '昨天', 6300),
('xingqi', '星期', 6200),
('zhoumo', '周末', 6100),
('shangwu', '上午', 6000),
('xiawu', '下午', 6000),
('wanshang', '晚上', 6100),
('zaoshang', '早上', 6000),
('zhongwu', '中午', 5900),
('zhidao', '知道', 6800),
('mingbai', '明白', 6300),
('renshi', '认识', 6300),
('liaojie', '了解', 6200),
('xiangxin', '相信', 6200),
('xiwang', '希望', 6300),
('juede', '觉得', 6500),
('xianzai', '现在', 8300),
('zhende', '真的', 6400),
('feichang', '非常', 6400),
('tai', '太', 7800),
('hen', '很', 8000),
('dou', '都', 7900),
('ye', '也', 7900),
('zai', '在', 8300),
('you', '有', 8300),
('ba', '吧', 7800),
('ma', '吗', 7900),
('ne', '呢', 7800),
('a', '啊', 7900),
('jianghu', '江湖', 5400),
('shijie', '世界', 8600),
('xianshi', '显示', 6400),
('shebei', '设备', 6500),
('kongzhi', '控制', 6500),
('yuancheng', '远程', 6300),
('zhijie', '直接', 6400),
('fuwu', '服务', 6600),
('qidong', '启动', 6400),
('guanbi', '关闭', 6300),
('chongqi', '重启', 6200),
('gengxin', '更新', 6400),
('anzhuang', '安装', 6300),
('xiezai', '卸载', 6100),
('denglu', '登录', 6200),
('zhuce', '注册', 6100),
('shenfen', '身份', 6000),
('mima', '密码', 6100),
('zhanghu', '账户', 6000),
('anquan', '安全', 6200),
('shebei', '设备', 6500),
('wangluo', '网络', 6600),
('lianjie', '连接', 6500),
('duankai', '断开', 6100),
('wuxian', '无线', 6200),
('xinyongka', '信用卡', 5800),
('weixin', '微信', 6000),
('zhifu', '支付', 6200),
('shoukuan', '收款', 5900),
('gongxiang', '共享', 6000),
('wenjian', '文件', 7000),
('mulu', '目录', 6200),
('lujing', '路径', 6100),
('mingzi', '名字', 6200),
('mingcheng', '名称', 6400),
('liulan', '浏览', 6100),
('dakai', '打开', 6300),
('baocun', '保存', 6300),
('shanchua', '删除', 6400),
('fuzhi', '复制', 6200),
('jiantie', '粘贴', 6100),
('yidong', '移动', 6200),
('chongming', '重命名', 6100),
('chuanjian', '创建', 6200),
('xinjian', '新建', 6200),
('daoru', '导入', 6000),
('daochu', '导出', 6000),
('yinyue', '音乐', 7200),
('geci', '歌词', 6200),
('geshou', '歌手', 6500),
('gequ', '歌曲', 6500),
('xunhuan', '循环', 6100),
('shunxu', '顺序', 6100),
('suiji', '随机', 6000),
('danqu', '单曲', 6000),
('quanshen', '全申', 5600),
('anli', '案例', 5600),
('zhangdan', '账单', 5600),
('chongzhi', '充值', 5800),
('jifen', '积分', 5800),
('huiyuan', '会员', 6000),
('shangcheng', '商城', 5900),
('dingdan', '订单', 6000),
('wuliu', '物流', 5800),
('kuaidi', '快递', 5800),
('didai', '地带', 5600),
('quyu', '区域', 6100),
('shuju', '数据', 6700),
('wenben', '文本', 6300),
('shuru', '输入', 6400),
('shuchu', '输出', 6300),
('jiance', '检测', 6200),
('yunchu', '运出', 5600),
('ceshi', '测试', 6300),
('fangshi', '方式', 6400),
('mubiao', '目标', 6200),
('zhuangtai', '状态', 6500),
('jieduan', '阶段', 6100),
('banben', '版本', 6200),
('gongneng', '功能', 6600),
('canshu', '参数', 6100),
('jieguo', '结果', 6500),
('cuowu', '错误', 6300),
('tishi', '提示', 6300),
('warn', '警告', 5600),
('xinxi', '信息', 6700),
('shuju', '数据', 6700),
('anquan', '安全', 6200),
('qiye', '企业', 6100),
('yonghu', '用户', 6600),
('touxiang', '头像', 6000),
('jinqing', '尽情', 5800),
('zaixian', '在线', 6200),
('xiaoxi', '消息', 6400),
('pengyou', '朋友', 7800),
('guanxi', '关系', 6200),
('meili', '美丽', 6100),
('piaoliang', '漂亮', 6100),
('kuaile', '快乐', 6200),
('kaixin', '开心', 6200),
('xingfu', '幸福', 6100),
('jiankang', '健康', 6200),
('shenti', '身体', 6200),
('anquan', '安全', 6200),
('baozhang', '保障', 5900),
('huanjing', '环境', 6200),
('ziyuan', '资源', 6200),
('nengyuan', '能源', 6000),
('huanbao', '环保', 5900),
('zhineng', '智能', 6200),
('renlei', '人类', 6100),
('shehui', '社会', 6200),
('guojia', '国家', 6200),
('renmin', '人民', 6100),
('zhengfu', '政府', 6100),
('jingji', '经济', 6200),
('wenhua', '文化', 6200),
('jiaoyu', '教育', 6200),
('kexue', '科学', 6200),
('jishu', '技术', 6500),
('gongye', '工业', 6100),
('nongye', '农业', 5900),
('shangye', '商业', 6200),
('jinrong', '金融', 6100),
('yiyao', '医药', 5900),
('fangchan', '房产', 5900),
('qiche', '汽车', 6200),
('jixie', '机械', 6000),
('dianqi', '电气', 6000),
('ruan', '软', 6000),
('yingjian', '硬件', 6100),
('shujuku', '数据库', 6100),
('caozuo', '操作', 6400),
('xitong', '系统', 6600),
('pingtai', '平台', 6300),
('kuangjia', '框架', 6100),
('jiekou', '接口', 6200),
('yemian', '页面', 6300),
('chuangkou', '窗口', 6200),
('gundong', '滚动', 6100),
('shuaxin', '刷新', 6200),
('jiazai', '加载', 6300),
('huanchong', '缓冲', 6000),
('duilie', '队列', 5900),
('xiancheng', '线程', 6000),
('jincheng', '进程', 6100),
('neicun', '内存', 6200),
('yingpan', '硬盘', 6100),
('cunchu', '存储', 6100),
('wenjian', '文件', 7000),
('wenjianjia', '文件夹', 6900),
('tu', '图', 6400),
('tupian', '图片', 6400),
('zhaopian', '照片', 6200),
('shouji', '手机', 6400),
('xiangji', '相机', 6200),
('luoyin', '录音', 6100),
('shipin', '视频', 6400),
('shexiang', '摄像', 6000),
('yinyue', '音乐', 7200),
('bojing', '剧情', 5800),
('juchang', '剧场', 5800),
('dianying', '电影', 6300),
('dianshiju', '电视剧', 6200),
('jilu', '记录', 6200),
('zuji', '足迹', 5600),
('ditu', '地图', 6200),
('daohang', '导航', 6100),
('weizhi', '位置', 6200),
('dingwei', '定位', 6100),
('zhuanti', '专题', 5900),
('fenlei', '分类', 6200),
('biaqian', '标签', 6100),
('zhuxiao', '注销', 5800),
('zhuxiao', '注销', 5800),
('huanying', '欢迎', 6200),
('liuyan', '留言', 6000),
('pinglun', '评论', 6200),
('dianzan', '点赞', 6100),
('fenxiang', '分享', 6300),
('shoucang', '收藏', 6300),
('guanzhu', '关注', 6200),
('yinshua', '印刷', 5900),
('banshi', '办事', 5700),
('shenpi', '审批', 5800),
('jinzhi', '禁止', 6000),
('yunxu', '允许', 6100),
('queren', '确认', 6300),
('queding', '确定', 6300),
('qian', '前', 7600),
('hou', '后', 7600),
('zuo', '左', 7500),
('you', '右', 7500),
('shang', '上', 8000),
('xia', '下', 8000),
('zhongjian', '中间', 6100),
('pangbian', '旁边', 6000),
('limian', '里面', 6100),
('waimian', '外面', 6000),
('qianmian', '前面', 6100),
('houmian', '后面', 6100),
('shangmian', '上面', 6200),
('xiamian', '下面', 6100),
('dibiao', '地表', 5600),
('gongyuan', '公园', 6000),
('jie', '街', 6000),
('lukou', '路口', 5900),
('fangxiang', '方向', 6200),
('dongnan', '东南', 5800),
('xinxi', '信息', 6700),
('shuzime', '数字', 6400),
('zimu', '字母', 6100),
('hanzi', '汉字', 6300),
('wenhao', '问号', 5900),
('juhao', '句号', 5900),
('douhao', '逗号', 5900),
]
def utf8_len(s):
return len(s.encode('utf-8'))
def main():
lines = []
lines.append('/* 自动生成 by gen_pinyin_lex.py —— 中文拼音→候选字/词 频序表(去声调,UTF-8,.rodata)。*/')
lines.append('/* 词库规模:音节 %d 条 + 词语 %d 条;候选字全部落在 GB2312 常用字集(common/gbk tier)。 */' % (len(SYL), len(WORDS)))
lines.append('#ifndef MEOW_PINYIN_LEX_H')
lines.append('#define MEOW_PINYIN_LEX_H')
lines.append('#include <stdint.h>')
lines.append('')
lines.append('typedef struct { const char *py; const char *hz; } py_syl;')
lines.append('typedef struct { const char *sp; const char *hz; int w; } py_word;')
lines.append('')
lines.append('#define SYL_N %d' % len(SYL))
lines.append('static const py_syl SYL[SYL_N] = {')
for k, v in SYL.items():
lines.append(' {"%s", "%s"},' % (k, v))
lines.append('};')
lines.append('')
lines.append('#define WORD_N %d' % len(WORDS))
lines.append('static const py_word WORDS[WORD_N] = {')
for sp, hz, w in WORDS:
lines.append(' {"%s","%s",%d},' % (sp, hz, w))
lines.append('};')
lines.append('')
lines.append('#endif /* MEOW_PINYIN_LEX_H */')
with open('meow_pinyin_lex.h', 'w', encoding='utf-8') as f:
f.write('\n'.join(lines) + '\n')
# ---- 体积账(与设计稿 §2.3 同口径:结构体 + 字符串,含 NUL)----
sz_syl_struct = 8 * len(SYL) # py ptr(4) + hz ptr(4)
sz_syl_str = sum(utf8_len(k) + 1 + utf8_len(v) + 1 for k, v in SYL.items())
sz_word_struct = 12 * len(WORDS) # sp ptr(4) + hz ptr(4) + w(4)
sz_word_str = sum(utf8_len(sp) + 1 + utf8_len(hz) + 1 for sp, hz, w in WORDS)
total = sz_syl_struct + sz_syl_str + sz_word_struct + sz_word_str
print('--- 词库体积账 ---')
print('音节表: %d 条, 结构体 %d B + 字符串 %d B = %d B' % (len(SYL), sz_syl_struct, sz_syl_str, sz_syl_struct + sz_syl_str))
print('词语表: %d 条, 结构体 %d B + 字符串 %d B = %d B' % (len(WORDS), sz_word_struct, sz_word_str, sz_word_struct + sz_word_str))
print('合计 .rodata 估算: %d B (%.1f KiB)' % (total, total / 1024.0))
print('--- 出错/重复检测 ---')
# 词表允许同键不同条目(如 'wei' 等),这里仅打印去重提示
print('生成完成 meow_pinyin_lex.h')
if __name__ == '__main__':
main()
+156
View File
@@ -0,0 +1,156 @@
/*
* meow_pinyin.c — 中文拼音Ⅰ级候选引擎(静态表驱动,纯 C,零堆)。
*
* 词库在 meow_pinyin_lex.h(gen_pinyin_lex.py 离线生成,.rodata)。
* 供 meow_ui_ime.c(MEOW_IME_PINYIN 模式)调用;SDK 集成时本 TU 进 meow_ui.a。
*
* 查询 = 当前组合拼音串(去声调小写,如 "nihao")→ 候选列表(UTF-8)。
* 排序规则(与设计稿 §4.4 / /tmp/pyim engine.c 一致):
* 1) 短语精确命中(sp==输入,如 nihao→你好)最优先;
* 2) 音节切分组合(ni+hao→你好/你号/呢好…)按「每音节候选字序号之和」升序;
* 3) 前缀整词提示(sp 以输入为前缀,如 ni→你好/你好吗)排最后(边打边出整词)。
*
* 设计约束:无 stdio(拉体积),无动态分配(MIPS32/14MB 意识)。切分 DFS + 组合
* 上限常量封死(MAXSPLIT/MAXCOMB),防长组合串爆炸。词表按 sp 前缀匹配(线性回溯)。
*/
#include "meow_pinyin_lex.h"
#include <string.h>
#include <stdlib.h>
#define MAXCAND 32 /* 每轮候选上限(与 IME 候选数组对齐) */
#define MAXSPLIT 64 /* 切分枚举上限 */
#define MAXSYL 8 /* 一次组合的音节数上限 */
#define TK 4 /* 每音节取前 K 个字符再组合(限组合数) */
#define MAXCOMB 120 /* 笛卡尔组合 guard(防爆炸) */
typedef struct { char hz[64]; int score; int src; int seg; } cand; /* src: 0=短语 1=切分组合 2=前缀词提示; seg=切分音节数 */
/* 定长拷贝一个 NUL 结尾字符串到 d[64];用 memcpy 规避 -Wstringop-truncation(来源可能超 63)。 */
static void set_hz(char *d, const char *s)
{
size_t n = strlen(s);
if (n > 63) n = 63;
memcpy(d, s, n);
d[n] = 0;
}
/* 收集某音节候选字(UTF-8 单字),填入 chars[][4](UTF-8 ≤3B + NUL)。返回字数。 */
static int syl_chars(int s, char chars[][4], int maxk)
{
const char *h = SYL[s].hz;
int n = 0, i = 0;
while (h[i] && n < maxk) {
int l = 1;
if ((unsigned char)h[i] >= 0xF0) l = 4;
else if ((unsigned char)h[i] >= 0xE0) l = 3;
else if ((unsigned char)h[i] >= 0xC0) l = 2;
if (n < maxk) { memcpy(chars[n], h + i, (size_t)l); chars[n][l] = 0; n++; }
i += l;
}
return n;
}
/* 枚举 py 全部合法音节切分(DFS)。g_split[i]=切分路径(音节下标数组)。 */
static int g_split[MAXSPLIT][MAXSYL];
static int g_splitlen[MAXSPLIT];
static int g_ns;
static void dfs_split(const char *py, int i, int path[], int plen)
{
if (g_ns >= MAXSPLIT) return;
if (py[i] == 0) {
if (plen == 0) return;
memcpy(g_split[g_ns], path, (size_t)plen * sizeof(int));
g_splitlen[g_ns] = plen;
g_ns++;
return;
}
for (int s = 0; s < SYL_N && g_ns < MAXSPLIT; s++) {
int l = (int)strlen(SYL[s].py);
if (strncmp(py + i, SYL[s].py, (size_t)l) == 0) {
path[plen] = s;
dfs_split(py, i + l, path, plen + 1);
}
}
}
static int cmp_cand(const void *a, const void *b)
{
const cand *x = (const cand *)a, *y = (const cand *)b;
if (x->score != y->score) return x->score - y->score; /* 升序:score 越小越常用 */
if (x->seg != y->seg) return x->seg - y->seg; /* score 相同:音节数越少越优先(完整音节读法) */
return x->src - y->src;
}
static void dedupe(cand *c, int *n)
{
for (int i = 0; i < *n; i++)
for (int j = i + 1; j < *n; j++)
if (strcmp(c[i].hz, c[j].hz) == 0) { c[j].score = 99999; }
int w = 0;
for (int i = 0; i < *n; i++) if (c[i].score != 99999) c[w++] = c[i];
*n = w;
}
/* 主入口:输入当前拼音组合串(去声调),输出候选列表(UTF-8),返回个数。
* out 至少 maxout 项,每项定长 64B(拷贝,非持久)。 */
int py_predict(const char *py, char out[][64], int maxout)
{
cand cs[MAXCAND]; int nc = 0;
const int len = (int)strlen(py);
if (len == 0 || maxout <= 0) return 0;
if (maxout > MAXCAND) maxout = MAXCAND;
/* 1) 短语精确命中(band 0:最优先)。词表可能多条同拼(如 'shi'/'dui' 单字词),按序给 rank。 */
int exact_rank = 0;
for (int i = 0; i < WORD_N && nc < MAXCAND; i++) {
if (strcmp(WORDS[i].sp, py) == 0) {
set_hz(cs[nc].hz, WORDS[i].hz);
cs[nc].score = exact_rank++;
cs[nc].src = 0; cs[nc].seg = 1; nc++;
}
}
/* 2) 前缀词提示(band 2:边打边出整词,排在单字组合之后) */
int pre_rank = 0;
for (int i = 0; i < WORD_N && nc < MAXCAND; i++) {
int wl = (int)strlen(WORDS[i].sp);
if (wl > len && strncmp(WORDS[i].sp, py, (size_t)len) == 0) {
set_hz(cs[nc].hz, WORDS[i].hz);
cs[nc].score = 200 + pre_rank++;
cs[nc].src = 2; cs[nc].seg = 1; nc++;
}
}
/* 3) 音节切分组合 */
g_ns = 0;
{ int path[MAXSYL]; dfs_split(py, 0, path, 0); }
for (int s = 0; s < g_ns && nc < MAXCAND; s++) {
const int *seg = g_split[s];
int nseg = g_splitlen[s];
char cset[MAXSYL][TK][4];
int cnt[MAXSYL];
for (int k = 0; k < nseg; k++) cnt[k] = syl_chars(seg[k], cset[k], TK);
int iter[MAXSYL]; for (int k = 0; k < nseg; k++) iter[k] = 0;
int guard = 0;
while (guard++ < MAXCOMB && nc < MAXCAND) {
char buf[64]; buf[0] = 0; int score = 0;
for (int k = 0; k < nseg; k++) { strcat(buf, cset[k][iter[k]]); score += iter[k]; }
if (buf[0]) {
set_hz(cs[nc].hz, buf);
cs[nc].score = 100 + score; /* band 1:切分组合(sum of char index 越小越常用) */
cs[nc].src = 1; cs[nc].seg = nseg; nc++;
}
int k = nseg - 1;
while (k >= 0) {
iter[k]++;
if (iter[k] < cnt[k]) break;
iter[k] = 0; k--;
}
if (k < 0) break;
}
}
dedupe(cs, &nc);
qsort(cs, (size_t)nc, sizeof(cand), cmp_cand);
if (nc > maxout) nc = maxout;
for (int i = 0; i < nc; i++) { memcpy(out[i], cs[i].hz, 64); }
return nc;
}
+12
View File
@@ -0,0 +1,12 @@
/*
* meow_pinyin.h — 中文拼音候选引擎公开接口(meow_ui_ime.c 调用;实现在 meow_pinyin.c)。
* 词库(meow_pinyin_lex.h)与本接口均进 meow_ui.a。纯 C 静态表驱动,零堆、无 stdio。
*/
#ifndef MEOW_PINYIN_H
#define MEOW_PINYIN_H
/* 输入当前拼音组合串(去声调小写,如 "nihao"),输出候选列表(UTF-8,out 拷贝),返回条数。
* out 至少 maxout 项、每项 ≥64B。无候选返回 0。 */
int py_predict(const char *py, char out[][64], int maxout);
#endif /* MEOW_PINYIN_H */
+750
View File
@@ -0,0 +1,750 @@
/* 自动生成 by gen_pinyin_lex.py —— 中文拼音→候选字/词 频序表(去声调,UTF-8,.rodata)。*/
/* 词库规模:音节 403 条 + 词语 329 条;候选字全部落在 GB2312 常用字集(common/gbk tier)。 */
#ifndef MEOW_PINYIN_LEX_H
#define MEOW_PINYIN_LEX_H
#include <stdint.h>
typedef struct { const char *py; const char *hz; } py_syl;
typedef struct { const char *sp; const char *hz; int w; } py_word;
#define SYL_N 403
static const py_syl SYL[SYL_N] = {
{"a", "啊阿吖呵腌"},
{"ai", "爱哀挨唉矮碍癌艾呆埃"},
{"an", "安按暗案岸俺鞍庵"},
{"ang", "昂盎肮"},
{"ao", "奥懊傲熬凹澳敖袄"},
{"ba", "把八吧爸拔罢霸巴扒芭"},
{"bai", "白百摆败拜柏伯佰"},
{"ban", "办半班搬版板伴扮拌瓣"},
{"bang", "帮棒榜绑膀磅邦蚌"},
{"bao", "报保包宝抱暴薄爆饱堡"},
{"bei", "被北背倍杯备悲辈贝碑"},
{"ben", "本奔笨苯"},
{"beng", "蹦崩甭泵迸"},
{"bi", "比必笔闭毕鼻避彼壁弊"},
{"bian", "变边便遍辨编辩扁鞭"},
{"biao", "表标彪镖膘"},
{"bie", "别憋鳖瘪"},
{"bin", "宾滨彬濒斌"},
{"bing", "病并兵冰饼丙柄秉"},
{"bo", "波播拨薄博伯驳脖膊"},
{"bu", "不步部布补捕怖埠簿"},
{"ca", "擦嚓"},
{"cai", "才菜采财猜彩裁踩材"},
{"can", "参残餐灿惨蚕惭"},
{"cang", "藏仓苍舱"},
{"cao", "草操曹槽糙"},
{"ce", "侧测策册厕"},
{"cen", "岑"},
{"ceng", "层曾蹭"},
{"cha", "查差茶插叉察刹诧"},
{"chai", "拆柴差钗"},
{"chan", "产缠颤馋禅蝉"},
{"chang", "长常场厂唱昌畅肠尝偿"},
{"chao", "超朝潮吵炒抄钞巢"},
{"che", "车扯撤彻澈"},
{"chen", "陈沉晨臣尘趁衬辰"},
{"cheng", "成城程称承诚乘盛橙惩"},
{"chi", "吃持池迟赤尺斥驰齿翅"},
{"chong", "冲重虫充宠崇憧"},
{"chou", "抽愁丑臭筹仇酬绸"},
{"chu", "出处初除楚厨触储础畜"},
{"chua", "欻"},
{"chuai", "揣踹"},
{"chuan", "传船穿川串喘"},
{"chuang", "创窗床闯疮"},
{"chui", "吹垂锤炊"},
{"chun", "春纯唇蠢淳"},
{"chuo", "戳绰辍"},
{"ci", "次此词刺辞瓷慈磁雌"},
{"cong", "从聪葱囱匆"},
{"cou", "凑"},
{"cu", "粗促醋簇卒"},
{"cuan", "窜窜蹿"},
{"cui", "催脆翠摧崔粹"},
{"cun", "存村寸蹲"},
{"cuo", "错措搓挫磋"},
{"da", "大打达答搭沓嗒"},
{"dai", "带代待戴袋呆逮贷岱"},
{"dan", "但担单弹蛋淡胆旦耽诞"},
{"dang", "当挡党荡档挡"},
{"dao", "到道倒刀岛导盗稻悼"},
{"de", "的得地德"},
{"deng", "等灯登邓凳瞪"},
{"di", "地第低底敌抵滴弟帝递"},
{"dian", "点电店典殿掂惦奠"},
{"diao", "调掉钓雕叼吊"},
{"die", "跌爹叠碟蝶迭"},
{"ding", "定顶丁订钉盯鼎"},
{"diu", "丢"},
{"dong", "动东懂冬洞冻董栋"},
{"dou", "都斗豆逗兜陡抖"},
{"du", "度读独毒肚堵赌渡杜"},
{"duan", "段断短端锻"},
{"dui", "对队堆兑"},
{"dun", "顿吨蹲盾钝敦"},
{"duo", "多朵夺躲堕舵跺"},
{"e", "饿恶额鹅俄蛾遏鄂"},
{"en", "恩摁"},
{"er", "而二儿耳尔饵洱"},
{"fa", "发法罚伐乏阀筏"},
{"fan", "反饭犯翻凡烦返泛范繁"},
{"fang", "方放房防访仿坊纺"},
{"fei", "非飞费肥废匪肺菲"},
{"fen", "分粉份奋愤纷芬坟焚"},
{"feng", "风封峰丰疯逢缝蜂凤"},
{"fo", "佛"},
{"fou", "否"},
{"fu", "副复福扶服府负富付妇父符附腐"},
{"ga", "嘎尬噶"},
{"gai", "该改盖概钙溉"},
{"gan", "干感敢赶甘杆肝竿"},
{"gang", "刚钢港岗缸杠"},
{"gao", "高告搞稿糕膏"},
{"ge", "个各歌格哥隔割阁胳"},
{"gei", "给"},
{"gen", "跟根跟"},
{"geng", "更耕颈梗耿"},
{"gong", "公共工功攻供宫拱贡恭"},
{"gou", "够狗购勾沟构钩"},
{"gu", "故古顾股骨鼓谷姑孤估"},
{"gua", "挂瓜刮寡卦"},
{"guai", "怪拐乖"},
{"guan", "关管观官冠馆惯罐"},
{"guang", "光广逛"},
{"gui", "贵归鬼规柜桂跪诡"},
{"gun", "滚棍"},
{"guo", "国过果锅裹郭"},
{"ha", "哈蛤"},
{"hai", "还海害孩嗨骇"},
{"han", "含喊汉汗寒韩罕憾"},
{"hang", "行航夯杭"},
{"hao", "好号浩豪毫郝耗昊"},
{"he", "和合何河喝盒禾鹤贺荷"},
{"hei", "黑嘿"},
{"hen", "很恨狠痕"},
{"heng", "横恒衡哼"},
{"hong", "红洪宏虹烘鸿"},
{"hou", "后候厚猴喉吼"},
{"hu", "湖户呼胡虎糊互忽壶糊"},
{"hua", "话花化画华滑划桦"},
{"huai", "坏怀淮"},
{"huan", "还换环欢缓幻患唤"},
{"huang", "黄慌皇荒晃煌幌"},
{"hui", "会回挥灰汇毁惠慧辉恢悔"},
{"hun", "混昏婚魂浑"},
{"huo", "或火活货获伙惑霍"},
{"ji", "及机级极即急集计记既济季几给基激肌迹际剂"},
{"jia", "家加价假架甲佳嘉嫁夹"},
{"jian", "见件间建减检简坚健剑渐尖肩荐监"},
{"jiang", "将讲江奖降酱姜僵"},
{"jiao", "叫教交角觉较脚焦骄娇郊浇"},
{"jie", "界接解节姐借街杰皆截戒届阶"},
{"jin", "进近金今紧仅尽禁劲津筋"},
{"jing", "经精京静境警惊景镜竟竞睛晶"},
{"jiong", "窘炯"},
{"jiu", "就九久酒旧救究舅揪"},
{"ju", "据居具局举句巨距聚拒剧"},
{"juan", "卷捐娟倦眷"},
{"jue", "觉决绝角掘爵倔嚼"},
{"jun", "军均君俊菌骏"},
{"ka", "卡咖喀"},
{"kai", "开凯慨楷"},
{"kan", "看砍刊堪坎勘"},
{"kang", "抗康炕扛慷"},
{"kao", "考靠烤拷"},
{"ke", "可课科克客刻渴棵颗咳"},
{"kei", "剋"},
{"ken", "肯垦啃恳"},
{"keng", "坑吭"},
{"kong", "空控孔恐"},
{"kou", "口扣抠寇"},
{"ku", "苦哭库裤酷枯窟"},
{"kua", "跨夸垮挎"},
{"kuai", "快块筷会脍"},
{"kuan", "宽款"},
{"kuang", "况狂矿框筐旷"},
{"kui", "亏溃愧窥葵"},
{"kun", "困昆捆坤"},
{"kuo", "扩阔括廓"},
{"la", "拉啦辣蜡腊喇"},
{"lai", "来莱赖"},
{"lan", "蓝栏拦懒烂览兰篮"},
{"lang", "浪狼朗郎"},
{"lao", "老劳牢捞唠姥"},
{"le", "了乐勒"},
{"lei", "类累泪雷蕾垒"},
{"leng", "冷愣棱"},
{"li", "里力理立利李离例历丽励粒厉礼"},
{"lia", "俩"},
{"lian", "连脸练联恋链帘莲"},
{"liang", "量两亮辆凉粮良梁"},
{"liao", "了料聊疗辽廖"},
{"lie", "列烈裂猎劣"},
{"lin", "林临邻淋琳磷"},
{"ling", "令领另零灵铃岭凌"},
{"liu", "六流留刘柳溜瘤"},
{"long", "龙隆笼聋拢垄"},
{"lou", "楼漏搂陋"},
{"lu", "路陆录露鲁卢炉鹿炉"},
{"luan", "乱卵峦"},
{"lun", "论轮伦沦"},
{"lue", "略掠"},
{"luo", "落罗洛络骆裸"},
{"lv", "率律绿虑旅驴吕铝"},
{"lve", "略掠"},
{"ma", "吗妈马麻骂码嘛玛"},
{"mai", "买卖麦埋迈脉"},
{"man", "满慢蛮瞒漫曼"},
{"mang", "忙茫盲芒莽"},
{"mao", "毛猫冒帽茂矛贸锚"},
{"me", "么"},
{"mei", "没每美妹梅眉媒霉"},
{"men", "们门闷萌"},
{"meng", "梦猛蒙盟萌"},
{"mi", "米密迷秘蜜谜觅"},
{"mian", "面免棉眠缅勉"},
{"miao", "秒妙苗描庙瞄"},
{"mie", "灭蔑"},
{"min", "民敏闽皿"},
{"ming", "明名命鸣铭茗"},
{"miu", "谬"},
{"mo", "摸模磨末莫默墨陌摩"},
{"mou", "某谋牟"},
{"mu", "目母木幕牧慕墓穆亩"},
{"na", "那拿哪纳娜呐"},
{"nai", "奶乃耐奈"},
{"nan", "难南男楠"},
{"nang", "囊"},
{"nao", "脑闹恼挠"},
{"ne", "呢"},
{"nei", "内馁"},
{"nen", "嫩"},
{"neng", "能"},
{"ni", "你呢泥尼妮霓逆拟腻匿"},
{"nian", "年念粘捻撵"},
{"niang", "娘酿"},
{"niao", "鸟尿"},
{"nie", "捏聂孽"},
{"nin", "您"},
{"ning", "宁凝拧"},
{"niu", "牛扭纽妞"},
{"nong", "弄农浓脓"},
{"nou", "耨"},
{"nu", "怒努奴"},
{"nuan", "暖"},
{"nuo", "诺挪懦"},
{"nv", "女"},
{"nve", "虐疟"},
{"o", "哦噢"},
{"ou", "欧偶呕殴藕"},
{"pa", "怕爬趴帕啪"},
{"pai", "排派拍牌徘湃"},
{"pan", "判盘盼攀畔叛"},
{"pang", "旁胖庞乓"},
{"pao", "跑炮泡抛袍"},
{"pei", "陪配佩赔培"},
{"pen", "喷盆"},
{"peng", "朋碰捧棚蓬鹏"},
{"pi", "皮批脾披疲劈匹屁"},
{"pian", "片篇偏骗"},
{"piao", "票飘漂瓢"},
{"pie", "撇瞥"},
{"pin", "品拼贫频聘"},
{"ping", "平评凭瓶苹萍乒"},
{"po", "破坡婆迫颇魄"},
{"pou", "剖"},
{"pu", "铺普朴谱浦葡仆"},
{"qi", "起期其气七齐器汽奇乞旗棋"},
{"qia", "恰掐洽"},
{"qian", "前钱千签浅欠牵铅迁谦"},
{"qiang", "强抢墙枪腔"},
{"qiao", "桥瞧敲俏巧悄壳"},
{"qie", "且切窃茄"},
{"qin", "亲琴勤侵秦"},
{"qing", "请清轻青情晴顷倾"},
{"qiong", "穷琼"},
{"qiu", "求球秋丘邱"},
{"qu", "去取区曲娶趣趋渠"},
{"quan", "全权圈劝拳泉犬"},
{"que", "却确缺雀鹊"},
{"qun", "群裙"},
{"ran", "然染燃"},
{"rang", "让嚷壤"},
{"rao", "绕饶扰"},
{"re", "热惹"},
{"ren", "人认任仁忍韧刃"},
{"reng", "仍扔"},
{"ri", "日"},
{"rong", "容荣融绒溶"},
{"rou", "肉柔揉"},
{"ru", "如入辱儒乳汝"},
{"rua", "挼"},
{"ruan", "软"},
{"rui", "瑞锐"},
{"run", "润闰"},
{"ruo", "若弱"},
{"sa", "撒洒萨"},
{"sai", "赛塞腮"},
{"san", "三散伞"},
{"sang", "桑嗓丧"},
{"sao", "扫嫂骚"},
{"se", "色涩瑟"},
{"sen", "森"},
{"seng", "僧"},
{"sha", "沙杀傻纱啥刹煞"},
{"shai", "晒筛"},
{"shan", "山善闪衫删扇陕"},
{"shang", "上商伤尚赏裳"},
{"shao", "少烧稍绍捎勺"},
{"she", "社设舍射蛇摄涉奢"},
{"shen", "身深神甚伸什申慎渗"},
{"sheng", "生声省胜圣升盛剩"},
{"shi", "是事时十式市师世史示使士失视实识施"},
{"shou", "手受收首守售寿授瘦"},
{"shu", "书数术树输属舒叔殊疏蔬"},
{"shua", "刷耍"},
{"shuai", "帅摔甩衰"},
{"shuan", "栓拴"},
{"shuang", "双爽"},
{"shui", "水睡税"},
{"shun", "顺瞬"},
{"shuo", "说硕烁"},
{"si", "四死思斯司私似撕寺饲"},
{"song", "送松宋诵颂"},
{"sou", "搜艘嗽"},
{"su", "素速苏诉宿俗塑肃"},
{"suan", "算酸蒜"},
{"sui", "岁随虽碎遂穗"},
{"sun", "孙损笋"},
{"suo", "所锁索缩嗦"},
{"ta", "他她它塔踏塌拓"},
{"tai", "太台抬态泰苔"},
{"tan", "谈弹叹探贪摊滩瘫"},
{"tang", "堂糖汤唐躺烫塘"},
{"tao", "讨套逃掏桃掏"},
{"te", "特"},
{"teng", "疼腾藤"},
{"ti", "体题提替踢蹄剃"},
{"tian", "天田甜填添"},
{"tiao", "条跳挑调"},
{"tie", "铁贴帖"},
{"ting", "听停厅挺庭亭艇"},
{"tong", "同通痛统童筒铜桶"},
{"tou", "头投偷透"},
{"tu", "图土突途徒吐兔涂"},
{"tuan", "团"},
{"tui", "推退腿褪"},
{"tun", "吞屯臀"},
{"tuo", "托脱拖妥驼椭"},
{"wa", "挖瓦蛙娃袜"},
{"wai", "外歪"},
{"wan", "完万晚玩弯碗湾挽"},
{"wang", "王往望网忘旺亡"},
{"wei", "为位未卫维微围委谓违唯尾"},
{"wen", "问文闻温稳吻纹"},
{"weng", "翁嗡瓮"},
{"wo", "我握窝卧沃"},
{"wu", "无五物误务武舞午污伍"},
{"xi", "系西习戏喜细吸希洗悉溪膝席惜"},
{"xia", "下夏吓虾峡狭暇霞"},
{"xian", "先现线县显险鲜闲限陷献"},
{"xiang", "想向相像响项香乡箱详"},
{"xiao", "小笑校晓消销肖效萧"},
{"xie", "写些鞋谢协血携斜泄"},
{"xin", "新心信辛欣薪芯"},
{"xing", "行性形星兴型醒幸姓"},
{"xiong", "雄胸凶兄熊"},
{"xiu", "修秀休羞锈袖嗅"},
{"xu", "需须许序续徐虚绪蓄"},
{"xuan", "选宣旋悬玄轩"},
{"xue", "学雪血穴靴"},
{"xun", "寻讯训迅询巡循旬"},
{"ya", "压呀牙亚讶雅鸦鸭"},
{"yan", "眼言演烟严沿研盐验颜宴艳延"},
{"yang", "样养洋阳羊央仰扬"},
{"yao", "要药摇腰咬耀邀遥谣"},
{"ye", "也业夜叶爷野冶页"},
{"yi", "一已以意义医衣依易忆疑谊益议"},
{"yin", "因音引银印隐饮阴姻"},
{"ying", "应英影营迎硬赢映鹰"},
{"yo", "哟"},
{"yong", "用永拥勇涌泳庸"},
{"you", "有又由右油游友优幼忧尤呦"},
{"yu", "于与语雨鱼玉育余遇欲愈预域娱"},
{"yuan", "元原员院远愿圆园源冤缘"},
{"yue", "月越约阅跃悦岳"},
{"yun", "云运允匀韵孕"},
{"za", "杂砸咋"},
{"zai", "在再载咱灾宰"},
{"zan", "咱赞暂攒"},
{"zang", "藏脏葬"},
{"zao", "早造遭糟澡灶燥躁"},
{"ze", "则责择泽"},
{"zei", "贼"},
{"zen", "怎"},
{"zeng", "增赠曾憎"},
{"zha", "炸查扎眨乍榨渣"},
{"zhai", "宅摘窄债斋"},
{"zhan", "战站占展沾粘斩"},
{"zhang", "张章长掌涨帐障丈"},
{"zhao", "找照招着朝召兆"},
{"zhe", "这者着折哲浙"},
{"zhen", "真阵镇针震振诊枕"},
{"zheng", "正整证政争征挣睁郑"},
{"zhi", "只之至制知指直纸志治支止职织质执"},
{"zhong", "中种重众忠终钟仲"},
{"zhou", "周州洲舟昼皱骤轴"},
{"zhu", "主住注助著朱猪竹煮筑祝驻"},
{"zhua", "抓"},
{"zhuai", "拽"},
{"zhuan", "转专赚传砖"},
{"zhuang", "装状撞庄壮"},
{"zhui", "追坠缀"},
{"zhun", "准"},
{"zhuo", "桌捉着灼卓浊"},
{"zi", "子自字资紫仔兹姿咨"},
{"zong", "总宗纵综棕踪"},
{"zou", "走奏揍邹"},
{"zu", "组足祖族租阻卒"},
{"zuan", "钻攥"},
{"zui", "最嘴罪醉"},
{"zun", "尊遵"},
{"zuo", "做作坐座左昨"},
};
#define WORD_N 329
static const py_word WORDS[WORD_N] = {
{"nihao","你好",10000},
{"zhongguo","中国",9200},
{"women","我们",9000},
{"shijie","世界",8600},
{"shijian","时间",8400},
{"xianzai","现在",8300},
{"keyi","可以",8200},
{"meiyou","没有",8100},
{"yijing","已经",8000},
{"haode","好的",7900},
{"pengyou","朋友",7800},
{"gongzuo","工作",7700},
{"shenghuo","生活",7600},
{"xuexi","学习",7500},
{"xihuan","喜欢",7400},
{"beijing","北京",7300},
{"shanghai","上海",7300},
{"yinyue","音乐",7200},
{"sousuo","搜索",7100},
{"xiaza","下载",7000},
{"wenjian","文件",7000},
{"wenjianjia","文件夹",6900},
{"shezhi","设置",6900},
{"yingyong","应用",6900},
{"tongzhi","通知",6800},
{"zhongwen","中文",6800},
{"pinyin","拼音",6800},
{"meiti","媒体",6700},
{"bofang","播放",6700},
{"zanting","暂停",6600},
{"jixu","继续",6600},
{"shangyi","上一",6600},
{"xiayi","下一",6600},
{"liebiao","列表",6550},
{"geshou","歌手",6500},
{"gequ","歌曲",6500},
{"zhuanji","专辑",6450},
{"gouwu","购物",6400},
{"dianhua","电话",6400},
{"shouji","手机",6400},
{"diannao","电脑",6350},
{"dianying","电影",6300},
{"dianshi","电视",6300},
{"tushu","图书",6250},
{"xuexiao","学校",6200},
{"daxue","大学",6200},
{"xiaoxue","小学",6150},
{"gongsi","公司",6100},
{"laoshi","老师",6100},
{"tongxue","同学",6050},
{"jiating","家庭",6000},
{"baba","爸爸",6000},
{"mama","妈妈",6000},
{"gege","哥哥",5900},
{"jiejie","姐姐",5900},
{"didi","弟弟",5850},
{"meimei","妹妹",5850},
{"pengyoumen","朋友们",5800},
{"nihaoma","你好吗",7000},
{"zhongguoren","中国人",6200},
{"shanghairen","上海人",4200},
{"beijingren","北京人",4200},
{"shenme","什么",6600},
{"weishenme","为什么",6500},
{"zemme","怎么",6500},
{"zenmeyang","怎么样",6400},
{"shenmeshihou","什么时候",6300},
{"nar","哪儿",6200},
{"duoshao","多少",6400},
{"dui","对",8200},
{"bushi","不是",8100},
{"shi","是",8300},
{"yexu","也许",6300},
{"keshi","可是",6200},
{"suoyi","所以",6300},
{"ranhou","然后",6200},
{"danshi","但是",6200},
{"haishi","还是",6400},
{"huozhe","或者",6300},
{"bingqie","并且",6200},
{"erqie","而且",6300},
{"yinwei","因为",6500},
{"suoyi","所以",6300},
{"jintian","今天",6500},
{"mingtian","明天",6400},
{"zuotian","昨天",6300},
{"xingqi","星期",6200},
{"zhoumo","周末",6100},
{"shangwu","上午",6000},
{"xiawu","下午",6000},
{"wanshang","晚上",6100},
{"zaoshang","早上",6000},
{"zhongwu","中午",5900},
{"zhidao","知道",6800},
{"mingbai","明白",6300},
{"renshi","认识",6300},
{"liaojie","了解",6200},
{"xiangxin","相信",6200},
{"xiwang","希望",6300},
{"juede","觉得",6500},
{"xianzai","现在",8300},
{"zhende","真的",6400},
{"feichang","非常",6400},
{"tai","太",7800},
{"hen","很",8000},
{"dou","都",7900},
{"ye","也",7900},
{"zai","在",8300},
{"you","有",8300},
{"ba","吧",7800},
{"ma","吗",7900},
{"ne","呢",7800},
{"a","啊",7900},
{"jianghu","江湖",5400},
{"shijie","世界",8600},
{"xianshi","显示",6400},
{"shebei","设备",6500},
{"kongzhi","控制",6500},
{"yuancheng","远程",6300},
{"zhijie","直接",6400},
{"fuwu","服务",6600},
{"qidong","启动",6400},
{"guanbi","关闭",6300},
{"chongqi","重启",6200},
{"gengxin","更新",6400},
{"anzhuang","安装",6300},
{"xiezai","卸载",6100},
{"denglu","登录",6200},
{"zhuce","注册",6100},
{"shenfen","身份",6000},
{"mima","密码",6100},
{"zhanghu","账户",6000},
{"anquan","安全",6200},
{"shebei","设备",6500},
{"wangluo","网络",6600},
{"lianjie","连接",6500},
{"duankai","断开",6100},
{"wuxian","无线",6200},
{"xinyongka","信用卡",5800},
{"weixin","微信",6000},
{"zhifu","支付",6200},
{"shoukuan","收款",5900},
{"gongxiang","共享",6000},
{"wenjian","文件",7000},
{"mulu","目录",6200},
{"lujing","路径",6100},
{"mingzi","名字",6200},
{"mingcheng","名称",6400},
{"liulan","浏览",6100},
{"dakai","打开",6300},
{"baocun","保存",6300},
{"shanchua","删除",6400},
{"fuzhi","复制",6200},
{"jiantie","粘贴",6100},
{"yidong","移动",6200},
{"chongming","重命名",6100},
{"chuanjian","创建",6200},
{"xinjian","新建",6200},
{"daoru","导入",6000},
{"daochu","导出",6000},
{"yinyue","音乐",7200},
{"geci","歌词",6200},
{"geshou","歌手",6500},
{"gequ","歌曲",6500},
{"xunhuan","循环",6100},
{"shunxu","顺序",6100},
{"suiji","随机",6000},
{"danqu","单曲",6000},
{"quanshen","全申",5600},
{"anli","案例",5600},
{"zhangdan","账单",5600},
{"chongzhi","充值",5800},
{"jifen","积分",5800},
{"huiyuan","会员",6000},
{"shangcheng","商城",5900},
{"dingdan","订单",6000},
{"wuliu","物流",5800},
{"kuaidi","快递",5800},
{"didai","地带",5600},
{"quyu","区域",6100},
{"shuju","数据",6700},
{"wenben","文本",6300},
{"shuru","输入",6400},
{"shuchu","输出",6300},
{"jiance","检测",6200},
{"yunchu","运出",5600},
{"ceshi","测试",6300},
{"fangshi","方式",6400},
{"mubiao","目标",6200},
{"zhuangtai","状态",6500},
{"jieduan","阶段",6100},
{"banben","版本",6200},
{"gongneng","功能",6600},
{"canshu","参数",6100},
{"jieguo","结果",6500},
{"cuowu","错误",6300},
{"tishi","提示",6300},
{"warn","警告",5600},
{"xinxi","信息",6700},
{"shuju","数据",6700},
{"anquan","安全",6200},
{"qiye","企业",6100},
{"yonghu","用户",6600},
{"touxiang","头像",6000},
{"jinqing","尽情",5800},
{"zaixian","在线",6200},
{"xiaoxi","消息",6400},
{"pengyou","朋友",7800},
{"guanxi","关系",6200},
{"meili","美丽",6100},
{"piaoliang","漂亮",6100},
{"kuaile","快乐",6200},
{"kaixin","开心",6200},
{"xingfu","幸福",6100},
{"jiankang","健康",6200},
{"shenti","身体",6200},
{"anquan","安全",6200},
{"baozhang","保障",5900},
{"huanjing","环境",6200},
{"ziyuan","资源",6200},
{"nengyuan","能源",6000},
{"huanbao","环保",5900},
{"zhineng","智能",6200},
{"renlei","人类",6100},
{"shehui","社会",6200},
{"guojia","国家",6200},
{"renmin","人民",6100},
{"zhengfu","政府",6100},
{"jingji","经济",6200},
{"wenhua","文化",6200},
{"jiaoyu","教育",6200},
{"kexue","科学",6200},
{"jishu","技术",6500},
{"gongye","工业",6100},
{"nongye","农业",5900},
{"shangye","商业",6200},
{"jinrong","金融",6100},
{"yiyao","医药",5900},
{"fangchan","房产",5900},
{"qiche","汽车",6200},
{"jixie","机械",6000},
{"dianqi","电气",6000},
{"ruan","软",6000},
{"yingjian","硬件",6100},
{"shujuku","数据库",6100},
{"caozuo","操作",6400},
{"xitong","系统",6600},
{"pingtai","平台",6300},
{"kuangjia","框架",6100},
{"jiekou","接口",6200},
{"yemian","页面",6300},
{"chuangkou","窗口",6200},
{"gundong","滚动",6100},
{"shuaxin","刷新",6200},
{"jiazai","加载",6300},
{"huanchong","缓冲",6000},
{"duilie","队列",5900},
{"xiancheng","线程",6000},
{"jincheng","进程",6100},
{"neicun","内存",6200},
{"yingpan","硬盘",6100},
{"cunchu","存储",6100},
{"wenjian","文件",7000},
{"wenjianjia","文件夹",6900},
{"tu","图",6400},
{"tupian","图片",6400},
{"zhaopian","照片",6200},
{"shouji","手机",6400},
{"xiangji","相机",6200},
{"luoyin","录音",6100},
{"shipin","视频",6400},
{"shexiang","摄像",6000},
{"yinyue","音乐",7200},
{"bojing","剧情",5800},
{"juchang","剧场",5800},
{"dianying","电影",6300},
{"dianshiju","电视剧",6200},
{"jilu","记录",6200},
{"zuji","足迹",5600},
{"ditu","地图",6200},
{"daohang","导航",6100},
{"weizhi","位置",6200},
{"dingwei","定位",6100},
{"zhuanti","专题",5900},
{"fenlei","分类",6200},
{"biaqian","标签",6100},
{"zhuxiao","注销",5800},
{"zhuxiao","注销",5800},
{"huanying","欢迎",6200},
{"liuyan","留言",6000},
{"pinglun","评论",6200},
{"dianzan","点赞",6100},
{"fenxiang","分享",6300},
{"shoucang","收藏",6300},
{"guanzhu","关注",6200},
{"yinshua","印刷",5900},
{"banshi","办事",5700},
{"shenpi","审批",5800},
{"jinzhi","禁止",6000},
{"yunxu","允许",6100},
{"queren","确认",6300},
{"queding","确定",6300},
{"qian","前",7600},
{"hou","后",7600},
{"zuo","左",7500},
{"you","右",7500},
{"shang","上",8000},
{"xia","下",8000},
{"zhongjian","中间",6100},
{"pangbian","旁边",6000},
{"limian","里面",6100},
{"waimian","外面",6000},
{"qianmian","前面",6100},
{"houmian","后面",6100},
{"shangmian","上面",6200},
{"xiamian","下面",6100},
{"dibiao","地表",5600},
{"gongyuan","公园",6000},
{"jie","街",6000},
{"lukou","路口",5900},
{"fangxiang","方向",6200},
{"dongnan","东南",5800},
{"xinxi","信息",6700},
{"shuzime","数字",6400},
{"zimu","字母",6100},
{"hanzi","汉字",6300},
{"wenhao","问号",5900},
{"juhao","句号",5900},
{"douhao","逗号",5900},
};
#endif /* MEOW_PINYIN_LEX_H */
+6 -1
View File
@@ -131,7 +131,8 @@ int meow_btn_pressed(meow_widget *w); /* 应用在 on_tap 命中后
/* ============================================================ */
/* §5.1 输入法(IME)—— 内置触屏软键盘(仅英文 QWERTY / 数字,ASCII) */
/* ============================================================ */
typedef enum { MEOW_IME_ASCII, MEOW_IME_NUM } meow_ime_mode;
/* 输入法模式(尾部追加枚举:ASCII/NUM 值不变,零 ABI 回归)。PINYIN=中文拼音(meow_ui_ime.c)。 */
typedef enum { MEOW_IME_ASCII, MEOW_IME_NUM, MEOW_IME_PINYIN } meow_ime_mode;
typedef struct meow_ime_ctx {
meow_ime_mode mode;
@@ -150,6 +151,10 @@ void meow_ui_text_input_async(meow_ime_ctx *ctx);
int meow_ime_active(void);
const char *meow_ime_text(void);
/* 拼音模式组合态:当前拼音组合串(去声调小写,如 "nihao")供 App 渲染字段预览;
* 无组合(或非拼音模式)返回 ""。组合串是 IME 内部态,不写入字段(§4.2)。 */
const char *meow_ime_composition(void);
/* ============================================================ */
/* §6 输入层 / 生命周期(B1) */
/* ============================================================ */
+348 -4
View File
@@ -13,6 +13,7 @@
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "meow_pinyin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -61,7 +62,8 @@ static void ime_restore(void)
meow_ui_flush();
}
typedef enum { K_CH, K_SHIFT, K_SPACE, K_BACK, K_ENTER, K_ESC } kkind;
typedef enum { K_CH, K_SHIFT, K_SPACE, K_BACK, K_ENTER, K_ESC,
K_PY, K_SWITCH, K_SYMBOL, K_COMMIT, K_CLEAR } kkind;
struct kbkey { int x, y, w, h; kkind kind; char ch; };
@@ -78,6 +80,40 @@ static meow_ime_ctx *g_ime_orig = NULL; /* 应用真实 ctx(buf 回填用)
static char g_ime_buf[256];
static int g_maxlen = 255;
/* ---- 拼音模式(MEOW_IME_PINYIN)几何 + 状态(设计稿 §4.1/§5.2)----
* 面板纵向分割:组合条 420..456、候选栏 456..504、键区 504..684(4 行×48,下缘留 36px)。
* 组合串 g_py_buf 是 IME 内部态,不进字段(字段只含已上屏文本);候选每页 ≤6。 */
#define PY_COMBO_Y (KB_TOP) /* 420 — 组合条顶 */
#define PY_COMBO_H 36 /* 组合条高 */
#define PY_CAND_Y 456 /* 候选栏顶 */
#define PY_CAND_H 48 /* 候选栏高 */
#define PY_KEY_TOP 504 /* 键区首行顶 */
#define PY_PAGE 6 /* 每页候选数 */
#define PY_MAXCAND 32 /* py_predict 候选上限(g_py_all 容量) */
#define PY_PREV_X 6
#define PY_PREV_W 34
#define PY_NEXT_X 440
#define PY_NEXT_W 34
#define PY_CAND_X0 44 /* 候选起始 x */
#define PY_CAND_GAP 14 /* 候选间距 */
#define PY_CLEAR_X 388 /* 组合条「清」按钮 */
#define PY_CLEAR_W 34
#define PY_BADGE_X 426 /* 组合条「拼音」模式徽标 */
#define PY_BADGE_W 46
#define PY_BADGE_H 28
#define PY_BADGE_Y (PY_COMBO_Y + 4)
static char g_py_buf[64]; /* 拼音组合串(去声调小写,≤8 音节) */
static char g_py_all[PY_MAXCAND][64];/* 当前组合全部候选(UTF-8) */
static int g_py_nc; /* 候选总数 */
static int g_py_page; /* 翻页偏移(每页 ≤6) */
static int g_py_active; /* 组合态标志 */
static int g_ime_swipe_y = -1; /* 最近按下 Y(候选栏滑动翻页判定) */
/* ASCII/NUM 顶条右侧「中」切换热区(不占键位,英文零回归;GEOM 与 meow_ime_draw / handle_tap 共用)。 */
#define ASCII_MID_X 430
#define ASCII_MID_W 44
/* ---- 布局 ---- */
static void add_key(int x, int y, int w, int h, kkind k, char ch)
{
@@ -101,7 +137,38 @@ static void build_kb(meow_ime_mode mode)
{
g_nkeys = 0;
int y = KB_TOP + 50;
if (mode == MEOW_IME_ASCII) {
if (mode == MEOW_IME_PINYIN) {
/* 4 行文本键(拼音只用到字母;数字/符号走 [?123] 切到既有 NUM 布局):
* 行首 y=504,行 y=504/552/600/648,下缘 684。 */
y = PY_KEY_TOP;
add_ch_row(y, "qwertyuiop");
/* 第 2 行:9 字母 + [拼/英](切 MEOW_IME_ASCII,复用 SHIFT 槽位) */
{
const char *r = "asdfghjkl";
int n = (int)strlen(r);
int total = 10 * GW + 9 * GAP;
int x = (480 - total) / 2;
for (int i = 0; i < n; i++) add_key(x + i * GCOL, y + GCOL, GW, GH, K_CH, r[i]);
add_key(x + 9 * GCOL, y + GCOL, GW, GH, K_SWITCH, '&');
}
/* 第 3 行:7 字母 + [←退格 3格] */
{
const char *r = "zxcvbnm";
int n = (int)strlen(r);
int total = 10 * GW + 9 * GAP;
int x = (480 - total) / 2;
for (int i = 0; i < n; i++) add_key(x + i * GCOL, y + GCOL * 2, GW, GH, K_CH, r[i]);
add_key(x + 7 * GCOL, y + GCOL * 2, (GW + GAP) * 3, GH, K_BACK, '<');
}
/* 第 4 行:[?123] [空格 6格] [OK](OK=只提交不关闭,同英文) */
{
int total = 10 * GW + 9 * GAP;
int x = (480 - total) / 2;
add_key(x, y + GCOL * 3, GW, GH, K_SYMBOL, '1');
add_key(x + GCOL, y + GCOL * 3, (GW + GAP) * 6, GH, K_SPACE, ' ');
add_key(x + GCOL * 8, y + GCOL * 3, 2 * GW + GAP, GH, K_ENTER, '>');
}
} else if (mode == MEOW_IME_ASCII) {
add_ch_row(y, "1234567890");
add_ch_row(y + GCOL, "qwertyuiop");
/* 第 3 行:9 字母 + SHIFT */
@@ -154,8 +221,104 @@ static void build_kb(meow_ime_mode mode)
/* ---- 编辑动作 ---- */
static int is_letter(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
/* ---- 拼音编辑动作(MEOW_IME_PINYIN) ---- */
static int py_pages(void) { return (g_py_nc + PY_PAGE - 1) / PY_PAGE; }
static void py_refresh(void)
{
g_py_nc = py_predict(g_py_buf, g_py_all, PY_MAXCAND);
g_py_page = 0;
g_py_active = (g_py_buf[0] != 0);
}
static void py_input(char c)
{
int len = (int)strlen(g_py_buf);
if (len < (int)sizeof(g_py_buf) - 1) { g_py_buf[len] = c; g_py_buf[len + 1] = 0; }
py_refresh();
}
static void py_back_combo(void)
{
int len = (int)strlen(g_py_buf);
if (len > 0) g_py_buf[len - 1] = 0;
py_refresh();
}
static void py_clear(void)
{
g_py_buf[0] = 0;
py_refresh();
g_ime_swipe_y = -1;
}
/* 上屏词条到字段 + on_change + 清组合。maxlen 为字节预算(含 NUL)。 */
static void py_commit(const char *s)
{
int len = (int)strlen(g_ime_buf);
int sl = (s && *s) ? (int)strlen(s) : 0;
if (sl > 0 && len + sl < g_maxlen) {
memcpy(g_ime_buf + len, s, (size_t)sl + 1);
if (g_ime_ctx.on_change) g_ime_ctx.on_change(g_ime_buf, g_ime_ctx.ctx);
}
py_clear();
}
/* UTF-8 安全退格:从字段末回退一个 codepoint(1-4 字节) */
static void py_back_field(void)
{
int len = (int)strlen(g_ime_buf);
if (len <= 0) return;
int i = len - 1;
while (i > 0 && ((unsigned char)g_ime_buf[i] & 0xC0) == 0x80) i--; /* 回退过续字节,找到起始字节 */
g_ime_buf[i] = 0;
if (g_ime_ctx.on_change) g_ime_ctx.on_change(g_ime_buf, g_ime_ctx.ctx);
}
static void py_page_next(void) { int p = py_pages(); if (g_py_page < p - 1) g_py_page++; }
static void py_page_prev(void) { if (g_py_page > 0) g_py_page--; }
static void py_commit_first(void) { if (g_py_nc > 0) py_commit(g_py_all[0]); }
static void py_set_mode(meow_ime_mode m)
{
g_ime_ctx.mode = m;
py_clear(); /* 组合态未上屏内容丢弃(设计稿 §5.3:关闭/切换同语义) */
g_shift = 0;
build_kb(m);
}
static void apply_key_py(const struct kbkey *k)
{
switch (k->kind) {
case K_CH: py_input((char)tolower((unsigned char)k->ch)); break; /* 仅 a-z 字母进组合 */
case K_BACK:
if (g_py_buf[0]) py_back_combo(); /* 组合非空:删组合末字母 */
else py_back_field(); /* 组合空:删字段末汉字(UTF-8 安全) */
break;
case K_SPACE:
if (g_py_buf[0]) py_commit_first(); /* 组合非空:上屏首候选 */
else { /* 空:上屏一个空格 */
int len = (int)strlen(g_ime_buf);
if (len < g_maxlen - 1) { g_ime_buf[len] = ' '; g_ime_buf[len + 1] = 0;
if (g_ime_ctx.on_change) g_ime_ctx.on_change(g_ime_buf, g_ime_ctx.ctx); }
}
break;
case K_ENTER:
if (g_py_buf[0]) py_commit_first(); /* 上屏首候选 + 清组合(不关闭) */
else g_ime_result = 1; /* 组合空:只提交不关闭(走 ESC/背景) */
break;
case K_SWITCH: py_set_mode(MEOW_IME_ASCII); break; /* 拼→英 */
case K_SYMBOL: py_set_mode(MEOW_IME_NUM); break; /* →数字/符号 */
case K_ESC: g_ime_result = 0; g_ime_done = 1; break;
case K_COMMIT: py_commit_first(); break;
case K_CLEAR: py_clear(); break;
default: break;
}
}
static void apply_key(const struct kbkey *k)
{
if (g_ime_ctx.mode == MEOW_IME_PINYIN) { apply_key_py(k); return; }
switch (k->kind) {
case K_CH: {
/* M2.71-IME-fix:键面存小写(build_kb "qwertyuiop"…),shift 开→大写、关→小写;
@@ -184,14 +347,103 @@ static void apply_key(const struct kbkey *k)
}
case K_ENTER: g_ime_result = 1; break; /* M2.71-IME-fix:回车只提交(mark),不关键盘;关闭走 ESC/背景 */
case K_ESC: g_ime_result = 0; g_ime_done = 1; break;
default: break; /* K_PY/K_SWITCH/K_SYMBOL/K_COMMIT/K_CLEAR 仅拼音布局出现,英文分支不触 */
}
}
/* ---- 绘制 ---- */
/* 拼音键面标签(K_CH=字母小写;其余为该键的内置文字)。 */
static const char *py_key_label(const struct kbkey *k, char *tmp)
{
switch (k->kind) {
case K_CH: { char c = (char)tolower((unsigned char)k->ch); tmp[0] = c; tmp[1] = 0; return tmp; }
case K_BACK: return "⌫";
case K_SPACE: return "空格";
case K_ENTER: return "确定";
case K_SWITCH: return "拼/英";
case K_SYMBOL: return "?123";
default: return "";
}
}
/* 候选栏内第 idx(页内 0..5)候选的实际 x/宽;越界返回 0。绘制与命中共用,保证几何一致。 */
static int py_cand_rect(int idx, int *x, int *w)
{
int gi = g_py_page * PY_PAGE + idx;
if (gi >= g_py_nc) return 0;
int xp = PY_CAND_X0;
for (int i = 0; i < idx; i++) {
int pgi = g_py_page * PY_PAGE + i;
if (pgi >= g_py_nc) break;
xp += meow_ui_text_width(g_py_all[pgi], 20) + PY_CAND_GAP;
}
*x = xp;
*w = meow_ui_text_width(g_py_all[gi], 20);
return 1;
}
static void meow_ime_draw_py(const meow_theme *th)
{
const int th20 = meow_ui_text_height(20);
/* 面板整体 + 组合条 + 候选栏 */
meow_ui_fill_rect(0, KB_TOP, 480, 720 - KB_TOP, th->surfaceContainerHighest);
meow_ui_fill_rect(0, PY_COMBO_Y, 480, PY_COMBO_H, th->surfaceContainerHigh);
/* 组合条:left=组合串实时显示(含 '_' 光标);right=「清」+ 模式徽标「拼音」 */
char pybuf[72];
snprintf(pybuf, sizeof pybuf, "%s_", g_py_buf);
meow_ui_text(14, PY_COMBO_Y + 8, pybuf, th->font_body, th->onSurface);
meow_gfx_fill_round(PY_CLEAR_X, PY_COMBO_Y + 4, PY_CLEAR_W, PY_BADGE_H, 8, th->secondaryContainer);
meow_ui_text_center(PY_CLEAR_X + PY_CLEAR_W / 2, PY_COMBO_Y + 4 + (PY_BADGE_H - th20) / 2, "清", 20, th->onSecondaryContainer);
meow_gfx_fill_round(PY_BADGE_X, PY_BADGE_Y, PY_BADGE_W, PY_BADGE_H, 8, th->primaryContainer);
meow_ui_text_center(PY_BADGE_X + PY_BADGE_W / 2, PY_BADGE_Y + (PY_BADGE_H - th20) / 2, "拼音", 20, th->onPrimaryContainer);
/* 候选栏:◀ ▶ 翻页 + 横排候选(高亮第 0 项) */
meow_ui_fill_rect(0, PY_CAND_Y, 480, PY_CAND_H, th->surfaceContainerHighest);
meow_gfx_fill_round(PY_PREV_X, PY_CAND_Y, PY_PREV_W, PY_CAND_H, 8, th->surfaceContainerLow);
meow_ui_text_center(PY_PREV_X + PY_PREV_W / 2, PY_CAND_Y + (PY_CAND_H - th20) / 2, "◀", 20, th->onSurface);
meow_gfx_fill_round(PY_NEXT_X, PY_CAND_Y, PY_NEXT_W, PY_CAND_H, 8, th->surfaceContainerLow);
meow_ui_text_center(PY_NEXT_X + PY_NEXT_W / 2, PY_CAND_Y + (PY_CAND_H - th20) / 2, "▶", 20, th->onSurface);
if (g_py_nc == 0) {
meow_ui_text(PY_CAND_X0, PY_CAND_Y + (PY_CAND_H - th20) / 2, "(无候选)", th->font_body, th->onSurfaceVariant);
} else {
int x = PY_CAND_X0;
for (int i = 0; i < PY_PAGE; i++) {
int gi = g_py_page * PY_PAGE + i;
if (gi >= g_py_nc) break;
const char *c = g_py_all[gi];
int w = meow_ui_text_width(c, 20);
int ty = PY_CAND_Y + (PY_CAND_H - th20) / 2;
if (i == 0) {
meow_gfx_fill_round(x - 6, PY_CAND_Y + 6, w + 12, PY_CAND_H - 12, 8, th->secondaryContainer);
meow_ui_text(x, ty, c, 20, th->onSecondaryContainer);
} else {
meow_ui_text(x, ty, c, 20, th->onSurface);
}
x += w + PY_CAND_GAP;
}
}
/* 4 行键 */
for (int i = 0; i < g_nkeys; i++) {
const struct kbkey *k = &g_keys[i];
uint32_t bg, fg;
if (k->kind == K_ENTER) { bg = th->primary; fg = th->onPrimary; }
else if (k->kind == K_SWITCH){ bg = th->tertiaryContainer; fg = th->onTertiaryContainer; }
else if (k->kind == K_CH) { bg = th->surfaceContainerLow; fg = th->onSurface; }
else { bg = th->primaryContainer; fg = th->onPrimaryContainer; }
meow_gfx_fill_round(k->x, k->y, k->w, k->h, 8, bg);
char tmp[4];
meow_ui_text_center(k->x + k->w / 2, k->y + (k->h - th20) / 2, py_key_label(k, tmp), 20, fg);
}
}
void meow_ime_draw(void)
{
if (!g_ime_active) return;
const meow_theme *th = meow_theme_get();
if (g_ime_ctx.mode == MEOW_IME_PINYIN) { meow_ime_draw_py(th); return; }
/* M2.71-bug7:配色全走 meow_theme(深/浅主题自动切),不再硬编码黑底白键。 */
meow_ui_fill_rect(0, KB_TOP, 480, 720 - KB_TOP, th->surfaceContainerHighest); /* 面板 */
meow_ui_fill_rect(0, KB_TOP, 480, 42, th->surfaceContainerHigh); /* 回显条 */
@@ -199,6 +451,12 @@ void meow_ime_draw(void)
/* 回显=用户已输入的字符(live echo,非候选/联想),随键入实时刷新。 */
snprintf(preview, sizeof preview, "%s_", g_ime_buf);
meow_ui_text(14, KB_TOP + 9, preview, th->font_body, th->onSurface);
/* ASCII/NUM 顶条右侧「中」热区:切到拼音模式(英文键布局零改动,零回归)。 */
{
int th20 = meow_ui_text_height(20);
meow_gfx_fill_round(ASCII_MID_X, KB_TOP + 7, ASCII_MID_W, 28, 8, th->primaryContainer);
meow_ui_text_center(ASCII_MID_X + ASCII_MID_W / 2, KB_TOP + 7 + (28 - th20) / 2, "中", 20, th->onPrimaryContainer);
}
/* 键 */
for (int i = 0; i < g_nkeys; i++) {
const struct kbkey *k = &g_keys[i];
@@ -218,10 +476,53 @@ void meow_ime_draw(void)
}
/* ---- 命中 ---- */
/* 找命中键(供拼音长按判定退格用)。 */
static const struct kbkey *key_at(int x, int y)
{
for (int i = 0; i < g_nkeys; i++) {
const struct kbkey *k = &g_keys[i];
if (x >= k->x && x < k->x + k->w && y >= k->y && y < k->y + k->h) return k;
}
return NULL;
}
/* 拼音命中:候选栏/组合条/键区。y<KB_TOP=取消(组合不上屏)。 */
static int meow_ime_handle_tap_py(int x, int y)
{
if (y < KB_TOP) { g_ime_result = 0; g_ime_done = 1; return 1; } /* 背景点=取消 */
if (y >= PY_CAND_Y && y < PY_CAND_Y + PY_CAND_H) { /* 候选栏 */
if (x >= PY_PREV_X && x < PY_PREV_X + PY_PREV_W) { py_page_prev(); return 1; }
if (x >= PY_NEXT_X && x < PY_NEXT_X + PY_NEXT_W) { py_page_next(); return 1; }
/* 点候选:命中即上屏(UTF-8),清组合 */
for (int i = 0; i < PY_PAGE; i++) {
int cx, cw;
if (!py_cand_rect(i, &cx, &cw)) break;
if (x >= cx - 6 && x < cx + cw + 6) { py_commit(g_py_all[g_py_page * PY_PAGE + i]); return 1; }
}
return 1; /* 候选栏空隙:消费但不做 */
}
if (y >= PY_COMBO_Y && y < PY_COMBO_Y + PY_COMBO_H) { /* 组合条 */
if (x >= PY_CLEAR_X && x < PY_CLEAR_X + PY_CLEAR_W) { py_clear(); return 1; } /* 清组合 */
if (x >= PY_BADGE_X && x < PY_BADGE_X + PY_BADGE_W) { py_set_mode(MEOW_IME_ASCII); return 1; } /* 拼→英 */
return 1; /* 组合条其他:消费 */
}
const struct kbkey *k = key_at(x, y);
if (k) { apply_key(k); return 1; }
return 1; /* 键区/面板空隙:消费但不做 */
}
int meow_ime_handle_tap(int x, int y)
{
if (!g_ime_active || g_ime_done) return 0;
if (y < KB_TOP) { g_ime_result = 0; g_ime_done = 1; return 1; } /* 背景点=取消 */
if (g_ime_ctx.mode == MEOW_IME_PINYIN) return meow_ime_handle_tap_py(x, y);
/* ASCII/NUM:顶条(回显条 KB_TOP..KB_TOP+42)右端「中」热区 → 切拼音;
* y<KB_TOP=上方背景=取消;顶条其它区域消费不取消(沿用既有行为)。 */
if (y < KB_TOP + 42) {
if (x >= ASCII_MID_X && x < ASCII_MID_X + ASCII_MID_W) { py_set_mode(MEOW_IME_PINYIN); return 1; }
if (y < KB_TOP) { g_ime_result = 0; g_ime_done = 1; return 1; } /* 背景点=取消 */
return 1; /* 顶条其它:消费但不做(不落键) */
}
for (int i = 0; i < g_nkeys; i++) {
const struct kbkey *k = &g_keys[i];
if (x >= k->x && x < k->x + k->w && y >= k->y && y < k->y + k->h) {
@@ -235,6 +536,45 @@ int meow_ime_handle_tap(int x, int y)
/* ---- 对外 ---- */
int meow_ime_active(void) { return g_ime_active && !g_ime_done; }
const char *meow_ime_text(void) { return g_ime_buf; }
const char *meow_ime_composition(void) { return g_ime_ctx.mode == MEOW_IME_PINYIN ? g_py_buf : ""; }
int meow_ime_mode_pinyin(void) { return g_ime_active && !g_ime_done && g_ime_ctx.mode == MEOW_IME_PINYIN; }
/* 候选栏横滑翻页:滑动起始于候选栏区(y∈[456,504))才消费;首/尾页钳位。 */
int meow_ime_swipe(int dir, int dist)
{
(void)dist;
if (!meow_ime_mode_pinyin()) return 0;
if (g_ime_swipe_y < PY_CAND_Y || g_ime_swipe_y >= PY_CAND_Y + PY_CAND_H) return 0;
if (dir == MEOW_SWIPE_LEFT) { py_page_next(); return 1; }
if (dir == MEOW_SWIPE_RIGHT) { py_page_prev(); return 1; }
return 0;
}
/* 拼音长按退格:长按落在「退格」键上 → 清空字段 + 组合(上屏 off 回调)。 */
int meow_ime_long_press(int x, int y)
{
if (!meow_ime_mode_pinyin()) return 0;
const struct kbkey *k = key_at(x, y);
if (!k || k->kind != K_BACK) return 0;
g_ime_buf[0] = 0;
py_clear();
if (g_ime_ctx.on_change) g_ime_ctx.on_change(g_ime_buf, g_ime_ctx.ctx);
return 1;
}
/* 统一事件入口:TAP/TOUCH_DOWN/SWIPE/LONG_PRESS(IME 打开时主循环/阻塞循环调用)。
* ASCII/NUM:TAP 走既有 handle_tap,其余返回 0(沿用英文行为,零回归)。 */
int meow_ime_handle_event(const meow_input_ev *e)
{
if (!g_ime_active || g_ime_done) return 0;
switch (e->type) {
case MEOW_EV_TAP: return meow_ime_handle_tap(e->a, e->b);
case MEOW_EV_TOUCH_DOWN: g_ime_swipe_y = e->b; return 1; /* 记录压点(候选栏滑动判定);覆盖层消费 */
case MEOW_EV_SWIPE: return meow_ime_swipe(e->a, e->b);
case MEOW_EV_LONG_PRESS: return meow_ime_long_press(e->a, e->b);
default: return 0;
}
}
void meow_ime_close(int result)
{
@@ -256,6 +596,8 @@ void meow_ui_text_input_async(meow_ime_ctx *ctx)
g_ime_buf[sizeof g_ime_buf - 1] = 0;
g_maxlen = ctx->maxlen > 0 ? ctx->maxlen : 255;
g_shift = 0; g_ime_result = 0; g_ime_done = 0; g_ime_active = 1;
/* 拼音状态复位(组合/候选/翻页/压点) */
g_py_buf[0] = 0; g_py_nc = 0; g_py_page = 0; g_py_active = 0; g_ime_swipe_y = -1;
build_kb(ctx->mode);
ime_capture(); /* M2.71-bug7:打开前拍下键盘区背景(关闭即还原) */
meow_input_start();
@@ -275,8 +617,10 @@ int meow_ui_text_input(meow_ime_ctx *ctx)
meow_input_ev e;
while (meow_input_pop(&e)) {
meow_ui_activity_ping(); /* M2.71-bug5:IME 打字交互也重置息屏计时 */
if (e.type == MEOW_EV_TAP) meow_ime_handle_tap(e.a, e.b);
meow_ime_handle_event(&e); /* TAP/TOUCH_DOWN/SWIPE/LONG_PRESS 统一分发(拼音/英文) */
}
/* 拼音 IME 打开时喂长按轮询(长按退格清空字段);英文模式不喂(保持既有行为)。 */
if (meow_ime_mode_pinyin()) meow_input_long_press_poll();
/* M2.71-IME-fix:阻塞 IME 期间喂 App on_tick(动态内容继续刷,progress/时钟不卡死)。
* 顺序要求:先喂 on_tick(App 重绘进 back_buf)→ 再 meow_ime_draw(IME 面板随后盖上去),
* 保证 IME 面板覆盖在最上,不被 App 的 d_paint_region 覆盖。 */
+11 -3
View File
@@ -522,6 +522,9 @@ void meow_ui_run(const meow_ui_events *ev)
if (!meow_ime_active()) {
meow_widget_touch_refresh();
if (now_ms() >= startup_cooldown_until) meow_input_long_press_poll();
} else if (meow_ime_mode_pinyin()) {
/* 拼音 IME 打开:喂长按轮询(长按退格清空字段)。英文模式不喂(保持既有行为)。 */
if (now_ms() >= startup_cooldown_until) meow_input_long_press_poll();
}
meow_input_ev e;
@@ -537,7 +540,7 @@ void meow_ui_run(const meow_ui_events *ev)
/* 抬手未达标:清视觉条 */
meow_ui_swipe_hint(-1);
meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6);
if (meow_ime_active() && meow_ime_handle_tap(e.a, e.b)) continue;
if (meow_ime_active() && meow_ime_handle_event(&e)) continue;
/* §6.1 触摸分流:③ 系统全局(IME/状态栏…已在上面消费) → ② App 全局控件
* (命中返回 1=消费,否则落到 ① active Activity 页面控件)。 */
if (meow_ui_global_hit(e.a, e.b)) continue;
@@ -549,6 +552,8 @@ void meow_ui_run(const meow_ui_events *ev)
/* 抬手达标:清视觉条(左滑返回),触发行消失 */
meow_ui_swipe_hint(-1);
meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6);
/* 拼音 IME 候选栏横滑翻页(英文模式 handle_event 返回 0 → 沿用既有行为)。 */
if (meow_ime_active() && meow_ime_handle_event(&e)) continue;
/* A-0 全局左缘返回(方案A):
* - APP 注册 on_swipe → APP 接管,自行决定(meow_ui_request_exit / 页内返回);
* - APP 未注册 on_swipe 且非全屏(sb_active)→ SDK 默认退出 app(返回=退出);
@@ -569,9 +574,10 @@ void meow_ui_run(const meow_ui_events *ev)
} else if (e.type == MEOW_EV_TOUCH_DOWN || e.type == MEOW_EV_TOUCH_MOVE) {
/* M2.71-bug2/6:拖动过程(跟手)。IME 弹出时触摸优先喂键盘(键盘覆盖层拦截,
* on_touch_* 丢弃,避免按到键盘却在背后控件上锁拖拽);启动窗口也吞(防残留点按
* 误锁拖拽)。模态(dialog/sheet)期间本主循环被阻塞,天然不在此分发。 */
* 误锁拖拽)。模态(dialog/sheet)期间本主循环被阻塞,天然不在此分发。
* IME 打开时先把压点喂给拼音(候选栏滑动翻页判定 g_ime_swipe_y),再丢弃。 */
if (in_startup) continue;
if (meow_ime_active()) continue;
if (meow_ime_active()) { meow_ime_handle_event(&e); continue; }
if (g_events_set && g_events.on_touch_down && e.type == MEOW_EV_TOUCH_DOWN)
g_events.on_touch_down(e.a, e.b);
if (g_events_set && g_events.on_touch_move && e.type == MEOW_EV_TOUCH_MOVE)
@@ -596,6 +602,8 @@ void meow_ui_run(const meow_ui_events *ev)
* meow_widget_hit/meow_listview_hit 判定命中控件/行)。已注册回调则分发;
* 其抬手已被 touch_release 抑制(不再产生 TAP/SWIPE),此处无需吞后续 tap。 */
if (in_startup) continue;
/* 拼音 IME 打开:长按退格清空字段(英文模式 handle_event 返回 0 → 走既有长按回调)。 */
if (meow_ime_active() && meow_ime_handle_event(&e)) continue;
if (g_long_press_cb) g_long_press_cb(e.a, e.b);
}
}
+8
View File
@@ -244,4 +244,12 @@ void meow_ime_draw(void);
/* 关闭 IME 并交付最终结果(result: 1=确认 0=取消)。 */
void meow_ime_close(int result);
/* 拼音 IME 事件钩子(meow_ui_input.c 主循环 / meow_ui_ime.c 阻塞循环调用,IME 打开时):
* 统一分发 TAP/TOUCH_DOWN/SWIPE/LONG_PRESS 给拼音模式(组合/候选栏翻页/长按清空)。
* 非拼音(ASCII/NUM)模式:TAP 走 meow_ime_handle_tap,其余事件返回 0(沿用既有英文行为,零回归)。
* 返回 1=已消费(调用方 continue),0=透传。 */
int meow_ime_handle_event(const meow_input_ev *e);
/* 当前 IME 是否拼音模式(供 meow_ui_input.c 决定是否喂长按轮询)。 */
int meow_ime_mode_pinyin(void);
#endif /* MEOW_UI_INTERNAL_H */
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
+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;
}