diff --git a/build_sdk.sh b/build_sdk.sh index 2150cfc..3c6090b 100755 --- a/build_sdk.sh +++ b/build_sdk.sh @@ -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 ==" diff --git a/gen_pinyin_lex.py b/gen_pinyin_lex.py new file mode 100644 index 0000000..6cd2840 --- /dev/null +++ b/gen_pinyin_lex.py @@ -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 ') + 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() diff --git a/meow_pinyin.c b/meow_pinyin.c new file mode 100644 index 0000000..343c6a8 --- /dev/null +++ b/meow_pinyin.c @@ -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 +#include + +#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; +} diff --git a/meow_pinyin.h b/meow_pinyin.h new file mode 100644 index 0000000..5a1fa33 --- /dev/null +++ b/meow_pinyin.h @@ -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 */ diff --git a/meow_pinyin_lex.h b/meow_pinyin_lex.h new file mode 100644 index 0000000..c00d2ed --- /dev/null +++ b/meow_pinyin_lex.h @@ -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 + +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 */ diff --git a/meow_ui.h b/meow_ui.h index 63a82c3..a7acd98 100644 --- a/meow_ui.h +++ b/meow_ui.h @@ -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) */ /* ============================================================ */ diff --git a/meow_ui_ime.c b/meow_ui_ime.c index 968566d..6ebfd71 100644 --- a/meow_ui_ime.c +++ b/meow_ui_ime.c @@ -13,6 +13,7 @@ */ #include "meow_ui.h" #include "meow_ui_internal.h" +#include "meow_pinyin.h" #include #include #include @@ -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= 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= 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 覆盖。 */ diff --git a/meow_ui_input.c b/meow_ui_input.c index ecc08e0..c7630fc 100644 --- a/meow_ui_input.c +++ b/meow_ui_input.c @@ -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); } } diff --git a/meow_ui_internal.h b/meow_ui_internal.h index 241b396..73a46a2 100644 --- a/meow_ui_internal.h +++ b/meow_ui_internal.h @@ -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 */ diff --git a/test_ime_fix b/test_ime_fix index c4d3ce7..4a9a69a 100755 Binary files a/test_ime_fix and b/test_ime_fix differ diff --git a/test_pinyin b/test_pinyin new file mode 100755 index 0000000..143017a Binary files /dev/null and b/test_pinyin differ diff --git a/test_pinyin.c b/test_pinyin.c new file mode 100644 index 0000000..fd8e143 --- /dev/null +++ b/test_pinyin.c @@ -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 +#include +#include +#include + +/* 拼音键位(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; +}