diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index c48de008..a99315cf 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -3,3 +3,6 @@ # style: 用 .clang-format 重排 server / windows / ui / log 42238a2af693fcd0dd80354b2b450c6647647dd2 + +# chore: settle line endings in .gitattributes +6a320d5d07f29a7757f5d066fc4e142cc521a300 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d410f4ea --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Line endings are decided here rather than by each contributor's core.autocrlf. The repository is +# already predominantly LF -- 382 text files against 50 -- and the stragglers are what break CI: an +# edit made on macOS or Linux writes LF lines into one of the CRLF files, and clang-format rejects +# the resulting mix. That is what took `quality / Workflow validation` red on KeyStateCategory.h, +# where three lines out of a CRLF file had arrived as LF. +* text=auto eol=lf + +# Declared rather than left to git's binary heuristic, which only samples the first 8000 bytes. +# user_dict.dat is a 40-byte seed that is almost entirely NUL, so the heuristic does catch it today, +# but a larger one written later would not be caught by a sample of its head. +*.dat binary +*.ico binary +*.mp3 binary +*.png binary diff --git a/windows/.clang-format b/windows/.clang-format index a6f75a0c..1e4a1c55 100644 --- a/windows/.clang-format +++ b/windows/.clang-format @@ -1,6 +1,6 @@ ---- -BasedOnStyle: Microsoft -IndentWidth: 4 - -# Do not sort headers -SortIncludes: false +--- +BasedOnStyle: Microsoft +IndentWidth: 4 + +# Do not sort headers +SortIncludes: false diff --git a/windows/.vscode/settings.json b/windows/.vscode/settings.json index b826feca..c14207d5 100644 --- a/windows/.vscode/settings.json +++ b/windows/.vscode/settings.json @@ -1,10 +1,10 @@ -{ - "editor.tabSize": 4, - "editor.indentSize": "tabSize", - "[cpp]": { - "editor.tabSize": 4 - }, - "[json]": { - "editor.tabSize": 2 - } +{ + "editor.tabSize": 4, + "editor.indentSize": "tabSize", + "[cpp]": { + "editor.tabSize": 4 + }, + "[json]": { + "editor.tabSize": 2 + } } \ No newline at end of file diff --git a/windows/Dictionary/.gitattributes b/windows/Dictionary/.gitattributes index c7f878dc..aa875784 100644 --- a/windows/Dictionary/.gitattributes +++ b/windows/Dictionary/.gitattributes @@ -1,3 +1,3 @@ -# The *.txt files in this directory are UTF-16-encoded and should -# therefore be treated as pure binary data. -*.txt binary +# The *.txt files in this directory are UTF-16-encoded and should +# therefore be treated as pure binary data. +*.txt binary diff --git a/windows/assets/helpcode.txt b/windows/assets/helpcode.txt index d05a10ff..c7a1ce72 100644 --- a/windows/assets/helpcode.txt +++ b/windows/assets/helpcode.txt @@ -1,8258 +1,8258 @@ -阿=ek -锕=jk -吖=ka -啊=kk -嗄=kw -唔=kk -嗯=kx -盎=mn -昂=oe -肮=oj -癌=be -皑=bv -蔼=cv -艾=cx -隘=em -崖=et -捱=ft -挨=fu -锿=jy -嗌=km -唉=ku -哎=kx -嗳=ky -嫒=ny -暧=oy -埃=tu -碍=uc -矮=un -砹=ux -爱=vy -哀=wy -瑷=wy -霭=yv -案=bm -安=bn -岸=eu -按=fn -揞=fo -鞍=gn -庵=gv -黯=ho -铵=jn -唵=kv -桉=mn -鹌=nd -胺=on -暗=oo -氨=qn -犴=qu -俺=rv -垵=tn -埯=tv -谙=yo -凹=aa -鳌=af -翱=by -澳=dd -聱=ef -拗=fl -廒=gw -熬=hf -螯=if -鏊=jf -嗷=kw -鏖=lj -骜=mf -媪=nm -奥=pd -袄=pd -岙=pe -獒=qf -傲=rw -坳=tl -敖=wf -懊=xd -遨=zf -鲅=ad -疤=bv -茇=cd -菝=cd -芭=cv -灞=do -岜=ev -扒=fb -捌=fd -拔=fd -把=fv -爸=fv -魃=gd -靶=gv -钯=jv -叭=kb -吧=kv -耙=lv -粑=mv -八=pn -罢=ss -坝=tb -巴=vv -笆=vv -霸=yo -跋=zd -髟=ap -鳔=ax -瘭=bx -飚=fh -飑=fv -表=fy -彪=hp -镳=jh -镖=jx -杓=md -标=mx -骠=mx -婊=ny -膘=ox -裱=py -飙=qf -俵=ry -本=aa -贲=bu -苯=ca -奔=dc -夯=dl -锛=jc -畚=st -坌=tb -笨=va -甭=aa -崩=eo -蚌=if -嘣=ko -祊=pf -绷=so -泵=ud -甏=wu -蹦=zo -迸=zq -浜=db -邦=ef -帮=jf -镑=jf -棒=ma -梆=me -榜=mf -膀=of -傍=rf -绑=se -磅=uf -谤=yf -毖=ad -弼=ag -比=av -毙=av -痹=ba -赑=bb -荜=cu -萆=cu -薜=cu -蓖=cv -芘=cv -蔽=cw -弊=cx -荸=cz -濞=da -滗=dm -必=dp -鄙=ek -陛=et -庳=gu -髀=gu -庇=gv -秘=hp -秕=hv -铋=jp -哔=ku -吡=kv -闭=mp -婢=nu -嬖=nu -妣=nv -臂=ou -睥=ou -鼻=pa -币=pj -裨=pu -匕=pv -狴=qt -彼=rp -俾=ru -畀=ta -壁=tu -毕=ua -屄=ub -辟=uu -碧=uw -箅=va -笔=vm -筚=vu -舭=vv -篦=vv -璧=wu -敝=wx -愎=xw -襞=yu -逼=za -避=zu -跸=zu -半=ba -颁=ba -癍=bw -瘢=by -班=dw -阪=ey -拌=fa -扮=fd -搬=fy -扳=fy -瓣=gu -钣=jy -办=ld -板=my -版=py -伴=ra -绊=sa -坂=ty -般=vy -舨=vy -斑=ww -鲍=av -宝=bw -薄=cc -葆=cm -苞=cv -瀑=dd -剥=de -刨=dg -抱=fv -报=fy -包=gv -爆=hd -煲=hr -龅=iv -鸨=np -暴=od -曝=od -胞=ov -褓=pm -勹=pv -保=rm -堡=tr -饱=uv -豹=vv -褒=wy -雹=yv -趵=zd -孢=zv -百=ab -掰=bu -摆=fs -捭=fu -稗=hu -呗=kb -柏=mb -白=po -佰=rb -拜=uf -擘=uu -败=wb -鳊=ac -贬=bn -窆=bn -苄=cd -卞=dd -汴=dd -辨=du -鞭=gn -扁=hc -煸=hc -蝙=ic -匾=kc -褊=pc -便=rn -编=sc -弁=sc -缏=sn -辫=su -碥=uc -砭=un -笾=vz -变=wy -忭=xd -辩=yu -遍=zh -边=zl -鬓=ab -宾=bb -濒=da -滨=db -殡=db -豳=ea -邠=eb -摈=fb -髌=gb -镔=jb -槟=mb -彬=mp -膑=ob -傧=rb -缤=sb -斌=wd -卜=ad -帛=bj -菠=cy -泊=db -渤=dl -波=dp -搏=fc -拨=fd -播=ft -钵=ja -铂=jb -钹=jd -啵=kp -檗=mu -驳=mx -鹁=nu -膊=oc -脖=oz -伯=rb -博=uc -礴=uc -勃=ul -饽=uz -舶=vb -箔=vb -簸=vy -玻=wp -亳=wq -踣=zk -跛=zp -不=ad -部=el -捕=fd -钚=jd -钸=jj -哺=kd -卟=kd -瓿=lw -晡=od -补=pd -埔=td -埠=tu -布=uj -簿=vc -步=vp -怖=xj -醭=yn -逋=za -踄=zp -鳖=ax -瘪=bv -别=dk -憋=xx -蹩=zx -丙=an -病=bn -冰=dd -邴=ea -摒=fc -炳=hn -梹=mb -柄=mn -兵=pb -秉=pn -并=qc -饼=uc -禀=wx -北=av -蓓=ck -邶=ea -陂=ep -鞴=ga -焙=hk -稖=hk -辈=ia -钡=jb -鐾=ju -贝=kr -杯=md -鹎=np -背=oa -褙=po -被=pp -卑=pu -狈=qb -倍=rk -碚=uk -碑=uu -孛=uz -备=wt -悲=xa -惫=xw -悖=xz -擦=fx -嚓=kx -礤=ux -凑=dd -辏=id -楱=md -腠=od -测=dd -厕=id -册=pa -侧=rd -策=vn -恻=xd -涔=dv -岑=ev -曾=bo -层=us -噌=ko -蹭=zo -藏=ca -苍=cv -沧=dv -仓=rv -伧=rv -舱=vv -玱=wv -赐=bp -疵=bv -茨=cr -茈=cv -刺=da -次=dr -呲=kv -糍=ms -鹚=nq -祠=pk -辞=pu -兹=qs -伺=rk -磁=us -雌=vf -此=vv -瓷=wd -慈=xq -词=yk -蚕=ai -残=dg -灿=he -黪=hp -=hp -粲=ma -骖=mp -参=sp -餐=ua -孱=uz -璨=wm -惭=xj -惨=xp -曹=ao -草=cu -漕=do -操=fm -螬=io -嘈=ko -槽=mo -糙=mz -艚=vo -黻=ad -才=ap -赙=bc -财=bp -菜=cm -蔡=cx -涪=dk -材=mp -睬=om -猜=qo -采=vm -彩=vp -裁=yg -踩=zm -鹾=ag -痤=bt -嵯=eg -搓=fg -措=fo -挫=ft -撮=fy -厝=io -错=jo -锉=jt -脞=ot -磋=ug -矬=ut -蜅=zd -蹉=zg -寸=ad -吋=kc -村=mc -皴=sp -存=uz -忖=xc -窜=ba -撺=fa -攒=fb -镩=ja -爨=ph -汆=rd -篡=vs -纂=vs -蹿=za -丛=ar -苁=cr -葱=cx -淙=dx -聪=ex -匆=gd -枞=mr -骢=mx -囱=pd -从=rr -琮=wx -璁=wx -悰=xx -蔟=cu -殂=dq -蹙=gz -粗=mq -猝=qu -徂=ra -促=rz -簇=vu -卒=wu -酢=ya -醋=yo -蹴=zd -瘁=bu -萃=cu -淬=du -崔=ef -摧=ff -焠=hu -啐=ku -毳=mm -粹=mu -榱=my -脆=ov -催=rf -璀=wf -悴=xu -翠=yu -大=an -瘩=bk -荙=cz -耷=de -=dz -打=fd -搭=fk -靼=ga -鞑=gz -嗒=kk -哒=kz -妲=na -褡=pk -笪=va -答=vk -怛=xa -达=zd -跶=zz -豆=aq -窦=bd -痘=bq -蔸=ce -斗=du -兜=ep -陡=er -都=et -抖=fu -蚪=iu -钭=ju -篼=ve -逗=za -鲷=ak -凋=dk -掉=fu -刁=ga -钓=jd -铫=jd -铞=jj -雕=pf -吊=kj -叼=kp -屌=uj -碉=uk -貂=vk -调=yk -的=bd -底=gd -锝=jc -得=rc -德=rx -澄=dd -登=dq -嶝=ed -邓=ey -灯=hd -镫=jd -噔=kd -瞪=od -戥=og -僜=rd -磴=ud -等=vc -簦=vd -凳=vj -蹬=zd -宕=bu -荡=cp -菪=cu -挡=fe -铛=je -凼=kd -档=me -裆=pe -砀=up -珰=we -当=xe -党=xe -谠=ye -弟=bp -荻=ch -蒂=cj -滴=dk -涤=dm -邸=ep -抵=fd -骶=gd -镝=jk -嘀=kk -帝=lj -柢=md -棣=mn -嫡=nk -娣=np -睇=op -氐=pd -狄=qh -低=rd -籴=rm -缔=sj -坻=td -堤=tr -地=tv -砥=ud -觌=uj -碲=uj -笛=va -第=vp -敌=wp -诋=yd -羝=yd -翟=yf -谛=yj -迪=za -递=zb -单=ba -疸=ba -瘅=ba -赕=bh -萏=ca -殚=da -淡=dh -澹=dy -聃=ea -郸=eb -耽=ev -担=fa -掸=fa -弹=ga -啖=kh -胆=oa -旦=oa -眈=ov -丹=pa -氮=qh -但=ra -儋=ry -箪=va -蛋=vi -惮=xa -诞=yv -到=da -捣=fe -纛=fs -刀=gp -稻=ha -焘=hf -帱=jc -叨=kd -梼=mc -盗=md -岛=ne -祷=pc -氘=qa -倒=rd -导=vc -忉=xd -悼=xu -蹈=za -道=zq -歹=ad -贷=br -殆=dk -岱=er -带=fj -黛=hr -呔=kd -呆=km -甙=qd -待=rc -代=rd -傣=rd -绐=sk -戴=tg -埭=tn -玳=wd -怠=xs -袋=yr -迨=zs -逮=zv -典=ab -电=av -癫=ba -癜=by -滇=db -淀=dr -巅=ea -阽=ek -掂=fk -靛=fr -店=gk -甸=gt -点=ha -钿=jt -奠=qd -佃=rt -垫=tf -坫=tk -颠=ua -碘=ub -殿=uy -簟=vu -玷=wk -惦=xk -踮=zk -夺=dc -剁=dj -沲=dv -掇=fy -铎=ja -朵=jm -哆=kd -咄=kk -哚=km -驮=md -柁=mv -多=pd -裰=py -缍=sa -堕=te -垛=tm -躲=um -舵=vv -惰=xo -敠=yy -跺=zm -踱=zy -顿=aa -趸=az -沌=dv -炖=hv -钝=jv -吨=kv -囤=kv -盹=ov -盾=po -墩=tw -砘=uv -礅=uw -敦=ww -惇=xz -蹲=zc -遁=zp -铥=js -丢=ps -煅=hy -锻=jy -端=la -断=mj -椴=my -段=py -缎=sy -短=ud -簖=vj -东=ad -董=ca -苳=cd -冻=dd -洞=dk -岽=ed -峒=ek -动=el -咚=kd -栋=md -鸫=na -胨=od -胴=ok -氡=qd -侗=rk -垌=tk -硐=uk -冬=wd -懂=xa -恫=xk -蠹=ai -赌=bo -芏=ct -渎=dd -渡=dy -毒=fd -髑=gi -黩=hd -度=gy -镀=jy -嘟=ke -椟=md -杜=mt -犊=nd -妒=nu -督=oa -睹=oo -肚=ot -牍=pd -独=qi -堵=to -笃=vm -读=yd -兑=be -队=er -镦=jw -堆=tf -碓=uf -憝=xw -怼=xy -对=yc -嗲=kd -鲽=am -爹=fd -揲=fm -瓞=gn -蝶=im -喋=km -牒=pm -绖=st -堞=tm -垤=tt -耋=tt -碟=um -叠=yq -谍=ym -蹀=zm -跌=zn -迭=zp -丁=aa -疔=bd -定=br -顶=da -耵=ed -钉=jd -锭=jr -叮=kd -啶=kr -盯=od -腚=or -鼎=ov -仃=rd -町=td -饤=ud -碇=ur -玎=wd -订=yd -酊=yd -鳄=ag -额=ba -莪=cd -萼=cg -苊=cv -峨=ed -鄂=ek -扼=fv -蛾=id -厄=iv -轭=iv -锇=jd -锷=jg -颚=ka -呃=kv -娥=nd -婀=nk -鹗=nk -鹅=np -腭=og -俄=rd -垩=ta -饿=ud -屙=uk -噩=wk -恶=xa -愕=xg -谔=yg -讹=yv -遏=zo -诶=yu -蒽=cx -摁=fx -恩=xk -而=ap -二=aa -耳=aa -鲕=aa -洱=de -尔=dx -铒=je -鸸=na -儿=pv -贰=qb -弍=qe -佴=re -饵=ue -珥=we -迩=zd -法=ds -阀=mg -乏=pn -伐=rg -罚=sd -垡=tr -砝=us -发=vd -筏=vg -珐=ws -否=ak -缶=rk -鲼=ab -分=bd -芬=cd -瀵=db -汾=dd -奋=dt -焚=hm -吩=kd -粪=mb -粉=md -棼=md -氛=qd -偾=rb -份=rd -纷=sd -坟=tw -鼢=ud -玢=wd -愤=xb -忿=xb -酚=yd -丰=aa -疯=bf -葑=cc -沣=df -冯=dm -酆=ee -峰=ef -奉=fa -烽=hf -蜂=if -锋=jf -风=jx -凤=jy -唪=ka -枫=mf -俸=ra -缝=sz -封=tc -砜=uf -讽=yf -逢=zw -鲂=af -芳=cf -防=ef -邡=ef -房=hf -钫=jf -枋=mf -妨=nf -肪=of -仿=rf -彷=rf -纺=sf -坊=tf -舫=vf -放=wf -方=wp -访=yf -贩=by -蘩=cs -藩=ct -蕃=ct -范=cv -樊=dm -泛=dn -烦=ha -燔=ht -凡=jd -帆=jd -钒=jd -幡=jt -梵=md -番=pt -反=py -翻=py -犯=qv -繁=sr -畈=ty -矾=ud -饭=uy -璠=wt -返=zp -蹯=zt -佛=ra -鲋=ac -甫=ad -黼=ad -夫=an -鳆=aw -赋=bd -负=bd -富=bt -父=bx -苻=cc -芾=cj -芙=cn -茯=cq -菔=cy -莩=cz -副=da -滏=da -浮=dz -附=ec -郛=ev -釜=fa -拂=fa -拊=fc -斧=fj -扶=fn -麸=fn -抚=fv -府=gc -腐=gr -匐=gt -馥=hw -稃=hz -辅=id -蚨=in -辐=it -蝮=iw -蜉=iz -幞=jn -幅=jt -咐=kc -呋=kn -驸=mc -桴=mz -妇=ne -凫=nj -腑=oc -脯=od -肤=on -腹=ow -服=oy -祓=pd -袱=pq -福=pt -阜=pu -孵=pz -氟=qa -付=rc -俯=rc -傅=rc -伏=rq -复=rw -俘=rz -绋=sa -缚=sc -罘=sd -绂=sd -砩=ua -砆=un -弗=va -符=vc -艴=vv -孚=vz -敷=wa -怫=xa -覆=xw -讣=yd -跗=zc -赴=zd -趺=zn -非=aa -鲱=aa -痱=ba -费=bv -菲=ca -沸=da -淝=dv -废=gd -扉=ha -蜚=ia -蜰=io -镄=jb -匪=ka -啡=ka -吠=kq -榧=ma -妃=nv -腓=oa -肺=oj -肥=ov -狒=qa -绯=sa -篚=va -飞=vd -斐=wa -悱=xa -诽=ya -翡=ya -霏=ya -夹=an -尕=gx -轧=iv -尬=ja -旮=jo -钆=jv -嘎=kg -咖=kk -噶=kv -尜=xx -购=bs -觏=cj -苟=ck -沟=ds -岣=ek -拘=fk -鞲=ga -够=gd -勾=gs -钩=js -枸=mk -构=ms -媾=na -鸲=ng -狗=qk -佝=rk -缑=su -垢=tk -彀=uy -篝=va -笱=vk -诟=yk -遘=za -洸=de -广=di -咣=ke -桄=me -胱=oe -犷=qg -光=xe -逛=zq -鬲=aa -戈=ad -疙=bg -革=ca -葛=cv -割=db -哥=dk -歌=dq -隔=ea -搁=fk -骼=gk -髂=gk -虼=ig -蛤=ik -镉=ja -铬=jk -嗝=ka -咯=kk -哿=lk -格=mk -阁=mk -盖=my -鸽=nr -膈=oa -胳=ok -袼=pk -颌=ra -个=ra -仡=rg -搿=ru -纥=sg -塥=ta -圪=tg -饹=uk -硌=uk -舸=vk -各=wk -亘=aa -茛=cn -哏=kn -根=mn -艮=vn -跟=zn -更=an -鲠=an -耿=eh -赓=gb -庚=gn -哽=kn -耕=la -梗=mn -鹒=ng -绠=sn -埂=tn -颈=va -羹=yd -刚=dk -港=dv -岗=ex -缸=fg -扛=fg -钢=jx -冈=kx -杠=mg -肛=og -罡=sv -纲=sx -筻=vn -戆=xl -干=au -疳=bc -甘=ca -苷=cc -泔=dc -淦=dj -澉=dw -擀=fu -秆=hu -尴=jm -赣=lb -柑=mc -杆=mu -橄=mw -肝=ou -旰=ou -绀=sc -坩=tc -乾=ug -矸=uu -竿=vu -玕=wu -敢=wv -感=xg -酐=yu -赶=zu -皋=bu -藁=cm -郜=en -搞=fk -稿=hk -羔=hy -镐=jk -锆=jk -糕=mh -槁=mk -槔=mu -告=nk -杲=om -睾=pu -缟=sk -篙=vk -高=wk -膏=wo -诰=yk -丐=ag -赅=br -芥=ca -溉=dv -陔=er -戤=gg -钙=jg -概=mv -胲=or -垓=tr -改=wv -该=yr -果=an -涡=dd -聒=ek -郭=ew -崞=ez -掴=fw -蜾=in -蝈=iw -锅=jd -帼=jw -呙=kr -国=kw -啯=kw -椁=mz -馘=qk -猓=qn -埚=td -虢=vh -裹=wy -过=zc -鲧=as -滚=dy -辊=iv -棍=mv -绲=sv -磙=uy -衮=wy -鳏=ad -冠=bc -官=bk -贯=bv -莞=ce -灌=df -涫=dk -掼=fb -罐=ff -爟=hf -棺=mk -盥=mp -鹳=nc -关=qd -倌=rk -纶=sv -馆=uk -管=vk -惯=xb -观=yj -工=aa -廾=aa -贡=bg -宫=bk -公=bs -共=cb -恭=cx -拱=fb -巩=gd -汞=gd -功=gl -蚣=is -觥=je -龚=lb -肱=os -供=rb -躬=ug -弓=vg -珙=wb -攻=wg -鲴=ak -谷=bk -痼=bk -贾=bx -菰=cg -菇=ck -沽=dk -汩=do -崮=ek -雇=hf -顾=ia -轱=ik -蛄=ik -觚=jg -锢=jk -钴=jk -呱=kg -固=kk -咕=kk -骨=ko -蛊=mi -梏=mk -鹘=ng -姑=nk -牯=nk -牿=nk -鸪=nu -瞽=ou -股=oy -臌=oy -估=rk -罟=sk -古=uk -辜=uu -鼓=uy -毂=uy -嘏=uy -箍=vj -故=wu -诂=yk -酤=yk -孤=zg -归=ae -规=aj -鳜=aq -鲑=at -贵=ba -宄=bj -皈=by -刿=de -刽=dr -龟=dv -庋=gy -轨=ij -匦=kj -柜=ma -桧=ms -桂=mt -闺=mt -妫=nd -炅=oh -晷=ok -鬼=ps -圭=tt -硅=ut -癸=vd -簋=vm -瑰=wg -诡=yv -跪=zv -寡=bd -剐=dk -刮=dp -括=fk -挂=ft -栝=mk -鸹=np -胍=og -褂=pd -瓜=pn -卦=td -诖=yt -拐=fl -乖=pv -怪=xt -给=sk -铪=jk -哈=kk -瘊=bu -骺=gk -厚=iz -喉=ku -吼=kv -糇=mu -后=pk -猴=qu -候=ru -侯=ru -後=rw -堠=tu -篌=vu -鲎=xa -逅=zp -鳇=aw -癀=bb -皇=bw -黄=cb -荒=cv -潢=db -滉=dg -湟=dw -隍=ew -煌=hw -蟥=ib -蝗=iw -幌=jg -凰=jw -锽=jw -晃=og -徨=rw -磺=ub -簧=vb -篁=vw -璜=wb -肓=wo -恍=xg -慌=xv -惶=xw -谎=yv -遑=zb -翮=ay -贺=bl -荷=ck -菏=ck -河=dk -涸=dk -郃=er -和=hk -龁=ig -蚵=ik -呵=kk -嗬=kk -喝=kv -盉=mh -阖=mm -盒=mr -核=mr -阂=mr -盍=mt -鹤=nd -曷=ov -禾=pm -褐=pv -合=rk -何=rk -壑=ta -赫=td -貉=vk -劾=wl -诃=yk -痕=bn -狠=qn -很=rn -恨=xn -蘅=cd -哼=ka -横=mb -桁=md -姮=na -衡=rd -亨=wa -珩=wd -恒=xa -沆=dj -吭=kj -杭=mj -行=rd -绗=sd -航=vj -颃=wa -虹=ig -顸=aa -寒=bd -罕=bu -菡=ck -涵=dk -汗=du -汉=dy -瀚=dy -邗=ea -邯=ec -捍=fu -撖=fw -撼=fx -焓=hk -焊=hu -蚶=ic -喊=kk -阚=mw -晗=ok -旱=ou -鼾=pu -颔=ra -含=rk -韩=ua -翰=uy -函=vk -悍=xu -憨=xv -憾=xx -酣=yc -皓=bk -薅=cc -蒿=ck -灏=da -浩=dk -濠=dn -郝=et -蚝=im -号=kg -嚆=kk -嚎=kn -嗥=ku -耗=lm -好=nz -颢=oa -昊=od -壕=tn -毫=wm -豪=wn -害=bk -海=dd -骸=gr -嗨=kd -咳=kr -骇=mr -氦=qr -亥=wr -醢=ym -还=za -孩=zr -夥=ad -豁=bk -货=br -藿=cf -获=cq -劐=dc -活=dk -火=dr -混=dv -攉=ff -或=ga -蠖=iy -钬=jh -锪=jx -镬=jy -嚯=kf -=kr -耠=lk -祸=pd -伙=rh -惑=xg -霍=yf -赍=bu -荤=ci -殛=da -浑=di -溷=dn -魂=eg -阍=mo -婚=no -昏=po -馄=uv -珲=wi -诨=yi -鲩=ae -鬟=ay -宦=ba -痪=bn -豢=bn -寰=by -萑=cf -洹=da -浣=de -涣=dn -奂=dn -漶=dx -郇=eg -换=fn -擐=fy -焕=hn -镮=jy -锾=jy -唤=kn -圜=ky -桓=ma -嬛=ny -獾=qf -缓=sy -缳=sy -幻=vg -环=wd -患=xa -欢=yq -逭=zb -宏=bs -蕻=cb -荭=cg -薨=cv -洪=db -鸿=dn -泓=ds -弘=gs -訇=gy -烘=hb -轰=iy -哄=kb -竑=ls -闳=ms -红=sg -黉=xb -讧=yg -互=aa -虍=aq -葫=co -滹=da -冱=da -瓠=dg -沪=dh -湖=do -户=du -浒=du -淴=dx -岵=ek -护=fh -弧=gg -烀=ha -虎=hj -煳=ho -戽=hu -扈=hv -轷=ia -蝴=io -斛=ju -唬=kh -囫=kp -呼=ku -唿=kx -糊=mo -槲=mu -鹄=nn -鹱=ny -祜=pk -乎=pu -狐=qg -猢=qo -壶=ua -鹕=un -胡=uo -觳=uy -笏=vp -琥=wh -瑚=wo -忽=xg -怙=xk -惚=xx -醐=yo -虺=ai -贿=bo -茴=ck -荟=cs -蕙=cx -汇=dk -洄=dk -浍=ds -彗=ef -隳=ex -挥=fi -辉=gi -麾=gm -秽=hd -烩=hs -蛔=ik -蟪=ix -哕=kd -咴=kh -回=kk -喙=kn -阓=mb -晦=od -晖=oi -毁=py -会=rs -徽=rw -缋=sb -绘=ss -恚=tx -卉=uc -灰=uh -惠=xa -悔=xd -慧=xf -恢=xh -讳=ya -诲=yd -诙=yh -翚=yi -画=ak -花=cv -划=dg -滑=dg -砉=fu -铧=ju -哗=ku -桦=mu -骅=mu -猾=qg -化=rv -华=ur -话=yk -淮=df -槐=mg -徊=rk -坏=td -怀=xd -踝=zn -黑=ha -嘿=kh -岔=be -察=bx -茶=cm -茬=ct -汊=dd -刹=dx -插=fa -搽=fm -锸=ja -镲=jx -喳=ka -查=ma -楂=ma -杈=md -槎=mg -檫=mx -姹=nq -衩=pd -碴=ua -馇=ua -叉=yd -差=yg -诧=yq -瘳=bp -抽=fa -稠=hk -瞅=oh -臭=pq -俦=rc -仇=rj -绸=sk -畴=tc -丑=va -筹=vc -愁=xh -惆=xk -酬=ya -诪=yc -雠=yf -踌=zc -窗=bd -疮=bv -创=dr -床=gm -幢=ja -闯=mm -怆=xv -车=aa -澈=dw -扯=fv -撤=fw -彻=rd -坼=td -砗=ui -掣=up -屮=va -宸=bn -沉=dj -陈=ed -抻=fa -辰=in -龀=iv -臣=ka -嗔=kb -郴=me -榇=mm -晨=on -衬=pc -尘=tx -碜=up -琛=wm -忱=xv -谶=yg -谌=yv -趁=zp -宬=bv -撑=fu -成=gv -秤=hu -程=hw -称=hx -蛏=it -铖=jv -呈=kw -橙=md -盛=mg -骋=mg -枨=mn -柽=mt -瞠=ot -塍=ot -晟=ov -乘=pn -裎=pw -城=tv -埕=tw -丞=va -承=vn -琤=wa -惩=xr -诚=yv -酲=yw -逞=zk -鲳=ao -厂=ap -畅=ap -苌=cn -菖=co -昶=do -锠=jo -唱=ko -鬯=kv -阊=mo -氅=mx -嫦=nj -娼=no -昌=oo -肠=op -长=pn -猖=qo -徜=rk -倘=rk -伥=rn -倡=ro -偿=rs -场=tp -敞=wx -常=xj -怅=xn -尝=xs -痴=bk -瘛=bx -苴=cq -茌=cu -池=dv -豉=dy -蚩=ei -耻=ev -持=fc -魑=gd -弛=gv -炽=hb -螭=id -斥=pd -哧=kd -吃=kg -嗤=ki -呎=kn -叱=kq -啻=lk -榘=mu -驰=mv -媸=ni -鸱=np -眵=od -眙=ok -褫=ph -侈=rd -傺=rx -赤=td -墀=tn -饬=ul -尺=un -翅=uy -篪=vh -齿=vk -笞=vk -敕=wa -踟=zk -迟=zu -觇=aj -冁=bn -蒇=cb -澶=da -浐=di -潺=dz -搀=fd -掺=fp -廛=gt -蝉=ia -蟾=iy -铲=ji -产=lp -阐=ma -骣=mz -婵=na -禅=pa -缠=sa -馋=ud -羼=uy -颤=wa -忏=xu -谄=ya -谗=yd -躔=zt -潮=do -剿=dv -抄=fp -炒=hp -焯=hu -钞=jp -嘲=ko -吵=kp -耖=lp -晁=od -绰=su -朝=uo -巢=vn -怊=xk -超=zk -虿=ai -瘥=bg -拆=fd -钗=jd -柴=mv -侪=ra -豺=vp -辍=iy -龊=iz -啜=ky -戳=yg -踔=zu -莼=cv -淳=dz -蠢=fi -春=fo -骱=ga -唇=ik -蝽=io -椿=mo -鹑=nw -纯=sv -醇=yz -串=aa -穿=bp -钏=ja -喘=ka -椽=mn -川=pa -舛=pa -氚=qa -传=rd -舡=vg -船=vk -巛=vv -遄=ze -虫=ad -宠=bl -茺=ce -冲=da -崇=ex -舂=fa -铳=je -重=pa -艟=va -充=we -憧=xa -忡=xa -翀=ya -亍=ad -贮=ba -刍=de -雏=df -滁=dh -除=eh -出=ek -搐=ft -黜=hk -厨=ic -蜍=ih -触=ji -锄=jl -橱=mc -樗=mg -楮=mo -楚=mr -杵=mu -初=pd -褚=po -储=ro -绌=sk -矗=ua -础=uk -处=wd -畜=wt -怵=xd -憷=xr -蹰=zc -躇=zo -陲=ea -捶=fa -炊=hq -锤=ja -吹=kq -棰=ma -椎=mf -槌=mz -垂=pa -揣=fa -搋=fh -膪=ok -踹=za -鲛=af -皎=bf -窖=bk -茭=cf -蕉=ch -艽=cj -角=da -浇=de -湫=dh -峤=ea -郊=ew -挢=fa -搅=fj -焦=hr -轿=ia -蛟=if -较=if -铰=jf -叫=ka -嚼=kc -噍=kh -骄=ma -椒=my -娇=na -姣=nf -鹪=nr -脚=oe -胶=of -狡=qf -侥=re -佼=rf -僬=rh -徼=rw -绞=sf -缴=sw -矫=ua -饺=uf -礁=uh -筊=vf -敫=wb -交=wf -教=wt -觉=xj -醮=yh -酵=yz -跤=zf -蒋=cc -茳=cg -洚=da -将=dc -浆=dd -奖=dd -豇=dg -江=dg -降=ea -疆=ga -强=gi -弶=gx -匠=kj -耩=la -桨=md -糨=mi -犟=ng -僵=ra -绛=sa -缰=sa -礓=ua -讲=ya -酱=yd -姜=yn -丌=aa -鲚=aa -冀=ab -鲫=ae -髻=ak -击=ak -棘=an -乩=av -寄=bk -瘠=bo -疾=bu -寂=by -荠=ca -蕺=ca -蓟=cd -芨=cn -藉=co -蒺=cu -芰=cy -济=da -剞=dd -汲=dn -脊=do -洎=do -剂=dw -激=dw -觊=ej -岌=en -嵴=eo -即=ev -际=ex -挤=fa -揖=fe -掎=fk -技=fy -积=hb -嵇=he -稽=ho -稷=hw -季=hz -辑=ie -虮=ij -哜=ka -唧=ke -戢=kg -叽=kj -咭=kk -麂=lj -骥=mb -楫=me -机=mj -极=mn -集=mr -姬=na -犄=nk -嫉=nu -妓=ny -鸡=ny -肌=oj -几=pg -及=pn -祭=px -佶=rk -偈=rv -伎=ry -绩=sb -缉=se -羁=sm -级=sn -继=sv -纪=sv -墼=ta -畸=tk -圾=tn -基=tq -戟=ug -饥=uj -矶=uj -吉=uk -屐=uy -亟=va -暨=va -箕=vb -笄=vc -笈=vn -籍=vo -畿=vt -既=vv -己=vv -齑=wa -玑=wj -急=xd -忌=xv -悸=xz -霁=ya -讥=yj -诘=yk -计=yu -记=yv -跻=za -迹=zw -跽=zx -戋=ad -戬=ag -柬=an -鲣=at -贱=bg -謇=by -蹇=bz -茧=ci -菅=ck -蒹=cn -荐=cz -湔=dd -溅=dg -渐=dj -减=dk -涧=do -剑=dr -歼=du -捡=fa -拣=fd -搛=fn -鞯=gz -肩=ho -煎=hq -鉴=ja -锏=jo -键=jz -见=kv -囝=kz -检=ma -监=ma -枧=mj -槛=mm -间=mo -楗=mz -毽=mz -鹣=nq -牮=nr -奸=nu -犍=nz -睑=oa -腱=oz -裥=po -剪=qd -兼=qn -翦=qy -俭=ra -件=rn -僭=ro -健=rz -缄=sk -缣=sn -坚=ta -硷=ua -饯=ug -碱=uk -箭=vd -笺=vg -笕=vj -舰=vj -简=vo -尖=xd -谫=yd -谏=yn -艰=yn -趼=zc -践=zg -建=zv -踺=zz -巾=aa -晋=ao -赆=bd -堇=ca -荩=cd -觐=cj -津=da -浸=dy -廑=ga -靳=gj -烬=hd -锦=jj -噤=kx -槿=ma -禁=mx -妗=nv -斤=pa -衿=pv -襟=px -金=rb -今=rv -仅=ry -紧=sa -缙=so -矜=sv -馑=ua -尽=ud -筋=vl -劲=vl -卺=vv -瑾=wa -谨=ya -进=za -近=zj -军=bi -皲=bp -菌=ch -莙=ck -浚=dw -郡=ev -峻=ew -捃=fk -钧=jd -麇=lh -竣=lw -骏=mw -隽=rp -俊=rw -均=td -筠=va -君=vk -逡=zw -韭=aa -鬏=ah -旧=ao -究=bj -疚=bn -酒=dy -揪=fh -厩=iv -啾=kh -桕=ma -柩=mn -阄=mv -鸠=nj -鹫=nw -臼=pa -九=pg -灸=ph -舅=pl -久=pn -僦=rd -纠=sa -救=wa -就=wd -咎=wk -玖=wn -赳=za -桊=bm -眷=bo -卷=bv -涓=do -鄄=ex -捐=fo -镌=jp -锩=jv -圈=kv -鹃=nk -娟=no -蠲=qi -狷=qo -倦=rv -绢=so -惓=xv -窘=bk -扃=hk -炯=hk -迥=zk -疬=bl -藜=cd -苈=cl -蕨=cq -澧=dd -劂=di -决=dn -崛=ek -掘=fk -抉=fn -撅=fq -攫=fy -爝=hc -厥=iq -觖=jn -镢=jq -噱=kn -噘=kq -桷=mj -橛=mq -矍=oy -獗=qq -倔=rk -绝=sv -爵=vc -孓=vn -玦=wn -珏=ww -谲=yk -诀=yn -蹶=zq -具=qb -窭=bn -疽=bq -苣=ca -莒=ck -菊=cm -沮=dq -剧=du -聚=en -拒=fa -飓=fb -据=fk -句=gk -鞠=gm -鞫=gy -炬=ha -焗=hk -龃=iq -钜=ja -锔=jk -锯=jk -巨=ka -咀=kq -榉=ma -驹=mk -椐=mk -橘=mk -桔=mk -犋=nb -裾=pk -雎=qf -狙=qq -俱=rb -倨=rk -矩=ua -居=uk -局=uk -屦=un -琚=wk -举=xa -惧=xb -讵=ya -醵=yn -距=za -踽=zd -遽=zh -踞=zk -趄=zq -颊=aa -甲=aa -戛=ag -痂=bk -家=bn -瘕=by -茄=ck -荚=cn -葭=cy -浃=dn -岬=ea -郏=ea -稼=hn -蛱=in -钾=ja -铗=jn -镓=jn -加=lk -枷=mk -驾=ml -架=ml -嫁=nn -胛=oa -袷=pk -价=ra -伽=rk -佳=rt -假=ry -嘉=uk -笳=vk -珈=wk -恝=xf -袈=yl -跏=zk -迦=zl -鲒=ak -疥=ba -皆=ba -疖=be -节=ce -洁=dk -阶=ea -撷=fa -拮=fk -接=fn -捷=fr -揭=fv -戒=gc -秸=hk -杰=hm -蚧=ia -解=jn -喈=kb -嗟=kg -唶=ko -竭=lv -楷=mb -桀=mp -姐=nq -婕=nr -睫=or -介=ra -街=rd -截=rg -借=ro -结=sk -界=ta -劫=tl -颉=ua -届=ua -碣=uv -孑=va -玠=wa -诫=yd -讦=yu -羯=yv -井=aa -鲸=ax -痉=bg -茎=cg -菁=co -净=da -荆=dc -濂=dn -潋=dw -阱=ea -旌=fa -静=fa -靓=fj -弪=gg -黥=hx -镜=je -竞=le -竟=le -靖=lo -粳=mn -精=mo -婧=no -肼=oa -胫=og -晶=oo -腈=oo -睛=oo -景=ox -獍=qe -径=rg -儆=rw -经=sg -境=te -兢=ue -敬=wc -京=wx -憬=xx -惊=xx -警=yy -迳=zv -卡=ad -咔=kd -喀=kk -胩=od -佧=rd -口=aa -囗=aa -寇=by -芤=cv -蔻=cy -叩=ek -扣=fk -抠=fx -眍=ox -筘=vk -贶=be -夼=da -况=de -邝=eg -劻=kl -哐=kw -匡=kw -框=mw -旷=og -眶=ow -狂=qw -纩=sg -圹=tg -矿=ug -筐=vw -诓=yw -诳=yw -颗=aa -疴=bk -客=bk -窠=bn -苛=ck -可=dk -溘=dm -渴=dv -刻=dw -岢=ek -髁=gn -稞=hn -科=hu -轲=ik -蝌=iu -钶=jk -锞=jn -嗑=km -柯=mk -骒=mn -棵=mn -瞌=om -氪=qe -缂=sg -坷=tk -克=ue -壳=uj -砢=uk -磕=um -颏=wa -珂=wk -恪=xk -课=yn -啃=ko -裉=pn -垦=tv -肯=vo -恳=xv -铿=jt -坑=tj -抗=fj -康=gn -炕=hj -钪=jj -闶=mj -糠=mn -伉=rj -亢=wj -慷=xn -莰=cq -刊=da -瞰=ow -戡=qg -勘=ql -龛=rl -侃=rv -坎=tq -堪=tv -磡=ul -看=uo -砍=uq -拷=fg -烤=hg -铐=jg -栲=mg -靠=na -犒=nk -考=tg -尻=uj -开=ac -蒈=cb -剀=de -凯=ej -揩=fb -锴=jb -锎=jc -铠=jv -垲=tv -忾=xq -恺=xv -慨=xv -扩=fg -廓=ge -蛞=ik -阔=mk -髡=ae -鲲=av -捆=fm -焜=hv -锟=jv -困=km -阃=mm -昆=ov -坤=ta -堃=tf -琨=wv -悃=xm -醌=yv -宽=bj -髋=gj -款=uq -空=bg -崆=eg -控=fg -倥=rg -箜=vg -恐=xg -孔=zv -窟=bk -苦=ck -刳=dd -库=gi -骷=gk -枯=mk -裤=pi -哭=qk -绔=sg -堀=tk -喾=xk -酷=yk -窥=bj -蒉=cb -葵=cd -溃=db -奎=dt -聩=eb -岿=ee -隗=eg -亏=eg -揆=fd -魁=gu -蝰=it -馗=jo -匮=kb -喟=ko -喹=kt -盔=mu -暌=od -睽=od -夔=qw -傀=rg -馈=ub -篑=vb -悝=xa -愦=xb -愧=xg -逵=zt -跬=zt -夸=dg -挎=fg -胯=og -侉=rg -垮=tg -跨=zg -蒯=dc -郐=er -哙=ks -脍=os -狯=qs -侩=rs -块=tn -筷=vn -快=xn -瘌=bd -落=ck -剌=da -拉=fl -蜡=io -喇=kd -啦=kl -辣=ln -旯=oj -腊=oo -垃=tl -砬=ul -邋=zv -瘘=bn -蒌=cn -漏=dd -嵝=en -陋=ev -搂=fn -髅=gn -蝼=in -镂=jn -喽=kn -耧=ln -楼=mn -娄=mn -偻=rn -篓=vn -露=yk -疗=ba -寥=bp -寮=bx -蓼=cp -潦=dx -聊=ee -撂=fk -撩=fx -廖=gp -燎=hx -钌=ja -尥=jd -镣=jx -嘹=kx -料=mu -鹩=nu -獠=qx -僚=rx -缭=sx -辽=zv -两=ad -良=dn -凉=dx -魉=gd -辆=id -唡=kd -梁=md -粱=md -粮=mn -椋=mx -量=oa -晾=ox -俩=rd -墚=tm -亮=wj -谅=yx -踉=zn -鳓=al -泐=dl -勒=gl -嘞=kl -叻=kl -肋=ol -乐=pd -仂=rl -了=va -冷=ds -楞=mf -棱=mw -塄=tf -愣=xf -莨=cn -蒗=cn -浪=dn -朗=do -郎=ed -廊=ge -稂=hn -螂=ie -锒=jn -啷=ke -榔=me -阆=mn -狼=qn -琅=wn -里=aa -鲤=aa -丽=ad -鲡=ad -鳢=ad -吏=an -嫠=an -痢=bd -疠=bp -莉=cd -荔=cl -莅=cl -蓠=cs -利=dh -沥=dl -溧=dm -漓=ds -郦=ea -力=gp -黎=hd -黧=hh -戾=hq -厘=ia -蜊=id -轹=id -历=il -励=il -厉=ip -蛎=ip -锂=ja -哩=ka -喱=ka -呖=kl -唳=kq -立=la -骊=md -栎=md -梨=mh -粒=ml -枥=ml -粝=mp -李=mz -鹂=na -娌=na -犁=nh -礼=pv -狸=qa -猁=qd -俚=ra -例=rd -俐=rd -俪=rd -傈=rm -罹=sf -缡=ss -詈=sy -坜=tl -砾=ud -砺=up -蠡=vi -笠=vl -篥=vm -隶=vn -篱=vs -理=wa -离=wd -璃=ws -栗=xm -醴=yd -雳=yl -逦=za -跞=zd -兰=bs -蓝=cm -澜=dd -滥=dm -漤=dn -岚=ef -揽=fj -拦=fs -烂=hs -览=ja -镧=jd -阑=md -榄=mj -栏=ms -婪=nm -褴=pm -缆=sj -罱=su -篮=vm -斓=wd -懒=xb -谰=yd -痨=bl -牢=bn -劳=cl -涝=dl -崂=el -捞=fl -烙=hk -铹=jl -铑=jv -唠=kl -耢=ll -栳=mv -嫪=np -姥=nv -佬=rv -老=tv -酪=yk -醪=yp -赖=ab -来=an -赉=ba -癞=bb -莱=cn -濑=db -涞=dn -崃=en -铼=jn -睐=on -徕=rn -籁=vb -鲢=az -帘=bj -蔹=cw -莲=cz -殓=da -奁=dx -涟=dz -联=ed -廉=gn -炼=hd -蠊=in -镰=jn -链=jz -楝=mn -脸=oa -臁=on -裣=pa -裢=pz -练=sd -敛=wr -琏=wz -怜=xs -恋=xw -连=zi -临=aa -鳞=aa -赁=br -蔺=cf -淋=dm -凛=dx -嶙=ea -邻=er -拎=fs -廪=gx -辚=ia -啉=km -麟=la -粦=ma -林=mm -粼=mv -檩=mx -瞵=oa -膦=oa -磷=ua -吝=wk -琳=wm -懔=xx -霖=ym -躏=zf -遴=zm -赂=bk -瘰=bs -萝=cd -荦=cn -泺=dd -洛=dk -漯=ds -捋=fc -摞=fs -螺=is -锣=jd -镙=js -啰=kd -椤=md -骆=mk -骡=ms -脶=od -裸=pn -猡=qd -倮=rn -罗=sd -络=sk -箩=vd -蠃=wd -雒=wf -珞=wk -逻=zs -沦=dv -抡=fv -轮=iv -囵=kv -伦=rv -仑=rv -论=yv -瘤=bt -浏=dd -溜=dt -流=dv -刘=dw -旒=fv -熘=ht -鎏=jd -镏=jt -锍=jv -柳=me -骝=mt -榴=mt -鹨=ny -绺=sk -留=tp -碌=ud -馏=ut -硫=uv -六=wb -琉=wv -遛=zp -滦=dm -卵=pd -乱=pv -峦=we -銮=wj -栾=wm -鸾=wn -娈=wn -脔=wr -挛=wu -孪=wz -癃=ba -窿=bf -茏=cl -泷=dl -隆=ef -陇=el -拢=fl -咙=kl -聋=le -砻=lu -栊=ml -胧=ol -垄=tl -垅=tl -龙=ud -笼=vl -珑=wl -掠=fx -锊=jc -略=tk -颅=aa -鲁=ao -卢=au -鲈=au -卤=ax -芦=ch -渌=dd -潞=dk -漉=dl -泸=du -陆=ek -掳=fl -撸=fo -庐=gh -鹿=gv -炉=hh -虏=hl -辂=ik -辘=il -轳=iu -镥=jo -噜=ko -麓=lm -栌=mi -橹=mo -氇=mo -鸬=na -鹭=nz -胪=ou -禄=pd -绿=sd -垆=tu -硵=ux -录=vd -箓=vd -舻=vi -簏=vl -璐=wk -戮=yg -路=zk -逯=zv -滤=dx -旅=fn -稆=hk -虑=hx -铝=jk -吕=kk -驴=mh -闾=mk -榈=mk -膂=of -褛=pn -氯=qd -律=ra -侣=rk -缕=sn -屡=un -履=uw -率=wu -鬣=aa -列=dd -冽=dd -洌=dd -捩=fq -烈=hd -咧=kd -猎=qo -埒=tc -劣=xl -裂=ya -趔=zd -躐=zv -鲮=aw -苓=cs -菱=cw -泠=ds -凌=dw -灵=eh -岭=es -聆=es -陵=ew -酃=ey -龄=is -蛉=is -铃=js -另=kl -囹=ks -呤=ks -棂=mh -柃=ms -鸰=nr -〇=oo -领=ra -令=rs -伶=rs -瓴=rw -翎=ry -绫=sw -玲=ws -零=ys -羚=ys -耒=an -蕾=ct -泪=do -擂=ft -镭=jt -类=md -檑=mt -嫘=ns -儡=rt -缧=ss -累=ts -垒=ts -礌=ut -磊=uu -羸=wd -酹=yc -诔=yn -雷=yt -抹=fn -麻=gm -蟆=id -蚂=im -吗=km -嘛=km -唛=kw -骂=mk -杩=mm -妈=nm -嬷=ns -祃=pm -么=ps -犸=qm -码=um -马=va -玛=wm -某=cm -蛑=in -鍪=js -哞=kn -眸=on -侔=rn -牟=sn -缪=sp -谋=ym -藐=ce -苗=ct -淼=dd -渺=dp -描=ft -庙=ga -秒=hp -喵=kt -杪=mp -鹋=nc -妙=np -眇=op -瞄=ot -缈=sp -邈=zv -麽=gs -门=dg -扪=fm -焖=hx -钔=jm -闷=mx -们=rm -懑=xd -悗=xv -蒙=cn -萌=co -瞢=co -甍=cw -蜢=im -蠓=in -虻=iv -锰=jm -幪=jn -梦=md -檬=mn -盟=mo -孟=mz -朦=on -猛=qm -獴=qn -礞=un -艋=vm -艨=vn -氓=wq -懵=xo -勐=zl -莽=cc -茫=cv -芒=cv -漭=dc -邙=ew -蟒=ic -厖=ip -铓=jv -硭=uv -盲=wo -忙=xv -芈=aa -宓=bd -密=be -蜜=bi -幂=bj -米=bm -蘼=ca -汨=do -泌=dp -靡=ga -弭=ge -糜=gm -縻=gs -弥=gx -嘧=ke -咪=km -麋=lm -眯=om -脒=om -祢=px -猕=qx -狝=qx -觅=vj -糸=vx -敉=wm -谧=ym -谜=yz -醚=yz -迷=zm -鳗=ay -颟=ca -蔓=cy -满=dd -漫=dy -鞔=gv -熳=hy -螨=id -幔=jy -镘=jy -瞒=od -曼=oy -缦=sy -埋=ta -墁=ty -馒=uy -蛮=wi -慢=xy -谩=yy -贸=bp -茆=ce -茂=cp -茅=cp -泖=de -峁=ee -卯=ep -旄=fm -蝥=is -铆=je -帽=jo -锚=jt -髦=ma -牦=nm -昴=oe -冒=oo -瞀=os -毛=pv -猫=qt -蟊=si -矛=sp -耄=tm -貌=ve -瑁=wo -袤=wy -懋=xm -劢=al -荬=cd -麦=fw -脉=on -卖=ud -买=vd -霾=ya -迈=za -面=aa -湎=da -勉=dl -免=dv -沔=dv -渑=dv -棉=mj -娩=nv -腼=oa -眠=oq -眄=ov -冕=ov -缅=sa -绵=sj -鳘=ar -苠=cq -泯=dq -岷=eq -抿=fq -皿=ka -黾=kv -闽=mi -闵=mw -旻=ow -缗=so -民=vq -珉=wq -敏=wr -玟=ww -愍=xv -悯=xw -末=an -寞=bd -瘼=bd -莫=cd -蓦=cm -茉=cn -摹=cu -漠=dd -沫=dn -没=dy -殁=dy -陌=eb -摸=fd -魔=gg -磨=gu -摩=gu -秣=hn -默=hq -镆=jd -耱=lu -模=md -嫫=nd -膜=od -墨=th -馍=ud -貊=vb -貘=vd -谟=yd -谬=yp -木=an -幕=cj -募=cl -苜=co -暮=co -墓=ct -慕=cx -沐=dm -拇=fd -穆=hp -钼=jo -目=ka -毪=mn -姆=nd -牡=nt -睦=ot -仫=rs -坶=td -母=vd -牧=wn -亩=wt -霂=ym -灭=ah -蔑=cd -乜=gv -蠛=id -咩=ky -篾=vd -冥=bb -茗=ck -溟=db -螟=ib -铭=jk -鸣=nk -暝=ob -明=oo -名=pk -命=re -酩=yk -寐=bn -莓=cd -湄=do -浼=dv -嵋=eo -郿=ev -魅=gn -煤=hm -镁=jd -镅=jo -梅=md -楣=mo -媒=nm -妹=nn -媚=no -鹛=nv -昧=on -眛=on -袂=pn -猸=qo -每=rd -眉=vo -枚=wm -玫=ww -美=yd -酶=yd -霉=yd -那=ev -捺=fx -钠=jd -镎=ju -哪=ke -呐=kr -呢=kv -娜=ne -肭=od -衲=pd -拿=ru -纳=sd -耨=lc -茑=cn -溺=dd -鸟=na -嬲=nl -袅=ny -脲=od -尿=ud -娘=nn -酿=yn -讷=yd -嫩=nw -恁=xr -能=sv -囊=ay -攮=fy -囔=ky -曩=oy -馕=uy -鲵=ae -泥=dv -拟=fr -旎=fv -铌=jv -匿=kk -妮=nv -腻=od -睨=oe -昵=ov -猊=qe -倪=re -伲=rv -你=rx -坭=tv -尼=uv -怩=xv -霓=ye -逆=zq -蝻=iu -囡=kn -喃=ku -楠=mu -腩=ou -男=tl -赧=ty -南=uu -难=yf -孬=az -淖=du -挠=fe -蛲=ie -铙=je -呶=ky -桡=me -闹=mj -脑=ok -猱=qm -垴=tk -硇=ux -瑙=wx -恼=xk -耐=ac -艿=cp -萘=cx -奈=dx -乃=gp -鼐=gv -柰=mx -奶=np -氖=qp -廿=aa -鲇=ak -鲶=ax -蔫=ch -撵=fi -拈=fk -捻=fx -黏=hk -辇=ia -粘=mk -年=ra -埝=tx -碾=un -念=xr -您=xr -搦=fd -挪=fe -锘=jk -喏=kk -糯=ma -傩=rf -懦=xa -诺=yk -扭=fa -钮=ja -妞=na -牛=pa -狃=qa -纽=sa -忸=xa -暖=oy -农=dn -浓=dn -秾=hn -哝=kn -脓=on -侬=rn -弄=wc -疟=ba -虐=ha -弩=gn -帑=jn -努=ln -驽=mn -奴=ny -胬=rn -怒=xn -孥=zn -钕=jn -女=va -恧=xa -衄=xa -蘖=cm -孽=cz -涅=dt -颞=ea -陧=et -聂=ey -捏=ft -镍=jm -镊=jy -啮=kk -嗫=ky -臬=pm -肀=va -蹑=zy -甯=ba -宁=bd -苎=ca -泞=dd -凝=dr -聍=ed -拧=fd -咛=kd -柠=md -狞=qd -佞=rn -内=ad -馁=un -噢=kd -哦=kd -喔=kt -嚄=ky -藕=cd -沤=dx -欧=kq -瓯=kw -呕=kx -区=kx -殴=ky -耦=ld -鸥=nk -其=qb -偶=rd -怄=xx -讴=yx -葩=cv -帕=jb -啪=kb -杷=mv -爬=vv -筢=vv -琶=wv -怕=xb -趴=zb -剖=dl -掊=fk -裒=wy -漂=dx -剽=dx -殍=dz -螵=ix -嘌=kx -朴=md -嫖=nx -瞟=ox -缥=sx -飘=xf -瓢=xg -票=xx -慓=xx -瓫=bw -湓=dm -喷=kb -喯=kc -盆=mb -蓬=cz -澎=dp -捧=fa -抨=fu -烹=hw -蟛=ip -嘭=kp -棚=mo -鹏=on -朋=oo -膨=op -堋=to -碰=ua -硼=uo -彭=up -砰=uu -篷=vz -怦=xu -蒡=cf -滂=df -庞=gl -螃=if -旁=lf -耪=lf -胖=oa -乓=pd -徬=rf -逄=zw -丕=aa -痞=bk -疲=bp -癖=bu -淠=da -劈=du -邳=ea -郫=ep -陴=eu -披=fp -擗=fu -批=fv -庀=gv -罴=hs -蜱=iu -蚍=iv -铍=jp -匹=ke -啤=ku -噼=ku -枇=mv -媲=nv -脾=ou -僻=ru -仳=rv -纰=sv -坯=ta -埤=tu -毗=tv -圮=tv -鼙=uu -屁=uv -砒=uv -疋=vr -貔=vv -皮=vy -甓=wu -琵=wv -霹=yu -譬=yu -爿=ap -叛=by -泮=da -判=db -潘=dt -拚=fc -蟠=it -攀=mu -盘=mv -盼=od -袢=pa -襻=pu -畔=ta -磐=uv -蹒=zd -疱=bv -泡=dv -匏=dv -抛=fl -庖=gv -炮=hv -咆=kv -脬=oz -袍=pv -狍=qv -跑=zv -蒎=cn -湃=df -派=dn -排=fa -拍=fb -哌=kn -牌=pu -徘=ra -俳=ra -迫=zb -翩=hy -骗=mc -骈=mc -犏=nc -胼=oc -片=pv -偏=rc -篇=vc -谝=yc -蹁=zc -贫=bb -聘=eg -拼=fc -品=kk -榀=mk -嫔=nb -姘=nc -牝=nv -频=va -颦=vu -攴=ay -魄=bg -皤=bt -泼=dd -鄱=ep -钋=jd -钷=jk -叵=kk -粕=mb -婆=nd -颇=pa -坡=tp -破=up -笸=vk -珀=wb -葡=cd -莆=cd -蒲=cd -菩=ck -溥=dc -浦=dd -濮=dn -扑=fd -匍=gd -铺=jd -镤=jn -镨=jo -圃=kd -噗=kn -氆=mo -普=qo -仆=rd -璞=wn -谱=yo -蹼=zn -苤=ca -撇=fw -瞥=ox -氕=qp -平=au -鲆=au -萍=cu -苹=cu -洴=dc -凭=jr -呯=ku -枰=mu -娉=ng -乒=pp -瓶=qw -俜=rg -坪=tu -屏=uc -评=yu -赔=bk -沛=dj -陪=ek -旆=fj -锫=jk -帔=jp -呸=ka -辔=ks -胚=oa -佩=rj -培=tk -珮=wj -裴=ya -霈=yj -醅=yk -配=yv -翘=ay -窍=bg -荞=ca -劁=dr -峭=eo -撬=fm -鞒=ga -巧=gg -鞘=go -锹=jh -桥=ma -樵=mh -橇=mm -瞧=oh -乔=pa -侨=ra -俏=ro -缲=sm -硚=ua -硗=ue -敲=wy -雀=xf -憔=xh -愀=xh -悄=xo -谯=yh -诮=yo -跷=ze -蔷=ck -抢=fv -炝=hv -蜣=iv -锵=jc -镪=ji -锖=jo -呛=kv -樯=mk -枪=mv -嫱=nk -腔=og -襁=pi -戗=rg -墙=tk -戕=vg -羟=yg -羌=yv -跄=zv -妻=an -鳍=ao -七=av -萁=cb -葺=ce -蕲=cj -萋=cn -芪=cq -芑=cv -淇=db -沏=dd -漆=dd -契=df -汔=dg -奇=dk -泣=dl -凄=dn -汽=dq -欹=dq -亓=ea -崎=ek -祁=ep -岂=ev -屺=ev -岖=ex -岐=ey -旗=fb -戚=gx -启=hk -蛴=ia -蜞=ib -颀=ja -器=kk -嘁=kx -麒=lb -棋=mb -骐=mb -柒=md -骑=mk -桤=mv -杞=mv -槭=mx -栖=mx -脐=oa -祺=pb -祈=pj -期=qo -欺=qq -綦=qs -乞=rg -气=rv -企=rv -绮=sk -圻=tj -耆=to -畦=tt -碛=ub -砌=ud -矻=ug -歧=vy -齐=wa -琪=wb -弃=wc -琦=wk -玘=wv -憩=xp -讫=yg -蹊=zd -踦=zk -迄=zr -起=zv -骞=bm -搴=bu -褰=by -荨=cc -芡=cq -芊=cu -茜=cx -浅=dg -牵=dn -潜=do -欠=dr -岍=ec -嵌=eq -阡=eu -掮=fo -扦=fu -黔=hv -虔=hw -钳=jc -钱=jg -铅=jk -钎=ju -钤=jv -椠=mi -肷=oq -千=pu -前=qd -歉=qq -佥=ra -倩=ro -仟=ru -纤=su -缱=sz -堑=ti -签=va -箝=vc -慊=xn -愆=xr -悭=xt -谦=yn -谴=yz -遣=za -迁=zp -寝=by -芹=cj -勤=cl -芩=cv -溱=dh -沁=dx -秦=fh -揿=fq -擒=fs -螓=ih -钦=jq -锓=jy -嗪=kh -噙=ks -吣=kx -亲=lm -檎=md -禽=rd -侵=ry -衾=ry -琴=wv -覃=xu -裙=pk -群=vy -求=ad -鳅=ah -裘=ay -赇=bd -酋=by -萩=ch -泅=dr -邱=ep -秋=hh -蚯=ia -虬=iv -蝤=iy -囚=kr -楸=mh -糗=mq -鹙=nh -丘=pa -鼽=pj -犰=qj -俅=rd -巯=vv -球=wd -逑=za -遒=zq -鬈=av -泉=bd -券=bd -拳=bu -痊=bw -颧=ca -荃=cw -犬=dd -蜷=iv -辁=iw -铨=jw -权=my -全=rw -绻=sv -畎=tq -筌=vw -悛=xw -劝=yl -诠=yw -醛=yw -踡=zv -穹=bg -穷=bl -茕=cu -邛=eg -蛩=ig -銎=jg -筇=ve -琼=wx -跫=zg -瘸=br -却=ee -缺=fn -炔=hn -阕=md -榷=mf -阙=mq -鹊=nc -确=uj -悫=xu -曲=aa -癯=bf -蕖=cm -蘧=cz -取=ey -劬=gl -觑=hj -黢=hw -蛐=ia -龋=id -蛆=iq -蠼=iy -渠=md -阒=mq -驱=mx -娶=ne -瞿=of -朐=ok -氍=om -祛=ps -衢=rd -去=ts -屈=uk -麴=um -磲=um -躯=ux -璩=wn -诎=yk -趋=ze -趣=zy -葜=cd -洽=dk -掐=fa -恰=xk -且=aa -窃=bd -郄=ex -锲=jd -妾=ln -切=qd -挈=uf -箧=vn -惬=xn -怯=xs -顷=aa -鲭=ao -苘=ck -清=do -青=fo -罄=fu -庼=ga -庆=gd -轻=ig -蜻=io -圊=ko -檠=mc -晴=oo -卿=pe -氢=qg -氰=qo -倾=ra -綮=sh -擎=uc -磬=uu -箐=vo -情=xo -请=yo -謦=yu -肉=ar -揉=fm -鞣=gm -糅=mm -柔=sm -蹂=zm -惹=cx -热=hf -韧=ad -荏=cu -葚=cv -刃=dd -稔=hx -轫=id -妊=nu -人=pn -壬=pu -衽=pu -仞=rd -仁=re -任=ru -纫=sd -纴=su -饪=uu -忍=xd -认=yr -扔=fp -仍=rp -攘=fy -穰=hy -嚷=ky -禳=py -壤=ty -瓤=wg -让=ya -日=ka -髯=aa -冉=aa -苒=ca -燃=hh -然=hp -蚺=ia -染=md -荛=ce -扰=fd -娆=ne -绕=se -饶=ue -若=ck -弱=gd -婼=nk -偌=rk -箬=vk -润=dw -闰=mw -阮=ee -软=iq -朊=oe -融=ai -冗=bj -容=bk -茸=ce -蓉=ck -荣=cm -溶=dk -嵘=em -戎=gu -熔=hk -蝾=im -镕=jk -榕=mk -肜=op -狨=qu -绒=su -薷=ca -蓐=cc -茹=ck -濡=da -溽=dc -洳=dk -汝=dn -蠕=ia -辱=ic -铷=jk -嚅=ka -如=nk -襦=pa -褥=pc -入=pn -儒=ra -缛=sc -乳=vv -颥=ya -孺=za -睿=ao -蕤=ca -芮=cr -蕊=cx -汭=dd -蚋=id -锐=je -枘=md -瑞=wa -卅=aa -萨=ci -洒=dx -撒=fw -飒=lf -脎=om -仨=rs -挲=ud -薮=cw -溲=dy -擞=fw -搜=fy -飕=fy -螋=iy -锼=jy -嗽=kq -嗾=ku -嗖=ky -瞍=oy -叟=py -馊=uy -艘=vy -塞=bt -色=dv -涩=dv -穑=hk -铯=jv -啬=uk -瑟=wd -森=mm -僧=ro -搡=fm -嗓=km -磉=um -丧=un -颡=ya -桑=ym -肆=aa -兕=ae -丝=av -澌=dj -泗=ds -死=dv -汜=dv -撕=fj -司=gk -私=hs -厮=ij -蛳=ij -锶=jx -咝=ka -四=ke -嘶=kj -嗣=kk -耜=lk -驷=ms -姒=nr -祀=pv -斯=qj -似=rr -俟=ru -偲=rx -缌=sx -寺=tc -饲=uk -笥=vk -鸶=vn -巳=vv -思=xt -三=ae -糁=mp -伞=ru -毵=sm -叁=ss -馓=uw -散=wc -霰=yw -鳋=ai -瘙=bi -扫=fe -搔=fi -骚=mi -嫂=ny -臊=om -缫=sn -埽=tj -鳃=ax -赛=bb -噻=kt -腮=ox -蓑=cy -锁=jb -唢=kb -嗍=ko -嗦=ks -唆=kw -桫=mp -梭=mw -娑=nd -睃=ow -所=pj -缩=sb -塑=tq -索=us -琐=wb -羧=yw -荪=cx -损=fb -榫=mu -飧=pu -狲=qx -隼=ur -笋=vp -孙=zx -蒜=cx -狻=qw -算=vc -酸=yw -颂=ba -宋=bm -菘=cs -淞=ds -嵩=ek -耸=er -崧=es -竦=ln -松=ms -悚=xn -怂=xr -忪=xs -诵=ya -讼=ys -送=zq -甦=aa -稣=ah -宿=bb -窣=bu -蓿=cb -苏=cd -蔌=cq -涑=dn -溯=do -素=fs -夙=jd -觫=jn -嗉=ks -俗=rk -僳=rm -肃=va -簌=vq -粟=xm -愫=xs -诉=yd -酥=yh -谡=yw -速=za -荽=cn -濉=df -岁=ed -隋=eo -祟=ex -随=ez -隧=ez -髓=gz -穗=hx -燧=hz -虽=ki -睢=of -眭=ot -绥=sn -碎=uu -谇=yu -邃=zb -遂=zq -鳎=ay -它=bv -沓=do -溻=dy -拓=fu -挞=fz -铊=jv -榻=my -闼=mz -她=nv -獭=qb -他=rv -塔=tk -塌=ty -趿=zn -踏=zo -遢=zo -蹋=zy -头=dd -投=fy -骰=gy -偷=rd -透=zh -髫=ak -鲦=am -窕=bd -苕=ck -粜=em -挑=fd -龆=ik -蜩=ik -眺=od -祧=pd -佻=rd -脩=ro -笤=vk -条=wm -跳=zd -迢=zd -铽=jd -特=nc -忒=qd -忑=xa -慝=xk -疼=bd -誊=by -藤=cd -滕=od -腾=om -溏=dk -淌=dk -汤=dp -搪=fk -唐=gk -烫=hd -螗=ik -螳=it -铴=jp -镗=jt -嘡=kt -耥=lk -糖=mk -樘=mt -膛=ot -傥=re -塘=tk -堂=tx -躺=uk -瑭=wk -惝=xk -棠=xm -羰=yh -醣=yk -趟=zk -荑=cn -剃=db -剔=do -涕=dp -提=fr -锑=jp -啼=kj -嚏=kr -梯=mp -鹈=nb -题=oa -替=oa -裼=pp -体=ra -倜=rk -绨=sp -缇=sr -屉=uv -惕=xp -悌=xp -醍=yr -蹄=zj -踢=zp -逖=zq -瘫=bf -痰=bh -贪=br -滩=df -潭=du -炭=eh -郯=eh -摊=ff -探=fm -钽=ja -锬=jh -镡=ju -叹=ky -檀=ma -毯=mh -昙=os -袒=pa -坦=ta -坛=ts -坍=tw -碳=uh -忐=xa -谈=yh -谭=yu -韬=aa -萄=cf -滔=da -涛=dc -洮=dd -淘=df -套=ds -陶=ef -鼗=ey -掏=ff -弢=gy -啕=kf -桃=md -绦=sm -饕=uk -讨=yc -逃=zp -鲐=ak -苔=ck -薹=ct -太=dd -汰=dd -邰=es -泰=fd -抬=fk -钛=jd -骀=mk -肽=od -胎=ok -炱=sh -台=sk -态=xd -酞=yd -跆=zk -天=ad -忝=ax -殄=dp -添=dx -掭=fx -畑=ht -铦=jk -田=ku -阗=mb -腆=ob -甜=pc -舔=px -填=tb -畋=wt -恬=xk -橐=am -沱=dv -陀=ev -托=fq -拖=fv -庹=gn -唾=ka -鼍=kv -柝=md -椭=mo -驼=mv -鸵=nv -脱=oe -乇=pq -佗=rv -坨=tv -砣=uv -箨=va -妥=vn -酡=yv -跎=zv -吞=ak -屯=av -豚=on -臀=ou -暾=ow -褪=pz -氽=rd -饨=uv -湍=da -抟=fd -团=kp -疃=ta -彖=vn -痛=ba -茼=ck -潼=da -捅=fa -铜=jk -同=kk -嗵=kz -童=la -桶=ma -桐=mk -瞳=oa -彤=pp -僮=ra -佟=rd -仝=rg -统=se -砼=ug -筒=vk -恸=xl -酮=yk -通=zs -凸=aa -突=bq -菟=cd -荼=ch -兔=dd -涂=dh -秃=hj -钍=jt -图=kd -吐=kt -徒=rr -堍=tv -土=ua -屠=uo -酴=yh -途=zr -推=ff -颓=ha -煺=hz -蜕=ie -腿=oz -退=zv -贴=bk -萜=ck -帖=jk -铁=jn -餮=ud -怗=xk -葶=cd -莛=cu -汀=dd -聤=ed -挺=fz -庭=gz -烃=hg -厅=id -蜓=iu -铤=jz -听=kj -梃=mz -婷=nd -停=rd -艇=vz -亭=wd -霆=yz -廷=zp -鲨=ad -痧=bp -莎=cp -沙=dp -煞=hd -厦=iw -铩=jm -啥=kk -唼=kn -杉=mp -歃=pq -傻=rw -纱=sp -砂=up -杀=xm -裟=yd -霎=yn -守=bc -兽=bk -瘦=by -寿=fc -授=fy -熟=hw -售=kr -手=pa -狩=qc -首=qo -绶=sy -艏=vo -受=vy -收=wv -爽=dx -孀=no -霜=yo -双=yy -赊=bx -奢=do -涉=dp -滠=dy -折=fj -摄=fy -厍=ii -蛇=iv -麝=lc -舌=pk -社=pt -猞=qk -舍=rk -歙=rq -畲=rt -佘=rx -射=uc -赦=wt -慑=xy -设=yy -申=aa -审=ba -莘=cu -渖=da -深=dm -渗=dp -沈=dv -燊=hm -蜃=ii -呻=ka -哂=kx -椹=mv -婶=na -娠=nn -肾=oa -胂=oa -神=pa -身=pp -甚=qv -伸=ra -什=ru -绅=sa -砷=ua -矧=ua -慎=xb -谉=ya -诜=ye -谂=yx -剩=dp -嵊=en -牲=nf -甥=nl -眚=no -昇=oc -胜=of -升=pc -生=pf -绳=sv -圣=ty -声=up -笙=vf -省=xo -上=aa -赏=bx -殇=dp -熵=hk -觞=jp -商=lk -晌=ok -伤=rl -绱=sk -墒=tk -垧=tk -尚=xk -裳=xy -事=aa -十=aa -鲥=ac -鲺=ai -师=aj -石=ak -史=an -豕=an -贳=bc -实=bd -莳=cc -蓍=co -世=cv -湿=da -奭=db -浉=dj -湜=dr -峙=ec -示=ex -拭=fd -拾=fk -施=fv -虱=gi -炻=hu -轼=id -螫=it -铈=jj -嘘=ka -嗜=ko -噬=kr -势=lf -柿=mj -驶=mn -始=nk -时=oc -是=or -匙=ov -释=pa -视=pj -失=pn -氏=pq -舐=pq -式=qd -狮=qj -侍=rc -矢=rd -使=rn -食=rn -仕=ru -室=tb -埘=tc -士=ua -蚀=ui -饰=uj -屎=um -尸=vp -筮=vr -市=wj -恃=xc -弑=xd -识=yb -诗=yc -试=yd -誓=yf -谥=ym -逝=zf -适=zp -鳝=ak -疝=be -赡=by -苫=ck -芟=cy -汕=de -剡=dh -潸=do -删=dp -陕=en -鄯=ey -擅=fa -扇=hy -煽=hy -蟮=ik -钐=jp -山=ka -栅=ma -闪=mr -骟=my -姗=na -嬗=na -膻=oa -膳=ok -衫=pp -缮=sk -舢=ve -珊=wa -讪=ye -善=yk -跚=za -芍=cd -召=dk -劭=dl -潲=do -邵=ed -卲=ed -捎=fo -勺=gd -烧=he -稍=ho -蛸=io -哨=ko -韶=lk -梢=mo -绍=sk -艄=vo -筲=vo -少=xp -晒=ox -筛=vj -酾=yd -蒴=co -搠=fo -烁=hd -铄=jd -槊=mq -妁=nd -朔=qo -硕=ua -说=ye -吮=ke -瞬=oa -顺=pa -舜=va -涮=dd -拴=fw -闩=ma -栓=mw -束=an -叔=ay -赎=bd -薯=co -蔬=cv -菽=cy -澍=dc -沭=dd -殊=dn -漱=dq -淑=dy -抒=fa -摅=fx -戍=gd -庶=gh -黍=hd -秫=hd -输=id -殳=jy -竖=la -树=mc -术=md -梳=mv -枢=mx -姝=nn -腧=od -暑=oo -曙=oo -鼠=pv -舒=ra -毹=rm -倏=rq -纾=sa -蜀=si -署=so -墅=ta -塾=tw -属=ud -书=vd -疏=vv -孰=wd -数=wm -恕=xn -述=za -水=an -税=he -睡=oa -谁=yf -耍=an -刷=du -唰=kd -帅=aj -摔=fu -蟀=iu -甩=pv -衰=wy -痄=ba -渣=da -揸=fa -扎=fv -炸=ha -蚱=ia -铡=jd -咋=ka -哳=kj -吒=kq -咤=kq -闸=ma -榨=ma -札=mv -眨=on -齄=pa -猹=qa -乍=ra -砟=ua -诈=ya -踬=zb -胄=ao -宙=ba -赒=bk -荮=cc -洲=da -州=da -皱=dp -帚=ej -轴=ia -咒=jk -啁=kk -粥=mg -骤=mn -妯=na -肘=oc -舟=pd -周=pk -纣=sc -绉=se -昼=ua -碡=ud -籀=vt -酎=yc -诌=ye -妆=dn -状=dq -壮=du -奘=dv -撞=fa -庄=gt -桩=mt -装=yd -蔗=ch -著=co -浙=dj -摺=fb -蜇=if -蛰=if -辄=iv -辙=iw -锗=jo -哲=kf -柘=mu -鹧=ng -褶=pb -者=to -赭=to -喆=uk -磔=um -谪=yk -着=yo -遮=zg -这=zw -臻=ah -贞=ba -赈=bn -疹=bp -蓁=ch -浈=db -阵=ei -振=fn -稹=hb -轸=ip -镇=jb -帧=jb -针=ju -桢=mb -榛=mh -枕=mv -鸩=nd -朕=od -胗=op -祯=pb -斟=qu -侦=rb -缜=sb -圳=ta -畛=tp -真=ub -砧=uk -箴=vk -珍=wp -甄=xw -震=yn -诊=yp -正=av -整=av -症=bv -蒸=ch -争=da -峥=ea -郑=eq -挣=fa -拯=fa -烝=hv -铮=ja -钲=jv -睁=oa -狰=qa -征=rv -徵=rw -筝=va -政=wa -怔=xv -诤=ya -证=yv -丈=an -账=bn -瘴=bu -涨=dn -漳=du -鄣=el -障=eu -嶂=eu -张=gn -蟑=iu -帐=jn -幛=ju -彰=lp -章=lu -杖=mn -樟=mu -嫜=nu -胀=on -獐=qu -仉=rj -仗=rn -璋=wu -掌=xu -止=aa -黹=ad -痔=bc -贽=bf -窒=bt -痣=bx -芝=cn -芷=cv -滞=dj -治=dk -之=dn -制=dp -殖=dq -汁=du -沚=dv -郅=ea -职=eb -陟=ep -执=fd -掷=fe -摭=fh -指=fo -稚=hf -秩=hn -轵=ib -蜘=ik -轾=it -蛭=it -觯=ja -帜=jb -帙=jn -只=kb -吱=ky -枳=mb -栉=me -骘=me -植=mq -桎=mt -栀=mv -枝=my -鸷=nf -胝=od -脂=oo -膣=ot -智=ou -肢=oy -质=pb -祗=pd -炙=ph -旨=po -卮=pv -祉=pv -值=rq -侄=rt -织=sb -絷=sf -纸=sq -置=sq -至=ta -埴=tq -址=tv -咫=ub -挚=uf -雉=uf -知=uk -直=uq -支=uy -豸=vp -彘=vv -致=wa -志=xu -忮=xy -酯=yo -踯=ze -跖=zk -趾=zv -战=ag -占=ak -蘸=ch -沾=dk -湛=dv -詹=dy -崭=ej -搌=fn -旃=fw -斩=ij -辗=in -站=lk -盏=mg -栈=mg -毡=mk -暂=oi -瞻=oy -绽=sr -展=un -谵=yy -钊=dj -沼=dk -找=fg -招=fk -肇=ha -照=ho -棹=mu -曌=og -昭=ok -兆=pd -爪=pn -罩=su -笊=vv -诏=yk -赵=zx -窄=ba -寨=bm -宅=bq -瘵=bx -择=fa -摘=fk -债=rb -砦=vu -斋=wa -桌=am -卓=au -茁=ck -濯=df -浊=di -涿=dn -浞=dz -擢=ff -拙=fk -捉=fz -灼=hd -镯=ji -啄=kn -椓=mn -禚=ph -倬=ru -斫=uj -琢=wn -酌=yd -诼=yn -窀=bv -准=df -肫=ov -谆=yz -专=ad -赚=bn -颛=ea -撰=fb -转=id -啭=kd -馔=ub -砖=ud -篆=vn -中=aa -冢=bn -种=ha -钟=ja -锺=ja -盅=ma -肿=oa -仲=ra -众=rr -终=sd -舯=va -螽=wi -衷=wy -忠=xa -踵=za -瘃=bn -疰=bw -茱=cn -洙=dn -潴=do -渚=do -主=dw -注=dw -邾=ep -拄=fw -烛=hi -煮=ht -炷=hw -蛛=in -蛀=iw -铸=jc -铢=jm -嘱=kd -麈=lw -杼=ma -株=mn -槠=mo -橥=mq -柱=mw -驻=mw -瞩=od -竹=pa -祝=pe -朱=pn -助=ql -猪=qo -伫=ra -侏=rn -住=rw -翥=ty -舳=va -筑=vd -竺=ve -箸=vo -珠=wn -诛=yn -诸=yo -逐=za -躅=zi -赘=bf -锥=jf -骓=mf -隹=rf -缀=sy -缒=sz -坠=te -惴=xa -追=zp -抓=fv -挝=fz -拽=fp -瓦=ad -洼=dt -挖=fg -蛙=it -哇=kt -娲=nr -娃=nt -腽=om -袜=pn -佤=rw -瘟=bm -刎=dg -温=dm -汶=dw -稳=hx -蚊=iw -吻=kp -闻=me -问=mk -榅=mm -阌=my -璺=pw -攵=px -缊=sm -纹=sw -紊=ws -文=wx -雯=yw -瓮=bw -翁=by -蕹=cf -蓊=cy -嗡=ky -王=at -尫=aw -汪=dw -魍=gv -辋=iv -罔=kv -网=kx -枉=mw -旺=ow -往=rw -妄=wn -亡=wv -望=ww -惘=xv -忘=xw -万=ap -完=be -皖=be -宛=bv -芄=cd -菀=cv -剜=db -湾=dg -豌=dv -顽=ea -挽=fv -丸=gd -烷=he -蜿=iv -婉=nv -脘=oe -晚=ov -腕=ov -纨=sd -绾=sk -畹=tv -碗=uv -玩=we -弯=wg -琬=wv -惋=xv -歪=av -外=pd -窝=bd -莴=cd -沃=dd -渥=dt -握=ft -蜗=id -龌=it -幄=jt -卧=kd -肟=og -我=pd -倭=rn -硪=ud -斡=uu -五=aa -兀=ae -无=av -痦=bk -寤=bk -芴=cp -芜=cv -污=dg -浯=dk -阢=ee -邬=ep -捂=fk -勿=gp -戊=gp -巫=gr -庑=gv -焐=hk -蜈=id -钨=ja -鋈=jd -呜=ka -吴=kd -杌=me -梧=mk -骛=ms -牾=nk -物=np -婺=ns -鹜=ns -鹉=nv -妩=nv -晤=ok -乌=pa -舞=ra -侮=rd -午=ru -仵=ru -伍=rw -坞=ta -圬=tg -碔=ud -鼯=uk -屋=ut -武=vd -毋=vp -吾=wk -务=wl -悟=xk -忤=xu -怃=xv -误=yd -雾=yl -诬=yr -迕=zr -韦=aa -軎=ak -未=an -鲔=ao -痿=bn -苇=ca -蔚=cc -葳=cn -萎=cn -薇=cw -涠=da -沩=dd -潍=df -洧=do -渭=do -危=dv -卫=ea -巍=eg -嵬=eg -崴=en -隈=en -威=gn -炜=ha -魏=hg -委=hn -煨=hn -帏=ja -帷=jf -围=ka -唯=kf -味=kn -喂=kn -为=ld -闱=ma -桅=mv -娓=nm -韪=oa -祎=pa -猥=qn -猬=qo -伟=ra -伪=rd -位=rl -偎=rn -微=rw -纬=sa -维=sf -畏=tn -胃=to -圩=tu -尉=uc -尾=um -艉=vm -玮=wa -惟=xf -慰=xu -诿=yn -谓=yo -违=za -逶=zh -小=ad -宵=bo -萧=ca -潇=da -消=do -削=dx -崤=eo -魈=go -销=jo -啸=ka -哓=ke -嚣=kk -哮=kz -骁=me -校=mf -枵=mg -鸮=nk -枭=nm -晓=oe -绡=so -孝=tz -硝=uo -屑=uo -箫=va -笑=vd -筱=vw -效=ww -肖=xo -霄=yo -逍=zx -鲞=ba -葙=co -芗=cp -巷=cv -象=dn -湘=do -项=ga -庠=gy -香=ho -蟓=in -厢=io -镶=jy -响=kk -橡=mn -相=mo -骧=my -向=pk -祥=py -像=rn -缃=so -饷=uk -箱=vo -乡=vp -飨=vu -襄=wy -享=wz -想=xm -详=yy -翔=yy -西=aa -穸=bd -兮=bg -皙=bm -菥=cj -昔=co -蓰=cr -葸=cx -汐=dd -溪=dd -洗=de -浠=dj -淅=dj -玺=dw -隰=eh -隙=ex -郗=ex -习=ga -席=gj -稀=hj -烯=hj -熙=hk -熹=hu -熄=hx -蜥=ij -蟋=ix -螅=ix -铣=je -锡=jp -钖=jp -唏=kj -嘻=kk -吸=kn -袭=ly -阋=me -析=mj -樨=mn -檄=mw -粞=mx -嬉=nk -媳=nx -牺=nx -膝=od -曦=og -晰=oj -晞=oj -夕=pd -禊=pd -舄=ph -觋=pj -禧=pk -系=ps -僖=rk -徙=rr -翕=ry -细=st -矽=ud -鼷=ud -喜=uk -囍=uk -犀=un -饩=uq -屣=ur -硒=ux -奚=vd -舾=vx -窸=xb -希=xj -惜=xo -息=xp -悉=xp -欷=xq -戏=yg -羲=yg -醯=ym -鲜=ay -贤=ba -宪=be -痫=bm -癣=by -莶=ca -苋=cj -藓=cy -冼=de -涎=dv -险=ea -陷=ea -岘=ej -限=en -掀=fq -咸=gk -弦=gs -燹=ha -蚬=ij -锨=jq -籼=me -闲=mm -先=ne -娴=nm -鹇=nm -嫌=nn -显=oa -腺=od -祆=pd -猃=qa -氙=qe -县=qs -衔=rd -仙=re -线=sg -馅=ua -献=uq -筅=ve -舷=vs -现=wj -酰=ye -羡=yr -暹=zo -跣=zp -跹=zz -薪=cj -芯=cx -心=dd -寻=ec -鑫=jj -欣=jq -锌=ju -新=lj -歆=lq -辛=lu -昕=oj -囟=px -信=rk -馨=uo -衅=xa -忻=xj -鲟=ac -薰=ch -荀=co -蕈=cu -浔=dc -洵=do -殉=do -汛=du -峋=eo -旬=go -熏=hp -勋=kl -驯=ma -曛=oh -獯=qh -循=ro -徇=ro -绚=so -埙=tb -巽=vb -恂=xo -训=ya -醺=yh -询=yo -讯=yu -迅=zv -巡=zv -逊=zz -髹=am -莠=cp -溴=dq -岫=ea -庥=gm -秀=hp -锈=jp -咻=km -嗅=kq -朽=mg -袖=pa -休=rm -鸺=rn -修=rp -绣=sp -馐=ua -貅=vm -琇=wp -羞=ya -宣=ba -痃=bs -萱=ca -渲=da -漩=dr -泫=ds -揎=fa -旋=fr -煊=ha -炫=hs -轩=iu -镟=jr -铉=js -喧=ka -楦=ma -暄=oa -眩=os -衒=rd -儇=ry -碹=ua -瑄=wa -璿=wo -璇=wr -玄=ws -悬=xq -谖=yy -选=zn -芎=cg -汹=dk -夐=dw -匈=gk -熊=hs -兄=ke -凶=kx -胸=ok -雄=uf -鳕=ae -穴=bb -茓=cb -薛=cu -靴=gv -血=pm -彐=va -泶=xd -学=xz -谑=ya -雪=ye -踅=zf -蓄=ct -洫=dx -溆=dy -序=ga -戌=ga -虚=ha -煦=ho -歔=hq -旭=jo -勖=kl -吁=ku -糈=mo -栩=my -婿=no -盱=ou -须=pa -徐=rh -叙=ry -续=sd -絮=sn -绪=so -墟=ta -胥=vo -顼=wa -恤=xx -需=ya -谞=yo -醑=yo -许=yu -酗=yx -诩=yy -下=ad -夏=aw -峡=en -罅=fu -黠=hk -虾=id -辖=ik -匣=ka -呷=ka -吓=kd -柙=ma -瞎=ok -暇=oy -狎=qa -狭=qn -侠=rn -硖=un -瑕=wy -霞=yy -遐=zv -写=ba -薤=ca -泻=da -瀣=da -渫=dm -泄=dv -邪=ea -卸=er -些=ev -挟=fn -携=fp -廨=gn -鞋=gt -蟹=ij -蝎=iv -叶=ku -勰=lx -械=mc -榭=mc -楔=md -榍=mo -胁=od -歇=oq -獬=qn -偕=rb -侑=ro -斜=ru -缬=sa -绁=sv -协=ud -亵=wy -懈=xn -谐=yb -谢=yc -燮=yh -邂=zj -躞=zy -形=ap -荥=cd -荇=cd -刑=da -邢=ea -陉=eg -擤=fa -杏=mk -姓=nf -腥=oa -星=of -猩=qf -型=ta -幸=tu -硎=ud -饧=up -惺=xa -兴=xb -性=xf -悻=xu -醒=yf -亚=aa -雅=af -牙=ap -丫=ba -痖=ba -芽=cp -涯=dt -岈=ep -押=fa -揠=fn -压=id -蚜=ip -=jp -哑=ka -呀=kp -桠=ma -鸭=na -鸦=na -娅=na -睚=ot -氩=qa -衙=rd -伢=rp -垭=ta -砑=up -琊=we -讶=yp -迓=za -由=aa -卣=aa -鱿=ad -疣=bd -宥=bo -莸=cd -莜=cw -油=da -游=dz -邮=ea -幽=ev -黝=hl -蚰=ia -蚴=il -蝣=iz -铀=ja -铕=jo -呦=kl -囿=ko -柚=ma -釉=pa -牖=pd -祐=pk -犹=qd -猷=qq -优=rd -佑=rk -攸=rw -鼬=ua -尤=ud -右=uk -有=uo -尢=uv -友=uy -幼=vl -又=vn -繇=vs -酉=xa -忧=xd -悠=xr -诱=yp -业=aa -野=aa -曳=ap -页=ba -冶=dk -液=dn -邺=ea -耶=ee -爷=fe -揶=fe -掖=fn -烨=hu -靥=ia -铘=je -咽=kd -噎=kd -椰=me -腋=od -晔=ou -也=vv -夜=wd -谒=yv -央=an -鸯=an -羊=ba -疡=bp -痒=by -泱=dn -殃=dn -漾=dn -洋=dy -阳=eo -扬=fp -飏=fp -鞅=gn -秧=hn -炀=hp -烊=hy -蛘=iy -杨=mp -样=my -旸=op -氧=qy -仰=re -佯=ry -徉=ry -怏=xn -恙=xy -养=ya -一=aa -夷=an -宧=ba -贻=bk -痍=bn -宜=bq -瘗=bt -癔=bx -疫=by -艺=cg -苡=cr -殪=dd -沂=dj -漪=dk -溢=dm -劓=dp -浥=dv -刈=dx -峄=ea -屹=eg -嶷=er -抑=fe -旖=fk -挹=fv -乙=gg -熠=hb -移=hd -黟=hd -蚁=id -轶=in -蜴=ip -钇=jg -镒=jm -镱=jx -铱=jy -颐=ka -呓=kg -咦=kn -咿=kp -医=ku -邑=kv -噫=kx -翳=ky -毅=ly -翊=ly -驿=ma -椅=mk -益=mq -姨=nn -胰=on -易=op -臆=ox -肄=pa -疑=pr -弋=qd -猗=qk -仪=rd -亿=rg -倚=rk -佚=rn -佾=ro -伊=rp -依=ry -役=ry -绎=sa -缢=sm -矣=su -埸=tp -圯=tv -壹=ud -饴=uk -懿=ux -异=vc -彝=vc -舣=vd -以=vr -已=vv -弈=wc -奕=wd -亦=wd -衣=wy -怿=xa -义=xd -忆=xg -怡=xk -意=xl -悒=xv -译=ya -翼=yb -羿=yc -议=yd -诒=yk -裔=yk -翌=yl -诣=yo -谊=yq -酏=yv -遗=za -逸=zd -迤=zr -焉=ah -严=ai -宴=bn -菸=cd -演=db -沿=dk -湮=dt -淹=dv -奄=dv -滟=dv -鄢=ea -郾=ek -岩=eu -崦=ev -艳=fv -掩=fv -焰=ha -燕=hc -烟=hd -炎=hh -焱=hh -厣=ia -赝=ib -雁=if -魇=ig -厌=iq -餍=iu -蜒=iv -唁=kk -匽=kn -颜=la -彦=lp -验=ma -阎=ma -阏=md -闫=ms -盐=mt -阉=mv -檐=my -妍=nc -嫣=nh -胭=od -眼=on -晏=on -腌=ov -殷=py -衍=rd -俨=ri -偃=rn -罨=sv -堰=tn -埏=tv -研=uc -砚=uj -鼹=un -筵=vv -兖=we -琰=wh -言=wk -恹=xq -酽=yi -谚=yp -谳=yq -延=zp -尧=ae -鳐=af -窑=bf -窈=bl -药=cd -淆=do -崾=en -摇=ff -耀=gf -轺=ik -钥=jo -咬=kf -吆=ks -杳=mo -妖=nd -姚=nd -鹞=nv -曜=of -腰=on -夭=pd -徭=rf -舀=va -幺=vs -珧=wd -瑶=wf -要=xn -肴=xo -爻=xx -谣=yf -邀=zb -遥=zv -寅=bb -窨=bo -瘾=bx -茵=cd -茚=ce -荫=co -洇=dd -淫=du -鄞=ec -阴=eo -印=ep -隐=ex -引=ga -蚓=ia -龈=in -铟=jd -银=jn -吲=ka -因=kd -喑=ko -吟=kv -音=lo -姻=nd -夤=pb -胤=pv -氤=qd -狺=qk -垠=tn -堙=tt -饮=ur -尹=vp -霪=yu -哟=kd -唷=ko -育=wo -韫=am -蕴=cm -芸=cs -殒=db -涢=db -陨=eb -郓=eb -郧=ek -云=es -匀=ga -孕=gz -熨=hu -韵=la -耘=ls -昀=od -晕=oi -狁=qe -氲=qm -允=se -纭=ss -恽=xi -愠=xm -酝=ys -运=ze -冤=bd -员=bk -芫=ce -苑=cv -渊=da -沅=de -源=dx -元=ee -院=ee -鼋=ev -掾=fn -援=fy -原=ix -螈=ix -愿=ix -辕=iy -圆=kb -园=ke -橼=mn -鸳=np -媛=ny -眢=op -鸢=qn -猿=qy -缘=sn -垣=ta -垸=te -塬=tx -袁=ty -箢=vv -爰=vy -瑗=wy -怨=xp -远=ze -鳙=aa -痈=ba -涌=da -永=dn -泳=dn -拥=fa -庸=ga -蛹=ia -镛=ja -喁=kd -咏=kn -臃=of -用=pa -佣=ra -俑=ra -甬=sa -勇=sl -墉=ta -壅=tw -邕=vv -雍=wf -饔=wu -慵=xa -恿=xs -踊=za -瀹=dc -刖=do -钺=jv -曰=ka -阅=me -樾=mv -月=pe -岳=pe -粤=pg -龠=rc -约=sd -悦=xe -跃=zd -越=zv -与=aa -雨=ad -禺=ad -欤=aq -于=au -寓=bd -瘀=bd -窬=bd -窳=bg -瘐=bn -欲=bq -宇=bu -蓣=ca -萸=cn -芋=cu -鱼=da -渔=da -淤=dd -渝=dd -浴=dk -屿=ea -隅=ed -嵛=ed -峪=ek -郁=eu -揄=fd -於=fd -羽=ga -鬻=ga -庾=gn -虞=hd -煜=hl -燠=hn -蝓=id -蜮=ik -龉=ik -钰=jw -喻=kd -圄=kk -圉=ku -盂=ma -阈=ma -榆=md -驭=my -妤=na -鹆=nb -娱=nd -鹬=ns -妪=nx -昱=ol -舆=pb -舁=pc -禹=pd -裕=pk -臾=pn -狳=qh -狱=qq -俞=rd -俣=rd -御=re -余=rh -觎=rj -毓=rv -伛=rx -预=sa -予=sa -豫=sn -纡=su -域=tk -饫=ud -馀=uh -聿=va -竽=vu -玙=wa -玉=wd -瑜=wd -愚=xa -愉=xd -愈=xr -誉=xy -谕=yd -雩=yg -语=yk -谀=yn -遇=za -迂=za -逾=zr -瘿=bn -荧=ch -萤=ci -蓥=cj -营=ck -英=cn -莺=cn -萦=cs -茔=ct -莹=cw -瀛=dd -潆=ds -滢=dw -郢=ek -罂=fb -撄=fn -应=ga -鹰=gn -膺=go -蝇=iv -嘤=kn -盈=mg -楹=mm -樱=mn -婴=nb -鹦=nb -映=on -媵=on -影=op -颖=pa -颍=pa -缨=sn -硬=un -嬴=wd -赢=wd -瑛=wn -璎=wn -迎=zp -拶=fd -杂=jm -匝=kj -咂=kj -咱=ko -柞=ma -砸=uj -鲰=ay -邹=ed -鄹=ee -陬=ey -奏=fd -揍=fd -驺=me -走=tr -诹=yy -责=bf -泽=da -则=db -仄=ir -帻=jb -啧=kb -赜=kb -昃=or -笮=va -舴=va -箦=vb -迮=zr -怎=xr -谮=yo -赠=bo -甑=bw -锃=jw -缯=so -罾=so -增=to -憎=xo -赃=bt -葬=cc -臧=ga -驵=mq -脏=ot -鲻=at -髭=av -资=bd -赀=bv -字=bz -渍=db -滋=ds -淄=dt -滓=du -嵫=es -秭=hp -辎=it -龇=iv -锱=jt -觜=jv -咨=kd -耔=lz -粢=md -梓=mu -籽=mz -姿=nd -姊=np -眦=ov -自=po -孳=qz -仔=rz -缁=st -紫=sv -子=va -笫=vp -甾=vt -孜=wz -恣=xd -谘=yk -訾=yv -趑=zq -孖=zz -赞=bn -錾=ji -糌=mo -簪=vo -瓒=wb -昝=wo -趱=zb -枣=ad -凿=au -皂=bq -藻=cm -澡=dm -燥=hm -灶=ht -噪=km -唣=kq -糟=mo -早=ou -蚤=yi -遭=zc -躁=zm -造=zn -再=aa -灾=bh -宰=bu -载=ig -哉=kg -栽=mg -在=ut -崽=xe -阼=ea -座=gt -唑=kt -嘬=ky -昨=oa -胙=oa -祚=pa -作=ra -佐=rg -做=rw -坐=tr -左=ug -怍=xa -鳟=ac -撙=fc -樽=mc -尊=qc -遵=zq -攥=fs -钻=jk -缵=sb -躜=zb -鬃=ax -宗=bx -粽=mx -棕=mx -腙=ox -偬=rx -纵=sr -综=sx -总=xb -踪=zx -菹=cq -阻=eq -族=fu -租=hq -镞=ju -足=kr -祖=pq -俎=rq -组=sq -诅=yq -蕞=cy -嘴=kj -最=oy -罪=ss -醉=yu -贼=bu -蘑=ck -刭=yd -凇=ds -薏=cx -掬=fm -呒=kv -泾=dg -腴=on -瞑=ob -蝠=it -苧=cd -㧐=fy -㧟=fk -耇=tk -耏=ap -耑=ea -耤=lo -耰=ly -䁖=on -䂮=ul -肸=oa -䃅=ua -胈=oy -䃎=ud -胠=oq -胣=ov -脟=oc -脿=oy -腒=ok -腘=ow -䅟=hp -腨=oa -腯=oo -膙=yi -臑=oa -臜=ob -舠=vd -舥=vv -舲=vs -艅=vh -艎=vw -芃=cd -芠=cw -芣=cd -芼=cm -苉=ca -苾=cx -茀=ca -茋=cd -茝=ca -䌹=sk -茽=ca -荁=ca -荄=cr -荓=cc -荖=cv -䎃=ay -䎖=by -莝=ct -莿=cd -菂=cd -菉=cd -菍=dx -䏝=od -䏡=ox -䏲=op -菼=ch -䐃=oh -萚=ca -萣=cr -萳=cu -萹=cc -葎=ca -葖=cq -葰=cw -葴=ck -蒄=cc -蒐=cs -蒟=ck -蒨=co -蒱=ca -蒻=cd -蓂=cb -蓇=co -蓏=cg -䓖=cl -䓛=ck -蓢=co -䓨=ce -䓫=ck -䓬=cu -蔀=ce -蔃=ci -蔈=cx -蔊=cu -蕗=ck -蕰=cm -薁=cd -薢=cn -薳=cy -薸=cx -薿=cr -䗖=ij -䗛=io -藟=ct -藠=cb -藦=cu -藨=ch -䗪=gi -䗴=ip -蘘=cy -虒=pj -虓=jj -虤=hj -虷=iu -虸=iz -蚄=if -蚆=iv -蚲=ku -蛃=ir -蜎=io -蜐=il -䜣=yj -蝘=in -䝙=vx -蝲=id -螠=im -螣=oi -螱=iu -蟏=ia -蟫=iu -蠋=ii -衃=xd -衎=rd -衠=rd -袆=ya -袗=yp -袪=ys -袯=yy -䢺=ee -䢼=ce -裈=yi -䣘=xe -裛=wy -褕=yd -褟=yy -褯=yj -襕=yd -襚=yz -襜=yy -襫=yr -䥽=jy -䦃=jo -觃=kz -觟=jt -觭=jk -觱=jg -觿=jk -訄=jy -訚=my -詟=ly -讱=yd -讻=yk -诇=yk -诐=yy -谼=bb -谿=vk -豨=aj -豮=ab -𡐓=tn -貆=va -䲟=ae -䲠=ao -䲢=oa -𠳐=ke -䴓=an -䴔=bn -䴕=nd -䴖=fn -䴗=on -䴘=pn -䴙=un -𠅤=wx -赗=bo -赟=bw -赪=tb -䶮=ld -趯=tf -跂=zy -跐=zv -跱=zc -丏=av -踒=zn -𣲘=dv -𣲗=da -踶=zr -乂=pn -蹅=za -𠙶=kv -蹐=zo -蹚=zt -蹜=zb -蹢=zk -乸=vd -蹽=zx -蹾=zw -亶=wa -亸=wu -亹=wq -𣗋=me -伈=rx -伋=rn -伣=rv -伭=rs -𦰡=ce -伾=ra -佁=rk -𦒍=ly -佖=rp -𥔲=ug -轪=id -佸=rk -佺=rw -佽=ww -辀=iv -侁=re -侂=wv -辌=ix -辒=im -侘=rv -𣸣=db -侴=rd -𥕢=uo -侹=rz -辿=ze -俍=rn -𡎚=tc -𤧛=wj -俙=rj -𨐈=ie -𥖨=um -俫=rm -俶=ry -迺=zx -倓=rh -倕=ra -倞=rx -倧=rx -倴=rc -逴=zu -倻=re -偁=ea -遆=zj -𨱔=jc -偓=rt -𨱑=jb -𨱏=jk -𨱇=jd -偡=rk -偭=ra -偰=rd -𤩽=wq -遹=zk -傃=rx -傉=rc -傒=rd -傕=rf -邘=ae -邨=ae -𤫉=wy -邲=de -邽=fe -邿=ue -僇=rp -郈=pe -僎=rb -𦈡=sa -僔=rc -郚=we -郤=be -郪=ae -僰=ar -鄀=ce -鄃=re -鄅=pe -鄌=ge -鄑=ae -鄗=we -鄘=ge -鄚=ce -鄜=ge -鄠=ye -儦=rh -𦙶=ok -鄫=be -𫐄=ie -儳=rd -儴=ry -酂=pe -𫠆=ub -酅=ee -𫠊=ms -𫐐=ie -𫐓=im -酦=yy -𫠜=ie -酺=yd -𥻗=ma -冏=kk -𫑡=ke -冔=ou -𫰛=ng -𬀪=oe -𬀩=ov -醨=ys -冮=dg -醾=ym -𪣻=tn -釐=mt -凓=dm -凘=dj -𪤗=tp -𫓧=jn -𫢸=ra -𫄧=sp -𬂩=mn -𫄨=sj -刬=gd -𨙸=ue -𬒈=xk -𫓶=jo -𫓹=jb -𫓯=jf -𬃊=mb -剅=dd -剋=ud -𨚕=se -𫄷=sx -剕=ad -𬱟=eb -𫄸=sh -𬱖=ab -𬒔=uo -𦝼=on -𦭜=cb -𫔎=jk -𫔍=jt -𬒗=ue -剟=yd -𫔶=mm -劄=vd -𨺙=ee -𬣡=yg -𬣞=ya -劼=jl -𬣙=yu -𬤊=yr -鋆=tj -勍=wl -𬤇=yt -勔=al -勚=al -𬣳=yn -勠=yl -𫶇=ej -𬕂=vb -𬴃=mu -𬴊=ma -𬤝=yx -𬳽=me -𫵷=el -𬴂=ma -𬳿=mh -𬳶=mk -𬳵=ma -𫖯=db -𫖮=mb -𫖳=eb -𫗧=un -𪨶=ei -𫇭=cd -匜=kv -𪨰=ea -𪩘=eq -匼=kk -𫗴=ua -𨭉=jw -𫘨=mr -𫘧=md -𫘦=mf -𫘬=md -𫘪=mx -𫘝=mn -𫘜=mw -𧿹=zm -𬶍=av -𬶋=ak -𬶐=av -𬶏=av -卬=pe -𬇙=db -𫷷=gc -𬇕=dv -𬶮=ak -𫸩=gx -𬶭=ax -𨟠=oe -𬶨=av -𬇹=dk -𬶠=an -𬶟=an -厾=vs -𬘬=sf -𬘫=se -𬘩=sp -𬘯=sw -叆=ay -叇=an -𬘭=sm -𬨎=iy -𬨂=in -𬘡=sd -𫚖=aa -𫚕=aj -叕=yy -𬘓=sp -𬷕=an -叚=vy -𬘘=sv -𬙋=sy -𬙊=xt -𩾃=sv -𬙂=sb -𩽾=an -𫚭=an -𫫇=kx -𬊈=hc -𬸦=nw -𬸣=nb -𬸪=pn -𫌀=yb -𬉼=hx -𬸚=nq -𬸘=kn -𩾌=an -𬊤=hu -𫛭=nq -吽=kn -呇=dk -𬩽=ee -呣=km -𫍣=yk -𬪩=yy -𬺈=ik -𬹼=ia -咇=kp -咉=kn -𫍽=yy -𪾢=ov -咍=kk -𪟝=fl -钘=jc -𫍯=yg -𬺓=ir -𫍲=yy -咡=ke -咥=et -𫭟=tx -𫭢=tv -咺=ka -𬌗=ak -𬜬=ck -哃=kk -𬜯=cr -𫞩=ww -𫮃=tu -铏=jd -𫭼=tl -铚=jt -𬬭=jv -哢=kc -𫟅=sx -𬬱=jj -𬬮=jn -𬍡=yd -𬬩=jg -𬍤=wc -𬍛=wx -哱=kz -铻=jk -𫟦=iz -𬭊=jt -𬭎=js -𬬻=ju -𬭁=jd -𬬿=jk -锜=jk -𬬹=ja -唝=kb -𬬸=jd -𬭬=je -𬭯=jw -锧=jb -𬭤=jd -𬭩=jy -𬭛=jy -锳=jn -𬭚=jz -𫟼=jz -𫟹=jb -镃=js -镈=jc -镋=je -𬭼=jz -𬞟=cb -镚=jo -𬭳=jk -𬭸=jm -𬭶=jh -镠=jp -𬮱=mt -啫=ko -𬟁=cn -啴=ku -镴=jn -镵=jd -𬯎=eb -𬯀=ea -𬮿=ev -喤=kw -𬟽=ix -嗐=kk -嗞=ks -闿=mv -阇=mo -阘=my -嘚=kc -阿=ek -噀=kb -噂=kc -噇=kt -陎=en -陑=ea -陞=et -隃=ed -嚚=kk -隩=ed -嚭=tk -隺=pf -雊=gf -雱=yv -囷=kh -霅=yk -圌=ka -圐=kv -圙=ka -圢=td -霨=yc -圫=tv -圲=tu -坉=tv -坋=td -坒=ta -坥=tq -坬=tn -靬=gu -坰=tk -靰=ge -靸=gn -靺=gn -坽=ts -靽=ga -靿=gl -鞁=gy -垈=tr -垍=to -垎=tk -垏=ta -垕=pt -垙=te -垚=tt -垞=tv -垟=tu -鞡=gl -鞧=gy -鞨=gv -鞬=gz -鞮=gr -垯=tz -垱=te -鞳=gk -垺=tz -垾=tu -垿=ta -韂=gk -埆=tj -埇=ta -埌=tn -埗=tp -韨=ay -埪=tg -埫=tk -埵=ta -埼=tk -堉=to -堌=tk -堎=ty -堐=tf -堧=td -堨=tv -堲=tv -堼=tt -堽=tv -堾=to -塅=ty -塆=tg -塝=tv -塱=td -墈=tl -颋=zb -颎=vb -墐=tt -墕=tn -墘=tv -颙=ab -墡=tk -墣=td -墦=tt -飐=fk -飔=fx -飗=ft -壸=ua -夬=vn -奓=dd -奡=aa -饳=ue -饸=uk -饻=uy -馃=ym -馉=yo -馌=um -妘=ns -馝=pp -馞=pz -妧=ne -馧=pm -妭=ny -姈=ns -姞=nk -姤=nk -姱=ng -姶=nk -姽=nv -娀=ng -娵=ny -婌=ny -婍=nk -婘=nv -婞=nu -婠=nk -婤=nk -婫=nv -驲=mo -婳=nk -婻=nu -媂=nj -骃=md -媄=nd -媆=nd -骉=mm -骍=mu -骎=my -媓=nw -骕=md -媖=nd -骙=md -媞=nr -骦=mo -媭=np -媱=nf -髃=gd -嫄=nx -髎=gp -嫕=nx -嫚=ny -髢=va -嫭=nu -嫽=nx -髽=za -鬒=za -鬘=oa -嬥=nf -嬬=na -鬶=wa -鬷=ay -嬿=nh -孅=ng -魆=pg -魋=pf -寁=br -尨=lp -尪=jw -屃=ub -屼=ee -鱽=ad -屾=ee -鱾=av -鲀=av -鲃=av -鲉=aa -岊=ve -鲊=aw -鲌=ab -鲏=ay -鲖=ak -鲗=ad -鲘=ak -鲙=as -鲝=ba -岞=ew -岠=wk -岨=eq -鲪=ak -鲬=aa -鲯=ab -鲹=ap -鲾=at -鲿=as -鳀=ar -鳁=am -峂=ed -鳂=qn -峃=xe -鳈=qd -鳉=qc -鳑=qv -鳒=ab -峗=ev -峘=ea -鳚=ac -峛=ed -鳛=qb -鳠=qy -鳡=qx -峣=ee -鳣=aa -鳤=ak -峧=ex -峱=qe -峿=ek -崀=en -崁=dq -崄=ea -崌=ek -崒=eu -崚=ew -崟=ej -崡=ek -崶=ec -崿=eg -嵁=ev -嵅=eg -嵎=ed -嵖=ea -嵚=eq -嵲=em -嶅=ef -嶍=eb -嶒=eo -嶓=et -嶟=ec -嶦=ek -嶲=ek -巇=eg -巉=ed -帡=jc -鸤=un -鸧=rn -帨=je -鸻=rn -鸼=vn -鹀=an -鹍=on -鹐=an -鹔=vn -幖=jx -鹖=on -鹝=an -鹟=bn -鹠=pn -鹡=dn -鹢=bn -鹮=sn -鹯=wn -鹲=cn -鹴=yn -麀=gv -麑=ge -麖=gx -庤=gc -庱=gy -麹=fm -廆=gs -黇=ck -廋=gy -廙=gb -黡=gh -弆=tc -弇=rc -鼒=av -弨=gk -鼩=pk -鼫=pk -鼱=po -弸=go -齁=pk -齇=pq -齉=py -彟=ey -彧=gk -彳=pr -龂=ij -徛=rk -龢=rh -鿍=ta -鿎=uz -鿏=jw -忞=wx -忳=xv -忺=xq -恓=xx -恔=xx -悆=rx -悈=xc -悢=xn -惎=ax -惔=xh -惙=xy -惛=xo -愃=xa -愐=xa -愔=xo -愭=xo -慆=xa -慥=xz -慬=xa -慭=xa -憕=xq -憙=ax -憭=xx -憺=xy -戣=dg -戭=bg -扂=dk -扅=dd -扆=dy -扊=dh -扞=fu -扺=fn -扽=fv -抃=fd -抔=fd -拃=fa -拤=fd -挓=fv -挦=fc -捯=fd -捽=fu -掞=fh -揕=fk -揳=fd -搒=fv -摏=fa -摛=fs -摴=fg -摽=fx -擿=fz -攽=bw -敔=ww -敩=xw -斝=ku -斠=cu -斶=ji -旐=fv -旞=vw -旴=oa -旵=oe -旿=ku -昄=oy -昈=ou -昉=of -昒=op -昡=os -昣=op -昤=os -昪=oc -昫=ok -昳=on -昺=od -昽=ol -晅=oa -晊=ot -晐=or -晙=ow -晢=of -晪=ob -晫=ou -晱=oh -暅=oa -暕=on -暲=ou -暵=on -暶=or -暿=ok -曈=oa -朏=oe -朓=od -朳=mb -朸=ml -杄=mu -杕=md -杙=md -杧=mk -杻=ma -枅=mc -枍=mg -枲=sm -枹=mv -柈=ma -柊=md -柖=mk -柷=me -栐=mn -栒=mo -栟=mc -栴=ma -栻=mg -桯=mw -桲=mz -桹=mn -梌=mh -梠=mk -梣=mv -梴=mz -梽=mx -梾=ml -梿=mz -棁=me -棐=am -棓=mk -棤=mo -棨=md -棪=mh -棫=mg -棬=mv -棻=cm -棽=mv -椀=mv -椆=mk -椑=mu -椪=ma -椸=mv -楒=mx -楙=mm -楩=mn -楪=mm -楯=mo -榃=mt -榑=mc -榖=ay -榰=mo -槃=mv -槚=mb -槜=mp -槱=mh -橑=mx -橞=mx -橦=ma -檞=mn -櫆=mu -欂=mc -欸=sq -欻=hq -歅=sq -殣=da -毌=va -毐=ap -氾=dv -氿=dj -汈=dp -汋=dd -汧=dc -汫=dc -沄=ds -沇=de -沘=dv -沨=df -沺=ft -泂=dk -泃=dk -泇=dk -泙=du -泚=dv -泜=dd -洈=dv -洑=dq -洓=dn -洘=dg -洢=dp -洣=dm -洨=dx -洭=dw -洺=dk -洿=dg -浕=dd -浛=dk -浟=dw -浡=dz -浬=da -浭=dn -浰=dd -浲=df -涄=dg -涍=dz -涐=dd -涘=dd -涴=dv -淏=dd -淜=do -淟=db -淯=do -渟=dd -渰=dc -渼=dd -湉=dk -湑=do -湝=db -湣=do -湲=dy -湴=da -溁=dm -溇=dn -溍=do -溚=dk -溞=di -溠=dg -溦=dw -溵=dy -溹=dx -滃=dy -滆=da -滍=di -滘=dk -滧=dw -滪=db -滫=do -漈=dx -漋=df -漖=dw -漦=ad -漴=dx -漷=de -漹=eh -漻=dp -漼=df -潏=dk -潖=dv -潟=dh -潩=db -潵=dw -潽=do -潾=da -澂=dw -澛=do -澥=dn -澪=ds -澭=sf -澴=dy -澼=du -澽=dn -濋=dr -濩=dy -瀌=dh -瀍=dt -瀔=dy -瀱=dd -瀼=dy -灈=df -炆=hw -炌=ha -炘=hj -炟=ha -炣=hk -烔=hk -烜=ha -烠=ho -烶=hz -烺=hn -烻=hz -焆=ho -焌=hw -焞=hz -煁=hk -煃=ht -煋=hf -煓=ha -煟=ho -煴=hm -熇=hl -熛=hx -熜=hx -熥=hz -熻=hy -燋=hh -燏=hk -燚=hh -爇=ch -爔=hp -爚=ha -牁=vk -牂=nu -牚=xp -牤=nk -牥=nv -牻=np -犇=nn -犨=nw -狉=qa -猄=qx -猇=qj -猯=qa -猰=qd -猺=qf -玃=qy -玒=ww -玓=wd -玚=wp -玞=wn -玡=wp -玤=wf -玥=wo -玭=wv -玶=wu -玹=ws -玼=wv -玿=wk -珅=wa -珇=wq -珋=qe -珌=wp -珒=wa -珕=wl -珖=we -珛=wo -珝=wy -珢=wn -珣=wo -珦=wk -珪=wt -珫=we -珵=ww -珷=wv -珸=wk -珹=wg -珺=wk -珽=wz -琀=wk -琄=wo -琈=wz -琎=wz -琔=wr -琟=wf -琡=wy -琫=wu -琭=wd -琯=wk -琲=wa -瑀=wd -瑂=wo -瑃=wo -瑅=wr -瑆=wf -㑇=we -㑊=wd -瑑=wn -瑓=wn -瑔=wd -瑖=wy -瑝=ww -瑢=wk -瑧=wh -瑨=wo -瑬=wd -瑱=wb -瑳=wg -璆=wp -璈=ww -璒=wq -璘=wa -璟=wx -璥=ww -璪=wm -璬=ww -璮=wa -璱=wp -璲=wz -瓀=wa -瓖=wy -瓘=wf -瓻=xv -甗=hv -甡=pa -甪=pa -畖=tn -畤=tc -畬=rt -㕮=kx -畯=tw -疁=tp -疍=va -疐=ar -疢=bh -疭=br -痓=bt -瘆=bp -㘎=kw -㙍=ty -癗=bt -㙘=tn -㙦=tr -癿=bv -皕=bb -皛=bb -皞=bu -皦=bw -皭=bc -㛃=bn -㛚=na -盦=rm -盷=od -㛹=nn -眊=ov -眬=ol -睄=oo -睎=oj -瞋=ob -瞫=ou -瞭=ox -㟃=ek -矞=vk -矰=uo -矼=ug -砄=un -㠇=ev -㠓=en -砠=uq -砫=uw -砮=un -砵=ua -硁=ug -硊=uv -硍=un -硔=ub -硙=uv -硿=ug -碃=uo -碈=uo -碏=uo -碨=uy -碶=ud -磏=un -磜=ux -磹=uu -磻=ut -㤘=xe -礵=uo -㥄=xw -祇=pn -祋=py -祏=pu -祕=pp -祲=py -祼=pn -祾=pw -禋=pt -禒=pn -禔=pr -禘=pj -禛=pb -禤=py -㧑=fd -秬=hk -稌=hx -稑=ht -稙=hq -穄=hx -穙=hd -穜=ha -穟=hz -窅=bo -窊=bn -窎=bn -竘=lk -竫=la -㫰=on -㬊=oy -㬎=oh -㬚=ow -笯=vy -筀=vt -㭎=mx -㭕=ms -筜=ve -筤=vn -筥=vk -筦=ve -筶=vk -筼=vb -箖=vm -㮾=md -篯=vg -㰀=mx -簃=vd -簉=vz -簕=vl -簝=vx -簠=vm -簰=vu -籥=va -粿=vn -㳇=vx -糒=va -㳘=de -㳚=dd -糵=cm -㴔=dx -㵐=dq -絜=sf -㶲=ha -㸆=oa -㸌=hf -縠=uy -縢=ox -繄=sk -㺄=qd -纮=ss -纻=sa -纼=sa -绤=sk -㻬=wg -绹=sf -缐=sd -缞=sy -罍=tf -㽏=ad -罶=st -罽=sd -羑=qn -羓=qv -羕=qd -羖=qy -羱=qx -翂=yd -翃=uy -翈=ay -翙=ey -翛=ry -㿠=be -翯=yk -翷=my -翾=sy -濛=dn -絺=sj -𣬠=mj -𣬶=mv -崑=eb +阿=ek +锕=jk +吖=ka +啊=kk +嗄=kw +唔=kk +嗯=kx +盎=mn +昂=oe +肮=oj +癌=be +皑=bv +蔼=cv +艾=cx +隘=em +崖=et +捱=ft +挨=fu +锿=jy +嗌=km +唉=ku +哎=kx +嗳=ky +嫒=ny +暧=oy +埃=tu +碍=uc +矮=un +砹=ux +爱=vy +哀=wy +瑷=wy +霭=yv +案=bm +安=bn +岸=eu +按=fn +揞=fo +鞍=gn +庵=gv +黯=ho +铵=jn +唵=kv +桉=mn +鹌=nd +胺=on +暗=oo +氨=qn +犴=qu +俺=rv +垵=tn +埯=tv +谙=yo +凹=aa +鳌=af +翱=by +澳=dd +聱=ef +拗=fl +廒=gw +熬=hf +螯=if +鏊=jf +嗷=kw +鏖=lj +骜=mf +媪=nm +奥=pd +袄=pd +岙=pe +獒=qf +傲=rw +坳=tl +敖=wf +懊=xd +遨=zf +鲅=ad +疤=bv +茇=cd +菝=cd +芭=cv +灞=do +岜=ev +扒=fb +捌=fd +拔=fd +把=fv +爸=fv +魃=gd +靶=gv +钯=jv +叭=kb +吧=kv +耙=lv +粑=mv +八=pn +罢=ss +坝=tb +巴=vv +笆=vv +霸=yo +跋=zd +髟=ap +鳔=ax +瘭=bx +飚=fh +飑=fv +表=fy +彪=hp +镳=jh +镖=jx +杓=md +标=mx +骠=mx +婊=ny +膘=ox +裱=py +飙=qf +俵=ry +本=aa +贲=bu +苯=ca +奔=dc +夯=dl +锛=jc +畚=st +坌=tb +笨=va +甭=aa +崩=eo +蚌=if +嘣=ko +祊=pf +绷=so +泵=ud +甏=wu +蹦=zo +迸=zq +浜=db +邦=ef +帮=jf +镑=jf +棒=ma +梆=me +榜=mf +膀=of +傍=rf +绑=se +磅=uf +谤=yf +毖=ad +弼=ag +比=av +毙=av +痹=ba +赑=bb +荜=cu +萆=cu +薜=cu +蓖=cv +芘=cv +蔽=cw +弊=cx +荸=cz +濞=da +滗=dm +必=dp +鄙=ek +陛=et +庳=gu +髀=gu +庇=gv +秘=hp +秕=hv +铋=jp +哔=ku +吡=kv +闭=mp +婢=nu +嬖=nu +妣=nv +臂=ou +睥=ou +鼻=pa +币=pj +裨=pu +匕=pv +狴=qt +彼=rp +俾=ru +畀=ta +壁=tu +毕=ua +屄=ub +辟=uu +碧=uw +箅=va +笔=vm +筚=vu +舭=vv +篦=vv +璧=wu +敝=wx +愎=xw +襞=yu +逼=za +避=zu +跸=zu +半=ba +颁=ba +癍=bw +瘢=by +班=dw +阪=ey +拌=fa +扮=fd +搬=fy +扳=fy +瓣=gu +钣=jy +办=ld +板=my +版=py +伴=ra +绊=sa +坂=ty +般=vy +舨=vy +斑=ww +鲍=av +宝=bw +薄=cc +葆=cm +苞=cv +瀑=dd +剥=de +刨=dg +抱=fv +报=fy +包=gv +爆=hd +煲=hr +龅=iv +鸨=np +暴=od +曝=od +胞=ov +褓=pm +勹=pv +保=rm +堡=tr +饱=uv +豹=vv +褒=wy +雹=yv +趵=zd +孢=zv +百=ab +掰=bu +摆=fs +捭=fu +稗=hu +呗=kb +柏=mb +白=po +佰=rb +拜=uf +擘=uu +败=wb +鳊=ac +贬=bn +窆=bn +苄=cd +卞=dd +汴=dd +辨=du +鞭=gn +扁=hc +煸=hc +蝙=ic +匾=kc +褊=pc +便=rn +编=sc +弁=sc +缏=sn +辫=su +碥=uc +砭=un +笾=vz +变=wy +忭=xd +辩=yu +遍=zh +边=zl +鬓=ab +宾=bb +濒=da +滨=db +殡=db +豳=ea +邠=eb +摈=fb +髌=gb +镔=jb +槟=mb +彬=mp +膑=ob +傧=rb +缤=sb +斌=wd +卜=ad +帛=bj +菠=cy +泊=db +渤=dl +波=dp +搏=fc +拨=fd +播=ft +钵=ja +铂=jb +钹=jd +啵=kp +檗=mu +驳=mx +鹁=nu +膊=oc +脖=oz +伯=rb +博=uc +礴=uc +勃=ul +饽=uz +舶=vb +箔=vb +簸=vy +玻=wp +亳=wq +踣=zk +跛=zp +不=ad +部=el +捕=fd +钚=jd +钸=jj +哺=kd +卟=kd +瓿=lw +晡=od +补=pd +埔=td +埠=tu +布=uj +簿=vc +步=vp +怖=xj +醭=yn +逋=za +踄=zp +鳖=ax +瘪=bv +别=dk +憋=xx +蹩=zx +丙=an +病=bn +冰=dd +邴=ea +摒=fc +炳=hn +梹=mb +柄=mn +兵=pb +秉=pn +并=qc +饼=uc +禀=wx +北=av +蓓=ck +邶=ea +陂=ep +鞴=ga +焙=hk +稖=hk +辈=ia +钡=jb +鐾=ju +贝=kr +杯=md +鹎=np +背=oa +褙=po +被=pp +卑=pu +狈=qb +倍=rk +碚=uk +碑=uu +孛=uz +备=wt +悲=xa +惫=xw +悖=xz +擦=fx +嚓=kx +礤=ux +凑=dd +辏=id +楱=md +腠=od +测=dd +厕=id +册=pa +侧=rd +策=vn +恻=xd +涔=dv +岑=ev +曾=bo +层=us +噌=ko +蹭=zo +藏=ca +苍=cv +沧=dv +仓=rv +伧=rv +舱=vv +玱=wv +赐=bp +疵=bv +茨=cr +茈=cv +刺=da +次=dr +呲=kv +糍=ms +鹚=nq +祠=pk +辞=pu +兹=qs +伺=rk +磁=us +雌=vf +此=vv +瓷=wd +慈=xq +词=yk +蚕=ai +残=dg +灿=he +黪=hp +=hp +粲=ma +骖=mp +参=sp +餐=ua +孱=uz +璨=wm +惭=xj +惨=xp +曹=ao +草=cu +漕=do +操=fm +螬=io +嘈=ko +槽=mo +糙=mz +艚=vo +黻=ad +才=ap +赙=bc +财=bp +菜=cm +蔡=cx +涪=dk +材=mp +睬=om +猜=qo +采=vm +彩=vp +裁=yg +踩=zm +鹾=ag +痤=bt +嵯=eg +搓=fg +措=fo +挫=ft +撮=fy +厝=io +错=jo +锉=jt +脞=ot +磋=ug +矬=ut +蜅=zd +蹉=zg +寸=ad +吋=kc +村=mc +皴=sp +存=uz +忖=xc +窜=ba +撺=fa +攒=fb +镩=ja +爨=ph +汆=rd +篡=vs +纂=vs +蹿=za +丛=ar +苁=cr +葱=cx +淙=dx +聪=ex +匆=gd +枞=mr +骢=mx +囱=pd +从=rr +琮=wx +璁=wx +悰=xx +蔟=cu +殂=dq +蹙=gz +粗=mq +猝=qu +徂=ra +促=rz +簇=vu +卒=wu +酢=ya +醋=yo +蹴=zd +瘁=bu +萃=cu +淬=du +崔=ef +摧=ff +焠=hu +啐=ku +毳=mm +粹=mu +榱=my +脆=ov +催=rf +璀=wf +悴=xu +翠=yu +大=an +瘩=bk +荙=cz +耷=de +=dz +打=fd +搭=fk +靼=ga +鞑=gz +嗒=kk +哒=kz +妲=na +褡=pk +笪=va +答=vk +怛=xa +达=zd +跶=zz +豆=aq +窦=bd +痘=bq +蔸=ce +斗=du +兜=ep +陡=er +都=et +抖=fu +蚪=iu +钭=ju +篼=ve +逗=za +鲷=ak +凋=dk +掉=fu +刁=ga +钓=jd +铫=jd +铞=jj +雕=pf +吊=kj +叼=kp +屌=uj +碉=uk +貂=vk +调=yk +的=bd +底=gd +锝=jc +得=rc +德=rx +澄=dd +登=dq +嶝=ed +邓=ey +灯=hd +镫=jd +噔=kd +瞪=od +戥=og +僜=rd +磴=ud +等=vc +簦=vd +凳=vj +蹬=zd +宕=bu +荡=cp +菪=cu +挡=fe +铛=je +凼=kd +档=me +裆=pe +砀=up +珰=we +当=xe +党=xe +谠=ye +弟=bp +荻=ch +蒂=cj +滴=dk +涤=dm +邸=ep +抵=fd +骶=gd +镝=jk +嘀=kk +帝=lj +柢=md +棣=mn +嫡=nk +娣=np +睇=op +氐=pd +狄=qh +低=rd +籴=rm +缔=sj +坻=td +堤=tr +地=tv +砥=ud +觌=uj +碲=uj +笛=va +第=vp +敌=wp +诋=yd +羝=yd +翟=yf +谛=yj +迪=za +递=zb +单=ba +疸=ba +瘅=ba +赕=bh +萏=ca +殚=da +淡=dh +澹=dy +聃=ea +郸=eb +耽=ev +担=fa +掸=fa +弹=ga +啖=kh +胆=oa +旦=oa +眈=ov +丹=pa +氮=qh +但=ra +儋=ry +箪=va +蛋=vi +惮=xa +诞=yv +到=da +捣=fe +纛=fs +刀=gp +稻=ha +焘=hf +帱=jc +叨=kd +梼=mc +盗=md +岛=ne +祷=pc +氘=qa +倒=rd +导=vc +忉=xd +悼=xu +蹈=za +道=zq +歹=ad +贷=br +殆=dk +岱=er +带=fj +黛=hr +呔=kd +呆=km +甙=qd +待=rc +代=rd +傣=rd +绐=sk +戴=tg +埭=tn +玳=wd +怠=xs +袋=yr +迨=zs +逮=zv +典=ab +电=av +癫=ba +癜=by +滇=db +淀=dr +巅=ea +阽=ek +掂=fk +靛=fr +店=gk +甸=gt +点=ha +钿=jt +奠=qd +佃=rt +垫=tf +坫=tk +颠=ua +碘=ub +殿=uy +簟=vu +玷=wk +惦=xk +踮=zk +夺=dc +剁=dj +沲=dv +掇=fy +铎=ja +朵=jm +哆=kd +咄=kk +哚=km +驮=md +柁=mv +多=pd +裰=py +缍=sa +堕=te +垛=tm +躲=um +舵=vv +惰=xo +敠=yy +跺=zm +踱=zy +顿=aa +趸=az +沌=dv +炖=hv +钝=jv +吨=kv +囤=kv +盹=ov +盾=po +墩=tw +砘=uv +礅=uw +敦=ww +惇=xz +蹲=zc +遁=zp +铥=js +丢=ps +煅=hy +锻=jy +端=la +断=mj +椴=my +段=py +缎=sy +短=ud +簖=vj +东=ad +董=ca +苳=cd +冻=dd +洞=dk +岽=ed +峒=ek +动=el +咚=kd +栋=md +鸫=na +胨=od +胴=ok +氡=qd +侗=rk +垌=tk +硐=uk +冬=wd +懂=xa +恫=xk +蠹=ai +赌=bo +芏=ct +渎=dd +渡=dy +毒=fd +髑=gi +黩=hd +度=gy +镀=jy +嘟=ke +椟=md +杜=mt +犊=nd +妒=nu +督=oa +睹=oo +肚=ot +牍=pd +独=qi +堵=to +笃=vm +读=yd +兑=be +队=er +镦=jw +堆=tf +碓=uf +憝=xw +怼=xy +对=yc +嗲=kd +鲽=am +爹=fd +揲=fm +瓞=gn +蝶=im +喋=km +牒=pm +绖=st +堞=tm +垤=tt +耋=tt +碟=um +叠=yq +谍=ym +蹀=zm +跌=zn +迭=zp +丁=aa +疔=bd +定=br +顶=da +耵=ed +钉=jd +锭=jr +叮=kd +啶=kr +盯=od +腚=or +鼎=ov +仃=rd +町=td +饤=ud +碇=ur +玎=wd +订=yd +酊=yd +鳄=ag +额=ba +莪=cd +萼=cg +苊=cv +峨=ed +鄂=ek +扼=fv +蛾=id +厄=iv +轭=iv +锇=jd +锷=jg +颚=ka +呃=kv +娥=nd +婀=nk +鹗=nk +鹅=np +腭=og +俄=rd +垩=ta +饿=ud +屙=uk +噩=wk +恶=xa +愕=xg +谔=yg +讹=yv +遏=zo +诶=yu +蒽=cx +摁=fx +恩=xk +而=ap +二=aa +耳=aa +鲕=aa +洱=de +尔=dx +铒=je +鸸=na +儿=pv +贰=qb +弍=qe +佴=re +饵=ue +珥=we +迩=zd +法=ds +阀=mg +乏=pn +伐=rg +罚=sd +垡=tr +砝=us +发=vd +筏=vg +珐=ws +否=ak +缶=rk +鲼=ab +分=bd +芬=cd +瀵=db +汾=dd +奋=dt +焚=hm +吩=kd +粪=mb +粉=md +棼=md +氛=qd +偾=rb +份=rd +纷=sd +坟=tw +鼢=ud +玢=wd +愤=xb +忿=xb +酚=yd +丰=aa +疯=bf +葑=cc +沣=df +冯=dm +酆=ee +峰=ef +奉=fa +烽=hf +蜂=if +锋=jf +风=jx +凤=jy +唪=ka +枫=mf +俸=ra +缝=sz +封=tc +砜=uf +讽=yf +逢=zw +鲂=af +芳=cf +防=ef +邡=ef +房=hf +钫=jf +枋=mf +妨=nf +肪=of +仿=rf +彷=rf +纺=sf +坊=tf +舫=vf +放=wf +方=wp +访=yf +贩=by +蘩=cs +藩=ct +蕃=ct +范=cv +樊=dm +泛=dn +烦=ha +燔=ht +凡=jd +帆=jd +钒=jd +幡=jt +梵=md +番=pt +反=py +翻=py +犯=qv +繁=sr +畈=ty +矾=ud +饭=uy +璠=wt +返=zp +蹯=zt +佛=ra +鲋=ac +甫=ad +黼=ad +夫=an +鳆=aw +赋=bd +负=bd +富=bt +父=bx +苻=cc +芾=cj +芙=cn +茯=cq +菔=cy +莩=cz +副=da +滏=da +浮=dz +附=ec +郛=ev +釜=fa +拂=fa +拊=fc +斧=fj +扶=fn +麸=fn +抚=fv +府=gc +腐=gr +匐=gt +馥=hw +稃=hz +辅=id +蚨=in +辐=it +蝮=iw +蜉=iz +幞=jn +幅=jt +咐=kc +呋=kn +驸=mc +桴=mz +妇=ne +凫=nj +腑=oc +脯=od +肤=on +腹=ow +服=oy +祓=pd +袱=pq +福=pt +阜=pu +孵=pz +氟=qa +付=rc +俯=rc +傅=rc +伏=rq +复=rw +俘=rz +绋=sa +缚=sc +罘=sd +绂=sd +砩=ua +砆=un +弗=va +符=vc +艴=vv +孚=vz +敷=wa +怫=xa +覆=xw +讣=yd +跗=zc +赴=zd +趺=zn +非=aa +鲱=aa +痱=ba +费=bv +菲=ca +沸=da +淝=dv +废=gd +扉=ha +蜚=ia +蜰=io +镄=jb +匪=ka +啡=ka +吠=kq +榧=ma +妃=nv +腓=oa +肺=oj +肥=ov +狒=qa +绯=sa +篚=va +飞=vd +斐=wa +悱=xa +诽=ya +翡=ya +霏=ya +夹=an +尕=gx +轧=iv +尬=ja +旮=jo +钆=jv +嘎=kg +咖=kk +噶=kv +尜=xx +购=bs +觏=cj +苟=ck +沟=ds +岣=ek +拘=fk +鞲=ga +够=gd +勾=gs +钩=js +枸=mk +构=ms +媾=na +鸲=ng +狗=qk +佝=rk +缑=su +垢=tk +彀=uy +篝=va +笱=vk +诟=yk +遘=za +洸=de +广=di +咣=ke +桄=me +胱=oe +犷=qg +光=xe +逛=zq +鬲=aa +戈=ad +疙=bg +革=ca +葛=cv +割=db +哥=dk +歌=dq +隔=ea +搁=fk +骼=gk +髂=gk +虼=ig +蛤=ik +镉=ja +铬=jk +嗝=ka +咯=kk +哿=lk +格=mk +阁=mk +盖=my +鸽=nr +膈=oa +胳=ok +袼=pk +颌=ra +个=ra +仡=rg +搿=ru +纥=sg +塥=ta +圪=tg +饹=uk +硌=uk +舸=vk +各=wk +亘=aa +茛=cn +哏=kn +根=mn +艮=vn +跟=zn +更=an +鲠=an +耿=eh +赓=gb +庚=gn +哽=kn +耕=la +梗=mn +鹒=ng +绠=sn +埂=tn +颈=va +羹=yd +刚=dk +港=dv +岗=ex +缸=fg +扛=fg +钢=jx +冈=kx +杠=mg +肛=og +罡=sv +纲=sx +筻=vn +戆=xl +干=au +疳=bc +甘=ca +苷=cc +泔=dc +淦=dj +澉=dw +擀=fu +秆=hu +尴=jm +赣=lb +柑=mc +杆=mu +橄=mw +肝=ou +旰=ou +绀=sc +坩=tc +乾=ug +矸=uu +竿=vu +玕=wu +敢=wv +感=xg +酐=yu +赶=zu +皋=bu +藁=cm +郜=en +搞=fk +稿=hk +羔=hy +镐=jk +锆=jk +糕=mh +槁=mk +槔=mu +告=nk +杲=om +睾=pu +缟=sk +篙=vk +高=wk +膏=wo +诰=yk +丐=ag +赅=br +芥=ca +溉=dv +陔=er +戤=gg +钙=jg +概=mv +胲=or +垓=tr +改=wv +该=yr +果=an +涡=dd +聒=ek +郭=ew +崞=ez +掴=fw +蜾=in +蝈=iw +锅=jd +帼=jw +呙=kr +国=kw +啯=kw +椁=mz +馘=qk +猓=qn +埚=td +虢=vh +裹=wy +过=zc +鲧=as +滚=dy +辊=iv +棍=mv +绲=sv +磙=uy +衮=wy +鳏=ad +冠=bc +官=bk +贯=bv +莞=ce +灌=df +涫=dk +掼=fb +罐=ff +爟=hf +棺=mk +盥=mp +鹳=nc +关=qd +倌=rk +纶=sv +馆=uk +管=vk +惯=xb +观=yj +工=aa +廾=aa +贡=bg +宫=bk +公=bs +共=cb +恭=cx +拱=fb +巩=gd +汞=gd +功=gl +蚣=is +觥=je +龚=lb +肱=os +供=rb +躬=ug +弓=vg +珙=wb +攻=wg +鲴=ak +谷=bk +痼=bk +贾=bx +菰=cg +菇=ck +沽=dk +汩=do +崮=ek +雇=hf +顾=ia +轱=ik +蛄=ik +觚=jg +锢=jk +钴=jk +呱=kg +固=kk +咕=kk +骨=ko +蛊=mi +梏=mk +鹘=ng +姑=nk +牯=nk +牿=nk +鸪=nu +瞽=ou +股=oy +臌=oy +估=rk +罟=sk +古=uk +辜=uu +鼓=uy +毂=uy +嘏=uy +箍=vj +故=wu +诂=yk +酤=yk +孤=zg +归=ae +规=aj +鳜=aq +鲑=at +贵=ba +宄=bj +皈=by +刿=de +刽=dr +龟=dv +庋=gy +轨=ij +匦=kj +柜=ma +桧=ms +桂=mt +闺=mt +妫=nd +炅=oh +晷=ok +鬼=ps +圭=tt +硅=ut +癸=vd +簋=vm +瑰=wg +诡=yv +跪=zv +寡=bd +剐=dk +刮=dp +括=fk +挂=ft +栝=mk +鸹=np +胍=og +褂=pd +瓜=pn +卦=td +诖=yt +拐=fl +乖=pv +怪=xt +给=sk +铪=jk +哈=kk +瘊=bu +骺=gk +厚=iz +喉=ku +吼=kv +糇=mu +后=pk +猴=qu +候=ru +侯=ru +後=rw +堠=tu +篌=vu +鲎=xa +逅=zp +鳇=aw +癀=bb +皇=bw +黄=cb +荒=cv +潢=db +滉=dg +湟=dw +隍=ew +煌=hw +蟥=ib +蝗=iw +幌=jg +凰=jw +锽=jw +晃=og +徨=rw +磺=ub +簧=vb +篁=vw +璜=wb +肓=wo +恍=xg +慌=xv +惶=xw +谎=yv +遑=zb +翮=ay +贺=bl +荷=ck +菏=ck +河=dk +涸=dk +郃=er +和=hk +龁=ig +蚵=ik +呵=kk +嗬=kk +喝=kv +盉=mh +阖=mm +盒=mr +核=mr +阂=mr +盍=mt +鹤=nd +曷=ov +禾=pm +褐=pv +合=rk +何=rk +壑=ta +赫=td +貉=vk +劾=wl +诃=yk +痕=bn +狠=qn +很=rn +恨=xn +蘅=cd +哼=ka +横=mb +桁=md +姮=na +衡=rd +亨=wa +珩=wd +恒=xa +沆=dj +吭=kj +杭=mj +行=rd +绗=sd +航=vj +颃=wa +虹=ig +顸=aa +寒=bd +罕=bu +菡=ck +涵=dk +汗=du +汉=dy +瀚=dy +邗=ea +邯=ec +捍=fu +撖=fw +撼=fx +焓=hk +焊=hu +蚶=ic +喊=kk +阚=mw +晗=ok +旱=ou +鼾=pu +颔=ra +含=rk +韩=ua +翰=uy +函=vk +悍=xu +憨=xv +憾=xx +酣=yc +皓=bk +薅=cc +蒿=ck +灏=da +浩=dk +濠=dn +郝=et +蚝=im +号=kg +嚆=kk +嚎=kn +嗥=ku +耗=lm +好=nz +颢=oa +昊=od +壕=tn +毫=wm +豪=wn +害=bk +海=dd +骸=gr +嗨=kd +咳=kr +骇=mr +氦=qr +亥=wr +醢=ym +还=za +孩=zr +夥=ad +豁=bk +货=br +藿=cf +获=cq +劐=dc +活=dk +火=dr +混=dv +攉=ff +或=ga +蠖=iy +钬=jh +锪=jx +镬=jy +嚯=kf +=kr +耠=lk +祸=pd +伙=rh +惑=xg +霍=yf +赍=bu +荤=ci +殛=da +浑=di +溷=dn +魂=eg +阍=mo +婚=no +昏=po +馄=uv +珲=wi +诨=yi +鲩=ae +鬟=ay +宦=ba +痪=bn +豢=bn +寰=by +萑=cf +洹=da +浣=de +涣=dn +奂=dn +漶=dx +郇=eg +换=fn +擐=fy +焕=hn +镮=jy +锾=jy +唤=kn +圜=ky +桓=ma +嬛=ny +獾=qf +缓=sy +缳=sy +幻=vg +环=wd +患=xa +欢=yq +逭=zb +宏=bs +蕻=cb +荭=cg +薨=cv +洪=db +鸿=dn +泓=ds +弘=gs +訇=gy +烘=hb +轰=iy +哄=kb +竑=ls +闳=ms +红=sg +黉=xb +讧=yg +互=aa +虍=aq +葫=co +滹=da +冱=da +瓠=dg +沪=dh +湖=do +户=du +浒=du +淴=dx +岵=ek +护=fh +弧=gg +烀=ha +虎=hj +煳=ho +戽=hu +扈=hv +轷=ia +蝴=io +斛=ju +唬=kh +囫=kp +呼=ku +唿=kx +糊=mo +槲=mu +鹄=nn +鹱=ny +祜=pk +乎=pu +狐=qg +猢=qo +壶=ua +鹕=un +胡=uo +觳=uy +笏=vp +琥=wh +瑚=wo +忽=xg +怙=xk +惚=xx +醐=yo +虺=ai +贿=bo +茴=ck +荟=cs +蕙=cx +汇=dk +洄=dk +浍=ds +彗=ef +隳=ex +挥=fi +辉=gi +麾=gm +秽=hd +烩=hs +蛔=ik +蟪=ix +哕=kd +咴=kh +回=kk +喙=kn +阓=mb +晦=od +晖=oi +毁=py +会=rs +徽=rw +缋=sb +绘=ss +恚=tx +卉=uc +灰=uh +惠=xa +悔=xd +慧=xf +恢=xh +讳=ya +诲=yd +诙=yh +翚=yi +画=ak +花=cv +划=dg +滑=dg +砉=fu +铧=ju +哗=ku +桦=mu +骅=mu +猾=qg +化=rv +华=ur +话=yk +淮=df +槐=mg +徊=rk +坏=td +怀=xd +踝=zn +黑=ha +嘿=kh +岔=be +察=bx +茶=cm +茬=ct +汊=dd +刹=dx +插=fa +搽=fm +锸=ja +镲=jx +喳=ka +查=ma +楂=ma +杈=md +槎=mg +檫=mx +姹=nq +衩=pd +碴=ua +馇=ua +叉=yd +差=yg +诧=yq +瘳=bp +抽=fa +稠=hk +瞅=oh +臭=pq +俦=rc +仇=rj +绸=sk +畴=tc +丑=va +筹=vc +愁=xh +惆=xk +酬=ya +诪=yc +雠=yf +踌=zc +窗=bd +疮=bv +创=dr +床=gm +幢=ja +闯=mm +怆=xv +车=aa +澈=dw +扯=fv +撤=fw +彻=rd +坼=td +砗=ui +掣=up +屮=va +宸=bn +沉=dj +陈=ed +抻=fa +辰=in +龀=iv +臣=ka +嗔=kb +郴=me +榇=mm +晨=on +衬=pc +尘=tx +碜=up +琛=wm +忱=xv +谶=yg +谌=yv +趁=zp +宬=bv +撑=fu +成=gv +秤=hu +程=hw +称=hx +蛏=it +铖=jv +呈=kw +橙=md +盛=mg +骋=mg +枨=mn +柽=mt +瞠=ot +塍=ot +晟=ov +乘=pn +裎=pw +城=tv +埕=tw +丞=va +承=vn +琤=wa +惩=xr +诚=yv +酲=yw +逞=zk +鲳=ao +厂=ap +畅=ap +苌=cn +菖=co +昶=do +锠=jo +唱=ko +鬯=kv +阊=mo +氅=mx +嫦=nj +娼=no +昌=oo +肠=op +长=pn +猖=qo +徜=rk +倘=rk +伥=rn +倡=ro +偿=rs +场=tp +敞=wx +常=xj +怅=xn +尝=xs +痴=bk +瘛=bx +苴=cq +茌=cu +池=dv +豉=dy +蚩=ei +耻=ev +持=fc +魑=gd +弛=gv +炽=hb +螭=id +斥=pd +哧=kd +吃=kg +嗤=ki +呎=kn +叱=kq +啻=lk +榘=mu +驰=mv +媸=ni +鸱=np +眵=od +眙=ok +褫=ph +侈=rd +傺=rx +赤=td +墀=tn +饬=ul +尺=un +翅=uy +篪=vh +齿=vk +笞=vk +敕=wa +踟=zk +迟=zu +觇=aj +冁=bn +蒇=cb +澶=da +浐=di +潺=dz +搀=fd +掺=fp +廛=gt +蝉=ia +蟾=iy +铲=ji +产=lp +阐=ma +骣=mz +婵=na +禅=pa +缠=sa +馋=ud +羼=uy +颤=wa +忏=xu +谄=ya +谗=yd +躔=zt +潮=do +剿=dv +抄=fp +炒=hp +焯=hu +钞=jp +嘲=ko +吵=kp +耖=lp +晁=od +绰=su +朝=uo +巢=vn +怊=xk +超=zk +虿=ai +瘥=bg +拆=fd +钗=jd +柴=mv +侪=ra +豺=vp +辍=iy +龊=iz +啜=ky +戳=yg +踔=zu +莼=cv +淳=dz +蠢=fi +春=fo +骱=ga +唇=ik +蝽=io +椿=mo +鹑=nw +纯=sv +醇=yz +串=aa +穿=bp +钏=ja +喘=ka +椽=mn +川=pa +舛=pa +氚=qa +传=rd +舡=vg +船=vk +巛=vv +遄=ze +虫=ad +宠=bl +茺=ce +冲=da +崇=ex +舂=fa +铳=je +重=pa +艟=va +充=we +憧=xa +忡=xa +翀=ya +亍=ad +贮=ba +刍=de +雏=df +滁=dh +除=eh +出=ek +搐=ft +黜=hk +厨=ic +蜍=ih +触=ji +锄=jl +橱=mc +樗=mg +楮=mo +楚=mr +杵=mu +初=pd +褚=po +储=ro +绌=sk +矗=ua +础=uk +处=wd +畜=wt +怵=xd +憷=xr +蹰=zc +躇=zo +陲=ea +捶=fa +炊=hq +锤=ja +吹=kq +棰=ma +椎=mf +槌=mz +垂=pa +揣=fa +搋=fh +膪=ok +踹=za +鲛=af +皎=bf +窖=bk +茭=cf +蕉=ch +艽=cj +角=da +浇=de +湫=dh +峤=ea +郊=ew +挢=fa +搅=fj +焦=hr +轿=ia +蛟=if +较=if +铰=jf +叫=ka +嚼=kc +噍=kh +骄=ma +椒=my +娇=na +姣=nf +鹪=nr +脚=oe +胶=of +狡=qf +侥=re +佼=rf +僬=rh +徼=rw +绞=sf +缴=sw +矫=ua +饺=uf +礁=uh +筊=vf +敫=wb +交=wf +教=wt +觉=xj +醮=yh +酵=yz +跤=zf +蒋=cc +茳=cg +洚=da +将=dc +浆=dd +奖=dd +豇=dg +江=dg +降=ea +疆=ga +强=gi +弶=gx +匠=kj +耩=la +桨=md +糨=mi +犟=ng +僵=ra +绛=sa +缰=sa +礓=ua +讲=ya +酱=yd +姜=yn +丌=aa +鲚=aa +冀=ab +鲫=ae +髻=ak +击=ak +棘=an +乩=av +寄=bk +瘠=bo +疾=bu +寂=by +荠=ca +蕺=ca +蓟=cd +芨=cn +藉=co +蒺=cu +芰=cy +济=da +剞=dd +汲=dn +脊=do +洎=do +剂=dw +激=dw +觊=ej +岌=en +嵴=eo +即=ev +际=ex +挤=fa +揖=fe +掎=fk +技=fy +积=hb +嵇=he +稽=ho +稷=hw +季=hz +辑=ie +虮=ij +哜=ka +唧=ke +戢=kg +叽=kj +咭=kk +麂=lj +骥=mb +楫=me +机=mj +极=mn +集=mr +姬=na +犄=nk +嫉=nu +妓=ny +鸡=ny +肌=oj +几=pg +及=pn +祭=px +佶=rk +偈=rv +伎=ry +绩=sb +缉=se +羁=sm +级=sn +继=sv +纪=sv +墼=ta +畸=tk +圾=tn +基=tq +戟=ug +饥=uj +矶=uj +吉=uk +屐=uy +亟=va +暨=va +箕=vb +笄=vc +笈=vn +籍=vo +畿=vt +既=vv +己=vv +齑=wa +玑=wj +急=xd +忌=xv +悸=xz +霁=ya +讥=yj +诘=yk +计=yu +记=yv +跻=za +迹=zw +跽=zx +戋=ad +戬=ag +柬=an +鲣=at +贱=bg +謇=by +蹇=bz +茧=ci +菅=ck +蒹=cn +荐=cz +湔=dd +溅=dg +渐=dj +减=dk +涧=do +剑=dr +歼=du +捡=fa +拣=fd +搛=fn +鞯=gz +肩=ho +煎=hq +鉴=ja +锏=jo +键=jz +见=kv +囝=kz +检=ma +监=ma +枧=mj +槛=mm +间=mo +楗=mz +毽=mz +鹣=nq +牮=nr +奸=nu +犍=nz +睑=oa +腱=oz +裥=po +剪=qd +兼=qn +翦=qy +俭=ra +件=rn +僭=ro +健=rz +缄=sk +缣=sn +坚=ta +硷=ua +饯=ug +碱=uk +箭=vd +笺=vg +笕=vj +舰=vj +简=vo +尖=xd +谫=yd +谏=yn +艰=yn +趼=zc +践=zg +建=zv +踺=zz +巾=aa +晋=ao +赆=bd +堇=ca +荩=cd +觐=cj +津=da +浸=dy +廑=ga +靳=gj +烬=hd +锦=jj +噤=kx +槿=ma +禁=mx +妗=nv +斤=pa +衿=pv +襟=px +金=rb +今=rv +仅=ry +紧=sa +缙=so +矜=sv +馑=ua +尽=ud +筋=vl +劲=vl +卺=vv +瑾=wa +谨=ya +进=za +近=zj +军=bi +皲=bp +菌=ch +莙=ck +浚=dw +郡=ev +峻=ew +捃=fk +钧=jd +麇=lh +竣=lw +骏=mw +隽=rp +俊=rw +均=td +筠=va +君=vk +逡=zw +韭=aa +鬏=ah +旧=ao +究=bj +疚=bn +酒=dy +揪=fh +厩=iv +啾=kh +桕=ma +柩=mn +阄=mv +鸠=nj +鹫=nw +臼=pa +九=pg +灸=ph +舅=pl +久=pn +僦=rd +纠=sa +救=wa +就=wd +咎=wk +玖=wn +赳=za +桊=bm +眷=bo +卷=bv +涓=do +鄄=ex +捐=fo +镌=jp +锩=jv +圈=kv +鹃=nk +娟=no +蠲=qi +狷=qo +倦=rv +绢=so +惓=xv +窘=bk +扃=hk +炯=hk +迥=zk +疬=bl +藜=cd +苈=cl +蕨=cq +澧=dd +劂=di +决=dn +崛=ek +掘=fk +抉=fn +撅=fq +攫=fy +爝=hc +厥=iq +觖=jn +镢=jq +噱=kn +噘=kq +桷=mj +橛=mq +矍=oy +獗=qq +倔=rk +绝=sv +爵=vc +孓=vn +玦=wn +珏=ww +谲=yk +诀=yn +蹶=zq +具=qb +窭=bn +疽=bq +苣=ca +莒=ck +菊=cm +沮=dq +剧=du +聚=en +拒=fa +飓=fb +据=fk +句=gk +鞠=gm +鞫=gy +炬=ha +焗=hk +龃=iq +钜=ja +锔=jk +锯=jk +巨=ka +咀=kq +榉=ma +驹=mk +椐=mk +橘=mk +桔=mk +犋=nb +裾=pk +雎=qf +狙=qq +俱=rb +倨=rk +矩=ua +居=uk +局=uk +屦=un +琚=wk +举=xa +惧=xb +讵=ya +醵=yn +距=za +踽=zd +遽=zh +踞=zk +趄=zq +颊=aa +甲=aa +戛=ag +痂=bk +家=bn +瘕=by +茄=ck +荚=cn +葭=cy +浃=dn +岬=ea +郏=ea +稼=hn +蛱=in +钾=ja +铗=jn +镓=jn +加=lk +枷=mk +驾=ml +架=ml +嫁=nn +胛=oa +袷=pk +价=ra +伽=rk +佳=rt +假=ry +嘉=uk +笳=vk +珈=wk +恝=xf +袈=yl +跏=zk +迦=zl +鲒=ak +疥=ba +皆=ba +疖=be +节=ce +洁=dk +阶=ea +撷=fa +拮=fk +接=fn +捷=fr +揭=fv +戒=gc +秸=hk +杰=hm +蚧=ia +解=jn +喈=kb +嗟=kg +唶=ko +竭=lv +楷=mb +桀=mp +姐=nq +婕=nr +睫=or +介=ra +街=rd +截=rg +借=ro +结=sk +界=ta +劫=tl +颉=ua +届=ua +碣=uv +孑=va +玠=wa +诫=yd +讦=yu +羯=yv +井=aa +鲸=ax +痉=bg +茎=cg +菁=co +净=da +荆=dc +濂=dn +潋=dw +阱=ea +旌=fa +静=fa +靓=fj +弪=gg +黥=hx +镜=je +竞=le +竟=le +靖=lo +粳=mn +精=mo +婧=no +肼=oa +胫=og +晶=oo +腈=oo +睛=oo +景=ox +獍=qe +径=rg +儆=rw +经=sg +境=te +兢=ue +敬=wc +京=wx +憬=xx +惊=xx +警=yy +迳=zv +卡=ad +咔=kd +喀=kk +胩=od +佧=rd +口=aa +囗=aa +寇=by +芤=cv +蔻=cy +叩=ek +扣=fk +抠=fx +眍=ox +筘=vk +贶=be +夼=da +况=de +邝=eg +劻=kl +哐=kw +匡=kw +框=mw +旷=og +眶=ow +狂=qw +纩=sg +圹=tg +矿=ug +筐=vw +诓=yw +诳=yw +颗=aa +疴=bk +客=bk +窠=bn +苛=ck +可=dk +溘=dm +渴=dv +刻=dw +岢=ek +髁=gn +稞=hn +科=hu +轲=ik +蝌=iu +钶=jk +锞=jn +嗑=km +柯=mk +骒=mn +棵=mn +瞌=om +氪=qe +缂=sg +坷=tk +克=ue +壳=uj +砢=uk +磕=um +颏=wa +珂=wk +恪=xk +课=yn +啃=ko +裉=pn +垦=tv +肯=vo +恳=xv +铿=jt +坑=tj +抗=fj +康=gn +炕=hj +钪=jj +闶=mj +糠=mn +伉=rj +亢=wj +慷=xn +莰=cq +刊=da +瞰=ow +戡=qg +勘=ql +龛=rl +侃=rv +坎=tq +堪=tv +磡=ul +看=uo +砍=uq +拷=fg +烤=hg +铐=jg +栲=mg +靠=na +犒=nk +考=tg +尻=uj +开=ac +蒈=cb +剀=de +凯=ej +揩=fb +锴=jb +锎=jc +铠=jv +垲=tv +忾=xq +恺=xv +慨=xv +扩=fg +廓=ge +蛞=ik +阔=mk +髡=ae +鲲=av +捆=fm +焜=hv +锟=jv +困=km +阃=mm +昆=ov +坤=ta +堃=tf +琨=wv +悃=xm +醌=yv +宽=bj +髋=gj +款=uq +空=bg +崆=eg +控=fg +倥=rg +箜=vg +恐=xg +孔=zv +窟=bk +苦=ck +刳=dd +库=gi +骷=gk +枯=mk +裤=pi +哭=qk +绔=sg +堀=tk +喾=xk +酷=yk +窥=bj +蒉=cb +葵=cd +溃=db +奎=dt +聩=eb +岿=ee +隗=eg +亏=eg +揆=fd +魁=gu +蝰=it +馗=jo +匮=kb +喟=ko +喹=kt +盔=mu +暌=od +睽=od +夔=qw +傀=rg +馈=ub +篑=vb +悝=xa +愦=xb +愧=xg +逵=zt +跬=zt +夸=dg +挎=fg +胯=og +侉=rg +垮=tg +跨=zg +蒯=dc +郐=er +哙=ks +脍=os +狯=qs +侩=rs +块=tn +筷=vn +快=xn +瘌=bd +落=ck +剌=da +拉=fl +蜡=io +喇=kd +啦=kl +辣=ln +旯=oj +腊=oo +垃=tl +砬=ul +邋=zv +瘘=bn +蒌=cn +漏=dd +嵝=en +陋=ev +搂=fn +髅=gn +蝼=in +镂=jn +喽=kn +耧=ln +楼=mn +娄=mn +偻=rn +篓=vn +露=yk +疗=ba +寥=bp +寮=bx +蓼=cp +潦=dx +聊=ee +撂=fk +撩=fx +廖=gp +燎=hx +钌=ja +尥=jd +镣=jx +嘹=kx +料=mu +鹩=nu +獠=qx +僚=rx +缭=sx +辽=zv +两=ad +良=dn +凉=dx +魉=gd +辆=id +唡=kd +梁=md +粱=md +粮=mn +椋=mx +量=oa +晾=ox +俩=rd +墚=tm +亮=wj +谅=yx +踉=zn +鳓=al +泐=dl +勒=gl +嘞=kl +叻=kl +肋=ol +乐=pd +仂=rl +了=va +冷=ds +楞=mf +棱=mw +塄=tf +愣=xf +莨=cn +蒗=cn +浪=dn +朗=do +郎=ed +廊=ge +稂=hn +螂=ie +锒=jn +啷=ke +榔=me +阆=mn +狼=qn +琅=wn +里=aa +鲤=aa +丽=ad +鲡=ad +鳢=ad +吏=an +嫠=an +痢=bd +疠=bp +莉=cd +荔=cl +莅=cl +蓠=cs +利=dh +沥=dl +溧=dm +漓=ds +郦=ea +力=gp +黎=hd +黧=hh +戾=hq +厘=ia +蜊=id +轹=id +历=il +励=il +厉=ip +蛎=ip +锂=ja +哩=ka +喱=ka +呖=kl +唳=kq +立=la +骊=md +栎=md +梨=mh +粒=ml +枥=ml +粝=mp +李=mz +鹂=na +娌=na +犁=nh +礼=pv +狸=qa +猁=qd +俚=ra +例=rd +俐=rd +俪=rd +傈=rm +罹=sf +缡=ss +詈=sy +坜=tl +砾=ud +砺=up +蠡=vi +笠=vl +篥=vm +隶=vn +篱=vs +理=wa +离=wd +璃=ws +栗=xm +醴=yd +雳=yl +逦=za +跞=zd +兰=bs +蓝=cm +澜=dd +滥=dm +漤=dn +岚=ef +揽=fj +拦=fs +烂=hs +览=ja +镧=jd +阑=md +榄=mj +栏=ms +婪=nm +褴=pm +缆=sj +罱=su +篮=vm +斓=wd +懒=xb +谰=yd +痨=bl +牢=bn +劳=cl +涝=dl +崂=el +捞=fl +烙=hk +铹=jl +铑=jv +唠=kl +耢=ll +栳=mv +嫪=np +姥=nv +佬=rv +老=tv +酪=yk +醪=yp +赖=ab +来=an +赉=ba +癞=bb +莱=cn +濑=db +涞=dn +崃=en +铼=jn +睐=on +徕=rn +籁=vb +鲢=az +帘=bj +蔹=cw +莲=cz +殓=da +奁=dx +涟=dz +联=ed +廉=gn +炼=hd +蠊=in +镰=jn +链=jz +楝=mn +脸=oa +臁=on +裣=pa +裢=pz +练=sd +敛=wr +琏=wz +怜=xs +恋=xw +连=zi +临=aa +鳞=aa +赁=br +蔺=cf +淋=dm +凛=dx +嶙=ea +邻=er +拎=fs +廪=gx +辚=ia +啉=km +麟=la +粦=ma +林=mm +粼=mv +檩=mx +瞵=oa +膦=oa +磷=ua +吝=wk +琳=wm +懔=xx +霖=ym +躏=zf +遴=zm +赂=bk +瘰=bs +萝=cd +荦=cn +泺=dd +洛=dk +漯=ds +捋=fc +摞=fs +螺=is +锣=jd +镙=js +啰=kd +椤=md +骆=mk +骡=ms +脶=od +裸=pn +猡=qd +倮=rn +罗=sd +络=sk +箩=vd +蠃=wd +雒=wf +珞=wk +逻=zs +沦=dv +抡=fv +轮=iv +囵=kv +伦=rv +仑=rv +论=yv +瘤=bt +浏=dd +溜=dt +流=dv +刘=dw +旒=fv +熘=ht +鎏=jd +镏=jt +锍=jv +柳=me +骝=mt +榴=mt +鹨=ny +绺=sk +留=tp +碌=ud +馏=ut +硫=uv +六=wb +琉=wv +遛=zp +滦=dm +卵=pd +乱=pv +峦=we +銮=wj +栾=wm +鸾=wn +娈=wn +脔=wr +挛=wu +孪=wz +癃=ba +窿=bf +茏=cl +泷=dl +隆=ef +陇=el +拢=fl +咙=kl +聋=le +砻=lu +栊=ml +胧=ol +垄=tl +垅=tl +龙=ud +笼=vl +珑=wl +掠=fx +锊=jc +略=tk +颅=aa +鲁=ao +卢=au +鲈=au +卤=ax +芦=ch +渌=dd +潞=dk +漉=dl +泸=du +陆=ek +掳=fl +撸=fo +庐=gh +鹿=gv +炉=hh +虏=hl +辂=ik +辘=il +轳=iu +镥=jo +噜=ko +麓=lm +栌=mi +橹=mo +氇=mo +鸬=na +鹭=nz +胪=ou +禄=pd +绿=sd +垆=tu +硵=ux +录=vd +箓=vd +舻=vi +簏=vl +璐=wk +戮=yg +路=zk +逯=zv +滤=dx +旅=fn +稆=hk +虑=hx +铝=jk +吕=kk +驴=mh +闾=mk +榈=mk +膂=of +褛=pn +氯=qd +律=ra +侣=rk +缕=sn +屡=un +履=uw +率=wu +鬣=aa +列=dd +冽=dd +洌=dd +捩=fq +烈=hd +咧=kd +猎=qo +埒=tc +劣=xl +裂=ya +趔=zd +躐=zv +鲮=aw +苓=cs +菱=cw +泠=ds +凌=dw +灵=eh +岭=es +聆=es +陵=ew +酃=ey +龄=is +蛉=is +铃=js +另=kl +囹=ks +呤=ks +棂=mh +柃=ms +鸰=nr +〇=oo +领=ra +令=rs +伶=rs +瓴=rw +翎=ry +绫=sw +玲=ws +零=ys +羚=ys +耒=an +蕾=ct +泪=do +擂=ft +镭=jt +类=md +檑=mt +嫘=ns +儡=rt +缧=ss +累=ts +垒=ts +礌=ut +磊=uu +羸=wd +酹=yc +诔=yn +雷=yt +抹=fn +麻=gm +蟆=id +蚂=im +吗=km +嘛=km +唛=kw +骂=mk +杩=mm +妈=nm +嬷=ns +祃=pm +么=ps +犸=qm +码=um +马=va +玛=wm +某=cm +蛑=in +鍪=js +哞=kn +眸=on +侔=rn +牟=sn +缪=sp +谋=ym +藐=ce +苗=ct +淼=dd +渺=dp +描=ft +庙=ga +秒=hp +喵=kt +杪=mp +鹋=nc +妙=np +眇=op +瞄=ot +缈=sp +邈=zv +麽=gs +门=dg +扪=fm +焖=hx +钔=jm +闷=mx +们=rm +懑=xd +悗=xv +蒙=cn +萌=co +瞢=co +甍=cw +蜢=im +蠓=in +虻=iv +锰=jm +幪=jn +梦=md +檬=mn +盟=mo +孟=mz +朦=on +猛=qm +獴=qn +礞=un +艋=vm +艨=vn +氓=wq +懵=xo +勐=zl +莽=cc +茫=cv +芒=cv +漭=dc +邙=ew +蟒=ic +厖=ip +铓=jv +硭=uv +盲=wo +忙=xv +芈=aa +宓=bd +密=be +蜜=bi +幂=bj +米=bm +蘼=ca +汨=do +泌=dp +靡=ga +弭=ge +糜=gm +縻=gs +弥=gx +嘧=ke +咪=km +麋=lm +眯=om +脒=om +祢=px +猕=qx +狝=qx +觅=vj +糸=vx +敉=wm +谧=ym +谜=yz +醚=yz +迷=zm +鳗=ay +颟=ca +蔓=cy +满=dd +漫=dy +鞔=gv +熳=hy +螨=id +幔=jy +镘=jy +瞒=od +曼=oy +缦=sy +埋=ta +墁=ty +馒=uy +蛮=wi +慢=xy +谩=yy +贸=bp +茆=ce +茂=cp +茅=cp +泖=de +峁=ee +卯=ep +旄=fm +蝥=is +铆=je +帽=jo +锚=jt +髦=ma +牦=nm +昴=oe +冒=oo +瞀=os +毛=pv +猫=qt +蟊=si +矛=sp +耄=tm +貌=ve +瑁=wo +袤=wy +懋=xm +劢=al +荬=cd +麦=fw +脉=on +卖=ud +买=vd +霾=ya +迈=za +面=aa +湎=da +勉=dl +免=dv +沔=dv +渑=dv +棉=mj +娩=nv +腼=oa +眠=oq +眄=ov +冕=ov +缅=sa +绵=sj +鳘=ar +苠=cq +泯=dq +岷=eq +抿=fq +皿=ka +黾=kv +闽=mi +闵=mw +旻=ow +缗=so +民=vq +珉=wq +敏=wr +玟=ww +愍=xv +悯=xw +末=an +寞=bd +瘼=bd +莫=cd +蓦=cm +茉=cn +摹=cu +漠=dd +沫=dn +没=dy +殁=dy +陌=eb +摸=fd +魔=gg +磨=gu +摩=gu +秣=hn +默=hq +镆=jd +耱=lu +模=md +嫫=nd +膜=od +墨=th +馍=ud +貊=vb +貘=vd +谟=yd +谬=yp +木=an +幕=cj +募=cl +苜=co +暮=co +墓=ct +慕=cx +沐=dm +拇=fd +穆=hp +钼=jo +目=ka +毪=mn +姆=nd +牡=nt +睦=ot +仫=rs +坶=td +母=vd +牧=wn +亩=wt +霂=ym +灭=ah +蔑=cd +乜=gv +蠛=id +咩=ky +篾=vd +冥=bb +茗=ck +溟=db +螟=ib +铭=jk +鸣=nk +暝=ob +明=oo +名=pk +命=re +酩=yk +寐=bn +莓=cd +湄=do +浼=dv +嵋=eo +郿=ev +魅=gn +煤=hm +镁=jd +镅=jo +梅=md +楣=mo +媒=nm +妹=nn +媚=no +鹛=nv +昧=on +眛=on +袂=pn +猸=qo +每=rd +眉=vo +枚=wm +玫=ww +美=yd +酶=yd +霉=yd +那=ev +捺=fx +钠=jd +镎=ju +哪=ke +呐=kr +呢=kv +娜=ne +肭=od +衲=pd +拿=ru +纳=sd +耨=lc +茑=cn +溺=dd +鸟=na +嬲=nl +袅=ny +脲=od +尿=ud +娘=nn +酿=yn +讷=yd +嫩=nw +恁=xr +能=sv +囊=ay +攮=fy +囔=ky +曩=oy +馕=uy +鲵=ae +泥=dv +拟=fr +旎=fv +铌=jv +匿=kk +妮=nv +腻=od +睨=oe +昵=ov +猊=qe +倪=re +伲=rv +你=rx +坭=tv +尼=uv +怩=xv +霓=ye +逆=zq +蝻=iu +囡=kn +喃=ku +楠=mu +腩=ou +男=tl +赧=ty +南=uu +难=yf +孬=az +淖=du +挠=fe +蛲=ie +铙=je +呶=ky +桡=me +闹=mj +脑=ok +猱=qm +垴=tk +硇=ux +瑙=wx +恼=xk +耐=ac +艿=cp +萘=cx +奈=dx +乃=gp +鼐=gv +柰=mx +奶=np +氖=qp +廿=aa +鲇=ak +鲶=ax +蔫=ch +撵=fi +拈=fk +捻=fx +黏=hk +辇=ia +粘=mk +年=ra +埝=tx +碾=un +念=xr +您=xr +搦=fd +挪=fe +锘=jk +喏=kk +糯=ma +傩=rf +懦=xa +诺=yk +扭=fa +钮=ja +妞=na +牛=pa +狃=qa +纽=sa +忸=xa +暖=oy +农=dn +浓=dn +秾=hn +哝=kn +脓=on +侬=rn +弄=wc +疟=ba +虐=ha +弩=gn +帑=jn +努=ln +驽=mn +奴=ny +胬=rn +怒=xn +孥=zn +钕=jn +女=va +恧=xa +衄=xa +蘖=cm +孽=cz +涅=dt +颞=ea +陧=et +聂=ey +捏=ft +镍=jm +镊=jy +啮=kk +嗫=ky +臬=pm +肀=va +蹑=zy +甯=ba +宁=bd +苎=ca +泞=dd +凝=dr +聍=ed +拧=fd +咛=kd +柠=md +狞=qd +佞=rn +内=ad +馁=un +噢=kd +哦=kd +喔=kt +嚄=ky +藕=cd +沤=dx +欧=kq +瓯=kw +呕=kx +区=kx +殴=ky +耦=ld +鸥=nk +其=qb +偶=rd +怄=xx +讴=yx +葩=cv +帕=jb +啪=kb +杷=mv +爬=vv +筢=vv +琶=wv +怕=xb +趴=zb +剖=dl +掊=fk +裒=wy +漂=dx +剽=dx +殍=dz +螵=ix +嘌=kx +朴=md +嫖=nx +瞟=ox +缥=sx +飘=xf +瓢=xg +票=xx +慓=xx +瓫=bw +湓=dm +喷=kb +喯=kc +盆=mb +蓬=cz +澎=dp +捧=fa +抨=fu +烹=hw +蟛=ip +嘭=kp +棚=mo +鹏=on +朋=oo +膨=op +堋=to +碰=ua +硼=uo +彭=up +砰=uu +篷=vz +怦=xu +蒡=cf +滂=df +庞=gl +螃=if +旁=lf +耪=lf +胖=oa +乓=pd +徬=rf +逄=zw +丕=aa +痞=bk +疲=bp +癖=bu +淠=da +劈=du +邳=ea +郫=ep +陴=eu +披=fp +擗=fu +批=fv +庀=gv +罴=hs +蜱=iu +蚍=iv +铍=jp +匹=ke +啤=ku +噼=ku +枇=mv +媲=nv +脾=ou +僻=ru +仳=rv +纰=sv +坯=ta +埤=tu +毗=tv +圮=tv +鼙=uu +屁=uv +砒=uv +疋=vr +貔=vv +皮=vy +甓=wu +琵=wv +霹=yu +譬=yu +爿=ap +叛=by +泮=da +判=db +潘=dt +拚=fc +蟠=it +攀=mu +盘=mv +盼=od +袢=pa +襻=pu +畔=ta +磐=uv +蹒=zd +疱=bv +泡=dv +匏=dv +抛=fl +庖=gv +炮=hv +咆=kv +脬=oz +袍=pv +狍=qv +跑=zv +蒎=cn +湃=df +派=dn +排=fa +拍=fb +哌=kn +牌=pu +徘=ra +俳=ra +迫=zb +翩=hy +骗=mc +骈=mc +犏=nc +胼=oc +片=pv +偏=rc +篇=vc +谝=yc +蹁=zc +贫=bb +聘=eg +拼=fc +品=kk +榀=mk +嫔=nb +姘=nc +牝=nv +频=va +颦=vu +攴=ay +魄=bg +皤=bt +泼=dd +鄱=ep +钋=jd +钷=jk +叵=kk +粕=mb +婆=nd +颇=pa +坡=tp +破=up +笸=vk +珀=wb +葡=cd +莆=cd +蒲=cd +菩=ck +溥=dc +浦=dd +濮=dn +扑=fd +匍=gd +铺=jd +镤=jn +镨=jo +圃=kd +噗=kn +氆=mo +普=qo +仆=rd +璞=wn +谱=yo +蹼=zn +苤=ca +撇=fw +瞥=ox +氕=qp +平=au +鲆=au +萍=cu +苹=cu +洴=dc +凭=jr +呯=ku +枰=mu +娉=ng +乒=pp +瓶=qw +俜=rg +坪=tu +屏=uc +评=yu +赔=bk +沛=dj +陪=ek +旆=fj +锫=jk +帔=jp +呸=ka +辔=ks +胚=oa +佩=rj +培=tk +珮=wj +裴=ya +霈=yj +醅=yk +配=yv +翘=ay +窍=bg +荞=ca +劁=dr +峭=eo +撬=fm +鞒=ga +巧=gg +鞘=go +锹=jh +桥=ma +樵=mh +橇=mm +瞧=oh +乔=pa +侨=ra +俏=ro +缲=sm +硚=ua +硗=ue +敲=wy +雀=xf +憔=xh +愀=xh +悄=xo +谯=yh +诮=yo +跷=ze +蔷=ck +抢=fv +炝=hv +蜣=iv +锵=jc +镪=ji +锖=jo +呛=kv +樯=mk +枪=mv +嫱=nk +腔=og +襁=pi +戗=rg +墙=tk +戕=vg +羟=yg +羌=yv +跄=zv +妻=an +鳍=ao +七=av +萁=cb +葺=ce +蕲=cj +萋=cn +芪=cq +芑=cv +淇=db +沏=dd +漆=dd +契=df +汔=dg +奇=dk +泣=dl +凄=dn +汽=dq +欹=dq +亓=ea +崎=ek +祁=ep +岂=ev +屺=ev +岖=ex +岐=ey +旗=fb +戚=gx +启=hk +蛴=ia +蜞=ib +颀=ja +器=kk +嘁=kx +麒=lb +棋=mb +骐=mb +柒=md +骑=mk +桤=mv +杞=mv +槭=mx +栖=mx +脐=oa +祺=pb +祈=pj +期=qo +欺=qq +綦=qs +乞=rg +气=rv +企=rv +绮=sk +圻=tj +耆=to +畦=tt +碛=ub +砌=ud +矻=ug +歧=vy +齐=wa +琪=wb +弃=wc +琦=wk +玘=wv +憩=xp +讫=yg +蹊=zd +踦=zk +迄=zr +起=zv +骞=bm +搴=bu +褰=by +荨=cc +芡=cq +芊=cu +茜=cx +浅=dg +牵=dn +潜=do +欠=dr +岍=ec +嵌=eq +阡=eu +掮=fo +扦=fu +黔=hv +虔=hw +钳=jc +钱=jg +铅=jk +钎=ju +钤=jv +椠=mi +肷=oq +千=pu +前=qd +歉=qq +佥=ra +倩=ro +仟=ru +纤=su +缱=sz +堑=ti +签=va +箝=vc +慊=xn +愆=xr +悭=xt +谦=yn +谴=yz +遣=za +迁=zp +寝=by +芹=cj +勤=cl +芩=cv +溱=dh +沁=dx +秦=fh +揿=fq +擒=fs +螓=ih +钦=jq +锓=jy +嗪=kh +噙=ks +吣=kx +亲=lm +檎=md +禽=rd +侵=ry +衾=ry +琴=wv +覃=xu +裙=pk +群=vy +求=ad +鳅=ah +裘=ay +赇=bd +酋=by +萩=ch +泅=dr +邱=ep +秋=hh +蚯=ia +虬=iv +蝤=iy +囚=kr +楸=mh +糗=mq +鹙=nh +丘=pa +鼽=pj +犰=qj +俅=rd +巯=vv +球=wd +逑=za +遒=zq +鬈=av +泉=bd +券=bd +拳=bu +痊=bw +颧=ca +荃=cw +犬=dd +蜷=iv +辁=iw +铨=jw +权=my +全=rw +绻=sv +畎=tq +筌=vw +悛=xw +劝=yl +诠=yw +醛=yw +踡=zv +穹=bg +穷=bl +茕=cu +邛=eg +蛩=ig +銎=jg +筇=ve +琼=wx +跫=zg +瘸=br +却=ee +缺=fn +炔=hn +阕=md +榷=mf +阙=mq +鹊=nc +确=uj +悫=xu +曲=aa +癯=bf +蕖=cm +蘧=cz +取=ey +劬=gl +觑=hj +黢=hw +蛐=ia +龋=id +蛆=iq +蠼=iy +渠=md +阒=mq +驱=mx +娶=ne +瞿=of +朐=ok +氍=om +祛=ps +衢=rd +去=ts +屈=uk +麴=um +磲=um +躯=ux +璩=wn +诎=yk +趋=ze +趣=zy +葜=cd +洽=dk +掐=fa +恰=xk +且=aa +窃=bd +郄=ex +锲=jd +妾=ln +切=qd +挈=uf +箧=vn +惬=xn +怯=xs +顷=aa +鲭=ao +苘=ck +清=do +青=fo +罄=fu +庼=ga +庆=gd +轻=ig +蜻=io +圊=ko +檠=mc +晴=oo +卿=pe +氢=qg +氰=qo +倾=ra +綮=sh +擎=uc +磬=uu +箐=vo +情=xo +请=yo +謦=yu +肉=ar +揉=fm +鞣=gm +糅=mm +柔=sm +蹂=zm +惹=cx +热=hf +韧=ad +荏=cu +葚=cv +刃=dd +稔=hx +轫=id +妊=nu +人=pn +壬=pu +衽=pu +仞=rd +仁=re +任=ru +纫=sd +纴=su +饪=uu +忍=xd +认=yr +扔=fp +仍=rp +攘=fy +穰=hy +嚷=ky +禳=py +壤=ty +瓤=wg +让=ya +日=ka +髯=aa +冉=aa +苒=ca +燃=hh +然=hp +蚺=ia +染=md +荛=ce +扰=fd +娆=ne +绕=se +饶=ue +若=ck +弱=gd +婼=nk +偌=rk +箬=vk +润=dw +闰=mw +阮=ee +软=iq +朊=oe +融=ai +冗=bj +容=bk +茸=ce +蓉=ck +荣=cm +溶=dk +嵘=em +戎=gu +熔=hk +蝾=im +镕=jk +榕=mk +肜=op +狨=qu +绒=su +薷=ca +蓐=cc +茹=ck +濡=da +溽=dc +洳=dk +汝=dn +蠕=ia +辱=ic +铷=jk +嚅=ka +如=nk +襦=pa +褥=pc +入=pn +儒=ra +缛=sc +乳=vv +颥=ya +孺=za +睿=ao +蕤=ca +芮=cr +蕊=cx +汭=dd +蚋=id +锐=je +枘=md +瑞=wa +卅=aa +萨=ci +洒=dx +撒=fw +飒=lf +脎=om +仨=rs +挲=ud +薮=cw +溲=dy +擞=fw +搜=fy +飕=fy +螋=iy +锼=jy +嗽=kq +嗾=ku +嗖=ky +瞍=oy +叟=py +馊=uy +艘=vy +塞=bt +色=dv +涩=dv +穑=hk +铯=jv +啬=uk +瑟=wd +森=mm +僧=ro +搡=fm +嗓=km +磉=um +丧=un +颡=ya +桑=ym +肆=aa +兕=ae +丝=av +澌=dj +泗=ds +死=dv +汜=dv +撕=fj +司=gk +私=hs +厮=ij +蛳=ij +锶=jx +咝=ka +四=ke +嘶=kj +嗣=kk +耜=lk +驷=ms +姒=nr +祀=pv +斯=qj +似=rr +俟=ru +偲=rx +缌=sx +寺=tc +饲=uk +笥=vk +鸶=vn +巳=vv +思=xt +三=ae +糁=mp +伞=ru +毵=sm +叁=ss +馓=uw +散=wc +霰=yw +鳋=ai +瘙=bi +扫=fe +搔=fi +骚=mi +嫂=ny +臊=om +缫=sn +埽=tj +鳃=ax +赛=bb +噻=kt +腮=ox +蓑=cy +锁=jb +唢=kb +嗍=ko +嗦=ks +唆=kw +桫=mp +梭=mw +娑=nd +睃=ow +所=pj +缩=sb +塑=tq +索=us +琐=wb +羧=yw +荪=cx +损=fb +榫=mu +飧=pu +狲=qx +隼=ur +笋=vp +孙=zx +蒜=cx +狻=qw +算=vc +酸=yw +颂=ba +宋=bm +菘=cs +淞=ds +嵩=ek +耸=er +崧=es +竦=ln +松=ms +悚=xn +怂=xr +忪=xs +诵=ya +讼=ys +送=zq +甦=aa +稣=ah +宿=bb +窣=bu +蓿=cb +苏=cd +蔌=cq +涑=dn +溯=do +素=fs +夙=jd +觫=jn +嗉=ks +俗=rk +僳=rm +肃=va +簌=vq +粟=xm +愫=xs +诉=yd +酥=yh +谡=yw +速=za +荽=cn +濉=df +岁=ed +隋=eo +祟=ex +随=ez +隧=ez +髓=gz +穗=hx +燧=hz +虽=ki +睢=of +眭=ot +绥=sn +碎=uu +谇=yu +邃=zb +遂=zq +鳎=ay +它=bv +沓=do +溻=dy +拓=fu +挞=fz +铊=jv +榻=my +闼=mz +她=nv +獭=qb +他=rv +塔=tk +塌=ty +趿=zn +踏=zo +遢=zo +蹋=zy +头=dd +投=fy +骰=gy +偷=rd +透=zh +髫=ak +鲦=am +窕=bd +苕=ck +粜=em +挑=fd +龆=ik +蜩=ik +眺=od +祧=pd +佻=rd +脩=ro +笤=vk +条=wm +跳=zd +迢=zd +铽=jd +特=nc +忒=qd +忑=xa +慝=xk +疼=bd +誊=by +藤=cd +滕=od +腾=om +溏=dk +淌=dk +汤=dp +搪=fk +唐=gk +烫=hd +螗=ik +螳=it +铴=jp +镗=jt +嘡=kt +耥=lk +糖=mk +樘=mt +膛=ot +傥=re +塘=tk +堂=tx +躺=uk +瑭=wk +惝=xk +棠=xm +羰=yh +醣=yk +趟=zk +荑=cn +剃=db +剔=do +涕=dp +提=fr +锑=jp +啼=kj +嚏=kr +梯=mp +鹈=nb +题=oa +替=oa +裼=pp +体=ra +倜=rk +绨=sp +缇=sr +屉=uv +惕=xp +悌=xp +醍=yr +蹄=zj +踢=zp +逖=zq +瘫=bf +痰=bh +贪=br +滩=df +潭=du +炭=eh +郯=eh +摊=ff +探=fm +钽=ja +锬=jh +镡=ju +叹=ky +檀=ma +毯=mh +昙=os +袒=pa +坦=ta +坛=ts +坍=tw +碳=uh +忐=xa +谈=yh +谭=yu +韬=aa +萄=cf +滔=da +涛=dc +洮=dd +淘=df +套=ds +陶=ef +鼗=ey +掏=ff +弢=gy +啕=kf +桃=md +绦=sm +饕=uk +讨=yc +逃=zp +鲐=ak +苔=ck +薹=ct +太=dd +汰=dd +邰=es +泰=fd +抬=fk +钛=jd +骀=mk +肽=od +胎=ok +炱=sh +台=sk +态=xd +酞=yd +跆=zk +天=ad +忝=ax +殄=dp +添=dx +掭=fx +畑=ht +铦=jk +田=ku +阗=mb +腆=ob +甜=pc +舔=px +填=tb +畋=wt +恬=xk +橐=am +沱=dv +陀=ev +托=fq +拖=fv +庹=gn +唾=ka +鼍=kv +柝=md +椭=mo +驼=mv +鸵=nv +脱=oe +乇=pq +佗=rv +坨=tv +砣=uv +箨=va +妥=vn +酡=yv +跎=zv +吞=ak +屯=av +豚=on +臀=ou +暾=ow +褪=pz +氽=rd +饨=uv +湍=da +抟=fd +团=kp +疃=ta +彖=vn +痛=ba +茼=ck +潼=da +捅=fa +铜=jk +同=kk +嗵=kz +童=la +桶=ma +桐=mk +瞳=oa +彤=pp +僮=ra +佟=rd +仝=rg +统=se +砼=ug +筒=vk +恸=xl +酮=yk +通=zs +凸=aa +突=bq +菟=cd +荼=ch +兔=dd +涂=dh +秃=hj +钍=jt +图=kd +吐=kt +徒=rr +堍=tv +土=ua +屠=uo +酴=yh +途=zr +推=ff +颓=ha +煺=hz +蜕=ie +腿=oz +退=zv +贴=bk +萜=ck +帖=jk +铁=jn +餮=ud +怗=xk +葶=cd +莛=cu +汀=dd +聤=ed +挺=fz +庭=gz +烃=hg +厅=id +蜓=iu +铤=jz +听=kj +梃=mz +婷=nd +停=rd +艇=vz +亭=wd +霆=yz +廷=zp +鲨=ad +痧=bp +莎=cp +沙=dp +煞=hd +厦=iw +铩=jm +啥=kk +唼=kn +杉=mp +歃=pq +傻=rw +纱=sp +砂=up +杀=xm +裟=yd +霎=yn +守=bc +兽=bk +瘦=by +寿=fc +授=fy +熟=hw +售=kr +手=pa +狩=qc +首=qo +绶=sy +艏=vo +受=vy +收=wv +爽=dx +孀=no +霜=yo +双=yy +赊=bx +奢=do +涉=dp +滠=dy +折=fj +摄=fy +厍=ii +蛇=iv +麝=lc +舌=pk +社=pt +猞=qk +舍=rk +歙=rq +畲=rt +佘=rx +射=uc +赦=wt +慑=xy +设=yy +申=aa +审=ba +莘=cu +渖=da +深=dm +渗=dp +沈=dv +燊=hm +蜃=ii +呻=ka +哂=kx +椹=mv +婶=na +娠=nn +肾=oa +胂=oa +神=pa +身=pp +甚=qv +伸=ra +什=ru +绅=sa +砷=ua +矧=ua +慎=xb +谉=ya +诜=ye +谂=yx +剩=dp +嵊=en +牲=nf +甥=nl +眚=no +昇=oc +胜=of +升=pc +生=pf +绳=sv +圣=ty +声=up +笙=vf +省=xo +上=aa +赏=bx +殇=dp +熵=hk +觞=jp +商=lk +晌=ok +伤=rl +绱=sk +墒=tk +垧=tk +尚=xk +裳=xy +事=aa +十=aa +鲥=ac +鲺=ai +师=aj +石=ak +史=an +豕=an +贳=bc +实=bd +莳=cc +蓍=co +世=cv +湿=da +奭=db +浉=dj +湜=dr +峙=ec +示=ex +拭=fd +拾=fk +施=fv +虱=gi +炻=hu +轼=id +螫=it +铈=jj +嘘=ka +嗜=ko +噬=kr +势=lf +柿=mj +驶=mn +始=nk +时=oc +是=or +匙=ov +释=pa +视=pj +失=pn +氏=pq +舐=pq +式=qd +狮=qj +侍=rc +矢=rd +使=rn +食=rn +仕=ru +室=tb +埘=tc +士=ua +蚀=ui +饰=uj +屎=um +尸=vp +筮=vr +市=wj +恃=xc +弑=xd +识=yb +诗=yc +试=yd +誓=yf +谥=ym +逝=zf +适=zp +鳝=ak +疝=be +赡=by +苫=ck +芟=cy +汕=de +剡=dh +潸=do +删=dp +陕=en +鄯=ey +擅=fa +扇=hy +煽=hy +蟮=ik +钐=jp +山=ka +栅=ma +闪=mr +骟=my +姗=na +嬗=na +膻=oa +膳=ok +衫=pp +缮=sk +舢=ve +珊=wa +讪=ye +善=yk +跚=za +芍=cd +召=dk +劭=dl +潲=do +邵=ed +卲=ed +捎=fo +勺=gd +烧=he +稍=ho +蛸=io +哨=ko +韶=lk +梢=mo +绍=sk +艄=vo +筲=vo +少=xp +晒=ox +筛=vj +酾=yd +蒴=co +搠=fo +烁=hd +铄=jd +槊=mq +妁=nd +朔=qo +硕=ua +说=ye +吮=ke +瞬=oa +顺=pa +舜=va +涮=dd +拴=fw +闩=ma +栓=mw +束=an +叔=ay +赎=bd +薯=co +蔬=cv +菽=cy +澍=dc +沭=dd +殊=dn +漱=dq +淑=dy +抒=fa +摅=fx +戍=gd +庶=gh +黍=hd +秫=hd +输=id +殳=jy +竖=la +树=mc +术=md +梳=mv +枢=mx +姝=nn +腧=od +暑=oo +曙=oo +鼠=pv +舒=ra +毹=rm +倏=rq +纾=sa +蜀=si +署=so +墅=ta +塾=tw +属=ud +书=vd +疏=vv +孰=wd +数=wm +恕=xn +述=za +水=an +税=he +睡=oa +谁=yf +耍=an +刷=du +唰=kd +帅=aj +摔=fu +蟀=iu +甩=pv +衰=wy +痄=ba +渣=da +揸=fa +扎=fv +炸=ha +蚱=ia +铡=jd +咋=ka +哳=kj +吒=kq +咤=kq +闸=ma +榨=ma +札=mv +眨=on +齄=pa +猹=qa +乍=ra +砟=ua +诈=ya +踬=zb +胄=ao +宙=ba +赒=bk +荮=cc +洲=da +州=da +皱=dp +帚=ej +轴=ia +咒=jk +啁=kk +粥=mg +骤=mn +妯=na +肘=oc +舟=pd +周=pk +纣=sc +绉=se +昼=ua +碡=ud +籀=vt +酎=yc +诌=ye +妆=dn +状=dq +壮=du +奘=dv +撞=fa +庄=gt +桩=mt +装=yd +蔗=ch +著=co +浙=dj +摺=fb +蜇=if +蛰=if +辄=iv +辙=iw +锗=jo +哲=kf +柘=mu +鹧=ng +褶=pb +者=to +赭=to +喆=uk +磔=um +谪=yk +着=yo +遮=zg +这=zw +臻=ah +贞=ba +赈=bn +疹=bp +蓁=ch +浈=db +阵=ei +振=fn +稹=hb +轸=ip +镇=jb +帧=jb +针=ju +桢=mb +榛=mh +枕=mv +鸩=nd +朕=od +胗=op +祯=pb +斟=qu +侦=rb +缜=sb +圳=ta +畛=tp +真=ub +砧=uk +箴=vk +珍=wp +甄=xw +震=yn +诊=yp +正=av +整=av +症=bv +蒸=ch +争=da +峥=ea +郑=eq +挣=fa +拯=fa +烝=hv +铮=ja +钲=jv +睁=oa +狰=qa +征=rv +徵=rw +筝=va +政=wa +怔=xv +诤=ya +证=yv +丈=an +账=bn +瘴=bu +涨=dn +漳=du +鄣=el +障=eu +嶂=eu +张=gn +蟑=iu +帐=jn +幛=ju +彰=lp +章=lu +杖=mn +樟=mu +嫜=nu +胀=on +獐=qu +仉=rj +仗=rn +璋=wu +掌=xu +止=aa +黹=ad +痔=bc +贽=bf +窒=bt +痣=bx +芝=cn +芷=cv +滞=dj +治=dk +之=dn +制=dp +殖=dq +汁=du +沚=dv +郅=ea +职=eb +陟=ep +执=fd +掷=fe +摭=fh +指=fo +稚=hf +秩=hn +轵=ib +蜘=ik +轾=it +蛭=it +觯=ja +帜=jb +帙=jn +只=kb +吱=ky +枳=mb +栉=me +骘=me +植=mq +桎=mt +栀=mv +枝=my +鸷=nf +胝=od +脂=oo +膣=ot +智=ou +肢=oy +质=pb +祗=pd +炙=ph +旨=po +卮=pv +祉=pv +值=rq +侄=rt +织=sb +絷=sf +纸=sq +置=sq +至=ta +埴=tq +址=tv +咫=ub +挚=uf +雉=uf +知=uk +直=uq +支=uy +豸=vp +彘=vv +致=wa +志=xu +忮=xy +酯=yo +踯=ze +跖=zk +趾=zv +战=ag +占=ak +蘸=ch +沾=dk +湛=dv +詹=dy +崭=ej +搌=fn +旃=fw +斩=ij +辗=in +站=lk +盏=mg +栈=mg +毡=mk +暂=oi +瞻=oy +绽=sr +展=un +谵=yy +钊=dj +沼=dk +找=fg +招=fk +肇=ha +照=ho +棹=mu +曌=og +昭=ok +兆=pd +爪=pn +罩=su +笊=vv +诏=yk +赵=zx +窄=ba +寨=bm +宅=bq +瘵=bx +择=fa +摘=fk +债=rb +砦=vu +斋=wa +桌=am +卓=au +茁=ck +濯=df +浊=di +涿=dn +浞=dz +擢=ff +拙=fk +捉=fz +灼=hd +镯=ji +啄=kn +椓=mn +禚=ph +倬=ru +斫=uj +琢=wn +酌=yd +诼=yn +窀=bv +准=df +肫=ov +谆=yz +专=ad +赚=bn +颛=ea +撰=fb +转=id +啭=kd +馔=ub +砖=ud +篆=vn +中=aa +冢=bn +种=ha +钟=ja +锺=ja +盅=ma +肿=oa +仲=ra +众=rr +终=sd +舯=va +螽=wi +衷=wy +忠=xa +踵=za +瘃=bn +疰=bw +茱=cn +洙=dn +潴=do +渚=do +主=dw +注=dw +邾=ep +拄=fw +烛=hi +煮=ht +炷=hw +蛛=in +蛀=iw +铸=jc +铢=jm +嘱=kd +麈=lw +杼=ma +株=mn +槠=mo +橥=mq +柱=mw +驻=mw +瞩=od +竹=pa +祝=pe +朱=pn +助=ql +猪=qo +伫=ra +侏=rn +住=rw +翥=ty +舳=va +筑=vd +竺=ve +箸=vo +珠=wn +诛=yn +诸=yo +逐=za +躅=zi +赘=bf +锥=jf +骓=mf +隹=rf +缀=sy +缒=sz +坠=te +惴=xa +追=zp +抓=fv +挝=fz +拽=fp +瓦=ad +洼=dt +挖=fg +蛙=it +哇=kt +娲=nr +娃=nt +腽=om +袜=pn +佤=rw +瘟=bm +刎=dg +温=dm +汶=dw +稳=hx +蚊=iw +吻=kp +闻=me +问=mk +榅=mm +阌=my +璺=pw +攵=px +缊=sm +纹=sw +紊=ws +文=wx +雯=yw +瓮=bw +翁=by +蕹=cf +蓊=cy +嗡=ky +王=at +尫=aw +汪=dw +魍=gv +辋=iv +罔=kv +网=kx +枉=mw +旺=ow +往=rw +妄=wn +亡=wv +望=ww +惘=xv +忘=xw +万=ap +完=be +皖=be +宛=bv +芄=cd +菀=cv +剜=db +湾=dg +豌=dv +顽=ea +挽=fv +丸=gd +烷=he +蜿=iv +婉=nv +脘=oe +晚=ov +腕=ov +纨=sd +绾=sk +畹=tv +碗=uv +玩=we +弯=wg +琬=wv +惋=xv +歪=av +外=pd +窝=bd +莴=cd +沃=dd +渥=dt +握=ft +蜗=id +龌=it +幄=jt +卧=kd +肟=og +我=pd +倭=rn +硪=ud +斡=uu +五=aa +兀=ae +无=av +痦=bk +寤=bk +芴=cp +芜=cv +污=dg +浯=dk +阢=ee +邬=ep +捂=fk +勿=gp +戊=gp +巫=gr +庑=gv +焐=hk +蜈=id +钨=ja +鋈=jd +呜=ka +吴=kd +杌=me +梧=mk +骛=ms +牾=nk +物=np +婺=ns +鹜=ns +鹉=nv +妩=nv +晤=ok +乌=pa +舞=ra +侮=rd +午=ru +仵=ru +伍=rw +坞=ta +圬=tg +碔=ud +鼯=uk +屋=ut +武=vd +毋=vp +吾=wk +务=wl +悟=xk +忤=xu +怃=xv +误=yd +雾=yl +诬=yr +迕=zr +韦=aa +軎=ak +未=an +鲔=ao +痿=bn +苇=ca +蔚=cc +葳=cn +萎=cn +薇=cw +涠=da +沩=dd +潍=df +洧=do +渭=do +危=dv +卫=ea +巍=eg +嵬=eg +崴=en +隈=en +威=gn +炜=ha +魏=hg +委=hn +煨=hn +帏=ja +帷=jf +围=ka +唯=kf +味=kn +喂=kn +为=ld +闱=ma +桅=mv +娓=nm +韪=oa +祎=pa +猥=qn +猬=qo +伟=ra +伪=rd +位=rl +偎=rn +微=rw +纬=sa +维=sf +畏=tn +胃=to +圩=tu +尉=uc +尾=um +艉=vm +玮=wa +惟=xf +慰=xu +诿=yn +谓=yo +违=za +逶=zh +小=ad +宵=bo +萧=ca +潇=da +消=do +削=dx +崤=eo +魈=go +销=jo +啸=ka +哓=ke +嚣=kk +哮=kz +骁=me +校=mf +枵=mg +鸮=nk +枭=nm +晓=oe +绡=so +孝=tz +硝=uo +屑=uo +箫=va +笑=vd +筱=vw +效=ww +肖=xo +霄=yo +逍=zx +鲞=ba +葙=co +芗=cp +巷=cv +象=dn +湘=do +项=ga +庠=gy +香=ho +蟓=in +厢=io +镶=jy +响=kk +橡=mn +相=mo +骧=my +向=pk +祥=py +像=rn +缃=so +饷=uk +箱=vo +乡=vp +飨=vu +襄=wy +享=wz +想=xm +详=yy +翔=yy +西=aa +穸=bd +兮=bg +皙=bm +菥=cj +昔=co +蓰=cr +葸=cx +汐=dd +溪=dd +洗=de +浠=dj +淅=dj +玺=dw +隰=eh +隙=ex +郗=ex +习=ga +席=gj +稀=hj +烯=hj +熙=hk +熹=hu +熄=hx +蜥=ij +蟋=ix +螅=ix +铣=je +锡=jp +钖=jp +唏=kj +嘻=kk +吸=kn +袭=ly +阋=me +析=mj +樨=mn +檄=mw +粞=mx +嬉=nk +媳=nx +牺=nx +膝=od +曦=og +晰=oj +晞=oj +夕=pd +禊=pd +舄=ph +觋=pj +禧=pk +系=ps +僖=rk +徙=rr +翕=ry +细=st +矽=ud +鼷=ud +喜=uk +囍=uk +犀=un +饩=uq +屣=ur +硒=ux +奚=vd +舾=vx +窸=xb +希=xj +惜=xo +息=xp +悉=xp +欷=xq +戏=yg +羲=yg +醯=ym +鲜=ay +贤=ba +宪=be +痫=bm +癣=by +莶=ca +苋=cj +藓=cy +冼=de +涎=dv +险=ea +陷=ea +岘=ej +限=en +掀=fq +咸=gk +弦=gs +燹=ha +蚬=ij +锨=jq +籼=me +闲=mm +先=ne +娴=nm +鹇=nm +嫌=nn +显=oa +腺=od +祆=pd +猃=qa +氙=qe +县=qs +衔=rd +仙=re +线=sg +馅=ua +献=uq +筅=ve +舷=vs +现=wj +酰=ye +羡=yr +暹=zo +跣=zp +跹=zz +薪=cj +芯=cx +心=dd +寻=ec +鑫=jj +欣=jq +锌=ju +新=lj +歆=lq +辛=lu +昕=oj +囟=px +信=rk +馨=uo +衅=xa +忻=xj +鲟=ac +薰=ch +荀=co +蕈=cu +浔=dc +洵=do +殉=do +汛=du +峋=eo +旬=go +熏=hp +勋=kl +驯=ma +曛=oh +獯=qh +循=ro +徇=ro +绚=so +埙=tb +巽=vb +恂=xo +训=ya +醺=yh +询=yo +讯=yu +迅=zv +巡=zv +逊=zz +髹=am +莠=cp +溴=dq +岫=ea +庥=gm +秀=hp +锈=jp +咻=km +嗅=kq +朽=mg +袖=pa +休=rm +鸺=rn +修=rp +绣=sp +馐=ua +貅=vm +琇=wp +羞=ya +宣=ba +痃=bs +萱=ca +渲=da +漩=dr +泫=ds +揎=fa +旋=fr +煊=ha +炫=hs +轩=iu +镟=jr +铉=js +喧=ka +楦=ma +暄=oa +眩=os +衒=rd +儇=ry +碹=ua +瑄=wa +璿=wo +璇=wr +玄=ws +悬=xq +谖=yy +选=zn +芎=cg +汹=dk +夐=dw +匈=gk +熊=hs +兄=ke +凶=kx +胸=ok +雄=uf +鳕=ae +穴=bb +茓=cb +薛=cu +靴=gv +血=pm +彐=va +泶=xd +学=xz +谑=ya +雪=ye +踅=zf +蓄=ct +洫=dx +溆=dy +序=ga +戌=ga +虚=ha +煦=ho +歔=hq +旭=jo +勖=kl +吁=ku +糈=mo +栩=my +婿=no +盱=ou +须=pa +徐=rh +叙=ry +续=sd +絮=sn +绪=so +墟=ta +胥=vo +顼=wa +恤=xx +需=ya +谞=yo +醑=yo +许=yu +酗=yx +诩=yy +下=ad +夏=aw +峡=en +罅=fu +黠=hk +虾=id +辖=ik +匣=ka +呷=ka +吓=kd +柙=ma +瞎=ok +暇=oy +狎=qa +狭=qn +侠=rn +硖=un +瑕=wy +霞=yy +遐=zv +写=ba +薤=ca +泻=da +瀣=da +渫=dm +泄=dv +邪=ea +卸=er +些=ev +挟=fn +携=fp +廨=gn +鞋=gt +蟹=ij +蝎=iv +叶=ku +勰=lx +械=mc +榭=mc +楔=md +榍=mo +胁=od +歇=oq +獬=qn +偕=rb +侑=ro +斜=ru +缬=sa +绁=sv +协=ud +亵=wy +懈=xn +谐=yb +谢=yc +燮=yh +邂=zj +躞=zy +形=ap +荥=cd +荇=cd +刑=da +邢=ea +陉=eg +擤=fa +杏=mk +姓=nf +腥=oa +星=of +猩=qf +型=ta +幸=tu +硎=ud +饧=up +惺=xa +兴=xb +性=xf +悻=xu +醒=yf +亚=aa +雅=af +牙=ap +丫=ba +痖=ba +芽=cp +涯=dt +岈=ep +押=fa +揠=fn +压=id +蚜=ip +=jp +哑=ka +呀=kp +桠=ma +鸭=na +鸦=na +娅=na +睚=ot +氩=qa +衙=rd +伢=rp +垭=ta +砑=up +琊=we +讶=yp +迓=za +由=aa +卣=aa +鱿=ad +疣=bd +宥=bo +莸=cd +莜=cw +油=da +游=dz +邮=ea +幽=ev +黝=hl +蚰=ia +蚴=il +蝣=iz +铀=ja +铕=jo +呦=kl +囿=ko +柚=ma +釉=pa +牖=pd +祐=pk +犹=qd +猷=qq +优=rd +佑=rk +攸=rw +鼬=ua +尤=ud +右=uk +有=uo +尢=uv +友=uy +幼=vl +又=vn +繇=vs +酉=xa +忧=xd +悠=xr +诱=yp +业=aa +野=aa +曳=ap +页=ba +冶=dk +液=dn +邺=ea +耶=ee +爷=fe +揶=fe +掖=fn +烨=hu +靥=ia +铘=je +咽=kd +噎=kd +椰=me +腋=od +晔=ou +也=vv +夜=wd +谒=yv +央=an +鸯=an +羊=ba +疡=bp +痒=by +泱=dn +殃=dn +漾=dn +洋=dy +阳=eo +扬=fp +飏=fp +鞅=gn +秧=hn +炀=hp +烊=hy +蛘=iy +杨=mp +样=my +旸=op +氧=qy +仰=re +佯=ry +徉=ry +怏=xn +恙=xy +养=ya +一=aa +夷=an +宧=ba +贻=bk +痍=bn +宜=bq +瘗=bt +癔=bx +疫=by +艺=cg +苡=cr +殪=dd +沂=dj +漪=dk +溢=dm +劓=dp +浥=dv +刈=dx +峄=ea +屹=eg +嶷=er +抑=fe +旖=fk +挹=fv +乙=gg +熠=hb +移=hd +黟=hd +蚁=id +轶=in +蜴=ip +钇=jg +镒=jm +镱=jx +铱=jy +颐=ka +呓=kg +咦=kn +咿=kp +医=ku +邑=kv +噫=kx +翳=ky +毅=ly +翊=ly +驿=ma +椅=mk +益=mq +姨=nn +胰=on +易=op +臆=ox +肄=pa +疑=pr +弋=qd +猗=qk +仪=rd +亿=rg +倚=rk +佚=rn +佾=ro +伊=rp +依=ry +役=ry +绎=sa +缢=sm +矣=su +埸=tp +圯=tv +壹=ud +饴=uk +懿=ux +异=vc +彝=vc +舣=vd +以=vr +已=vv +弈=wc +奕=wd +亦=wd +衣=wy +怿=xa +义=xd +忆=xg +怡=xk +意=xl +悒=xv +译=ya +翼=yb +羿=yc +议=yd +诒=yk +裔=yk +翌=yl +诣=yo +谊=yq +酏=yv +遗=za +逸=zd +迤=zr +焉=ah +严=ai +宴=bn +菸=cd +演=db +沿=dk +湮=dt +淹=dv +奄=dv +滟=dv +鄢=ea +郾=ek +岩=eu +崦=ev +艳=fv +掩=fv +焰=ha +燕=hc +烟=hd +炎=hh +焱=hh +厣=ia +赝=ib +雁=if +魇=ig +厌=iq +餍=iu +蜒=iv +唁=kk +匽=kn +颜=la +彦=lp +验=ma +阎=ma +阏=md +闫=ms +盐=mt +阉=mv +檐=my +妍=nc +嫣=nh +胭=od +眼=on +晏=on +腌=ov +殷=py +衍=rd +俨=ri +偃=rn +罨=sv +堰=tn +埏=tv +研=uc +砚=uj +鼹=un +筵=vv +兖=we +琰=wh +言=wk +恹=xq +酽=yi +谚=yp +谳=yq +延=zp +尧=ae +鳐=af +窑=bf +窈=bl +药=cd +淆=do +崾=en +摇=ff +耀=gf +轺=ik +钥=jo +咬=kf +吆=ks +杳=mo +妖=nd +姚=nd +鹞=nv +曜=of +腰=on +夭=pd +徭=rf +舀=va +幺=vs +珧=wd +瑶=wf +要=xn +肴=xo +爻=xx +谣=yf +邀=zb +遥=zv +寅=bb +窨=bo +瘾=bx +茵=cd +茚=ce +荫=co +洇=dd +淫=du +鄞=ec +阴=eo +印=ep +隐=ex +引=ga +蚓=ia +龈=in +铟=jd +银=jn +吲=ka +因=kd +喑=ko +吟=kv +音=lo +姻=nd +夤=pb +胤=pv +氤=qd +狺=qk +垠=tn +堙=tt +饮=ur +尹=vp +霪=yu +哟=kd +唷=ko +育=wo +韫=am +蕴=cm +芸=cs +殒=db +涢=db +陨=eb +郓=eb +郧=ek +云=es +匀=ga +孕=gz +熨=hu +韵=la +耘=ls +昀=od +晕=oi +狁=qe +氲=qm +允=se +纭=ss +恽=xi +愠=xm +酝=ys +运=ze +冤=bd +员=bk +芫=ce +苑=cv +渊=da +沅=de +源=dx +元=ee +院=ee +鼋=ev +掾=fn +援=fy +原=ix +螈=ix +愿=ix +辕=iy +圆=kb +园=ke +橼=mn +鸳=np +媛=ny +眢=op +鸢=qn +猿=qy +缘=sn +垣=ta +垸=te +塬=tx +袁=ty +箢=vv +爰=vy +瑗=wy +怨=xp +远=ze +鳙=aa +痈=ba +涌=da +永=dn +泳=dn +拥=fa +庸=ga +蛹=ia +镛=ja +喁=kd +咏=kn +臃=of +用=pa +佣=ra +俑=ra +甬=sa +勇=sl +墉=ta +壅=tw +邕=vv +雍=wf +饔=wu +慵=xa +恿=xs +踊=za +瀹=dc +刖=do +钺=jv +曰=ka +阅=me +樾=mv +月=pe +岳=pe +粤=pg +龠=rc +约=sd +悦=xe +跃=zd +越=zv +与=aa +雨=ad +禺=ad +欤=aq +于=au +寓=bd +瘀=bd +窬=bd +窳=bg +瘐=bn +欲=bq +宇=bu +蓣=ca +萸=cn +芋=cu +鱼=da +渔=da +淤=dd +渝=dd +浴=dk +屿=ea +隅=ed +嵛=ed +峪=ek +郁=eu +揄=fd +於=fd +羽=ga +鬻=ga +庾=gn +虞=hd +煜=hl +燠=hn +蝓=id +蜮=ik +龉=ik +钰=jw +喻=kd +圄=kk +圉=ku +盂=ma +阈=ma +榆=md +驭=my +妤=na +鹆=nb +娱=nd +鹬=ns +妪=nx +昱=ol +舆=pb +舁=pc +禹=pd +裕=pk +臾=pn +狳=qh +狱=qq +俞=rd +俣=rd +御=re +余=rh +觎=rj +毓=rv +伛=rx +预=sa +予=sa +豫=sn +纡=su +域=tk +饫=ud +馀=uh +聿=va +竽=vu +玙=wa +玉=wd +瑜=wd +愚=xa +愉=xd +愈=xr +誉=xy +谕=yd +雩=yg +语=yk +谀=yn +遇=za +迂=za +逾=zr +瘿=bn +荧=ch +萤=ci +蓥=cj +营=ck +英=cn +莺=cn +萦=cs +茔=ct +莹=cw +瀛=dd +潆=ds +滢=dw +郢=ek +罂=fb +撄=fn +应=ga +鹰=gn +膺=go +蝇=iv +嘤=kn +盈=mg +楹=mm +樱=mn +婴=nb +鹦=nb +映=on +媵=on +影=op +颖=pa +颍=pa +缨=sn +硬=un +嬴=wd +赢=wd +瑛=wn +璎=wn +迎=zp +拶=fd +杂=jm +匝=kj +咂=kj +咱=ko +柞=ma +砸=uj +鲰=ay +邹=ed +鄹=ee +陬=ey +奏=fd +揍=fd +驺=me +走=tr +诹=yy +责=bf +泽=da +则=db +仄=ir +帻=jb +啧=kb +赜=kb +昃=or +笮=va +舴=va +箦=vb +迮=zr +怎=xr +谮=yo +赠=bo +甑=bw +锃=jw +缯=so +罾=so +增=to +憎=xo +赃=bt +葬=cc +臧=ga +驵=mq +脏=ot +鲻=at +髭=av +资=bd +赀=bv +字=bz +渍=db +滋=ds +淄=dt +滓=du +嵫=es +秭=hp +辎=it +龇=iv +锱=jt +觜=jv +咨=kd +耔=lz +粢=md +梓=mu +籽=mz +姿=nd +姊=np +眦=ov +自=po +孳=qz +仔=rz +缁=st +紫=sv +子=va +笫=vp +甾=vt +孜=wz +恣=xd +谘=yk +訾=yv +趑=zq +孖=zz +赞=bn +錾=ji +糌=mo +簪=vo +瓒=wb +昝=wo +趱=zb +枣=ad +凿=au +皂=bq +藻=cm +澡=dm +燥=hm +灶=ht +噪=km +唣=kq +糟=mo +早=ou +蚤=yi +遭=zc +躁=zm +造=zn +再=aa +灾=bh +宰=bu +载=ig +哉=kg +栽=mg +在=ut +崽=xe +阼=ea +座=gt +唑=kt +嘬=ky +昨=oa +胙=oa +祚=pa +作=ra +佐=rg +做=rw +坐=tr +左=ug +怍=xa +鳟=ac +撙=fc +樽=mc +尊=qc +遵=zq +攥=fs +钻=jk +缵=sb +躜=zb +鬃=ax +宗=bx +粽=mx +棕=mx +腙=ox +偬=rx +纵=sr +综=sx +总=xb +踪=zx +菹=cq +阻=eq +族=fu +租=hq +镞=ju +足=kr +祖=pq +俎=rq +组=sq +诅=yq +蕞=cy +嘴=kj +最=oy +罪=ss +醉=yu +贼=bu +蘑=ck +刭=yd +凇=ds +薏=cx +掬=fm +呒=kv +泾=dg +腴=on +瞑=ob +蝠=it +苧=cd +㧐=fy +㧟=fk +耇=tk +耏=ap +耑=ea +耤=lo +耰=ly +䁖=on +䂮=ul +肸=oa +䃅=ua +胈=oy +䃎=ud +胠=oq +胣=ov +脟=oc +脿=oy +腒=ok +腘=ow +䅟=hp +腨=oa +腯=oo +膙=yi +臑=oa +臜=ob +舠=vd +舥=vv +舲=vs +艅=vh +艎=vw +芃=cd +芠=cw +芣=cd +芼=cm +苉=ca +苾=cx +茀=ca +茋=cd +茝=ca +䌹=sk +茽=ca +荁=ca +荄=cr +荓=cc +荖=cv +䎃=ay +䎖=by +莝=ct +莿=cd +菂=cd +菉=cd +菍=dx +䏝=od +䏡=ox +䏲=op +菼=ch +䐃=oh +萚=ca +萣=cr +萳=cu +萹=cc +葎=ca +葖=cq +葰=cw +葴=ck +蒄=cc +蒐=cs +蒟=ck +蒨=co +蒱=ca +蒻=cd +蓂=cb +蓇=co +蓏=cg +䓖=cl +䓛=ck +蓢=co +䓨=ce +䓫=ck +䓬=cu +蔀=ce +蔃=ci +蔈=cx +蔊=cu +蕗=ck +蕰=cm +薁=cd +薢=cn +薳=cy +薸=cx +薿=cr +䗖=ij +䗛=io +藟=ct +藠=cb +藦=cu +藨=ch +䗪=gi +䗴=ip +蘘=cy +虒=pj +虓=jj +虤=hj +虷=iu +虸=iz +蚄=if +蚆=iv +蚲=ku +蛃=ir +蜎=io +蜐=il +䜣=yj +蝘=in +䝙=vx +蝲=id +螠=im +螣=oi +螱=iu +蟏=ia +蟫=iu +蠋=ii +衃=xd +衎=rd +衠=rd +袆=ya +袗=yp +袪=ys +袯=yy +䢺=ee +䢼=ce +裈=yi +䣘=xe +裛=wy +褕=yd +褟=yy +褯=yj +襕=yd +襚=yz +襜=yy +襫=yr +䥽=jy +䦃=jo +觃=kz +觟=jt +觭=jk +觱=jg +觿=jk +訄=jy +訚=my +詟=ly +讱=yd +讻=yk +诇=yk +诐=yy +谼=bb +谿=vk +豨=aj +豮=ab +𡐓=tn +貆=va +䲟=ae +䲠=ao +䲢=oa +𠳐=ke +䴓=an +䴔=bn +䴕=nd +䴖=fn +䴗=on +䴘=pn +䴙=un +𠅤=wx +赗=bo +赟=bw +赪=tb +䶮=ld +趯=tf +跂=zy +跐=zv +跱=zc +丏=av +踒=zn +𣲘=dv +𣲗=da +踶=zr +乂=pn +蹅=za +𠙶=kv +蹐=zo +蹚=zt +蹜=zb +蹢=zk +乸=vd +蹽=zx +蹾=zw +亶=wa +亸=wu +亹=wq +𣗋=me +伈=rx +伋=rn +伣=rv +伭=rs +𦰡=ce +伾=ra +佁=rk +𦒍=ly +佖=rp +𥔲=ug +轪=id +佸=rk +佺=rw +佽=ww +辀=iv +侁=re +侂=wv +辌=ix +辒=im +侘=rv +𣸣=db +侴=rd +𥕢=uo +侹=rz +辿=ze +俍=rn +𡎚=tc +𤧛=wj +俙=rj +𨐈=ie +𥖨=um +俫=rm +俶=ry +迺=zx +倓=rh +倕=ra +倞=rx +倧=rx +倴=rc +逴=zu +倻=re +偁=ea +遆=zj +𨱔=jc +偓=rt +𨱑=jb +𨱏=jk +𨱇=jd +偡=rk +偭=ra +偰=rd +𤩽=wq +遹=zk +傃=rx +傉=rc +傒=rd +傕=rf +邘=ae +邨=ae +𤫉=wy +邲=de +邽=fe +邿=ue +僇=rp +郈=pe +僎=rb +𦈡=sa +僔=rc +郚=we +郤=be +郪=ae +僰=ar +鄀=ce +鄃=re +鄅=pe +鄌=ge +鄑=ae +鄗=we +鄘=ge +鄚=ce +鄜=ge +鄠=ye +儦=rh +𦙶=ok +鄫=be +𫐄=ie +儳=rd +儴=ry +酂=pe +𫠆=ub +酅=ee +𫠊=ms +𫐐=ie +𫐓=im +酦=yy +𫠜=ie +酺=yd +𥻗=ma +冏=kk +𫑡=ke +冔=ou +𫰛=ng +𬀪=oe +𬀩=ov +醨=ys +冮=dg +醾=ym +𪣻=tn +釐=mt +凓=dm +凘=dj +𪤗=tp +𫓧=jn +𫢸=ra +𫄧=sp +𬂩=mn +𫄨=sj +刬=gd +𨙸=ue +𬒈=xk +𫓶=jo +𫓹=jb +𫓯=jf +𬃊=mb +剅=dd +剋=ud +𨚕=se +𫄷=sx +剕=ad +𬱟=eb +𫄸=sh +𬱖=ab +𬒔=uo +𦝼=on +𦭜=cb +𫔎=jk +𫔍=jt +𬒗=ue +剟=yd +𫔶=mm +劄=vd +𨺙=ee +𬣡=yg +𬣞=ya +劼=jl +𬣙=yu +𬤊=yr +鋆=tj +勍=wl +𬤇=yt +勔=al +勚=al +𬣳=yn +勠=yl +𫶇=ej +𬕂=vb +𬴃=mu +𬴊=ma +𬤝=yx +𬳽=me +𫵷=el +𬴂=ma +𬳿=mh +𬳶=mk +𬳵=ma +𫖯=db +𫖮=mb +𫖳=eb +𫗧=un +𪨶=ei +𫇭=cd +匜=kv +𪨰=ea +𪩘=eq +匼=kk +𫗴=ua +𨭉=jw +𫘨=mr +𫘧=md +𫘦=mf +𫘬=md +𫘪=mx +𫘝=mn +𫘜=mw +𧿹=zm +𬶍=av +𬶋=ak +𬶐=av +𬶏=av +卬=pe +𬇙=db +𫷷=gc +𬇕=dv +𬶮=ak +𫸩=gx +𬶭=ax +𨟠=oe +𬶨=av +𬇹=dk +𬶠=an +𬶟=an +厾=vs +𬘬=sf +𬘫=se +𬘩=sp +𬘯=sw +叆=ay +叇=an +𬘭=sm +𬨎=iy +𬨂=in +𬘡=sd +𫚖=aa +𫚕=aj +叕=yy +𬘓=sp +𬷕=an +叚=vy +𬘘=sv +𬙋=sy +𬙊=xt +𩾃=sv +𬙂=sb +𩽾=an +𫚭=an +𫫇=kx +𬊈=hc +𬸦=nw +𬸣=nb +𬸪=pn +𫌀=yb +𬉼=hx +𬸚=nq +𬸘=kn +𩾌=an +𬊤=hu +𫛭=nq +吽=kn +呇=dk +𬩽=ee +呣=km +𫍣=yk +𬪩=yy +𬺈=ik +𬹼=ia +咇=kp +咉=kn +𫍽=yy +𪾢=ov +咍=kk +𪟝=fl +钘=jc +𫍯=yg +𬺓=ir +𫍲=yy +咡=ke +咥=et +𫭟=tx +𫭢=tv +咺=ka +𬌗=ak +𬜬=ck +哃=kk +𬜯=cr +𫞩=ww +𫮃=tu +铏=jd +𫭼=tl +铚=jt +𬬭=jv +哢=kc +𫟅=sx +𬬱=jj +𬬮=jn +𬍡=yd +𬬩=jg +𬍤=wc +𬍛=wx +哱=kz +铻=jk +𫟦=iz +𬭊=jt +𬭎=js +𬬻=ju +𬭁=jd +𬬿=jk +锜=jk +𬬹=ja +唝=kb +𬬸=jd +𬭬=je +𬭯=jw +锧=jb +𬭤=jd +𬭩=jy +𬭛=jy +锳=jn +𬭚=jz +𫟼=jz +𫟹=jb +镃=js +镈=jc +镋=je +𬭼=jz +𬞟=cb +镚=jo +𬭳=jk +𬭸=jm +𬭶=jh +镠=jp +𬮱=mt +啫=ko +𬟁=cn +啴=ku +镴=jn +镵=jd +𬯎=eb +𬯀=ea +𬮿=ev +喤=kw +𬟽=ix +嗐=kk +嗞=ks +闿=mv +阇=mo +阘=my +嘚=kc +阿=ek +噀=kb +噂=kc +噇=kt +陎=en +陑=ea +陞=et +隃=ed +嚚=kk +隩=ed +嚭=tk +隺=pf +雊=gf +雱=yv +囷=kh +霅=yk +圌=ka +圐=kv +圙=ka +圢=td +霨=yc +圫=tv +圲=tu +坉=tv +坋=td +坒=ta +坥=tq +坬=tn +靬=gu +坰=tk +靰=ge +靸=gn +靺=gn +坽=ts +靽=ga +靿=gl +鞁=gy +垈=tr +垍=to +垎=tk +垏=ta +垕=pt +垙=te +垚=tt +垞=tv +垟=tu +鞡=gl +鞧=gy +鞨=gv +鞬=gz +鞮=gr +垯=tz +垱=te +鞳=gk +垺=tz +垾=tu +垿=ta +韂=gk +埆=tj +埇=ta +埌=tn +埗=tp +韨=ay +埪=tg +埫=tk +埵=ta +埼=tk +堉=to +堌=tk +堎=ty +堐=tf +堧=td +堨=tv +堲=tv +堼=tt +堽=tv +堾=to +塅=ty +塆=tg +塝=tv +塱=td +墈=tl +颋=zb +颎=vb +墐=tt +墕=tn +墘=tv +颙=ab +墡=tk +墣=td +墦=tt +飐=fk +飔=fx +飗=ft +壸=ua +夬=vn +奓=dd +奡=aa +饳=ue +饸=uk +饻=uy +馃=ym +馉=yo +馌=um +妘=ns +馝=pp +馞=pz +妧=ne +馧=pm +妭=ny +姈=ns +姞=nk +姤=nk +姱=ng +姶=nk +姽=nv +娀=ng +娵=ny +婌=ny +婍=nk +婘=nv +婞=nu +婠=nk +婤=nk +婫=nv +驲=mo +婳=nk +婻=nu +媂=nj +骃=md +媄=nd +媆=nd +骉=mm +骍=mu +骎=my +媓=nw +骕=md +媖=nd +骙=md +媞=nr +骦=mo +媭=np +媱=nf +髃=gd +嫄=nx +髎=gp +嫕=nx +嫚=ny +髢=va +嫭=nu +嫽=nx +髽=za +鬒=za +鬘=oa +嬥=nf +嬬=na +鬶=wa +鬷=ay +嬿=nh +孅=ng +魆=pg +魋=pf +寁=br +尨=lp +尪=jw +屃=ub +屼=ee +鱽=ad +屾=ee +鱾=av +鲀=av +鲃=av +鲉=aa +岊=ve +鲊=aw +鲌=ab +鲏=ay +鲖=ak +鲗=ad +鲘=ak +鲙=as +鲝=ba +岞=ew +岠=wk +岨=eq +鲪=ak +鲬=aa +鲯=ab +鲹=ap +鲾=at +鲿=as +鳀=ar +鳁=am +峂=ed +鳂=qn +峃=xe +鳈=qd +鳉=qc +鳑=qv +鳒=ab +峗=ev +峘=ea +鳚=ac +峛=ed +鳛=qb +鳠=qy +鳡=qx +峣=ee +鳣=aa +鳤=ak +峧=ex +峱=qe +峿=ek +崀=en +崁=dq +崄=ea +崌=ek +崒=eu +崚=ew +崟=ej +崡=ek +崶=ec +崿=eg +嵁=ev +嵅=eg +嵎=ed +嵖=ea +嵚=eq +嵲=em +嶅=ef +嶍=eb +嶒=eo +嶓=et +嶟=ec +嶦=ek +嶲=ek +巇=eg +巉=ed +帡=jc +鸤=un +鸧=rn +帨=je +鸻=rn +鸼=vn +鹀=an +鹍=on +鹐=an +鹔=vn +幖=jx +鹖=on +鹝=an +鹟=bn +鹠=pn +鹡=dn +鹢=bn +鹮=sn +鹯=wn +鹲=cn +鹴=yn +麀=gv +麑=ge +麖=gx +庤=gc +庱=gy +麹=fm +廆=gs +黇=ck +廋=gy +廙=gb +黡=gh +弆=tc +弇=rc +鼒=av +弨=gk +鼩=pk +鼫=pk +鼱=po +弸=go +齁=pk +齇=pq +齉=py +彟=ey +彧=gk +彳=pr +龂=ij +徛=rk +龢=rh +鿍=ta +鿎=uz +鿏=jw +忞=wx +忳=xv +忺=xq +恓=xx +恔=xx +悆=rx +悈=xc +悢=xn +惎=ax +惔=xh +惙=xy +惛=xo +愃=xa +愐=xa +愔=xo +愭=xo +慆=xa +慥=xz +慬=xa +慭=xa +憕=xq +憙=ax +憭=xx +憺=xy +戣=dg +戭=bg +扂=dk +扅=dd +扆=dy +扊=dh +扞=fu +扺=fn +扽=fv +抃=fd +抔=fd +拃=fa +拤=fd +挓=fv +挦=fc +捯=fd +捽=fu +掞=fh +揕=fk +揳=fd +搒=fv +摏=fa +摛=fs +摴=fg +摽=fx +擿=fz +攽=bw +敔=ww +敩=xw +斝=ku +斠=cu +斶=ji +旐=fv +旞=vw +旴=oa +旵=oe +旿=ku +昄=oy +昈=ou +昉=of +昒=op +昡=os +昣=op +昤=os +昪=oc +昫=ok +昳=on +昺=od +昽=ol +晅=oa +晊=ot +晐=or +晙=ow +晢=of +晪=ob +晫=ou +晱=oh +暅=oa +暕=on +暲=ou +暵=on +暶=or +暿=ok +曈=oa +朏=oe +朓=od +朳=mb +朸=ml +杄=mu +杕=md +杙=md +杧=mk +杻=ma +枅=mc +枍=mg +枲=sm +枹=mv +柈=ma +柊=md +柖=mk +柷=me +栐=mn +栒=mo +栟=mc +栴=ma +栻=mg +桯=mw +桲=mz +桹=mn +梌=mh +梠=mk +梣=mv +梴=mz +梽=mx +梾=ml +梿=mz +棁=me +棐=am +棓=mk +棤=mo +棨=md +棪=mh +棫=mg +棬=mv +棻=cm +棽=mv +椀=mv +椆=mk +椑=mu +椪=ma +椸=mv +楒=mx +楙=mm +楩=mn +楪=mm +楯=mo +榃=mt +榑=mc +榖=ay +榰=mo +槃=mv +槚=mb +槜=mp +槱=mh +橑=mx +橞=mx +橦=ma +檞=mn +櫆=mu +欂=mc +欸=sq +欻=hq +歅=sq +殣=da +毌=va +毐=ap +氾=dv +氿=dj +汈=dp +汋=dd +汧=dc +汫=dc +沄=ds +沇=de +沘=dv +沨=df +沺=ft +泂=dk +泃=dk +泇=dk +泙=du +泚=dv +泜=dd +洈=dv +洑=dq +洓=dn +洘=dg +洢=dp +洣=dm +洨=dx +洭=dw +洺=dk +洿=dg +浕=dd +浛=dk +浟=dw +浡=dz +浬=da +浭=dn +浰=dd +浲=df +涄=dg +涍=dz +涐=dd +涘=dd +涴=dv +淏=dd +淜=do +淟=db +淯=do +渟=dd +渰=dc +渼=dd +湉=dk +湑=do +湝=db +湣=do +湲=dy +湴=da +溁=dm +溇=dn +溍=do +溚=dk +溞=di +溠=dg +溦=dw +溵=dy +溹=dx +滃=dy +滆=da +滍=di +滘=dk +滧=dw +滪=db +滫=do +漈=dx +漋=df +漖=dw +漦=ad +漴=dx +漷=de +漹=eh +漻=dp +漼=df +潏=dk +潖=dv +潟=dh +潩=db +潵=dw +潽=do +潾=da +澂=dw +澛=do +澥=dn +澪=ds +澭=sf +澴=dy +澼=du +澽=dn +濋=dr +濩=dy +瀌=dh +瀍=dt +瀔=dy +瀱=dd +瀼=dy +灈=df +炆=hw +炌=ha +炘=hj +炟=ha +炣=hk +烔=hk +烜=ha +烠=ho +烶=hz +烺=hn +烻=hz +焆=ho +焌=hw +焞=hz +煁=hk +煃=ht +煋=hf +煓=ha +煟=ho +煴=hm +熇=hl +熛=hx +熜=hx +熥=hz +熻=hy +燋=hh +燏=hk +燚=hh +爇=ch +爔=hp +爚=ha +牁=vk +牂=nu +牚=xp +牤=nk +牥=nv +牻=np +犇=nn +犨=nw +狉=qa +猄=qx +猇=qj +猯=qa +猰=qd +猺=qf +玃=qy +玒=ww +玓=wd +玚=wp +玞=wn +玡=wp +玤=wf +玥=wo +玭=wv +玶=wu +玹=ws +玼=wv +玿=wk +珅=wa +珇=wq +珋=qe +珌=wp +珒=wa +珕=wl +珖=we +珛=wo +珝=wy +珢=wn +珣=wo +珦=wk +珪=wt +珫=we +珵=ww +珷=wv +珸=wk +珹=wg +珺=wk +珽=wz +琀=wk +琄=wo +琈=wz +琎=wz +琔=wr +琟=wf +琡=wy +琫=wu +琭=wd +琯=wk +琲=wa +瑀=wd +瑂=wo +瑃=wo +瑅=wr +瑆=wf +㑇=we +㑊=wd +瑑=wn +瑓=wn +瑔=wd +瑖=wy +瑝=ww +瑢=wk +瑧=wh +瑨=wo +瑬=wd +瑱=wb +瑳=wg +璆=wp +璈=ww +璒=wq +璘=wa +璟=wx +璥=ww +璪=wm +璬=ww +璮=wa +璱=wp +璲=wz +瓀=wa +瓖=wy +瓘=wf +瓻=xv +甗=hv +甡=pa +甪=pa +畖=tn +畤=tc +畬=rt +㕮=kx +畯=tw +疁=tp +疍=va +疐=ar +疢=bh +疭=br +痓=bt +瘆=bp +㘎=kw +㙍=ty +癗=bt +㙘=tn +㙦=tr +癿=bv +皕=bb +皛=bb +皞=bu +皦=bw +皭=bc +㛃=bn +㛚=na +盦=rm +盷=od +㛹=nn +眊=ov +眬=ol +睄=oo +睎=oj +瞋=ob +瞫=ou +瞭=ox +㟃=ek +矞=vk +矰=uo +矼=ug +砄=un +㠇=ev +㠓=en +砠=uq +砫=uw +砮=un +砵=ua +硁=ug +硊=uv +硍=un +硔=ub +硙=uv +硿=ug +碃=uo +碈=uo +碏=uo +碨=uy +碶=ud +磏=un +磜=ux +磹=uu +磻=ut +㤘=xe +礵=uo +㥄=xw +祇=pn +祋=py +祏=pu +祕=pp +祲=py +祼=pn +祾=pw +禋=pt +禒=pn +禔=pr +禘=pj +禛=pb +禤=py +㧑=fd +秬=hk +稌=hx +稑=ht +稙=hq +穄=hx +穙=hd +穜=ha +穟=hz +窅=bo +窊=bn +窎=bn +竘=lk +竫=la +㫰=on +㬊=oy +㬎=oh +㬚=ow +笯=vy +筀=vt +㭎=mx +㭕=ms +筜=ve +筤=vn +筥=vk +筦=ve +筶=vk +筼=vb +箖=vm +㮾=md +篯=vg +㰀=mx +簃=vd +簉=vz +簕=vl +簝=vx +簠=vm +簰=vu +籥=va +粿=vn +㳇=vx +糒=va +㳘=de +㳚=dd +糵=cm +㴔=dx +㵐=dq +絜=sf +㶲=ha +㸆=oa +㸌=hf +縠=uy +縢=ox +繄=sk +㺄=qd +纮=ss +纻=sa +纼=sa +绤=sk +㻬=wg +绹=sf +缐=sd +缞=sy +罍=tf +㽏=ad +罶=st +罽=sd +羑=qn +羓=qv +羕=qd +羖=qy +羱=qx +翂=yd +翃=uy +翈=ay +翙=ey +翛=ry +㿠=be +翯=yk +翷=my +翾=sy +濛=dn +絺=sj +𣬠=mj +𣬶=mv +崑=eb diff --git a/windows/scripts/lcompile.ps1 b/windows/scripts/lcompile.ps1 index 8b298cbd..946f5645 100644 --- a/windows/scripts/lcompile.ps1 +++ b/windows/scripts/lcompile.ps1 @@ -1,65 +1,65 @@ -# Generate and compile dll files -# Default is to compile 64-bit dll, if 32-bit is specified, 32-bit dll would be compiled -$currentDirectory = Get-Location -$cmakeListsPath = Join-Path -Path $currentDirectory -ChildPath "CMakeLists.txt" - -if (-not (Test-Path $cmakeListsPath)) -{ - Write-Host("No CMakeLists.txt in current directory, please check.") - return -} - -Write-Host "Start generating and compiling..." - - -$arch = $args[0] - -if ($arch -eq "32") -{ - $buildFolderPath = ".\build32" - - if (-not (Test-Path $buildFolderPath)) - { - New-Item -ItemType Directory -Path $buildFolderPath | Out-Null - Write-Host "build32 folder created." - } - - cmake --preset=for32 - - if ($LASTEXITCODE -eq 0) - { - cmake --build ./build32/ - } -} elseif ($arch -eq "64") -{ - $buildFolderPath = ".\build64" - - if (-not (Test-Path $buildFolderPath)) - { - New-Item -ItemType Directory -Path $buildFolderPath | Out-Null - Write-Host "build64 folder created." - } - - cmake --preset=for64 - - if ($LASTEXITCODE -eq 0) - { - cmake --build ./build64/ - } -} else -{ # Default is 64-bit - $buildFolderPath = ".\build64" - - if (-not (Test-Path $buildFolderPath)) - { - New-Item -ItemType Directory -Path $buildFolderPath | Out-Null - Write-Host "build64 folder created." - } - - cmake --preset=for64 - - if ($LASTEXITCODE -eq 0) - { - cmake --build ./build64/ - } -} +# Generate and compile dll files +# Default is to compile 64-bit dll, if 32-bit is specified, 32-bit dll would be compiled +$currentDirectory = Get-Location +$cmakeListsPath = Join-Path -Path $currentDirectory -ChildPath "CMakeLists.txt" + +if (-not (Test-Path $cmakeListsPath)) +{ + Write-Host("No CMakeLists.txt in current directory, please check.") + return +} + +Write-Host "Start generating and compiling..." + + +$arch = $args[0] + +if ($arch -eq "32") +{ + $buildFolderPath = ".\build32" + + if (-not (Test-Path $buildFolderPath)) + { + New-Item -ItemType Directory -Path $buildFolderPath | Out-Null + Write-Host "build32 folder created." + } + + cmake --preset=for32 + + if ($LASTEXITCODE -eq 0) + { + cmake --build ./build32/ + } +} elseif ($arch -eq "64") +{ + $buildFolderPath = ".\build64" + + if (-not (Test-Path $buildFolderPath)) + { + New-Item -ItemType Directory -Path $buildFolderPath | Out-Null + Write-Host "build64 folder created." + } + + cmake --preset=for64 + + if ($LASTEXITCODE -eq 0) + { + cmake --build ./build64/ + } +} else +{ # Default is 64-bit + $buildFolderPath = ".\build64" + + if (-not (Test-Path $buildFolderPath)) + { + New-Item -ItemType Directory -Path $buildFolderPath | Out-Null + Write-Host "build64 folder created." + } + + cmake --preset=for64 + + if ($LASTEXITCODE -eq 0) + { + cmake --build ./build64/ + } +} diff --git a/windows/src/Candidate/EnumTfCandidates.cpp b/windows/src/Candidate/EnumTfCandidates.cpp index 898e42cd..6c476ce6 100644 --- a/windows/src/Candidate/EnumTfCandidates.cpp +++ b/windows/src/Candidate/EnumTfCandidates.cpp @@ -1,122 +1,122 @@ -#include "Private.h" -#include "EnumTfCandidates.h" - -HRESULT CEnumTfCandidates::CreateInstance(_Outptr_ IEnumTfCandidates **ppEnum, - _In_ const CMetasequoiaImeArray &rgelm, UINT currentNum) -{ - if (ppEnum == nullptr) - { - return E_INVALIDARG; - } - *ppEnum = new (std::nothrow) CEnumTfCandidates(rgelm, currentNum); - if (*ppEnum == nullptr) - { - return E_OUTOFMEMORY; - } - - (*ppEnum)->AddRef(); - return S_OK; -} - -CEnumTfCandidates::CEnumTfCandidates(_In_ const CMetasequoiaImeArray &rgelm, UINT currentNum) -{ - _refCount = 0; - _rgelm = rgelm; - _currentCandidateStrIndex = currentNum; -} - -CEnumTfCandidates::~CEnumTfCandidates() -{ -} - -// -// IUnknown methods -// -STDMETHODIMP CEnumTfCandidates::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_POINTER; - } - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(IEnumTfCandidates))) - { - *ppvObj = (IEnumTfCandidates *)this; - } - - if (*ppvObj == nullptr) - { - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) CEnumTfCandidates::AddRef() -{ - return (ULONG)InterlockedIncrement(&_refCount); -} - -STDMETHODIMP_(ULONG) CEnumTfCandidates::Release() -{ - ULONG cRef = (ULONG)InterlockedDecrement(&_refCount); - if (0 < cRef) - { - return cRef; - } - - delete this; - return 0; -} - -// -// IEnumTfCandidates methods -// -STDMETHODIMP CEnumTfCandidates::Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched) -{ - ULONG fetched = 0; - if (ppObj == nullptr) - { - return E_INVALIDARG; - } - *ppObj = nullptr; - - while ((fetched < ulCount) && (_currentCandidateStrIndex < _rgelm.Count())) - { - *ppObj = *_rgelm.GetAt(_currentCandidateStrIndex); - (*ppObj)->AddRef(); - _currentCandidateStrIndex++; - fetched++; - } - - if (pcFetched) - { - *pcFetched = fetched; - } - - return (fetched == ulCount) ? S_OK : S_FALSE; -} - -STDMETHODIMP CEnumTfCandidates::Skip(ULONG ulCount) -{ - while ((0 < ulCount) && (_currentCandidateStrIndex < _rgelm.Count())) - { - _currentCandidateStrIndex++; - ulCount--; - } - - return (0 < ulCount) ? S_FALSE : S_OK; -} - -STDMETHODIMP CEnumTfCandidates::Reset() -{ - _currentCandidateStrIndex = 0; - return S_OK; -} - -STDMETHODIMP CEnumTfCandidates::Clone(_Out_ IEnumTfCandidates **ppEnum) -{ - return CreateInstance(ppEnum, _rgelm, _currentCandidateStrIndex); -} +#include "Private.h" +#include "EnumTfCandidates.h" + +HRESULT CEnumTfCandidates::CreateInstance(_Outptr_ IEnumTfCandidates **ppEnum, + _In_ const CMetasequoiaImeArray &rgelm, UINT currentNum) +{ + if (ppEnum == nullptr) + { + return E_INVALIDARG; + } + *ppEnum = new (std::nothrow) CEnumTfCandidates(rgelm, currentNum); + if (*ppEnum == nullptr) + { + return E_OUTOFMEMORY; + } + + (*ppEnum)->AddRef(); + return S_OK; +} + +CEnumTfCandidates::CEnumTfCandidates(_In_ const CMetasequoiaImeArray &rgelm, UINT currentNum) +{ + _refCount = 0; + _rgelm = rgelm; + _currentCandidateStrIndex = currentNum; +} + +CEnumTfCandidates::~CEnumTfCandidates() +{ +} + +// +// IUnknown methods +// +STDMETHODIMP CEnumTfCandidates::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(IEnumTfCandidates))) + { + *ppvObj = (IEnumTfCandidates *)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CEnumTfCandidates::AddRef() +{ + return (ULONG)InterlockedIncrement(&_refCount); +} + +STDMETHODIMP_(ULONG) CEnumTfCandidates::Release() +{ + ULONG cRef = (ULONG)InterlockedDecrement(&_refCount); + if (0 < cRef) + { + return cRef; + } + + delete this; + return 0; +} + +// +// IEnumTfCandidates methods +// +STDMETHODIMP CEnumTfCandidates::Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched) +{ + ULONG fetched = 0; + if (ppObj == nullptr) + { + return E_INVALIDARG; + } + *ppObj = nullptr; + + while ((fetched < ulCount) && (_currentCandidateStrIndex < _rgelm.Count())) + { + *ppObj = *_rgelm.GetAt(_currentCandidateStrIndex); + (*ppObj)->AddRef(); + _currentCandidateStrIndex++; + fetched++; + } + + if (pcFetched) + { + *pcFetched = fetched; + } + + return (fetched == ulCount) ? S_OK : S_FALSE; +} + +STDMETHODIMP CEnumTfCandidates::Skip(ULONG ulCount) +{ + while ((0 < ulCount) && (_currentCandidateStrIndex < _rgelm.Count())) + { + _currentCandidateStrIndex++; + ulCount--; + } + + return (0 < ulCount) ? S_FALSE : S_OK; +} + +STDMETHODIMP CEnumTfCandidates::Reset() +{ + _currentCandidateStrIndex = 0; + return S_OK; +} + +STDMETHODIMP CEnumTfCandidates::Clone(_Out_ IEnumTfCandidates **ppEnum) +{ + return CreateInstance(ppEnum, _rgelm, _currentCandidateStrIndex); +} diff --git a/windows/src/Candidate/EnumTfCandidates.h b/windows/src/Candidate/EnumTfCandidates.h index 6b548c4e..41d02fb2 100644 --- a/windows/src/Candidate/EnumTfCandidates.h +++ b/windows/src/Candidate/EnumTfCandidates.h @@ -1,33 +1,33 @@ -#pragma once - -#include "Globals.h" - -class CEnumTfCandidates : public IEnumTfCandidates -{ - protected: - // constructor/destructor - CEnumTfCandidates(_In_ const CMetasequoiaImeArray &rgelm, UINT currentNum); - - virtual ~CEnumTfCandidates(void); - - public: - // create instance - static HRESULT CreateInstance(_Outptr_ IEnumTfCandidates **ppEnum, - _In_ const CMetasequoiaImeArray &rgelm, UINT currentNum = 0); - - // IUnknown methods - virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - virtual STDMETHODIMP_(ULONG) AddRef(void); - virtual STDMETHODIMP_(ULONG) Release(void); - - // IEnumTfCandidates methods - virtual STDMETHODIMP Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched); - virtual STDMETHODIMP Skip(ULONG ulCount); - virtual STDMETHODIMP Reset(); - virtual STDMETHODIMP Clone(_Out_ IEnumTfCandidates **ppEnum); - - protected: - LONG _refCount; - CMetasequoiaImeArray _rgelm; - UINT _currentCandidateStrIndex; -}; +#pragma once + +#include "Globals.h" + +class CEnumTfCandidates : public IEnumTfCandidates +{ + protected: + // constructor/destructor + CEnumTfCandidates(_In_ const CMetasequoiaImeArray &rgelm, UINT currentNum); + + virtual ~CEnumTfCandidates(void); + + public: + // create instance + static HRESULT CreateInstance(_Outptr_ IEnumTfCandidates **ppEnum, + _In_ const CMetasequoiaImeArray &rgelm, UINT currentNum = 0); + + // IUnknown methods + virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + virtual STDMETHODIMP_(ULONG) AddRef(void); + virtual STDMETHODIMP_(ULONG) Release(void); + + // IEnumTfCandidates methods + virtual STDMETHODIMP Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched); + virtual STDMETHODIMP Skip(ULONG ulCount); + virtual STDMETHODIMP Reset(); + virtual STDMETHODIMP Clone(_Out_ IEnumTfCandidates **ppEnum); + + protected: + LONG _refCount; + CMetasequoiaImeArray _rgelm; + UINT _currentCandidateStrIndex; +}; diff --git a/windows/src/Candidate/SearchCandidateProvider.cpp b/windows/src/Candidate/SearchCandidateProvider.cpp index 0b10c8a3..52d9a27c 100644 --- a/windows/src/Candidate/SearchCandidateProvider.cpp +++ b/windows/src/Candidate/SearchCandidateProvider.cpp @@ -1,192 +1,192 @@ -#include "Private.h" -#include "SearchCandidateProvider.h" -#include -#include "MetasequoiaIME.h" -#include "CompositionProcessorEngine.h" -#include "TipCandidateList.h" -#include "TipCandidateString.h" - -/*------------------------------------------------------------------------------ - -create instance of CSearchCandidateProvider - -------------------------------------------------------------------------------*/ -HRESULT CSearchCandidateProvider::CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, - _In_ ITfTextInputProcessorEx *ptip) -{ - if (ppobj == nullptr) - { - return E_INVALIDARG; - } - *ppobj = nullptr; - - *ppobj = new (std::nothrow) CSearchCandidateProvider(ptip); - if (nullptr == *ppobj) - { - return E_OUTOFMEMORY; - } - - (*ppobj)->AddRef(); - return S_OK; -} - -/*------------------------------------------------------------------------------ - -constructor of CSearchCandidateProvider - -------------------------------------------------------------------------------*/ -CSearchCandidateProvider::CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip) -{ - assert(ptip != nullptr); - - _pTip = ptip; - _refCount = 0; -} - -/*------------------------------------------------------------------------------ - -destructor of CSearchCandidateProvider - -------------------------------------------------------------------------------*/ -CSearchCandidateProvider::~CSearchCandidateProvider(void) -{ -} - -/*------------------------------------------------------------------------------ - -query interface -(IUnknown method) - -------------------------------------------------------------------------------*/ -STDMETHODIMP CSearchCandidateProvider::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_POINTER; - } - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(ITfFnSearchCandidateProvider))) - { - *ppvObj = (ITfFnSearchCandidateProvider *)this; - } - else if (IsEqualIID(riid, IID_ITfFunction)) - { - *ppvObj = (ITfFunction *)this; - } - - if (*ppvObj == nullptr) - { - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; -} - -/*------------------------------------------------------------------------------ - -increment reference count -(IUnknown method) - -------------------------------------------------------------------------------*/ -STDMETHODIMP_(ULONG) CSearchCandidateProvider::AddRef() -{ - return (ULONG)InterlockedIncrement(&_refCount); -} - -/*------------------------------------------------------------------------------ - -decrement reference count and release object -(IUnknown method) - -------------------------------------------------------------------------------*/ -STDMETHODIMP_(ULONG) CSearchCandidateProvider::Release() -{ - ULONG ref = (ULONG)InterlockedDecrement(&_refCount); - if (0 < ref) - { - return ref; - } - - delete this; - return 0; -} - -STDMETHODIMP CSearchCandidateProvider::GetDisplayName(_Out_ BSTR *pbstrName) -{ - if (pbstrName == nullptr) - { - return E_INVALIDARG; - } - - *pbstrName = SysAllocString(L"SearchCandidateProvider"); - return S_OK; -} - -STDMETHODIMP CSearchCandidateProvider::GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, - _Outptr_result_maybenull_ ITfCandidateList **pplist) -{ - bstrApplicationID; - bstrQuery; - HRESULT hr = E_FAIL; - *pplist = nullptr; - - if (nullptr == _pTip) - { - return hr; - } - - CCompositionProcessorEngine *pCompositionProcessorEngine = - ((CMetasequoiaIME *)_pTip)->GetCompositionProcessorEngine(); - if (nullptr == pCompositionProcessorEngine) - { - return hr; - } - - CMetasequoiaImeArray candidateList; - pCompositionProcessorEngine->GetCandidateList(&candidateList, TRUE, FALSE); - - int cCand = min(candidateList.Count(), FAKECANDIDATENUMBER); - if (0 < cCand) - { - hr = CTipCandidateList::CreateInstance(pplist, cCand); - if (FAILED(hr)) - { - return hr; - } - for (int iCand = 0; iCand < cCand; iCand++) - { - CTipCandidateString *pCandStr = nullptr; - hr = CTipCandidateString::CreateInstance(&pCandStr); - if (FAILED(hr) || (pCandStr == nullptr)) - { - return hr; - } - - pCandStr->SetIndex(iCand); - pCandStr->SetString(candidateList.GetAt(iCand)->_ItemString.Get(), - candidateList.GetAt(iCand)->_ItemString.GetLength()); - - ((CTipCandidateList *)(*pplist))->SetCandidate(pCandStr); - } - } - hr = S_OK; - - return hr; -} - -/*------------------------------------------------------------------------------ - -set result -(ITfFnSearchCandidateProvider method) - -------------------------------------------------------------------------------*/ -STDMETHODIMP CSearchCandidateProvider::SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult) -{ - bstrQuery; - bstrApplicationID; - bstrResult; - - return E_NOTIMPL; -} +#include "Private.h" +#include "SearchCandidateProvider.h" +#include +#include "MetasequoiaIME.h" +#include "CompositionProcessorEngine.h" +#include "TipCandidateList.h" +#include "TipCandidateString.h" + +/*------------------------------------------------------------------------------ + +create instance of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +HRESULT CSearchCandidateProvider::CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, + _In_ ITfTextInputProcessorEx *ptip) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CSearchCandidateProvider(ptip); + if (nullptr == *ppobj) + { + return E_OUTOFMEMORY; + } + + (*ppobj)->AddRef(); + return S_OK; +} + +/*------------------------------------------------------------------------------ + +constructor of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +CSearchCandidateProvider::CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip) +{ + assert(ptip != nullptr); + + _pTip = ptip; + _refCount = 0; +} + +/*------------------------------------------------------------------------------ + +destructor of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +CSearchCandidateProvider::~CSearchCandidateProvider(void) +{ +} + +/*------------------------------------------------------------------------------ + +query interface +(IUnknown method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP CSearchCandidateProvider::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(ITfFnSearchCandidateProvider))) + { + *ppvObj = (ITfFnSearchCandidateProvider *)this; + } + else if (IsEqualIID(riid, IID_ITfFunction)) + { + *ppvObj = (ITfFunction *)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +/*------------------------------------------------------------------------------ + +increment reference count +(IUnknown method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP_(ULONG) CSearchCandidateProvider::AddRef() +{ + return (ULONG)InterlockedIncrement(&_refCount); +} + +/*------------------------------------------------------------------------------ + +decrement reference count and release object +(IUnknown method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP_(ULONG) CSearchCandidateProvider::Release() +{ + ULONG ref = (ULONG)InterlockedDecrement(&_refCount); + if (0 < ref) + { + return ref; + } + + delete this; + return 0; +} + +STDMETHODIMP CSearchCandidateProvider::GetDisplayName(_Out_ BSTR *pbstrName) +{ + if (pbstrName == nullptr) + { + return E_INVALIDARG; + } + + *pbstrName = SysAllocString(L"SearchCandidateProvider"); + return S_OK; +} + +STDMETHODIMP CSearchCandidateProvider::GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, + _Outptr_result_maybenull_ ITfCandidateList **pplist) +{ + bstrApplicationID; + bstrQuery; + HRESULT hr = E_FAIL; + *pplist = nullptr; + + if (nullptr == _pTip) + { + return hr; + } + + CCompositionProcessorEngine *pCompositionProcessorEngine = + ((CMetasequoiaIME *)_pTip)->GetCompositionProcessorEngine(); + if (nullptr == pCompositionProcessorEngine) + { + return hr; + } + + CMetasequoiaImeArray candidateList; + pCompositionProcessorEngine->GetCandidateList(&candidateList, TRUE, FALSE); + + int cCand = min(candidateList.Count(), FAKECANDIDATENUMBER); + if (0 < cCand) + { + hr = CTipCandidateList::CreateInstance(pplist, cCand); + if (FAILED(hr)) + { + return hr; + } + for (int iCand = 0; iCand < cCand; iCand++) + { + CTipCandidateString *pCandStr = nullptr; + hr = CTipCandidateString::CreateInstance(&pCandStr); + if (FAILED(hr) || (pCandStr == nullptr)) + { + return hr; + } + + pCandStr->SetIndex(iCand); + pCandStr->SetString(candidateList.GetAt(iCand)->_ItemString.Get(), + candidateList.GetAt(iCand)->_ItemString.GetLength()); + + ((CTipCandidateList *)(*pplist))->SetCandidate(pCandStr); + } + } + hr = S_OK; + + return hr; +} + +/*------------------------------------------------------------------------------ + +set result +(ITfFnSearchCandidateProvider method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP CSearchCandidateProvider::SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult) +{ + bstrQuery; + bstrApplicationID; + bstrResult; + + return E_NOTIMPL; +} diff --git a/windows/src/Candidate/SearchCandidateProvider.h b/windows/src/Candidate/SearchCandidateProvider.h index 68baef80..354e50d7 100644 --- a/windows/src/Candidate/SearchCandidateProvider.h +++ b/windows/src/Candidate/SearchCandidateProvider.h @@ -1,44 +1,44 @@ -#pragma once - -#include "Private.h" - -// -// CSearchCandidateProvider -// -// TfFnSearchCandidateProvider is used for search integration feature, CSearchCandidateProvider -// implements this interface that can be called by search integration, and offer the candidate list -// as search integration suggestion. -// -#define FAKECANDIDATENUMBER (16) -class CSearchCandidateProvider : public ITfFnSearchCandidateProvider -{ - protected: - // constructor/destructor - CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip); - virtual ~CSearchCandidateProvider(); - - public: - // create instance - static HRESULT CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, _In_ ITfTextInputProcessorEx *ptip); - - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfFunction methods - STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrName); - - // ITfFnSearchCandidateProvider methods - // - // GetSearchCandidates is responsible for supporting the candidates to caller, search integration - // SetResult is not used - // - STDMETHODIMP GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, - _Outptr_result_maybenull_ ITfCandidateList **pplist); - STDMETHODIMP SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult); - - private: - LONG _refCount; - ITfTextInputProcessorEx *_pTip; -}; +#pragma once + +#include "Private.h" + +// +// CSearchCandidateProvider +// +// TfFnSearchCandidateProvider is used for search integration feature, CSearchCandidateProvider +// implements this interface that can be called by search integration, and offer the candidate list +// as search integration suggestion. +// +#define FAKECANDIDATENUMBER (16) +class CSearchCandidateProvider : public ITfFnSearchCandidateProvider +{ + protected: + // constructor/destructor + CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip); + virtual ~CSearchCandidateProvider(); + + public: + // create instance + static HRESULT CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, _In_ ITfTextInputProcessorEx *ptip); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfFunction methods + STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrName); + + // ITfFnSearchCandidateProvider methods + // + // GetSearchCandidates is responsible for supporting the candidates to caller, search integration + // SetResult is not used + // + STDMETHODIMP GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, + _Outptr_result_maybenull_ ITfCandidateList **pplist); + STDMETHODIMP SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult); + + private: + LONG _refCount; + ITfTextInputProcessorEx *_pTip; +}; diff --git a/windows/src/Candidate/TipCandidateList.cpp b/windows/src/Candidate/TipCandidateList.cpp index aee73c43..af0e0b20 100644 --- a/windows/src/Candidate/TipCandidateList.cpp +++ b/windows/src/Candidate/TipCandidateList.cpp @@ -1,170 +1,170 @@ -#include "Private.h" -#include "TipCandidateList.h" -#include "EnumTfCandidates.h" -#include "TipCandidateString.h" - -HRESULT CTipCandidateList::CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize) -{ - if (ppobj == nullptr) - { - return E_INVALIDARG; - } - *ppobj = nullptr; - - *ppobj = new (std::nothrow) CTipCandidateList(candStrReserveSize); - if (*ppobj == nullptr) - { - return E_OUTOFMEMORY; - } - - (*ppobj)->AddRef(); - return S_OK; -} - -CTipCandidateList::CTipCandidateList(size_t candStrReserveSize) -{ - _refCount = 0; - - if (0 < candStrReserveSize) - { - _tfCandStrList.reserve(candStrReserveSize); - } -} - -CTipCandidateList::~CTipCandidateList() -{ - for (UINT i = 0; i < _tfCandStrList.Count(); i++) - { - ITfCandidateString **ppCandStr = _tfCandStrList.GetAt(i); - if ((ppCandStr != nullptr) && (*ppCandStr != nullptr)) - { - (*ppCandStr)->Release(); - } - } -} - -STDMETHODIMP CTipCandidateList::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_POINTER; - } - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown)) - { - *ppvObj = (ITfCandidateList *)this; - } - else if (IsEqualIID(riid, IID_ITfCandidateList)) - { - *ppvObj = (ITfCandidateList *)this; - } - - if (*ppvObj == nullptr) - { - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) CTipCandidateList::AddRef() -{ - return (ULONG)InterlockedIncrement((LONG *)&_refCount); -} - -STDMETHODIMP_(ULONG) CTipCandidateList::Release() -{ - ULONG cRefT = (ULONG)InterlockedDecrement((LONG *)&_refCount); - if (0 < cRefT) - { - return cRefT; - } - - delete this; - - return 0; -} - -STDMETHODIMP CTipCandidateList::EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum) -{ - return CEnumTfCandidates::CreateInstance(ppEnum, _tfCandStrList); -} - -STDMETHODIMP CTipCandidateList::GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr) -{ - if (ppCandStr == nullptr) - { - return E_POINTER; - } - *ppCandStr = nullptr; - - ULONG sizeCandStr = (ULONG)_tfCandStrList.Count(); - if (sizeCandStr <= nIndex) - { - return E_FAIL; - } - - ITfCandidateString **ppCandStrCur = _tfCandStrList.GetAt(static_cast(nIndex)); - if ((ppCandStrCur == nullptr) || (*ppCandStrCur == nullptr)) - { - return E_FAIL; - } - - BSTR bstr = nullptr; - CTipCandidateString *pTipCandidateStrCur = static_cast(*ppCandStrCur); - if (FAILED(pTipCandidateStrCur->GetString(&bstr))) - { - return E_FAIL; - } - - CTipCandidateString *pCandidateString = nullptr; - const HRESULT hr = CTipCandidateString::CreateInstance(&pCandidateString); - if (SUCCEEDED(hr) && (pCandidateString != nullptr)) - { - pCandidateString->SetIndex(nIndex); - pCandidateString->SetString((LPCWSTR)bstr, SysStringLen(bstr)); - *ppCandStr = pCandidateString; - } - - SysFreeString(bstr); - return hr; -} - -STDMETHODIMP CTipCandidateList::GetCandidateNum(_Out_ ULONG *pnCnt) -{ - if (pnCnt == nullptr) - { - return E_POINTER; - } - - *pnCnt = (ULONG)(_tfCandStrList.Count()); - return S_OK; -} - -STDMETHODIMP CTipCandidateList::SetResult(ULONG nIndex, TfCandidateResult imcr) -{ - nIndex; - imcr; - - return E_NOTIMPL; -} - -STDMETHODIMP CTipCandidateList::SetCandidate(_In_ ITfCandidateString *pCandStr) -{ - if (pCandStr == nullptr) - { - return E_POINTER; - } - - ITfCandidateString **ppCandLast = _tfCandStrList.Append(); - if (ppCandLast) - { - *ppCandLast = pCandStr; - return S_OK; - } - - pCandStr->Release(); - return E_FAIL; -} +#include "Private.h" +#include "TipCandidateList.h" +#include "EnumTfCandidates.h" +#include "TipCandidateString.h" + +HRESULT CTipCandidateList::CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CTipCandidateList(candStrReserveSize); + if (*ppobj == nullptr) + { + return E_OUTOFMEMORY; + } + + (*ppobj)->AddRef(); + return S_OK; +} + +CTipCandidateList::CTipCandidateList(size_t candStrReserveSize) +{ + _refCount = 0; + + if (0 < candStrReserveSize) + { + _tfCandStrList.reserve(candStrReserveSize); + } +} + +CTipCandidateList::~CTipCandidateList() +{ + for (UINT i = 0; i < _tfCandStrList.Count(); i++) + { + ITfCandidateString **ppCandStr = _tfCandStrList.GetAt(i); + if ((ppCandStr != nullptr) && (*ppCandStr != nullptr)) + { + (*ppCandStr)->Release(); + } + } +} + +STDMETHODIMP CTipCandidateList::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = (ITfCandidateList *)this; + } + else if (IsEqualIID(riid, IID_ITfCandidateList)) + { + *ppvObj = (ITfCandidateList *)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CTipCandidateList::AddRef() +{ + return (ULONG)InterlockedIncrement((LONG *)&_refCount); +} + +STDMETHODIMP_(ULONG) CTipCandidateList::Release() +{ + ULONG cRefT = (ULONG)InterlockedDecrement((LONG *)&_refCount); + if (0 < cRefT) + { + return cRefT; + } + + delete this; + + return 0; +} + +STDMETHODIMP CTipCandidateList::EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum) +{ + return CEnumTfCandidates::CreateInstance(ppEnum, _tfCandStrList); +} + +STDMETHODIMP CTipCandidateList::GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr) +{ + if (ppCandStr == nullptr) + { + return E_POINTER; + } + *ppCandStr = nullptr; + + ULONG sizeCandStr = (ULONG)_tfCandStrList.Count(); + if (sizeCandStr <= nIndex) + { + return E_FAIL; + } + + ITfCandidateString **ppCandStrCur = _tfCandStrList.GetAt(static_cast(nIndex)); + if ((ppCandStrCur == nullptr) || (*ppCandStrCur == nullptr)) + { + return E_FAIL; + } + + BSTR bstr = nullptr; + CTipCandidateString *pTipCandidateStrCur = static_cast(*ppCandStrCur); + if (FAILED(pTipCandidateStrCur->GetString(&bstr))) + { + return E_FAIL; + } + + CTipCandidateString *pCandidateString = nullptr; + const HRESULT hr = CTipCandidateString::CreateInstance(&pCandidateString); + if (SUCCEEDED(hr) && (pCandidateString != nullptr)) + { + pCandidateString->SetIndex(nIndex); + pCandidateString->SetString((LPCWSTR)bstr, SysStringLen(bstr)); + *ppCandStr = pCandidateString; + } + + SysFreeString(bstr); + return hr; +} + +STDMETHODIMP CTipCandidateList::GetCandidateNum(_Out_ ULONG *pnCnt) +{ + if (pnCnt == nullptr) + { + return E_POINTER; + } + + *pnCnt = (ULONG)(_tfCandStrList.Count()); + return S_OK; +} + +STDMETHODIMP CTipCandidateList::SetResult(ULONG nIndex, TfCandidateResult imcr) +{ + nIndex; + imcr; + + return E_NOTIMPL; +} + +STDMETHODIMP CTipCandidateList::SetCandidate(_In_ ITfCandidateString *pCandStr) +{ + if (pCandStr == nullptr) + { + return E_POINTER; + } + + ITfCandidateString **ppCandLast = _tfCandStrList.Append(); + if (ppCandLast) + { + *ppCandLast = pCandStr; + return S_OK; + } + + pCandStr->Release(); + return E_FAIL; +} diff --git a/windows/src/Candidate/TipCandidateList.h b/windows/src/Candidate/TipCandidateList.h index dc5be144..e3607b14 100644 --- a/windows/src/Candidate/TipCandidateList.h +++ b/windows/src/Candidate/TipCandidateList.h @@ -1,35 +1,35 @@ -#pragma once - -#include "Private.h" -#include "MetasequoiaIMEBaseStructure.h" - -// -// CTipCandidateList -// - -class CTipCandidateList : public ITfCandidateList -{ - protected: - CTipCandidateList(size_t candStrReserveSize); - virtual ~CTipCandidateList(); - - public: - static HRESULT CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize = 0); - - // IUnknown methods - virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - virtual STDMETHODIMP_(ULONG) AddRef(); - virtual STDMETHODIMP_(ULONG) Release(); - - // ITfCandidateList methods - virtual STDMETHODIMP EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum); - virtual STDMETHODIMP GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr); - virtual STDMETHODIMP GetCandidateNum(_Out_ ULONG *pnCnt); - virtual STDMETHODIMP SetResult(ULONG nIndex, TfCandidateResult imcr); - - virtual STDMETHODIMP SetCandidate(_In_ ITfCandidateString *pCandStr); - - protected: - long _refCount; - CMetasequoiaImeArray _tfCandStrList; -}; +#pragma once + +#include "Private.h" +#include "MetasequoiaIMEBaseStructure.h" + +// +// CTipCandidateList +// + +class CTipCandidateList : public ITfCandidateList +{ + protected: + CTipCandidateList(size_t candStrReserveSize); + virtual ~CTipCandidateList(); + + public: + static HRESULT CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize = 0); + + // IUnknown methods + virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + virtual STDMETHODIMP_(ULONG) AddRef(); + virtual STDMETHODIMP_(ULONG) Release(); + + // ITfCandidateList methods + virtual STDMETHODIMP EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum); + virtual STDMETHODIMP GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr); + virtual STDMETHODIMP GetCandidateNum(_Out_ ULONG *pnCnt); + virtual STDMETHODIMP SetResult(ULONG nIndex, TfCandidateResult imcr); + + virtual STDMETHODIMP SetCandidate(_In_ ITfCandidateString *pCandStr); + + protected: + long _refCount; + CMetasequoiaImeArray _tfCandStrList; +}; diff --git a/windows/src/Candidate/TipCandidateString.cpp b/windows/src/Candidate/TipCandidateString.cpp index 9e2dd09b..0d1fb34f 100644 --- a/windows/src/Candidate/TipCandidateString.cpp +++ b/windows/src/Candidate/TipCandidateString.cpp @@ -1,105 +1,105 @@ -#include "Private.h" -#include "TipCandidateString.h" - -HRESULT CTipCandidateString::CreateInstance(_Outptr_ CTipCandidateString **ppobj) -{ - if (ppobj == nullptr) - { - return E_INVALIDARG; - } - *ppobj = nullptr; - - *ppobj = new (std::nothrow) CTipCandidateString(); - if (*ppobj == nullptr) - { - return E_OUTOFMEMORY; - } - - (*ppobj)->AddRef(); - return S_OK; -} - -CTipCandidateString::CTipCandidateString(void) -{ - _refCount = 0; - _index = 0; -} - -CTipCandidateString::~CTipCandidateString() -{ -} - -// IUnknown methods -STDMETHODIMP CTipCandidateString::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_POINTER; - } - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown)) - { - *ppvObj = (CTipCandidateString *)this; - } - else if (IsEqualIID(riid, IID_ITfCandidateString)) - { - *ppvObj = (CTipCandidateString *)this; - } - - if (*ppvObj == nullptr) - { - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) CTipCandidateString::AddRef(void) -{ - return (ULONG)InterlockedIncrement((LONG *)&_refCount); -} - -STDMETHODIMP_(ULONG) CTipCandidateString::Release(void) -{ - ULONG refT = (ULONG)InterlockedDecrement((LONG *)&_refCount); - if (0 < refT) - { - return refT; - } - - delete this; - - return 0; -} - -// ITfCandidateString methods -STDMETHODIMP CTipCandidateString::GetString(BSTR *pbstr) -{ - *pbstr = SysAllocString(_candidateStr.c_str()); - return S_OK; -} - -STDMETHODIMP CTipCandidateString::GetIndex(_Out_ ULONG *pnIndex) -{ - if (pnIndex == nullptr) - { - return E_POINTER; - } - - *pnIndex = _index; - return S_OK; -} - -STDMETHODIMP CTipCandidateString::SetIndex(ULONG uIndex) -{ - _index = uIndex; - return S_OK; -} - -STDMETHODIMP CTipCandidateString::SetString(_In_ const WCHAR *pch, DWORD_PTR length) -{ - _candidateStr.assign(pch, 0, length); - return S_OK; -} +#include "Private.h" +#include "TipCandidateString.h" + +HRESULT CTipCandidateString::CreateInstance(_Outptr_ CTipCandidateString **ppobj) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CTipCandidateString(); + if (*ppobj == nullptr) + { + return E_OUTOFMEMORY; + } + + (*ppobj)->AddRef(); + return S_OK; +} + +CTipCandidateString::CTipCandidateString(void) +{ + _refCount = 0; + _index = 0; +} + +CTipCandidateString::~CTipCandidateString() +{ +} + +// IUnknown methods +STDMETHODIMP CTipCandidateString::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = (CTipCandidateString *)this; + } + else if (IsEqualIID(riid, IID_ITfCandidateString)) + { + *ppvObj = (CTipCandidateString *)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CTipCandidateString::AddRef(void) +{ + return (ULONG)InterlockedIncrement((LONG *)&_refCount); +} + +STDMETHODIMP_(ULONG) CTipCandidateString::Release(void) +{ + ULONG refT = (ULONG)InterlockedDecrement((LONG *)&_refCount); + if (0 < refT) + { + return refT; + } + + delete this; + + return 0; +} + +// ITfCandidateString methods +STDMETHODIMP CTipCandidateString::GetString(BSTR *pbstr) +{ + *pbstr = SysAllocString(_candidateStr.c_str()); + return S_OK; +} + +STDMETHODIMP CTipCandidateString::GetIndex(_Out_ ULONG *pnIndex) +{ + if (pnIndex == nullptr) + { + return E_POINTER; + } + + *pnIndex = _index; + return S_OK; +} + +STDMETHODIMP CTipCandidateString::SetIndex(ULONG uIndex) +{ + _index = uIndex; + return S_OK; +} + +STDMETHODIMP CTipCandidateString::SetString(_In_ const WCHAR *pch, DWORD_PTR length) +{ + _candidateStr.assign(pch, 0, length); + return S_OK; +} diff --git a/windows/src/Candidate/TipCandidateString.h b/windows/src/Candidate/TipCandidateString.h index d67acfe7..1747c8b5 100644 --- a/windows/src/Candidate/TipCandidateString.h +++ b/windows/src/Candidate/TipCandidateString.h @@ -1,31 +1,31 @@ -#pragma once - -#include "Private.h" -#include - -class CTipCandidateString : public ITfCandidateString -{ - protected: - CTipCandidateString(); - virtual ~CTipCandidateString(); - - public: - static HRESULT CreateInstance(_Outptr_ CTipCandidateString **ppobj); - - // IUnknown methods - virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - virtual STDMETHODIMP_(ULONG) AddRef(); - virtual STDMETHODIMP_(ULONG) Release(); - - // ITfCandidateString methods - virtual STDMETHODIMP GetString(BSTR *pbstr); - virtual STDMETHODIMP GetIndex(_Out_ ULONG *pnIndex); - - virtual STDMETHODIMP SetIndex(ULONG uIndex); - virtual STDMETHODIMP SetString(_In_ const WCHAR *pch, DWORD_PTR length); - - protected: - long _refCount; - int _index; - std::wstring _candidateStr; -}; +#pragma once + +#include "Private.h" +#include + +class CTipCandidateString : public ITfCandidateString +{ + protected: + CTipCandidateString(); + virtual ~CTipCandidateString(); + + public: + static HRESULT CreateInstance(_Outptr_ CTipCandidateString **ppobj); + + // IUnknown methods + virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + virtual STDMETHODIMP_(ULONG) AddRef(); + virtual STDMETHODIMP_(ULONG) Release(); + + // ITfCandidateString methods + virtual STDMETHODIMP GetString(BSTR *pbstr); + virtual STDMETHODIMP GetIndex(_Out_ ULONG *pnIndex); + + virtual STDMETHODIMP SetIndex(ULONG uIndex); + virtual STDMETHODIMP SetString(_In_ const WCHAR *pch, DWORD_PTR length); + + protected: + long _refCount; + int _index; + std::wstring _candidateStr; +}; diff --git a/windows/src/Compartment/Compartment.cpp b/windows/src/Compartment/Compartment.cpp index d1f9256c..0b16b8f7 100644 --- a/windows/src/Compartment/Compartment.cpp +++ b/windows/src/Compartment/Compartment.cpp @@ -1,363 +1,363 @@ -#include "Private.h" -#include "Compartment.h" -#include "Globals.h" - -////////////////////////////////////////////////////////////////////// -// -// CCompartment -// -////////////////////////////////////////////////////////////////////// - -//+--------------------------------------------------------------------------- -// ctor -//---------------------------------------------------------------------------- - -CCompartment::CCompartment(_In_ IUnknown *punk, TfClientId tfClientId, _In_ REFGUID guidCompartment) -{ - _guidCompartment = guidCompartment; - - _punk = punk; - _punk->AddRef(); - - _tfClientId = tfClientId; -} - -//+--------------------------------------------------------------------------- -// dtor -//---------------------------------------------------------------------------- - -CCompartment::~CCompartment() -{ - _punk->Release(); -} - -//+--------------------------------------------------------------------------- -// _GetCompartment -//---------------------------------------------------------------------------- - -HRESULT CCompartment::_GetCompartment(_Outptr_ ITfCompartment **ppCompartment) -{ - HRESULT hr = S_OK; - ITfCompartmentMgr *pCompartmentMgr = nullptr; - - hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); - if (SUCCEEDED(hr)) - { - hr = pCompartmentMgr->GetCompartment(_guidCompartment, ppCompartment); - pCompartmentMgr->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// _GetCompartmentBOOL -//---------------------------------------------------------------------------- - -HRESULT CCompartment::_GetCompartmentBOOL(_Out_ BOOL &flag) -{ - HRESULT hr = S_OK; - ITfCompartment *pCompartment = nullptr; - flag = FALSE; - - if ((hr = _GetCompartment(&pCompartment)) == S_OK) - { - VARIANT var; - if ((hr = pCompartment->GetValue(&var)) == S_OK) - { - if (var.vt == VT_I4) // Even VT_EMPTY, GetValue() can succeed - { - flag = (BOOL)var.lVal; - } - else - { - hr = S_FALSE; - } - } - pCompartment->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// _SetCompartmentBOOL -//---------------------------------------------------------------------------- - -HRESULT CCompartment::_SetCompartmentBOOL(_In_ BOOL flag) -{ - HRESULT hr = S_OK; - ITfCompartment *pCompartment = nullptr; - - hr = _GetCompartment(&pCompartment); - if (SUCCEEDED(hr)) - { - VARIANT var; - var.vt = VT_I4; - var.lVal = flag; - hr = pCompartment->SetValue(_tfClientId, &var); - pCompartment->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// _GetCompartmentDWORD -//---------------------------------------------------------------------------- - -HRESULT CCompartment::_GetCompartmentDWORD(_Out_ DWORD &dw) -{ - HRESULT hr = S_OK; - ITfCompartment *pCompartment = nullptr; - dw = 0; - - hr = _GetCompartment(&pCompartment); - if (SUCCEEDED(hr)) - { - VARIANT var; - if ((hr = pCompartment->GetValue(&var)) == S_OK) - { - if (var.vt == VT_I4) // Even VT_EMPTY, GetValue() can succeed - { - dw = (DWORD)var.lVal; - } - else - { - hr = S_FALSE; - } - } - pCompartment->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// _SetCompartmentDWORD -//---------------------------------------------------------------------------- - -HRESULT CCompartment::_SetCompartmentDWORD(_In_ DWORD dw) -{ - HRESULT hr = S_OK; - ITfCompartment *pCompartment = nullptr; - - hr = _GetCompartment(&pCompartment); - if (SUCCEEDED(hr)) - { - VARIANT var; - var.vt = VT_I4; - var.lVal = dw; - hr = pCompartment->SetValue(_tfClientId, &var); - pCompartment->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _ClearCompartment -// -//---------------------------------------------------------------------------- - -HRESULT CCompartment::_ClearCompartment() -{ - if (IsEqualGUID(_guidCompartment, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) - { - return S_FALSE; - } - - HRESULT hr = S_OK; - ITfCompartmentMgr *pCompartmentMgr = nullptr; - - if ((hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr)) == S_OK) - { - hr = pCompartmentMgr->ClearCompartment(_tfClientId, _guidCompartment); - pCompartmentMgr->Release(); - } - - return hr; -} - -////////////////////////////////////////////////////////////////////// -// -// CCompartmentEventSink -// -////////////////////////////////////////////////////////////////////// - -//+--------------------------------------------------------------------------- -// ctor -//---------------------------------------------------------------------------- - -CCompartmentEventSink::CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv) -{ - _pCompartment = nullptr; - _dwCookie = TF_INVALID_COOKIE; - _pfnCallback = pfnCallback; - _pv = pv; - _refCount = 1; -} - -//+--------------------------------------------------------------------------- -// dtor -//---------------------------------------------------------------------------- - -CCompartmentEventSink::~CCompartmentEventSink() -{ -} - -//+--------------------------------------------------------------------------- -// -// QueryInterface -// -//---------------------------------------------------------------------------- - -STDAPI CCompartmentEventSink::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - return E_INVALIDARG; - - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCompartmentEventSink)) - { - *ppvObj = (CCompartmentEventSink *)this; - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -//+--------------------------------------------------------------------------- -// -// AddRef -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CCompartmentEventSink::AddRef() -{ - return ++_refCount; -} - -//+--------------------------------------------------------------------------- -// -// Release -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CCompartmentEventSink::Release() -{ - LONG cr = --_refCount; - - assert(_refCount >= 0); - - if (_refCount == 0) - { - delete this; - } - - return cr; -} - -//+--------------------------------------------------------------------------- -// -// OnChange -// -//---------------------------------------------------------------------------- - -STDAPI CCompartmentEventSink::OnChange(_In_ REFGUID guidCompartment) -{ - return _pfnCallback(_pv, guidCompartment); -} - -//+--------------------------------------------------------------------------- -// -// _Advise -// -//---------------------------------------------------------------------------- - -HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment) -{ - if (!punk) - { - return E_INVALIDARG; - } - if (_pCompartment) - { - _Unadvise(); - } - - HRESULT hr = S_OK; - ITfCompartmentMgr *pCompartmentMgr = nullptr; - ITfSource *pSource = nullptr; - - hr = punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); - if (FAILED(hr)) - { - return hr; - } - - hr = pCompartmentMgr->GetCompartment(guidCompartment, &_pCompartment); - if (SUCCEEDED(hr)) - { - hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); - if (SUCCEEDED(hr)) - { - hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &_dwCookie); - pSource->Release(); - } - } - - pCompartmentMgr->Release(); - - if (FAILED(hr)) - { - if (_pCompartment) - { - _pCompartment->Release(); - _pCompartment = nullptr; - } - _dwCookie = TF_INVALID_COOKIE; - } - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _Unadvise -// -//---------------------------------------------------------------------------- - -HRESULT CCompartmentEventSink::_Unadvise() -{ - if (!_pCompartment) - { - _dwCookie = TF_INVALID_COOKIE; - return S_OK; - } - - HRESULT hr = S_OK; - ITfSource *pSource = nullptr; - - if (_dwCookie != TF_INVALID_COOKIE) - { - hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); - if (SUCCEEDED(hr)) - { - hr = pSource->UnadviseSink(_dwCookie); - pSource->Release(); - } - } - - _pCompartment->Release(); - _pCompartment = nullptr; - _dwCookie = TF_INVALID_COOKIE; - - return hr; -} +#include "Private.h" +#include "Compartment.h" +#include "Globals.h" + +////////////////////////////////////////////////////////////////////// +// +// CCompartment +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// ctor +//---------------------------------------------------------------------------- + +CCompartment::CCompartment(_In_ IUnknown *punk, TfClientId tfClientId, _In_ REFGUID guidCompartment) +{ + _guidCompartment = guidCompartment; + + _punk = punk; + _punk->AddRef(); + + _tfClientId = tfClientId; +} + +//+--------------------------------------------------------------------------- +// dtor +//---------------------------------------------------------------------------- + +CCompartment::~CCompartment() +{ + _punk->Release(); +} + +//+--------------------------------------------------------------------------- +// _GetCompartment +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_GetCompartment(_Outptr_ ITfCompartment **ppCompartment) +{ + HRESULT hr = S_OK; + ITfCompartmentMgr *pCompartmentMgr = nullptr; + + hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); + if (SUCCEEDED(hr)) + { + hr = pCompartmentMgr->GetCompartment(_guidCompartment, ppCompartment); + pCompartmentMgr->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _GetCompartmentBOOL +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_GetCompartmentBOOL(_Out_ BOOL &flag) +{ + HRESULT hr = S_OK; + ITfCompartment *pCompartment = nullptr; + flag = FALSE; + + if ((hr = _GetCompartment(&pCompartment)) == S_OK) + { + VARIANT var; + if ((hr = pCompartment->GetValue(&var)) == S_OK) + { + if (var.vt == VT_I4) // Even VT_EMPTY, GetValue() can succeed + { + flag = (BOOL)var.lVal; + } + else + { + hr = S_FALSE; + } + } + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _SetCompartmentBOOL +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_SetCompartmentBOOL(_In_ BOOL flag) +{ + HRESULT hr = S_OK; + ITfCompartment *pCompartment = nullptr; + + hr = _GetCompartment(&pCompartment); + if (SUCCEEDED(hr)) + { + VARIANT var; + var.vt = VT_I4; + var.lVal = flag; + hr = pCompartment->SetValue(_tfClientId, &var); + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _GetCompartmentDWORD +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_GetCompartmentDWORD(_Out_ DWORD &dw) +{ + HRESULT hr = S_OK; + ITfCompartment *pCompartment = nullptr; + dw = 0; + + hr = _GetCompartment(&pCompartment); + if (SUCCEEDED(hr)) + { + VARIANT var; + if ((hr = pCompartment->GetValue(&var)) == S_OK) + { + if (var.vt == VT_I4) // Even VT_EMPTY, GetValue() can succeed + { + dw = (DWORD)var.lVal; + } + else + { + hr = S_FALSE; + } + } + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _SetCompartmentDWORD +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_SetCompartmentDWORD(_In_ DWORD dw) +{ + HRESULT hr = S_OK; + ITfCompartment *pCompartment = nullptr; + + hr = _GetCompartment(&pCompartment); + if (SUCCEEDED(hr)) + { + VARIANT var; + var.vt = VT_I4; + var.lVal = dw; + hr = pCompartment->SetValue(_tfClientId, &var); + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _ClearCompartment +// +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_ClearCompartment() +{ + if (IsEqualGUID(_guidCompartment, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) + { + return S_FALSE; + } + + HRESULT hr = S_OK; + ITfCompartmentMgr *pCompartmentMgr = nullptr; + + if ((hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr)) == S_OK) + { + hr = pCompartmentMgr->ClearCompartment(_tfClientId, _guidCompartment); + pCompartmentMgr->Release(); + } + + return hr; +} + +////////////////////////////////////////////////////////////////////// +// +// CCompartmentEventSink +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// ctor +//---------------------------------------------------------------------------- + +CCompartmentEventSink::CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv) +{ + _pCompartment = nullptr; + _dwCookie = TF_INVALID_COOKIE; + _pfnCallback = pfnCallback; + _pv = pv; + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// dtor +//---------------------------------------------------------------------------- + +CCompartmentEventSink::~CCompartmentEventSink() +{ +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CCompartmentEventSink::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + return E_INVALIDARG; + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCompartmentEventSink)) + { + *ppvObj = (CCompartmentEventSink *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CCompartmentEventSink::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CCompartmentEventSink::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// OnChange +// +//---------------------------------------------------------------------------- + +STDAPI CCompartmentEventSink::OnChange(_In_ REFGUID guidCompartment) +{ + return _pfnCallback(_pv, guidCompartment); +} + +//+--------------------------------------------------------------------------- +// +// _Advise +// +//---------------------------------------------------------------------------- + +HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment) +{ + if (!punk) + { + return E_INVALIDARG; + } + if (_pCompartment) + { + _Unadvise(); + } + + HRESULT hr = S_OK; + ITfCompartmentMgr *pCompartmentMgr = nullptr; + ITfSource *pSource = nullptr; + + hr = punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); + if (FAILED(hr)) + { + return hr; + } + + hr = pCompartmentMgr->GetCompartment(guidCompartment, &_pCompartment); + if (SUCCEEDED(hr)) + { + hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); + if (SUCCEEDED(hr)) + { + hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &_dwCookie); + pSource->Release(); + } + } + + pCompartmentMgr->Release(); + + if (FAILED(hr)) + { + if (_pCompartment) + { + _pCompartment->Release(); + _pCompartment = nullptr; + } + _dwCookie = TF_INVALID_COOKIE; + } + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _Unadvise +// +//---------------------------------------------------------------------------- + +HRESULT CCompartmentEventSink::_Unadvise() +{ + if (!_pCompartment) + { + _dwCookie = TF_INVALID_COOKIE; + return S_OK; + } + + HRESULT hr = S_OK; + ITfSource *pSource = nullptr; + + if (_dwCookie != TF_INVALID_COOKIE) + { + hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); + if (SUCCEEDED(hr)) + { + hr = pSource->UnadviseSink(_dwCookie); + pSource->Release(); + } + } + + _pCompartment->Release(); + _pCompartment = nullptr; + _dwCookie = TF_INVALID_COOKIE; + + return hr; +} diff --git a/windows/src/Compartment/Compartment.h b/windows/src/Compartment/Compartment.h index 3a7126ff..3b15c18e 100644 --- a/windows/src/Compartment/Compartment.h +++ b/windows/src/Compartment/Compartment.h @@ -1,56 +1,56 @@ -#pragma once - -#include "Private.h" - -class CCompartment -{ - public: - CCompartment(_In_ IUnknown *punk, TfClientId tfClientId, _In_ REFGUID guidCompartment); - ~CCompartment(); - - HRESULT _GetCompartment(_Outptr_ ITfCompartment **ppCompartment); - HRESULT _GetCompartmentBOOL(_Out_ BOOL &flag); - HRESULT _SetCompartmentBOOL(_In_ BOOL flag); - HRESULT _GetCompartmentDWORD(_Out_ DWORD &dw); - HRESULT _SetCompartmentDWORD(_In_ DWORD dw); - HRESULT _ClearCompartment(); - - VOID _GetGUID(GUID *pguid) - { - *pguid = _guidCompartment; - } - - private: - GUID _guidCompartment; - IUnknown *_punk; - TfClientId _tfClientId; -}; - -typedef HRESULT (*CESCALLBACK)(void *pv, REFGUID guidCompartment); - -class CCompartmentEventSink : public ITfCompartmentEventSink -{ - public: - CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv); - ~CCompartmentEventSink(); - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfCompartmentEventSink - STDMETHODIMP OnChange(_In_ REFGUID guid); - - // function - HRESULT _Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment); - HRESULT _Unadvise(); - - private: - ITfCompartment *_pCompartment; - DWORD _dwCookie; - CESCALLBACK _pfnCallback; - void *_pv; - - LONG _refCount; -}; +#pragma once + +#include "Private.h" + +class CCompartment +{ + public: + CCompartment(_In_ IUnknown *punk, TfClientId tfClientId, _In_ REFGUID guidCompartment); + ~CCompartment(); + + HRESULT _GetCompartment(_Outptr_ ITfCompartment **ppCompartment); + HRESULT _GetCompartmentBOOL(_Out_ BOOL &flag); + HRESULT _SetCompartmentBOOL(_In_ BOOL flag); + HRESULT _GetCompartmentDWORD(_Out_ DWORD &dw); + HRESULT _SetCompartmentDWORD(_In_ DWORD dw); + HRESULT _ClearCompartment(); + + VOID _GetGUID(GUID *pguid) + { + *pguid = _guidCompartment; + } + + private: + GUID _guidCompartment; + IUnknown *_punk; + TfClientId _tfClientId; +}; + +typedef HRESULT (*CESCALLBACK)(void *pv, REFGUID guidCompartment); + +class CCompartmentEventSink : public ITfCompartmentEventSink +{ + public: + CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv); + ~CCompartmentEventSink(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfCompartmentEventSink + STDMETHODIMP OnChange(_In_ REFGUID guid); + + // function + HRESULT _Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment); + HRESULT _Unadvise(); + + private: + ITfCompartment *_pCompartment; + DWORD _dwCookie; + CESCALLBACK _pfnCallback; + void *_pv; + + LONG _refCount; +}; diff --git a/windows/src/Composition/Composition.cpp b/windows/src/Composition/Composition.cpp index 901beea6..d6902235 100644 --- a/windows/src/Composition/Composition.cpp +++ b/windows/src/Composition/Composition.cpp @@ -1,947 +1,947 @@ -#include "Private.h" -#include "Globals.h" -#include "MetasequoiaIME.h" -#include "CompositionProcessorEngine.h" -#include -#include -#include -#include -#include "FanyDefines.h" -#include "Ipc.h" -#include "../Utils/PerfTimer.h" - -namespace -{ -// Keep SEH helpers free of C++ objects with destructors (C2712). -HRESULT SafeRangeSetText(_In_ ITfRange *range, TfEditCookie ec, DWORD flags, _In_reads_opt_(len) const WCHAR *text, - LONG len) -{ - if (range == nullptr) - { - return E_INVALIDARG; - } - - __try - { - return range->SetText(ec, flags, text, len); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return E_FAIL; - } -} - -HRESULT SafeRangeGetText(_In_ ITfRange *range, TfEditCookie ec, DWORD flags, _Out_writes_(len) WCHAR *text, ULONG len, - _Out_ ULONG *fetched) -{ - if (range == nullptr || text == nullptr || fetched == nullptr || len == 0) - { - return E_INVALIDARG; - } - - __try - { - return range->GetText(ec, flags, text, len, fetched); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - *fetched = 0; - return E_FAIL; - } -} - -HRESULT SafeRangeShiftStart(_In_ ITfRange *range, TfEditCookie ec, LONG count, _Out_ LONG *shifted) -{ - if (range == nullptr || shifted == nullptr) - { - return E_INVALIDARG; - } - - __try - { - return range->ShiftStart(ec, count, shifted, nullptr); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - *shifted = 0; - return E_FAIL; - } -} -} // namespace - -WCHAR CMetasequoiaIME::_GetPrecedingDocumentChar(TfEditCookie ec, _In_ ITfContext *pContext) -{ - if (pContext == nullptr) - { - return 0; - } - - ITfRange *pAnchor = nullptr; - bool releaseAnchor = false; - - if (_IsComposing() && _pComposition != nullptr) - { - if (FAILED(_pComposition->GetRange(&pAnchor)) || pAnchor == nullptr) - { - return 0; - } - releaseAnchor = true; - } - else - { - TF_SELECTION tfSelection = {}; - ULONG fetched = 0; - const HRESULT hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched); - if (FAILED(hr) || fetched != 1 || tfSelection.range == nullptr) - { - return 0; - } - pAnchor = tfSelection.range; - releaseAnchor = true; - } - - ITfRange *pClone = nullptr; - WCHAR preceding = 0; - HRESULT hr = pAnchor->Clone(&pClone); - if (SUCCEEDED(hr) && pClone != nullptr) - { - hr = pClone->Collapse(ec, TF_ANCHOR_START); - if (SUCCEEDED(hr)) - { - LONG shifted = 0; - hr = SafeRangeShiftStart(pClone, ec, -1, &shifted); - if (SUCCEEDED(hr) && shifted == -1) - { - // Terminals and other shallow text stores accept the shift but - // expose no text, leaving preceding at 0. - WCHAR buffer[2] = {}; - ULONG fetched = 0; - hr = SafeRangeGetText(pClone, ec, 0, buffer, 1, &fetched); - if (SUCCEEDED(hr) && fetched == 1) - { - preceding = buffer[0]; - } - } - } - pClone->Release(); - } - - if (releaseAnchor && pAnchor != nullptr) - { - pAnchor->Release(); - } - return preceding; -} - -WCHAR CMetasequoiaIME::_GetPrecedingCharForSmartPunctuation(TfEditCookie ec, _In_ ITfContext *pContext) -{ - if (_smartPunctuationShadowValid) - { - return _smartPunctuationShadowChar; - } - return _GetPrecedingDocumentChar(ec, pContext); -} - -void CMetasequoiaIME::_ResetSmartPunctuationHistory() -{ - _smartPunctuationKey = 0; - _smartPunctuationPrecedingChar = 0; - _smartPunctuationCommittedAscii = false; - _smartPunctuationAsciiRejected = false; - _smartPunctuationCommitTick = 0; - _smartPunctuationFocusToken = 0; - _smartPunctuationForegroundWindow = nullptr; -} - -bool CMetasequoiaIME::_QueueRepeatedSmartPunctuationReplacement(WCHAR wch) -{ - // Backspace rejection means the ASCII form is already gone. Treating the - // next press as "replace the still-visible ASCII punct" would SendInput a - // Backspace into the preceding character instead. - if (!_smartPunctuationCommittedAscii || _smartPunctuationAsciiRejected || _smartPunctuationKey != wch || - _smartPunctuationCommitTick == 0 || _msgWndHandle == nullptr || _pCompositionProcessorEngine == nullptr || - _IsComposing() || _candidateMode != CANDIDATE_NONE || - !Global::SmartPunctuationEnabled.load(std::memory_order_relaxed) || - !Global::SmartPunctuationRepeatToChineseEnabled.load(std::memory_order_relaxed)) - { - return false; - } - - const ULONGLONG now = GetTickCount64(); - if (now - _smartPunctuationCommitTick > SMART_PUNCTUATION_REPEAT_INTERVAL_MS || - !_IsFocusSessionCurrent(_smartPunctuationFocusToken) || - GetForegroundWindow() != _smartPunctuationForegroundWindow) - { - return false; - } - - const WCHAR *chinese = _pCompositionProcessorEngine->GetPunctuation(wch); - if (chinese == nullptr || chinese[0] == L'\0' || chinese[1] != L'\0') - { - return false; - } - - _pendingSmartPunctuationReplacement = chinese[0]; - _pendingSmartPunctuationFocusToken = _smartPunctuationFocusToken; - _pendingSmartPunctuationForegroundWindow = _smartPunctuationForegroundWindow; - _pendingSmartPunctuationDeadline = _smartPunctuationCommitTick + SMART_PUNCTUATION_REPEAT_INTERVAL_MS; - - const uint64_t focusToken = _pendingSmartPunctuationFocusToken; - if (!PostMessage(_msgWndHandle, WM_ReplaceRepeatedSmartPunctuation, static_cast(focusToken & 0xFFFFFFFFULL), - static_cast((focusToken >> 32) & 0xFFFFFFFFULL))) - { - _pendingSmartPunctuationReplacement = 0; - _pendingSmartPunctuationFocusToken = 0; - _pendingSmartPunctuationForegroundWindow = nullptr; - _pendingSmartPunctuationDeadline = 0; - return false; - } - - _ResetSmartPunctuationHistory(); - return true; -} - -void CMetasequoiaIME::_InvalidateSmartPunctuationShadow() -{ - _smartPunctuationShadowChar = 0; - _smartPunctuationShadowValid = false; -} - -void CMetasequoiaIME::_UpdateSmartPunctuationShadow(UINT code, WCHAR wch, bool isEaten) -{ - switch (code) - { - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_MENU: - case VK_LMENU: - case VK_RMENU: - case VK_CAPITAL: - // Modifier presses edit nothing, so the shadow still describes the caret. - return; - case VK_BACK: - case VK_DELETE: - case VK_INSERT: - case VK_RETURN: - case VK_TAB: - case VK_ESCAPE: - case VK_LEFT: - case VK_RIGHT: - case VK_UP: - case VK_DOWN: - case VK_HOME: - case VK_END: - case VK_PRIOR: - case VK_NEXT: - _InvalidateSmartPunctuationShadow(); - return; - default: - break; - } - - if (CCompositionProcessorEngine::IsSmartAsciiPunctuationKey(wch)) - { - // _ResolveSmartPunctuation needs the current shadow to decide the form, - // and records whatever it commits once that decision is made. - return; - } - - if (isEaten || wch == 0 || std::iswprint(static_cast(wch)) == 0) - { - // Eaten keys feed the composition and reach the document as committed - // text, which even a proxy store exposes, so let the document answer. - _InvalidateSmartPunctuationShadow(); - return; - } - - _smartPunctuationShadowChar = wch; - _smartPunctuationShadowValid = true; -} - -void CMetasequoiaIME::_NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten) -{ - // The replacement message normally runs before another input event. If it - // does not, never let a later key leave the queued Backspace targeting an - // unrelated character. - if (_pendingSmartPunctuationReplacement != 0) - { - _pendingSmartPunctuationReplacement = 0; - _pendingSmartPunctuationFocusToken = 0; - _pendingSmartPunctuationForegroundWindow = nullptr; - _pendingSmartPunctuationDeadline = 0; - } - - _UpdateSmartPunctuationShadow(code, wch, isEaten); - - if (_smartPunctuationKey == 0) - { - return; - } - - switch (code) - { - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_MENU: - case VK_LMENU: - case VK_RMENU: - case VK_CAPITAL: - // ':' needs Shift; modifier presses are not edits. - return; - case VK_BACK: - // Only deleting the ASCII form says that form was unwanted. Deleting - // the Chinese punctuation that replaced it must not undo the rejection. - if (_smartPunctuationCommittedAscii) - { - _smartPunctuationAsciiRejected = true; - // ASCII punct is gone; disarm repeat-to-Chinese replacement so a - // quick retype takes the reject path instead of SendInput(VK_BACK). - _smartPunctuationCommitTick = 0; - _smartPunctuationFocusToken = 0; - _smartPunctuationForegroundWindow = nullptr; - // UpdateShadow already cleared the punctuation shadow. Restore the - // preceding character recorded at commit so a retype can still match - // the reject spot when the host text store cannot re-read it. - if (_smartPunctuationPrecedingChar != 0) - { - _smartPunctuationShadowChar = _smartPunctuationPrecedingChar; - _smartPunctuationShadowValid = true; - } - } - return; - case VK_DECIMAL: - // Numpad '.' bypasses smart punctuation entirely. - _ResetSmartPunctuationHistory(); - return; - default: - break; - } - - if (wch != _smartPunctuationKey) - { - _ResetSmartPunctuationHistory(); - } -} - -std::wstring CMetasequoiaIME::_ResolveSmartPunctuation(WCHAR wch, WCHAR precedingChar) -{ - if (_pCompositionProcessorEngine == nullptr) - { - return {}; - } - - const bool smartEnabled = Global::SmartPunctuationEnabled.load(std::memory_order_relaxed); - std::wstring resolved = _pCompositionProcessorEngine->ResolvePunctuation(wch, precedingChar); - if (!CCompositionProcessorEngine::IsSmartAsciiPunctuationKey(wch) || !smartEnabled) - { - _ResetSmartPunctuationHistory(); - return resolved; - } - - bool committedAscii = resolved.size() == 1 && resolved[0] == wch; - // The rejection is sticky for as long as this spot survives, so repeated - // delete/retype cycles keep producing Chinese punctuation. - const bool asciiRejected = _smartPunctuationAsciiRejected && _smartPunctuationKey == wch && - _smartPunctuationPrecedingChar == precedingChar; - if (asciiRejected && committedAscii) - { - const WCHAR *chinese = _pCompositionProcessorEngine->GetPunctuation(wch); - if (chinese != nullptr && *chinese != L'\0') - { - resolved.assign(chinese); - committedAscii = false; - } - } - - _smartPunctuationKey = wch; - _smartPunctuationPrecedingChar = precedingChar; - _smartPunctuationCommittedAscii = committedAscii; - _smartPunctuationAsciiRejected = asciiRejected; - if (committedAscii) - { - _smartPunctuationCommitTick = GetTickCount64(); - _smartPunctuationFocusToken = _CaptureFocusSessionToken(); - _smartPunctuationForegroundWindow = GetForegroundWindow(); - } - else - { - _smartPunctuationCommitTick = 0; - _smartPunctuationFocusToken = 0; - _smartPunctuationForegroundWindow = nullptr; - } - if (!resolved.empty()) - { - _smartPunctuationShadowChar = resolved.back(); - _smartPunctuationShadowValid = true; - } - return resolved; -} - -//+--------------------------------------------------------------------------- -// -// ITfCompositionSink::OnCompositionTerminated -// -// Callback for ITfCompositionSink. The system calls this method whenever -// someone other than this service ends a composition. -//---------------------------------------------------------------------------- - -STDAPI CMetasequoiaIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition) -{ - PerfTimer timer; - if (pComposition == nullptr || !_IsCompositionCurrent(pComposition)) - { - DebugTsfIssue47(L"host-terminated-stale-composition", FANY_IME_NO_REQUEST_ID, 0, L'\0', 0, 0, -1, - _IsComposing(), - _pCompositionProcessorEngine ? _pCompositionProcessorEngine->GetVirtualKeyLength() : 0, S_FALSE, - _CaptureCompositionEpoch()); - // A delayed termination callback for an older composition must never - // delete the current candidate presenter or release newer ownership. - return S_OK; - } - - DebugTsfIssue47(L"host-terminated-current-composition", FANY_IME_NO_REQUEST_ID, 0, L'\0', 0, 0, -1, TRUE, - _pCompositionProcessorEngine ? _pCompositionProcessorEngine->GetVirtualKeyLength() : 0, S_OK, - _CaptureCompositionEpoch()); - - // The callback already carries a write cookie and the host has already - // ended this exact composition. Detach ownership before making COM calls, - // so a re-entrant/stale callback cannot observe it as current or tear down - // a composition created later. - ITfComposition *terminatedComposition = pComposition; - terminatedComposition->AddRef(); - _pComposition->Release(); - _pComposition = nullptr; - _voiceCompositionActive = false; - - ITfContext *ownerContext = _pContext; - if (ownerContext) - { - ownerContext->AddRef(); - _pContext->Release(); - _pContext = nullptr; - } - - uint64_t nextEpoch = _compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1; - if (nextEpoch == 0) - { - _compositionEpoch.fetch_add(1, std::memory_order_acq_rel); - } - - // Detach and end the old candidate/session before the COM cleanup calls - // below can re-enter and create a presenter for a newer composition. - _DeleteCandidateList(FALSE, ownerContext); - if (Global::g_connected) - { - // EndCandidateUiSession is intentionally idempotent, but a presenter - // can exist before its UI session becomes active. Always send one - // exact routed clear so the Server cannot retain that composition. - SendHideCandidateWndEventToUIProcess(); - } - - // Do NOT SetText(empty) here. Cancel paths already wipe via - // _HandleCancel → _RemoveDummyCompositionForComposing. Wiping again after - // a normal commit/EndComposition can delete the just-committed text and - // destabilize fragile hosts (notably QQ). - if (ownerContext) - { - _ClearCompositionDisplayAttributes(ecWrite, ownerContext, terminatedComposition); - } - terminatedComposition->Release(); - - if (ownerContext) - { - ownerContext->Release(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// _IsComposing -// -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_IsComposing() -{ - return _pComposition != nullptr; -} - -//+--------------------------------------------------------------------------- -// -// _SetComposition -// -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_SetComposition(_In_ ITfComposition *pComposition) -{ - _pComposition = pComposition; - uint64_t nextEpoch = _compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1; - if (nextEpoch == 0) - { - _compositionEpoch.fetch_add(1, std::memory_order_acq_rel); - } -} - -//+--------------------------------------------------------------------------- -// -// _AddComposingAndChar -// -//---------------------------------------------------------------------------- - -HRESULT CMetasequoiaIME::_AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ CStringRange *pstrAddString) -{ - HRESULT hr = S_OK; - - if (_pComposition != nullptr) - { - PerfTimer fastUpdateTimer; - ITfRange *pRangeComposition = nullptr; - hr = _pComposition->GetRange(&pRangeComposition); - if (SUCCEEDED(hr) && pRangeComposition != nullptr) - { - PerfTimer setTextTimer; - hr = SafeRangeSetText(pRangeComposition, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); - double setTextElapsedMs = setTextTimer.ElapsedMs(); - if (SUCCEEDED(hr)) - { - PerfTimer displayAttrTimer; - _SetCompositionDisplayAttributesForRange(ec, pContext, pRangeComposition, _gaDisplayAttributeInput); - double displayAttrElapsedMs = displayAttrTimer.ElapsedMs(); - - PerfTimer selectionTimer; - TF_SELECTION sel; - pRangeComposition->Collapse(ec, TF_ANCHOR_END); - sel.range = pRangeComposition; - sel.style.ase = TF_AE_NONE; - sel.style.fInterimChar = FALSE; - pContext->SetSelection(ec, 1, &sel); - double selectionElapsedMs = selectionTimer.ElapsedMs(); - - pRangeComposition->Release(); - return hr; - } - pRangeComposition->Release(); - } - } - - ULONG fetched = 0; - TF_SELECTION tfSelection; - - if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0) - return S_FALSE; - - // - // make range start to selection - // - ITfRange *pAheadSelection = nullptr; - hr = pContext->GetStart(ec, &pAheadSelection); - if (SUCCEEDED(hr)) - { - hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START); - if (SUCCEEDED(hr)) - { - ITfRange *pRange = nullptr; - BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange); - - std::wstring strAddString(pstrAddString->Get(), pstrAddString->GetLength()); - - _SetInputString(ec, pContext, pRange, pstrAddString, exist_composing); - - if (pRange) - { - pRange->Release(); - } - } - } - - tfSelection.range->Release(); - - if (pAheadSelection) - { - pAheadSelection->Release(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// _AddCharAndFinalize -// -//---------------------------------------------------------------------------- - -HRESULT CMetasequoiaIME::_AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ CStringRange *pstrAddString) -{ - HRESULT hr = E_FAIL; - PerfTimer timer; - - if (_pComposition != nullptr) - { - PerfTimer directSetTimer; - hr = _SetCompositionTextAndSelection(ec, pContext, pstrAddString); - double directSetElapsedMs = directSetTimer.ElapsedMs(); - if (SUCCEEDED(hr)) - { - return hr; - } - } - - ULONG fetched = 0; - TF_SELECTION tfSelection; - - if ((hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) != S_OK || fetched != 1) - return hr; - - // We use SetText here instead of InsertTextAtSelection because we've already started a composition - // We don't want to the app to adjust the insertion point inside our composition - PerfTimer setTextTimer; - hr = SafeRangeSetText(tfSelection.range, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); - double setTextElapsedMs = setTextTimer.ElapsedMs(); - double setSelectionElapsedMs = 0; - if (hr == S_OK) - { - // Update the selection, we'll make it an insertion point just past - // the inserted text. - tfSelection.range->Collapse(ec, TF_ANCHOR_END); - PerfTimer setSelectionTimer; - pContext->SetSelection(ec, 1, &tfSelection); - setSelectionElapsedMs = setSelectionTimer.ElapsedMs(); - } - - tfSelection.range->Release(); - - return hr; -} - -HRESULT CMetasequoiaIME::_InsertTextToComposition(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ CStringRange *pstrAddString) -{ - PerfTimer timer; - if (_pComposition == nullptr) - { - return E_FAIL; - } - - ITfRange *pRangeComposition = nullptr; - PerfTimer getRangeTimer; - HRESULT hr = _pComposition->GetRange(&pRangeComposition); - double getRangeElapsedMs = getRangeTimer.ElapsedMs(); - if (FAILED(hr) || pRangeComposition == nullptr) - { - return FAILED(hr) ? hr : E_FAIL; - } - - PerfTimer setTextTimer; - hr = SafeRangeSetText(pRangeComposition, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); - double setTextElapsedMs = setTextTimer.ElapsedMs(); - double setSelectionElapsedMs = 0; - if (SUCCEEDED(hr)) - { - TF_SELECTION tfSelection; - pRangeComposition->Collapse(ec, TF_ANCHOR_END); - tfSelection.range = pRangeComposition; - tfSelection.style.ase = TF_AE_NONE; - tfSelection.style.fInterimChar = FALSE; - PerfTimer setSelectionTimer; - pContext->SetSelection(ec, 1, &tfSelection); - setSelectionElapsedMs = setSelectionTimer.ElapsedMs(); - } - - pRangeComposition->Release(); - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _SetCompositionTextAndSelection -// -//---------------------------------------------------------------------------- - -HRESULT CMetasequoiaIME::_SetCompositionTextAndSelection(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ CStringRange *pstrAddString) -{ - PerfTimer timer; - if (_pComposition == nullptr) - { - return E_FAIL; - } - - ITfRange *pRangeComposition = nullptr; - PerfTimer getRangeTimer; - HRESULT hr = _pComposition->GetRange(&pRangeComposition); - double getRangeElapsedMs = getRangeTimer.ElapsedMs(); - if (FAILED(hr) || pRangeComposition == nullptr) - { - return FAILED(hr) ? hr : E_FAIL; - } - - PerfTimer setTextTimer; - hr = SafeRangeSetText(pRangeComposition, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); - double setTextElapsedMs = setTextTimer.ElapsedMs(); - double setSelectionElapsedMs = 0; - if (SUCCEEDED(hr)) - { - TF_SELECTION tfSelection; - pRangeComposition->Collapse(ec, TF_ANCHOR_END); - tfSelection.range = pRangeComposition; - tfSelection.style.ase = TF_AE_NONE; - tfSelection.style.fInterimChar = FALSE; - PerfTimer setSelectionTimer; - pContext->SetSelection(ec, 1, &tfSelection); - setSelectionElapsedMs = setSelectionTimer.ElapsedMs(); - } - - pRangeComposition->Release(); - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _FindComposingRange -// -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, - _Outptr_result_maybenull_ ITfRange **ppRange) -{ - if (ppRange == nullptr) - { - return FALSE; - } - - *ppRange = nullptr; - - // find GUID_PROP_COMPOSING - ITfProperty *pPropComp = nullptr; - IEnumTfRanges *enumComp = nullptr; - - HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp); - if (FAILED(hr) || pPropComp == nullptr) - { - return FALSE; - } - - hr = pPropComp->EnumRanges(ec, &enumComp, pSelection); - if (FAILED(hr) || enumComp == nullptr) - { - pPropComp->Release(); - return FALSE; - } - - BOOL isCompExist = FALSE; - VARIANT var; - ULONG fetched = 0; - - while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1) - { - hr = pPropComp->GetValue(ec, *ppRange, &var); - if (hr == S_OK) - { - if (var.vt == VT_I4 && var.lVal != 0) - { - isCompExist = TRUE; - break; - } - } - (*ppRange)->Release(); - *ppRange = nullptr; - } - - pPropComp->Release(); - enumComp->Release(); - - return isCompExist; -} - -//+--------------------------------------------------------------------------- -// -// _SetInputString -// -//---------------------------------------------------------------------------- - -HRESULT CMetasequoiaIME::_SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, - _In_ CStringRange *pstrAddString, BOOL exist_composing) -{ - ITfRange *pRangeInsert = nullptr; - if (!exist_composing) - { - _InsertAtSelection(ec, pContext, pstrAddString, &pRangeInsert); - if (pRangeInsert == nullptr) - { - return S_OK; - } - else - { - // pRange = pRangeInsert; - - /* To make TsfPad work, we need to get range manually */ - _pComposition->GetRange(&pRange); - } - } - if (pRange != nullptr) - { - SafeRangeSetText(pRange, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); - } - - _SetCompositionLanguage(ec, pContext); - - _SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput); - - // update the selection, we'll make it an insertion point just past - // the inserted text. - ITfRange *pSelection = nullptr; - TF_SELECTION sel; - - if ((pRange != nullptr) && (pRange->Clone(&pSelection) == S_OK)) - { - pSelection->Collapse(ec, TF_ANCHOR_END); - - sel.range = pSelection; - sel.style.ase = TF_AE_NONE; - sel.style.fInterimChar = FALSE; - pContext->SetSelection(ec, 1, &sel); - pSelection->Release(); - } - - if (pRangeInsert) - { - pRangeInsert->Release(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// _InsertAtSelection -// -//---------------------------------------------------------------------------- - -HRESULT CMetasequoiaIME::_InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ CStringRange *pstrAddString, _Outptr_ ITfRange **ppCompRange) -{ - ITfRange *rangeInsert = nullptr; - ITfInsertAtSelection *pias = nullptr; - HRESULT hr = S_OK; - - if (ppCompRange == nullptr) - { - hr = E_INVALIDARG; - goto Exit; - } - - *ppCompRange = nullptr; - - hr = pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pias); - if (FAILED(hr)) - { - goto Exit; - } - - hr = pias->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, pstrAddString->Get(), (LONG)pstrAddString->GetLength(), - &rangeInsert); - - if (FAILED(hr) || rangeInsert == nullptr) - { - rangeInsert = nullptr; - pias->Release(); - goto Exit; - } - - *ppCompRange = rangeInsert; - pias->Release(); - hr = S_OK; - -Exit: - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _RemoveDummyCompositionForComposing -// -//---------------------------------------------------------------------------- - -HRESULT CMetasequoiaIME::_RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition) -{ - HRESULT hr = S_OK; - - ITfRange *pRange = nullptr; - - if (pComposition) - { - hr = pComposition->GetRange(&pRange); - if (SUCCEEDED(hr)) - { - hr = SafeRangeSetText(pRange, ec, 0, nullptr, 0); - pRange->Release(); - } - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _SetCompositionLanguage -// -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext) -{ - HRESULT hr = S_OK; - BOOL ret = TRUE; - - CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr; - pCompositionProcessorEngine = _pCompositionProcessorEngine; - - LANGID langidProfile = 0; - pCompositionProcessorEngine->GetLanguageProfile(&langidProfile); - - ITfRange *pRangeComposition = nullptr; - ITfProperty *pLanguageProperty = nullptr; - - // we need a range and the context it lives in - hr = _pComposition->GetRange(&pRangeComposition); - if (FAILED(hr)) - { - ret = FALSE; - goto Exit; - } - - // get our the language property - hr = pContext->GetProperty(GUID_PROP_LANGID, &pLanguageProperty); - if (FAILED(hr)) - { - ret = FALSE; - goto Exit; - } - - VARIANT var; - var.vt = VT_I4; // we're going to set DWORD - var.lVal = langidProfile; - - hr = pLanguageProperty->SetValue(ec, pRangeComposition, &var); - if (FAILED(hr)) - { - ret = FALSE; - goto Exit; - } - - pLanguageProperty->Release(); - pRangeComposition->Release(); - -Exit: - return ret; -} +#include "Private.h" +#include "Globals.h" +#include "MetasequoiaIME.h" +#include "CompositionProcessorEngine.h" +#include +#include +#include +#include +#include "FanyDefines.h" +#include "Ipc.h" +#include "../Utils/PerfTimer.h" + +namespace +{ +// Keep SEH helpers free of C++ objects with destructors (C2712). +HRESULT SafeRangeSetText(_In_ ITfRange *range, TfEditCookie ec, DWORD flags, _In_reads_opt_(len) const WCHAR *text, + LONG len) +{ + if (range == nullptr) + { + return E_INVALIDARG; + } + + __try + { + return range->SetText(ec, flags, text, len); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return E_FAIL; + } +} + +HRESULT SafeRangeGetText(_In_ ITfRange *range, TfEditCookie ec, DWORD flags, _Out_writes_(len) WCHAR *text, ULONG len, + _Out_ ULONG *fetched) +{ + if (range == nullptr || text == nullptr || fetched == nullptr || len == 0) + { + return E_INVALIDARG; + } + + __try + { + return range->GetText(ec, flags, text, len, fetched); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + *fetched = 0; + return E_FAIL; + } +} + +HRESULT SafeRangeShiftStart(_In_ ITfRange *range, TfEditCookie ec, LONG count, _Out_ LONG *shifted) +{ + if (range == nullptr || shifted == nullptr) + { + return E_INVALIDARG; + } + + __try + { + return range->ShiftStart(ec, count, shifted, nullptr); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + *shifted = 0; + return E_FAIL; + } +} +} // namespace + +WCHAR CMetasequoiaIME::_GetPrecedingDocumentChar(TfEditCookie ec, _In_ ITfContext *pContext) +{ + if (pContext == nullptr) + { + return 0; + } + + ITfRange *pAnchor = nullptr; + bool releaseAnchor = false; + + if (_IsComposing() && _pComposition != nullptr) + { + if (FAILED(_pComposition->GetRange(&pAnchor)) || pAnchor == nullptr) + { + return 0; + } + releaseAnchor = true; + } + else + { + TF_SELECTION tfSelection = {}; + ULONG fetched = 0; + const HRESULT hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched); + if (FAILED(hr) || fetched != 1 || tfSelection.range == nullptr) + { + return 0; + } + pAnchor = tfSelection.range; + releaseAnchor = true; + } + + ITfRange *pClone = nullptr; + WCHAR preceding = 0; + HRESULT hr = pAnchor->Clone(&pClone); + if (SUCCEEDED(hr) && pClone != nullptr) + { + hr = pClone->Collapse(ec, TF_ANCHOR_START); + if (SUCCEEDED(hr)) + { + LONG shifted = 0; + hr = SafeRangeShiftStart(pClone, ec, -1, &shifted); + if (SUCCEEDED(hr) && shifted == -1) + { + // Terminals and other shallow text stores accept the shift but + // expose no text, leaving preceding at 0. + WCHAR buffer[2] = {}; + ULONG fetched = 0; + hr = SafeRangeGetText(pClone, ec, 0, buffer, 1, &fetched); + if (SUCCEEDED(hr) && fetched == 1) + { + preceding = buffer[0]; + } + } + } + pClone->Release(); + } + + if (releaseAnchor && pAnchor != nullptr) + { + pAnchor->Release(); + } + return preceding; +} + +WCHAR CMetasequoiaIME::_GetPrecedingCharForSmartPunctuation(TfEditCookie ec, _In_ ITfContext *pContext) +{ + if (_smartPunctuationShadowValid) + { + return _smartPunctuationShadowChar; + } + return _GetPrecedingDocumentChar(ec, pContext); +} + +void CMetasequoiaIME::_ResetSmartPunctuationHistory() +{ + _smartPunctuationKey = 0; + _smartPunctuationPrecedingChar = 0; + _smartPunctuationCommittedAscii = false; + _smartPunctuationAsciiRejected = false; + _smartPunctuationCommitTick = 0; + _smartPunctuationFocusToken = 0; + _smartPunctuationForegroundWindow = nullptr; +} + +bool CMetasequoiaIME::_QueueRepeatedSmartPunctuationReplacement(WCHAR wch) +{ + // Backspace rejection means the ASCII form is already gone. Treating the + // next press as "replace the still-visible ASCII punct" would SendInput a + // Backspace into the preceding character instead. + if (!_smartPunctuationCommittedAscii || _smartPunctuationAsciiRejected || _smartPunctuationKey != wch || + _smartPunctuationCommitTick == 0 || _msgWndHandle == nullptr || _pCompositionProcessorEngine == nullptr || + _IsComposing() || _candidateMode != CANDIDATE_NONE || + !Global::SmartPunctuationEnabled.load(std::memory_order_relaxed) || + !Global::SmartPunctuationRepeatToChineseEnabled.load(std::memory_order_relaxed)) + { + return false; + } + + const ULONGLONG now = GetTickCount64(); + if (now - _smartPunctuationCommitTick > SMART_PUNCTUATION_REPEAT_INTERVAL_MS || + !_IsFocusSessionCurrent(_smartPunctuationFocusToken) || + GetForegroundWindow() != _smartPunctuationForegroundWindow) + { + return false; + } + + const WCHAR *chinese = _pCompositionProcessorEngine->GetPunctuation(wch); + if (chinese == nullptr || chinese[0] == L'\0' || chinese[1] != L'\0') + { + return false; + } + + _pendingSmartPunctuationReplacement = chinese[0]; + _pendingSmartPunctuationFocusToken = _smartPunctuationFocusToken; + _pendingSmartPunctuationForegroundWindow = _smartPunctuationForegroundWindow; + _pendingSmartPunctuationDeadline = _smartPunctuationCommitTick + SMART_PUNCTUATION_REPEAT_INTERVAL_MS; + + const uint64_t focusToken = _pendingSmartPunctuationFocusToken; + if (!PostMessage(_msgWndHandle, WM_ReplaceRepeatedSmartPunctuation, static_cast(focusToken & 0xFFFFFFFFULL), + static_cast((focusToken >> 32) & 0xFFFFFFFFULL))) + { + _pendingSmartPunctuationReplacement = 0; + _pendingSmartPunctuationFocusToken = 0; + _pendingSmartPunctuationForegroundWindow = nullptr; + _pendingSmartPunctuationDeadline = 0; + return false; + } + + _ResetSmartPunctuationHistory(); + return true; +} + +void CMetasequoiaIME::_InvalidateSmartPunctuationShadow() +{ + _smartPunctuationShadowChar = 0; + _smartPunctuationShadowValid = false; +} + +void CMetasequoiaIME::_UpdateSmartPunctuationShadow(UINT code, WCHAR wch, bool isEaten) +{ + switch (code) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_CAPITAL: + // Modifier presses edit nothing, so the shadow still describes the caret. + return; + case VK_BACK: + case VK_DELETE: + case VK_INSERT: + case VK_RETURN: + case VK_TAB: + case VK_ESCAPE: + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + case VK_HOME: + case VK_END: + case VK_PRIOR: + case VK_NEXT: + _InvalidateSmartPunctuationShadow(); + return; + default: + break; + } + + if (CCompositionProcessorEngine::IsSmartAsciiPunctuationKey(wch)) + { + // _ResolveSmartPunctuation needs the current shadow to decide the form, + // and records whatever it commits once that decision is made. + return; + } + + if (isEaten || wch == 0 || std::iswprint(static_cast(wch)) == 0) + { + // Eaten keys feed the composition and reach the document as committed + // text, which even a proxy store exposes, so let the document answer. + _InvalidateSmartPunctuationShadow(); + return; + } + + _smartPunctuationShadowChar = wch; + _smartPunctuationShadowValid = true; +} + +void CMetasequoiaIME::_NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten) +{ + // The replacement message normally runs before another input event. If it + // does not, never let a later key leave the queued Backspace targeting an + // unrelated character. + if (_pendingSmartPunctuationReplacement != 0) + { + _pendingSmartPunctuationReplacement = 0; + _pendingSmartPunctuationFocusToken = 0; + _pendingSmartPunctuationForegroundWindow = nullptr; + _pendingSmartPunctuationDeadline = 0; + } + + _UpdateSmartPunctuationShadow(code, wch, isEaten); + + if (_smartPunctuationKey == 0) + { + return; + } + + switch (code) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_CAPITAL: + // ':' needs Shift; modifier presses are not edits. + return; + case VK_BACK: + // Only deleting the ASCII form says that form was unwanted. Deleting + // the Chinese punctuation that replaced it must not undo the rejection. + if (_smartPunctuationCommittedAscii) + { + _smartPunctuationAsciiRejected = true; + // ASCII punct is gone; disarm repeat-to-Chinese replacement so a + // quick retype takes the reject path instead of SendInput(VK_BACK). + _smartPunctuationCommitTick = 0; + _smartPunctuationFocusToken = 0; + _smartPunctuationForegroundWindow = nullptr; + // UpdateShadow already cleared the punctuation shadow. Restore the + // preceding character recorded at commit so a retype can still match + // the reject spot when the host text store cannot re-read it. + if (_smartPunctuationPrecedingChar != 0) + { + _smartPunctuationShadowChar = _smartPunctuationPrecedingChar; + _smartPunctuationShadowValid = true; + } + } + return; + case VK_DECIMAL: + // Numpad '.' bypasses smart punctuation entirely. + _ResetSmartPunctuationHistory(); + return; + default: + break; + } + + if (wch != _smartPunctuationKey) + { + _ResetSmartPunctuationHistory(); + } +} + +std::wstring CMetasequoiaIME::_ResolveSmartPunctuation(WCHAR wch, WCHAR precedingChar) +{ + if (_pCompositionProcessorEngine == nullptr) + { + return {}; + } + + const bool smartEnabled = Global::SmartPunctuationEnabled.load(std::memory_order_relaxed); + std::wstring resolved = _pCompositionProcessorEngine->ResolvePunctuation(wch, precedingChar); + if (!CCompositionProcessorEngine::IsSmartAsciiPunctuationKey(wch) || !smartEnabled) + { + _ResetSmartPunctuationHistory(); + return resolved; + } + + bool committedAscii = resolved.size() == 1 && resolved[0] == wch; + // The rejection is sticky for as long as this spot survives, so repeated + // delete/retype cycles keep producing Chinese punctuation. + const bool asciiRejected = _smartPunctuationAsciiRejected && _smartPunctuationKey == wch && + _smartPunctuationPrecedingChar == precedingChar; + if (asciiRejected && committedAscii) + { + const WCHAR *chinese = _pCompositionProcessorEngine->GetPunctuation(wch); + if (chinese != nullptr && *chinese != L'\0') + { + resolved.assign(chinese); + committedAscii = false; + } + } + + _smartPunctuationKey = wch; + _smartPunctuationPrecedingChar = precedingChar; + _smartPunctuationCommittedAscii = committedAscii; + _smartPunctuationAsciiRejected = asciiRejected; + if (committedAscii) + { + _smartPunctuationCommitTick = GetTickCount64(); + _smartPunctuationFocusToken = _CaptureFocusSessionToken(); + _smartPunctuationForegroundWindow = GetForegroundWindow(); + } + else + { + _smartPunctuationCommitTick = 0; + _smartPunctuationFocusToken = 0; + _smartPunctuationForegroundWindow = nullptr; + } + if (!resolved.empty()) + { + _smartPunctuationShadowChar = resolved.back(); + _smartPunctuationShadowValid = true; + } + return resolved; +} + +//+--------------------------------------------------------------------------- +// +// ITfCompositionSink::OnCompositionTerminated +// +// Callback for ITfCompositionSink. The system calls this method whenever +// someone other than this service ends a composition. +//---------------------------------------------------------------------------- + +STDAPI CMetasequoiaIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition) +{ + PerfTimer timer; + if (pComposition == nullptr || !_IsCompositionCurrent(pComposition)) + { + DebugTsfIssue47(L"host-terminated-stale-composition", FANY_IME_NO_REQUEST_ID, 0, L'\0', 0, 0, -1, + _IsComposing(), + _pCompositionProcessorEngine ? _pCompositionProcessorEngine->GetVirtualKeyLength() : 0, S_FALSE, + _CaptureCompositionEpoch()); + // A delayed termination callback for an older composition must never + // delete the current candidate presenter or release newer ownership. + return S_OK; + } + + DebugTsfIssue47(L"host-terminated-current-composition", FANY_IME_NO_REQUEST_ID, 0, L'\0', 0, 0, -1, TRUE, + _pCompositionProcessorEngine ? _pCompositionProcessorEngine->GetVirtualKeyLength() : 0, S_OK, + _CaptureCompositionEpoch()); + + // The callback already carries a write cookie and the host has already + // ended this exact composition. Detach ownership before making COM calls, + // so a re-entrant/stale callback cannot observe it as current or tear down + // a composition created later. + ITfComposition *terminatedComposition = pComposition; + terminatedComposition->AddRef(); + _pComposition->Release(); + _pComposition = nullptr; + _voiceCompositionActive = false; + + ITfContext *ownerContext = _pContext; + if (ownerContext) + { + ownerContext->AddRef(); + _pContext->Release(); + _pContext = nullptr; + } + + uint64_t nextEpoch = _compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1; + if (nextEpoch == 0) + { + _compositionEpoch.fetch_add(1, std::memory_order_acq_rel); + } + + // Detach and end the old candidate/session before the COM cleanup calls + // below can re-enter and create a presenter for a newer composition. + _DeleteCandidateList(FALSE, ownerContext); + if (Global::g_connected) + { + // EndCandidateUiSession is intentionally idempotent, but a presenter + // can exist before its UI session becomes active. Always send one + // exact routed clear so the Server cannot retain that composition. + SendHideCandidateWndEventToUIProcess(); + } + + // Do NOT SetText(empty) here. Cancel paths already wipe via + // _HandleCancel → _RemoveDummyCompositionForComposing. Wiping again after + // a normal commit/EndComposition can delete the just-committed text and + // destabilize fragile hosts (notably QQ). + if (ownerContext) + { + _ClearCompositionDisplayAttributes(ecWrite, ownerContext, terminatedComposition); + } + terminatedComposition->Release(); + + if (ownerContext) + { + ownerContext->Release(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _IsComposing +// +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_IsComposing() +{ + return _pComposition != nullptr; +} + +//+--------------------------------------------------------------------------- +// +// _SetComposition +// +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_SetComposition(_In_ ITfComposition *pComposition) +{ + _pComposition = pComposition; + uint64_t nextEpoch = _compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1; + if (nextEpoch == 0) + { + _compositionEpoch.fetch_add(1, std::memory_order_acq_rel); + } +} + +//+--------------------------------------------------------------------------- +// +// _AddComposingAndChar +// +//---------------------------------------------------------------------------- + +HRESULT CMetasequoiaIME::_AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ CStringRange *pstrAddString) +{ + HRESULT hr = S_OK; + + if (_pComposition != nullptr) + { + PerfTimer fastUpdateTimer; + ITfRange *pRangeComposition = nullptr; + hr = _pComposition->GetRange(&pRangeComposition); + if (SUCCEEDED(hr) && pRangeComposition != nullptr) + { + PerfTimer setTextTimer; + hr = SafeRangeSetText(pRangeComposition, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + double setTextElapsedMs = setTextTimer.ElapsedMs(); + if (SUCCEEDED(hr)) + { + PerfTimer displayAttrTimer; + _SetCompositionDisplayAttributesForRange(ec, pContext, pRangeComposition, _gaDisplayAttributeInput); + double displayAttrElapsedMs = displayAttrTimer.ElapsedMs(); + + PerfTimer selectionTimer; + TF_SELECTION sel; + pRangeComposition->Collapse(ec, TF_ANCHOR_END); + sel.range = pRangeComposition; + sel.style.ase = TF_AE_NONE; + sel.style.fInterimChar = FALSE; + pContext->SetSelection(ec, 1, &sel); + double selectionElapsedMs = selectionTimer.ElapsedMs(); + + pRangeComposition->Release(); + return hr; + } + pRangeComposition->Release(); + } + } + + ULONG fetched = 0; + TF_SELECTION tfSelection; + + if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0) + return S_FALSE; + + // + // make range start to selection + // + ITfRange *pAheadSelection = nullptr; + hr = pContext->GetStart(ec, &pAheadSelection); + if (SUCCEEDED(hr)) + { + hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START); + if (SUCCEEDED(hr)) + { + ITfRange *pRange = nullptr; + BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange); + + std::wstring strAddString(pstrAddString->Get(), pstrAddString->GetLength()); + + _SetInputString(ec, pContext, pRange, pstrAddString, exist_composing); + + if (pRange) + { + pRange->Release(); + } + } + } + + tfSelection.range->Release(); + + if (pAheadSelection) + { + pAheadSelection->Release(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _AddCharAndFinalize +// +//---------------------------------------------------------------------------- + +HRESULT CMetasequoiaIME::_AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ CStringRange *pstrAddString) +{ + HRESULT hr = E_FAIL; + PerfTimer timer; + + if (_pComposition != nullptr) + { + PerfTimer directSetTimer; + hr = _SetCompositionTextAndSelection(ec, pContext, pstrAddString); + double directSetElapsedMs = directSetTimer.ElapsedMs(); + if (SUCCEEDED(hr)) + { + return hr; + } + } + + ULONG fetched = 0; + TF_SELECTION tfSelection; + + if ((hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) != S_OK || fetched != 1) + return hr; + + // We use SetText here instead of InsertTextAtSelection because we've already started a composition + // We don't want to the app to adjust the insertion point inside our composition + PerfTimer setTextTimer; + hr = SafeRangeSetText(tfSelection.range, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + double setTextElapsedMs = setTextTimer.ElapsedMs(); + double setSelectionElapsedMs = 0; + if (hr == S_OK) + { + // Update the selection, we'll make it an insertion point just past + // the inserted text. + tfSelection.range->Collapse(ec, TF_ANCHOR_END); + PerfTimer setSelectionTimer; + pContext->SetSelection(ec, 1, &tfSelection); + setSelectionElapsedMs = setSelectionTimer.ElapsedMs(); + } + + tfSelection.range->Release(); + + return hr; +} + +HRESULT CMetasequoiaIME::_InsertTextToComposition(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ CStringRange *pstrAddString) +{ + PerfTimer timer; + if (_pComposition == nullptr) + { + return E_FAIL; + } + + ITfRange *pRangeComposition = nullptr; + PerfTimer getRangeTimer; + HRESULT hr = _pComposition->GetRange(&pRangeComposition); + double getRangeElapsedMs = getRangeTimer.ElapsedMs(); + if (FAILED(hr) || pRangeComposition == nullptr) + { + return FAILED(hr) ? hr : E_FAIL; + } + + PerfTimer setTextTimer; + hr = SafeRangeSetText(pRangeComposition, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + double setTextElapsedMs = setTextTimer.ElapsedMs(); + double setSelectionElapsedMs = 0; + if (SUCCEEDED(hr)) + { + TF_SELECTION tfSelection; + pRangeComposition->Collapse(ec, TF_ANCHOR_END); + tfSelection.range = pRangeComposition; + tfSelection.style.ase = TF_AE_NONE; + tfSelection.style.fInterimChar = FALSE; + PerfTimer setSelectionTimer; + pContext->SetSelection(ec, 1, &tfSelection); + setSelectionElapsedMs = setSelectionTimer.ElapsedMs(); + } + + pRangeComposition->Release(); + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _SetCompositionTextAndSelection +// +//---------------------------------------------------------------------------- + +HRESULT CMetasequoiaIME::_SetCompositionTextAndSelection(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ CStringRange *pstrAddString) +{ + PerfTimer timer; + if (_pComposition == nullptr) + { + return E_FAIL; + } + + ITfRange *pRangeComposition = nullptr; + PerfTimer getRangeTimer; + HRESULT hr = _pComposition->GetRange(&pRangeComposition); + double getRangeElapsedMs = getRangeTimer.ElapsedMs(); + if (FAILED(hr) || pRangeComposition == nullptr) + { + return FAILED(hr) ? hr : E_FAIL; + } + + PerfTimer setTextTimer; + hr = SafeRangeSetText(pRangeComposition, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + double setTextElapsedMs = setTextTimer.ElapsedMs(); + double setSelectionElapsedMs = 0; + if (SUCCEEDED(hr)) + { + TF_SELECTION tfSelection; + pRangeComposition->Collapse(ec, TF_ANCHOR_END); + tfSelection.range = pRangeComposition; + tfSelection.style.ase = TF_AE_NONE; + tfSelection.style.fInterimChar = FALSE; + PerfTimer setSelectionTimer; + pContext->SetSelection(ec, 1, &tfSelection); + setSelectionElapsedMs = setSelectionTimer.ElapsedMs(); + } + + pRangeComposition->Release(); + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _FindComposingRange +// +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, + _Outptr_result_maybenull_ ITfRange **ppRange) +{ + if (ppRange == nullptr) + { + return FALSE; + } + + *ppRange = nullptr; + + // find GUID_PROP_COMPOSING + ITfProperty *pPropComp = nullptr; + IEnumTfRanges *enumComp = nullptr; + + HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp); + if (FAILED(hr) || pPropComp == nullptr) + { + return FALSE; + } + + hr = pPropComp->EnumRanges(ec, &enumComp, pSelection); + if (FAILED(hr) || enumComp == nullptr) + { + pPropComp->Release(); + return FALSE; + } + + BOOL isCompExist = FALSE; + VARIANT var; + ULONG fetched = 0; + + while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1) + { + hr = pPropComp->GetValue(ec, *ppRange, &var); + if (hr == S_OK) + { + if (var.vt == VT_I4 && var.lVal != 0) + { + isCompExist = TRUE; + break; + } + } + (*ppRange)->Release(); + *ppRange = nullptr; + } + + pPropComp->Release(); + enumComp->Release(); + + return isCompExist; +} + +//+--------------------------------------------------------------------------- +// +// _SetInputString +// +//---------------------------------------------------------------------------- + +HRESULT CMetasequoiaIME::_SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, + _In_ CStringRange *pstrAddString, BOOL exist_composing) +{ + ITfRange *pRangeInsert = nullptr; + if (!exist_composing) + { + _InsertAtSelection(ec, pContext, pstrAddString, &pRangeInsert); + if (pRangeInsert == nullptr) + { + return S_OK; + } + else + { + // pRange = pRangeInsert; + + /* To make TsfPad work, we need to get range manually */ + _pComposition->GetRange(&pRange); + } + } + if (pRange != nullptr) + { + SafeRangeSetText(pRange, ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + } + + _SetCompositionLanguage(ec, pContext); + + _SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput); + + // update the selection, we'll make it an insertion point just past + // the inserted text. + ITfRange *pSelection = nullptr; + TF_SELECTION sel; + + if ((pRange != nullptr) && (pRange->Clone(&pSelection) == S_OK)) + { + pSelection->Collapse(ec, TF_ANCHOR_END); + + sel.range = pSelection; + sel.style.ase = TF_AE_NONE; + sel.style.fInterimChar = FALSE; + pContext->SetSelection(ec, 1, &sel); + pSelection->Release(); + } + + if (pRangeInsert) + { + pRangeInsert->Release(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InsertAtSelection +// +//---------------------------------------------------------------------------- + +HRESULT CMetasequoiaIME::_InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ CStringRange *pstrAddString, _Outptr_ ITfRange **ppCompRange) +{ + ITfRange *rangeInsert = nullptr; + ITfInsertAtSelection *pias = nullptr; + HRESULT hr = S_OK; + + if (ppCompRange == nullptr) + { + hr = E_INVALIDARG; + goto Exit; + } + + *ppCompRange = nullptr; + + hr = pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pias); + if (FAILED(hr)) + { + goto Exit; + } + + hr = pias->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, pstrAddString->Get(), (LONG)pstrAddString->GetLength(), + &rangeInsert); + + if (FAILED(hr) || rangeInsert == nullptr) + { + rangeInsert = nullptr; + pias->Release(); + goto Exit; + } + + *ppCompRange = rangeInsert; + pias->Release(); + hr = S_OK; + +Exit: + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _RemoveDummyCompositionForComposing +// +//---------------------------------------------------------------------------- + +HRESULT CMetasequoiaIME::_RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition) +{ + HRESULT hr = S_OK; + + ITfRange *pRange = nullptr; + + if (pComposition) + { + hr = pComposition->GetRange(&pRange); + if (SUCCEEDED(hr)) + { + hr = SafeRangeSetText(pRange, ec, 0, nullptr, 0); + pRange->Release(); + } + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _SetCompositionLanguage +// +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hr = S_OK; + BOOL ret = TRUE; + + CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + LANGID langidProfile = 0; + pCompositionProcessorEngine->GetLanguageProfile(&langidProfile); + + ITfRange *pRangeComposition = nullptr; + ITfProperty *pLanguageProperty = nullptr; + + // we need a range and the context it lives in + hr = _pComposition->GetRange(&pRangeComposition); + if (FAILED(hr)) + { + ret = FALSE; + goto Exit; + } + + // get our the language property + hr = pContext->GetProperty(GUID_PROP_LANGID, &pLanguageProperty); + if (FAILED(hr)) + { + ret = FALSE; + goto Exit; + } + + VARIANT var; + var.vt = VT_I4; // we're going to set DWORD + var.lVal = langidProfile; + + hr = pLanguageProperty->SetValue(ec, pRangeComposition, &var); + if (FAILED(hr)) + { + ret = FALSE; + goto Exit; + } + + pLanguageProperty->Release(); + pRangeComposition->Release(); + +Exit: + return ret; +} diff --git a/windows/src/Composition/CompositionProcessorEngine.h b/windows/src/Composition/CompositionProcessorEngine.h index f4f4bd0e..d0db4290 100644 --- a/windows/src/Composition/CompositionProcessorEngine.h +++ b/windows/src/Composition/CompositionProcessorEngine.h @@ -1,290 +1,290 @@ -#pragma once - -#include "MetasequoiaIME.h" -#include "sal.h" -#include "KeyHandlerEditSession.h" -#include "MetasequoiaIMEBaseStructure.h" -#include "Compartment.h" -#include "define.h" - -class CCompositionProcessorEngine -{ - friend class CMetasequoiaIME; - - public: - enum class PreservedKeyAction - { - None, - ToggleImeMode, - ToggleDoubleSingleByteMode, - TogglePunctuationMode - }; - - explicit CCompositionProcessorEngine(_In_ CMetasequoiaIME *pTextService); - ~CCompositionProcessorEngine(void); - - BOOL SetupLanguageProfile(LANGID langid, REFGUID guidLanguageProfile, _In_ ITfThreadMgr *pThreadMgr, - TfClientId tfClientId, BOOL isSecureMode, BOOL isComLessMode); - - // Get language profile. - GUID GetLanguageProfile(LANGID *plangid) - { - *plangid = _langid; - return _guidProfile; - } - // Get locale - LCID GetLocale() - { - return MAKELCID(_langid, SORT_DEFAULT); - } - - BOOL IsVirtualKeyNeed(UINT uCode, _In_reads_(1) WCHAR *pwch, BOOL fComposing, CANDIDATE_MODE candidateMode, - BOOL hasCandidateWithWildcard, _Out_opt_ _KEYSTROKE_STATE *pKeyState); - BOOL IsVirtualKeyNeedForFreshComposition(UINT uCode, _In_reads_(1) WCHAR *pwch, - _Out_opt_ _KEYSTROKE_STATE *pKeyState); - - BOOL AddVirtualKey(WCHAR wch); - void RemoveVirtualKey(DWORD_PTR dwIndex); - BOOL RemoveVirtualKeyBeforeCaret(); - BOOL RemoveVirtualKeyAtCaret(); - void PurgeVirtualKey(); - BOOL MoveCaret(int offset); - DWORD_PTR GetCaretPosition() const - { - return _caretPosition; - } - void SetRenderedPreedit(std::wstring preedit, size_t prefixLength); - DWORD_PTR GetRenderedCaretPosition() const; - - DWORD_PTR GetVirtualKeyLength() - { - return _keystrokeBuffer.GetLength(); - } - CStringRange &GetKeystrokeBuffer() - { - return _keystrokeBuffer; - }; - WCHAR GetVirtualKey(DWORD_PTR dwIndex); - // Shift+U unicode input: composition buffer starts with 'U'. - BOOL IsUnicodeModeComposition() const; - - void GetReadingStrings( // - _Inout_ CMetasequoiaImeArray *pReadingStrings, // - _Out_ BOOL *pIsWildcardIncluded // - ); - void GetCandidateList( // - _Inout_ CMetasequoiaImeArray *pCandidateList, // - BOOL isIncrementalWordSearch, BOOL isWildcardSearch // - ); - - // Preserved key handler - BOOL IsPreservedKeyEligible(REFGUID rguid); - PreservedKeyAction GetPreservedKeyAction(REFGUID rguid) const; - void OnPreservedKey(ITfContext *pContext, REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr, - TfClientId tfClientId, BOOL *pNeedToggleIMEMode, BOOL isPrevalidated = FALSE, - BOOL notifyServer = TRUE); - - // Toggle IME Mode - void ToggleIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - void SetIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL bOpen); - BOOL CCompositionProcessorEngine::GetIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - // Apply CN/EN compartment change deferred until after composition commit. - // Closing KEYBOARD_OPENCLOSE before EndComposition makes CUAS/Win32 EDIT - // finalize the same preedit twice (Chrome/TSF-only hosts are unaffected). - void ApplyPendingImeModeAfterCompositionCommit(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - void SetPunctuationMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL bOpen); - BOOL GetPunctuationMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - void SetDoubleSingleByteMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL bOpen); - BOOL GetDoubleSingleByteMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - - // Punctuation - BOOL IsPunctuation(WCHAR wch); - const WCHAR *GetPunctuation(WCHAR wch); - // Smart punctuation (rime digit_separators-style): after ASCII letters/digits, - // keep ',' '.' ':' as ASCII instead of mapping to Chinese punctuation. - static BOOL IsSmartAsciiPunctuationKey(WCHAR wch); - std::wstring ResolvePunctuation(WCHAR wch, WCHAR precedingChar); - - BOOL IsDoubleSingleByte(WCHAR wch); - BOOL IsWildcard() - { - return _isWildcard; - } - BOOL IsDisableWildcardAtFirst() - { - return _isDisableWildcardAtFirst; - } - BOOL IsWildcardChar(WCHAR wch) - { - return ((IsWildcardOneChar(wch) || IsWildcardAllChar(wch)) ? TRUE : FALSE); - } - BOOL IsWildcardOneChar(WCHAR wch) - { - return (wch == L'?' ? TRUE : FALSE); - } - BOOL IsWildcardAllChar(WCHAR wch) - { - return (wch == L'*' ? TRUE : FALSE); - } - BOOL IsKeystrokeSort() - { - return _isKeystrokeSort; - } - - // Language bar control - void SetLanguageBarStatus(DWORD status, BOOL isSet); - - void ConversionModeCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr); - - void ShowAllLanguageBarIcons(); - void HideAllLanguageBarIcons(); - void RefreshLanguageBarIcons(); - - inline CCandidateRange *GetCandidateListIndexRange() - { - return &_candidateListIndexRange; - } - inline UINT GetCandidateListPhraseModifier() - { - return _candidateListPhraseModifier; - } - inline UINT GetCandidateWindowWidth() - { - return _candidateWndWidth; - } - - private: - void InitKeyStrokeTable(); - BOOL InitLanguageBar(_In_ CLangBarItemButton *pLanguageBar, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, - REFGUID guidCompartment); - - struct _KEYSTROKE; - BOOL IsVirtualKeyKeystrokeComposition(UINT uCode, _Out_opt_ _KEYSTROKE_STATE *pKeyState, - KEYSTROKE_FUNCTION function); - BOOL IsVirtualKeyKeystrokeCandidate(UINT uCode, _In_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode, - _Out_ BOOL *pfRetCode, _In_ CMetasequoiaImeArray<_KEYSTROKE> *pKeystrokeMetric); - BOOL IsKeystrokeRange(UINT uCode, _Out_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode); - - void SetupKeystroke(); - void SetupPreserved(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - void SetupConfiguration(); - void SetupLanguageBar(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode); - void SetKeystrokeTable(_Inout_ CMetasequoiaImeArray<_KEYSTROKE> *pKeystroke); - void SetupPunctuationPair(); - void CreateLanguageBarButton(DWORD dwEnable, GUID guidLangBar, _In_z_ LPCWSTR pwszDescriptionValue, - _In_z_ LPCWSTR pwszTooltipValue, DWORD dwOnIconIndex, DWORD dwOffIconIndex, - _Outptr_result_maybenull_ CLangBarItemButton **ppLangBarItemButton, BOOL isSecureMode); - void SetInitialCandidateListRange(); - void SetDefaultCandidateTextFont(); - void InitializeMetasequoiaIMECompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - - class XPreservedKey; - void SetPreservedKey(const CLSID clsid, TF_PRESERVEDKEY &tfPreservedKey, _In_z_ LPCWSTR pwszDescription, - _Out_ XPreservedKey *pXPreservedKey); - BOOL InitPreservedKey(_In_ XPreservedKey *pXPreservedKey, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); - BOOL CheckShiftKeyOnly(_In_ CMetasequoiaImeArray *pTSFPreservedKeyTable); - - static HRESULT CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); - void PrivateCompartmentsUpdated(_In_ ITfThreadMgr *pThreadMgr); - void KeyboardOpenCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr); - HRESULT SetKeyboardOpenCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isOpen); - void SyncPunctuationWithImeMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isOpen); - void CommitCompositionOnExternalKeyboardClose(); - void ReleaseConfiguredImeModeDefense(); - - private: - struct _KEYSTROKE - { - UINT VirtualKey; - UINT Modifiers; - KEYSTROKE_FUNCTION Function; - - _KEYSTROKE() - { - VirtualKey = 0; - Modifiers = 0; - Function = FUNCTION_NONE; - } - }; - _KEYSTROKE _keystrokeTable[26]; - - CStringRange _keystrokeBuffer; - DWORD_PTR _caretPosition = 0; - std::wstring _renderedPreedit; - size_t _renderedPreeditPrefixLength = 0; - - BOOL _hasWildcardIncludedInKeystrokeBuffer; - - LANGID _langid; - GUID _guidProfile; - TfClientId _tfClientId; - - CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeComposition; - CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeCandidate; - CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeCandidateWildcard; - CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeCandidateSymbol; - CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeSymbol; - - // Preserved key data - class XPreservedKey - { - public: - XPreservedKey(); - ~XPreservedKey(); - BOOL UninitPreservedKey(_In_ ITfThreadMgr *pThreadMgr); - - public: - CMetasequoiaImeArray TSFPreservedKeyTable; - GUID Guid; - LPCWSTR Description; - }; - - XPreservedKey _PreservedKey_IMEMode; - XPreservedKey _PreservedKey_IMEMode02; - XPreservedKey _PreservedKey_IMEMode03; - XPreservedKey _PreservedKey_EnglishInputMode; - XPreservedKey _PreservedKey_DoubleSingleByte; - XPreservedKey _PreservedKey_Punctuation; - - // Punctuation data - CMetasequoiaImeArray _PunctuationPair; - CMetasequoiaImeArray _PunctuationNestPair; - - // Language bar data - CLangBarItemButton *_pLanguageBar_IMEMode; - CLangBarItemButton *_pLanguageBar_DoubleSingleByte; - CLangBarItemButton *_pLanguageBar_Punctuation; - - // Compartment - CCompartment *_pCompartmentConversion; - CCompartmentEventSink *_pCompartmentConversionEventSink; - CCompartmentEventSink *_pCompartmentKeyboardOpenEventSink; - CCompartmentEventSink *_pCompartmentDoubleSingleByteEventSink; - CCompartmentEventSink *_pCompartmentPunctuationEventSink; - ITfThreadMgr *_pOwnerThreadMgr; - HWND _ownerMsgWndHandle; - CMetasequoiaIME *_pTextService; - BOOL _keyboardOpen; - BOOL _keyboardOpenKnown; - BOOL _suppressKeyboardCloseCommit; - // After Activate applies input.default_ime_mode, Chromium (and Electron) - // often rewrites TF_CONVERSIONMODE_* (NATIVE / SYMBOL / FULLSHAPE) while - // focus is still on the non-editable shell. ConversionModeCompartmentUpdated - // would then mirror those bits into OPENCLOSE and the punctuation / - // fullwidth compartments. Defend by reasserting private state until the - // user explicitly chooses a mode (Shift / langbar / FTB / preserved key). - BOOL _defendConfiguredImeMode; - BOOL _hasPendingImeModeAfterCompositionCommit; - BOOL _pendingImeModeAfterCompositionCommit; - - // Configuration data - BOOL _isWildcard : 1; - BOOL _isDisableWildcardAtFirst : 1; - BOOL _isKeystrokeSort : 1; - BOOL _isComLessMode : 1; - CCandidateRange _candidateListIndexRange; - UINT _candidateListPhraseModifier; - UINT _candidateWndWidth; - - static const int OUT_OF_FILE_INDEX = -1; -}; +#pragma once + +#include "MetasequoiaIME.h" +#include "sal.h" +#include "KeyHandlerEditSession.h" +#include "MetasequoiaIMEBaseStructure.h" +#include "Compartment.h" +#include "define.h" + +class CCompositionProcessorEngine +{ + friend class CMetasequoiaIME; + + public: + enum class PreservedKeyAction + { + None, + ToggleImeMode, + ToggleDoubleSingleByteMode, + TogglePunctuationMode + }; + + explicit CCompositionProcessorEngine(_In_ CMetasequoiaIME *pTextService); + ~CCompositionProcessorEngine(void); + + BOOL SetupLanguageProfile(LANGID langid, REFGUID guidLanguageProfile, _In_ ITfThreadMgr *pThreadMgr, + TfClientId tfClientId, BOOL isSecureMode, BOOL isComLessMode); + + // Get language profile. + GUID GetLanguageProfile(LANGID *plangid) + { + *plangid = _langid; + return _guidProfile; + } + // Get locale + LCID GetLocale() + { + return MAKELCID(_langid, SORT_DEFAULT); + } + + BOOL IsVirtualKeyNeed(UINT uCode, _In_reads_(1) WCHAR *pwch, BOOL fComposing, CANDIDATE_MODE candidateMode, + BOOL hasCandidateWithWildcard, _Out_opt_ _KEYSTROKE_STATE *pKeyState); + BOOL IsVirtualKeyNeedForFreshComposition(UINT uCode, _In_reads_(1) WCHAR *pwch, + _Out_opt_ _KEYSTROKE_STATE *pKeyState); + + BOOL AddVirtualKey(WCHAR wch); + void RemoveVirtualKey(DWORD_PTR dwIndex); + BOOL RemoveVirtualKeyBeforeCaret(); + BOOL RemoveVirtualKeyAtCaret(); + void PurgeVirtualKey(); + BOOL MoveCaret(int offset); + DWORD_PTR GetCaretPosition() const + { + return _caretPosition; + } + void SetRenderedPreedit(std::wstring preedit, size_t prefixLength); + DWORD_PTR GetRenderedCaretPosition() const; + + DWORD_PTR GetVirtualKeyLength() + { + return _keystrokeBuffer.GetLength(); + } + CStringRange &GetKeystrokeBuffer() + { + return _keystrokeBuffer; + }; + WCHAR GetVirtualKey(DWORD_PTR dwIndex); + // Shift+U unicode input: composition buffer starts with 'U'. + BOOL IsUnicodeModeComposition() const; + + void GetReadingStrings( // + _Inout_ CMetasequoiaImeArray *pReadingStrings, // + _Out_ BOOL *pIsWildcardIncluded // + ); + void GetCandidateList( // + _Inout_ CMetasequoiaImeArray *pCandidateList, // + BOOL isIncrementalWordSearch, BOOL isWildcardSearch // + ); + + // Preserved key handler + BOOL IsPreservedKeyEligible(REFGUID rguid); + PreservedKeyAction GetPreservedKeyAction(REFGUID rguid) const; + void OnPreservedKey(ITfContext *pContext, REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr, + TfClientId tfClientId, BOOL *pNeedToggleIMEMode, BOOL isPrevalidated = FALSE, + BOOL notifyServer = TRUE); + + // Toggle IME Mode + void ToggleIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + void SetIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL bOpen); + BOOL CCompositionProcessorEngine::GetIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + // Apply CN/EN compartment change deferred until after composition commit. + // Closing KEYBOARD_OPENCLOSE before EndComposition makes CUAS/Win32 EDIT + // finalize the same preedit twice (Chrome/TSF-only hosts are unaffected). + void ApplyPendingImeModeAfterCompositionCommit(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + void SetPunctuationMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL bOpen); + BOOL GetPunctuationMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + void SetDoubleSingleByteMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL bOpen); + BOOL GetDoubleSingleByteMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + + // Punctuation + BOOL IsPunctuation(WCHAR wch); + const WCHAR *GetPunctuation(WCHAR wch); + // Smart punctuation (rime digit_separators-style): after ASCII letters/digits, + // keep ',' '.' ':' as ASCII instead of mapping to Chinese punctuation. + static BOOL IsSmartAsciiPunctuationKey(WCHAR wch); + std::wstring ResolvePunctuation(WCHAR wch, WCHAR precedingChar); + + BOOL IsDoubleSingleByte(WCHAR wch); + BOOL IsWildcard() + { + return _isWildcard; + } + BOOL IsDisableWildcardAtFirst() + { + return _isDisableWildcardAtFirst; + } + BOOL IsWildcardChar(WCHAR wch) + { + return ((IsWildcardOneChar(wch) || IsWildcardAllChar(wch)) ? TRUE : FALSE); + } + BOOL IsWildcardOneChar(WCHAR wch) + { + return (wch == L'?' ? TRUE : FALSE); + } + BOOL IsWildcardAllChar(WCHAR wch) + { + return (wch == L'*' ? TRUE : FALSE); + } + BOOL IsKeystrokeSort() + { + return _isKeystrokeSort; + } + + // Language bar control + void SetLanguageBarStatus(DWORD status, BOOL isSet); + + void ConversionModeCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr); + + void ShowAllLanguageBarIcons(); + void HideAllLanguageBarIcons(); + void RefreshLanguageBarIcons(); + + inline CCandidateRange *GetCandidateListIndexRange() + { + return &_candidateListIndexRange; + } + inline UINT GetCandidateListPhraseModifier() + { + return _candidateListPhraseModifier; + } + inline UINT GetCandidateWindowWidth() + { + return _candidateWndWidth; + } + + private: + void InitKeyStrokeTable(); + BOOL InitLanguageBar(_In_ CLangBarItemButton *pLanguageBar, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, + REFGUID guidCompartment); + + struct _KEYSTROKE; + BOOL IsVirtualKeyKeystrokeComposition(UINT uCode, _Out_opt_ _KEYSTROKE_STATE *pKeyState, + KEYSTROKE_FUNCTION function); + BOOL IsVirtualKeyKeystrokeCandidate(UINT uCode, _In_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode, + _Out_ BOOL *pfRetCode, _In_ CMetasequoiaImeArray<_KEYSTROKE> *pKeystrokeMetric); + BOOL IsKeystrokeRange(UINT uCode, _Out_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode); + + void SetupKeystroke(); + void SetupPreserved(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + void SetupConfiguration(); + void SetupLanguageBar(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode); + void SetKeystrokeTable(_Inout_ CMetasequoiaImeArray<_KEYSTROKE> *pKeystroke); + void SetupPunctuationPair(); + void CreateLanguageBarButton(DWORD dwEnable, GUID guidLangBar, _In_z_ LPCWSTR pwszDescriptionValue, + _In_z_ LPCWSTR pwszTooltipValue, DWORD dwOnIconIndex, DWORD dwOffIconIndex, + _Outptr_result_maybenull_ CLangBarItemButton **ppLangBarItemButton, BOOL isSecureMode); + void SetInitialCandidateListRange(); + void SetDefaultCandidateTextFont(); + void InitializeMetasequoiaIMECompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + + class XPreservedKey; + void SetPreservedKey(const CLSID clsid, TF_PRESERVEDKEY &tfPreservedKey, _In_z_ LPCWSTR pwszDescription, + _Out_ XPreservedKey *pXPreservedKey); + BOOL InitPreservedKey(_In_ XPreservedKey *pXPreservedKey, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + BOOL CheckShiftKeyOnly(_In_ CMetasequoiaImeArray *pTSFPreservedKeyTable); + + static HRESULT CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); + void PrivateCompartmentsUpdated(_In_ ITfThreadMgr *pThreadMgr); + void KeyboardOpenCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr); + HRESULT SetKeyboardOpenCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isOpen); + void SyncPunctuationWithImeMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isOpen); + void CommitCompositionOnExternalKeyboardClose(); + void ReleaseConfiguredImeModeDefense(); + + private: + struct _KEYSTROKE + { + UINT VirtualKey; + UINT Modifiers; + KEYSTROKE_FUNCTION Function; + + _KEYSTROKE() + { + VirtualKey = 0; + Modifiers = 0; + Function = FUNCTION_NONE; + } + }; + _KEYSTROKE _keystrokeTable[26]; + + CStringRange _keystrokeBuffer; + DWORD_PTR _caretPosition = 0; + std::wstring _renderedPreedit; + size_t _renderedPreeditPrefixLength = 0; + + BOOL _hasWildcardIncludedInKeystrokeBuffer; + + LANGID _langid; + GUID _guidProfile; + TfClientId _tfClientId; + + CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeComposition; + CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeCandidate; + CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeCandidateWildcard; + CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeCandidateSymbol; + CMetasequoiaImeArray<_KEYSTROKE> _KeystrokeSymbol; + + // Preserved key data + class XPreservedKey + { + public: + XPreservedKey(); + ~XPreservedKey(); + BOOL UninitPreservedKey(_In_ ITfThreadMgr *pThreadMgr); + + public: + CMetasequoiaImeArray TSFPreservedKeyTable; + GUID Guid; + LPCWSTR Description; + }; + + XPreservedKey _PreservedKey_IMEMode; + XPreservedKey _PreservedKey_IMEMode02; + XPreservedKey _PreservedKey_IMEMode03; + XPreservedKey _PreservedKey_EnglishInputMode; + XPreservedKey _PreservedKey_DoubleSingleByte; + XPreservedKey _PreservedKey_Punctuation; + + // Punctuation data + CMetasequoiaImeArray _PunctuationPair; + CMetasequoiaImeArray _PunctuationNestPair; + + // Language bar data + CLangBarItemButton *_pLanguageBar_IMEMode; + CLangBarItemButton *_pLanguageBar_DoubleSingleByte; + CLangBarItemButton *_pLanguageBar_Punctuation; + + // Compartment + CCompartment *_pCompartmentConversion; + CCompartmentEventSink *_pCompartmentConversionEventSink; + CCompartmentEventSink *_pCompartmentKeyboardOpenEventSink; + CCompartmentEventSink *_pCompartmentDoubleSingleByteEventSink; + CCompartmentEventSink *_pCompartmentPunctuationEventSink; + ITfThreadMgr *_pOwnerThreadMgr; + HWND _ownerMsgWndHandle; + CMetasequoiaIME *_pTextService; + BOOL _keyboardOpen; + BOOL _keyboardOpenKnown; + BOOL _suppressKeyboardCloseCommit; + // After Activate applies input.default_ime_mode, Chromium (and Electron) + // often rewrites TF_CONVERSIONMODE_* (NATIVE / SYMBOL / FULLSHAPE) while + // focus is still on the non-editable shell. ConversionModeCompartmentUpdated + // would then mirror those bits into OPENCLOSE and the punctuation / + // fullwidth compartments. Defend by reasserting private state until the + // user explicitly chooses a mode (Shift / langbar / FTB / preserved key). + BOOL _defendConfiguredImeMode; + BOOL _hasPendingImeModeAfterCompositionCommit; + BOOL _pendingImeModeAfterCompositionCommit; + + // Configuration data + BOOL _isWildcard : 1; + BOOL _isDisableWildcardAtFirst : 1; + BOOL _isKeystrokeSort : 1; + BOOL _isComLessMode : 1; + CCandidateRange _candidateListIndexRange; + UINT _candidateListPhraseModifier; + UINT _candidateWndWidth; + + static const int OUT_OF_FILE_INDEX = -1; +}; diff --git a/windows/src/Composition/StartComposition.cpp b/windows/src/Composition/StartComposition.cpp index 1fd03cfb..2e05cdb5 100644 --- a/windows/src/Composition/StartComposition.cpp +++ b/windows/src/Composition/StartComposition.cpp @@ -1,129 +1,129 @@ -#include "Private.h" -#include "Globals.h" -#include "EditSession.h" -#include "MetasequoiaIME.h" - -//+--------------------------------------------------------------------------- -// -// CStartCompositinoEditSession -// -//---------------------------------------------------------------------------- - -class CStartCompositionEditSession : public CEditSessionBase -{ - public: - CStartCompositionEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext) - : CEditSessionBase(pTextService, pContext) - { - } - - // ITfEditSession - STDMETHODIMP DoEditSession(TfEditCookie ec); -}; - -//+--------------------------------------------------------------------------- -// -// ITfEditSession::DoEditSession -// -//---------------------------------------------------------------------------- - -STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) -{ - ITfInsertAtSelection *pInsertAtSelection = nullptr; - ITfRange *pRangeInsert = nullptr; - ITfContextComposition *pContextComposition = nullptr; - ITfComposition *pComposition = nullptr; - - if (FAILED(_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection))) - { - goto Exit; - } - - if (FAILED(pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert))) - { - goto Exit; - } - - if (FAILED(_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition))) - { - goto Exit; - } - - if (SUCCEEDED(pContextComposition->StartComposition(ec, pRangeInsert, _pTextService, &pComposition)) && - (nullptr != pComposition)) - { - _pTextService->_SetComposition(pComposition); - - // set selection to the adjusted range - TF_SELECTION tfSelection; - tfSelection.range = pRangeInsert; - tfSelection.style.ase = TF_AE_NONE; - tfSelection.style.fInterimChar = FALSE; - - _pContext->SetSelection(ec, 1, &tfSelection); - _pTextService->_SaveCompositionContext(_pContext); - } - -Exit: - if (nullptr != pContextComposition) - { - pContextComposition->Release(); - } - - if (nullptr != pRangeInsert) - { - pRangeInsert->Release(); - } - - if (nullptr != pInsertAtSelection) - { - pInsertAtSelection->Release(); - } - - return S_OK; -} - -////////////////////////////////////////////////////////////////////// -// -// CMetasequoiaIME class -// -////////////////////////////////////////////////////////////////////// - -//+--------------------------------------------------------------------------- -// -// _StartComposition -// -// this starts the new (std::nothrow) composition at the selection of the current -// focus context. -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_StartComposition(_In_ ITfContext *pContext) -{ - CStartCompositionEditSession *pStartCompositionEditSession = - new (std::nothrow) CStartCompositionEditSession(this, pContext); - - if (nullptr != pStartCompositionEditSession) - { - HRESULT hr = S_OK; - pContext->RequestEditSession(_tfClientId, pStartCompositionEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hr); - - pStartCompositionEditSession->Release(); - } -} - -//+--------------------------------------------------------------------------- -// -// _SaveCompositionContext -// -// this saves the context _pComposition belongs to; we need this to clear -// text attribute in case composition has not been terminated on -// deactivation -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_SaveCompositionContext(_In_ ITfContext *pContext) -{ - assert(_pContext == nullptr); - - pContext->AddRef(); - _pContext = pContext; -} +#include "Private.h" +#include "Globals.h" +#include "EditSession.h" +#include "MetasequoiaIME.h" + +//+--------------------------------------------------------------------------- +// +// CStartCompositinoEditSession +// +//---------------------------------------------------------------------------- + +class CStartCompositionEditSession : public CEditSessionBase +{ + public: + CStartCompositionEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext) + : CEditSessionBase(pTextService, pContext) + { + } + + // ITfEditSession + STDMETHODIMP DoEditSession(TfEditCookie ec); +}; + +//+--------------------------------------------------------------------------- +// +// ITfEditSession::DoEditSession +// +//---------------------------------------------------------------------------- + +STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) +{ + ITfInsertAtSelection *pInsertAtSelection = nullptr; + ITfRange *pRangeInsert = nullptr; + ITfContextComposition *pContextComposition = nullptr; + ITfComposition *pComposition = nullptr; + + if (FAILED(_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection))) + { + goto Exit; + } + + if (FAILED(pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert))) + { + goto Exit; + } + + if (FAILED(_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition))) + { + goto Exit; + } + + if (SUCCEEDED(pContextComposition->StartComposition(ec, pRangeInsert, _pTextService, &pComposition)) && + (nullptr != pComposition)) + { + _pTextService->_SetComposition(pComposition); + + // set selection to the adjusted range + TF_SELECTION tfSelection; + tfSelection.range = pRangeInsert; + tfSelection.style.ase = TF_AE_NONE; + tfSelection.style.fInterimChar = FALSE; + + _pContext->SetSelection(ec, 1, &tfSelection); + _pTextService->_SaveCompositionContext(_pContext); + } + +Exit: + if (nullptr != pContextComposition) + { + pContextComposition->Release(); + } + + if (nullptr != pRangeInsert) + { + pRangeInsert->Release(); + } + + if (nullptr != pInsertAtSelection) + { + pInsertAtSelection->Release(); + } + + return S_OK; +} + +////////////////////////////////////////////////////////////////////// +// +// CMetasequoiaIME class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// _StartComposition +// +// this starts the new (std::nothrow) composition at the selection of the current +// focus context. +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_StartComposition(_In_ ITfContext *pContext) +{ + CStartCompositionEditSession *pStartCompositionEditSession = + new (std::nothrow) CStartCompositionEditSession(this, pContext); + + if (nullptr != pStartCompositionEditSession) + { + HRESULT hr = S_OK; + pContext->RequestEditSession(_tfClientId, pStartCompositionEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hr); + + pStartCompositionEditSession->Release(); + } +} + +//+--------------------------------------------------------------------------- +// +// _SaveCompositionContext +// +// this saves the context _pComposition belongs to; we need this to clear +// text attribute in case composition has not been terminated on +// deactivation +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_SaveCompositionContext(_In_ ITfContext *pContext) +{ + assert(_pContext == nullptr); + + pContext->AddRef(); + _pContext = pContext; +} diff --git a/windows/src/DisplayAttribute/DisplayAttribute.cpp b/windows/src/DisplayAttribute/DisplayAttribute.cpp index f4513bc4..c5ae4779 100644 --- a/windows/src/DisplayAttribute/DisplayAttribute.cpp +++ b/windows/src/DisplayAttribute/DisplayAttribute.cpp @@ -1,136 +1,136 @@ -#include "Private.h" -#include "Globals.h" -#include "MetasequoiaIME.h" - -//+--------------------------------------------------------------------------- -// -// _ClearCompositionDisplayAttributes -// -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, - _In_opt_ ITfComposition *expectedComposition) -{ - ITfRange *pRangeComposition = nullptr; - ITfProperty *pDisplayAttributeProperty = nullptr; - ITfComposition *composition = expectedComposition ? expectedComposition : _pComposition; - if (composition == nullptr) - { - return; - } - - // get the compositon range. - if (FAILED(composition->GetRange(&pRangeComposition))) - { - return; - } - - // get our the display attribute property - if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) - { - // clear the value over the range - pDisplayAttributeProperty->Clear(ec, pRangeComposition); - - pDisplayAttributeProperty->Release(); - } - - pRangeComposition->Release(); -} - -//+--------------------------------------------------------------------------- -// -// _SetCompositionDisplayAttributes -// -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, - TfGuidAtom gaDisplayAttribute) -{ - ITfRange *pRangeComposition = nullptr; - HRESULT hr = S_OK; - - // we need a range and the context it lives in - hr = _pComposition->GetRange(&pRangeComposition); - if (FAILED(hr)) - { - return FALSE; - } - - hr = E_FAIL; - BOOL ret = _SetCompositionDisplayAttributesForRange(ec, pContext, pRangeComposition, gaDisplayAttribute); - pRangeComposition->Release(); - return ret; -} - -//+--------------------------------------------------------------------------- -// -// _SetCompositionDisplayAttributesForRange -// -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_SetCompositionDisplayAttributesForRange(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ ITfRange *pRangeComposition, - TfGuidAtom gaDisplayAttribute) -{ - if (pRangeComposition == nullptr) - { - return FALSE; - } - - ITfProperty *pDisplayAttributeProperty = nullptr; - HRESULT hr = E_FAIL; - - // get our the display attribute property - if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) - { - VARIANT var; - // set the value over the range - // the application will use this guid atom to lookup the acutal rendering information - var.vt = VT_I4; // we're going to set a TfGuidAtom - var.lVal = gaDisplayAttribute; - - hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var); - - pDisplayAttributeProperty->Release(); - } - - return (hr == S_OK); -} - -//+--------------------------------------------------------------------------- -// -// _InitDisplayAttributeGuidAtom -// -// Because it's expensive to map our display attribute GUID to a TSF -// TfGuidAtom, we do it once when Activate is called. -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_InitDisplayAttributeGuidAtom() -{ - ITfCategoryMgr *pCategoryMgr = nullptr; - HRESULT hr = CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, - (void **)&pCategoryMgr); - - if (FAILED(hr)) - { - return FALSE; - } - - // register the display attribute for input text. - hr = pCategoryMgr->RegisterGUID(Global::MetasequoiaIMEGuidDisplayAttributeInput, &_gaDisplayAttributeInput); - if (FAILED(hr)) - { - goto Exit; - } - // register the display attribute for the converted text. - hr = pCategoryMgr->RegisterGUID(Global::MetasequoiaIMEGuidDisplayAttributeConverted, &_gaDisplayAttributeConverted); - if (FAILED(hr)) - { - goto Exit; - } - -Exit: - pCategoryMgr->Release(); - - return (hr == S_OK); -} +#include "Private.h" +#include "Globals.h" +#include "MetasequoiaIME.h" + +//+--------------------------------------------------------------------------- +// +// _ClearCompositionDisplayAttributes +// +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, + _In_opt_ ITfComposition *expectedComposition) +{ + ITfRange *pRangeComposition = nullptr; + ITfProperty *pDisplayAttributeProperty = nullptr; + ITfComposition *composition = expectedComposition ? expectedComposition : _pComposition; + if (composition == nullptr) + { + return; + } + + // get the compositon range. + if (FAILED(composition->GetRange(&pRangeComposition))) + { + return; + } + + // get our the display attribute property + if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) + { + // clear the value over the range + pDisplayAttributeProperty->Clear(ec, pRangeComposition); + + pDisplayAttributeProperty->Release(); + } + + pRangeComposition->Release(); +} + +//+--------------------------------------------------------------------------- +// +// _SetCompositionDisplayAttributes +// +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, + TfGuidAtom gaDisplayAttribute) +{ + ITfRange *pRangeComposition = nullptr; + HRESULT hr = S_OK; + + // we need a range and the context it lives in + hr = _pComposition->GetRange(&pRangeComposition); + if (FAILED(hr)) + { + return FALSE; + } + + hr = E_FAIL; + BOOL ret = _SetCompositionDisplayAttributesForRange(ec, pContext, pRangeComposition, gaDisplayAttribute); + pRangeComposition->Release(); + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _SetCompositionDisplayAttributesForRange +// +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_SetCompositionDisplayAttributesForRange(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ ITfRange *pRangeComposition, + TfGuidAtom gaDisplayAttribute) +{ + if (pRangeComposition == nullptr) + { + return FALSE; + } + + ITfProperty *pDisplayAttributeProperty = nullptr; + HRESULT hr = E_FAIL; + + // get our the display attribute property + if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) + { + VARIANT var; + // set the value over the range + // the application will use this guid atom to lookup the acutal rendering information + var.vt = VT_I4; // we're going to set a TfGuidAtom + var.lVal = gaDisplayAttribute; + + hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var); + + pDisplayAttributeProperty->Release(); + } + + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// _InitDisplayAttributeGuidAtom +// +// Because it's expensive to map our display attribute GUID to a TSF +// TfGuidAtom, we do it once when Activate is called. +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_InitDisplayAttributeGuidAtom() +{ + ITfCategoryMgr *pCategoryMgr = nullptr; + HRESULT hr = CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, + (void **)&pCategoryMgr); + + if (FAILED(hr)) + { + return FALSE; + } + + // register the display attribute for input text. + hr = pCategoryMgr->RegisterGUID(Global::MetasequoiaIMEGuidDisplayAttributeInput, &_gaDisplayAttributeInput); + if (FAILED(hr)) + { + goto Exit; + } + // register the display attribute for the converted text. + hr = pCategoryMgr->RegisterGUID(Global::MetasequoiaIMEGuidDisplayAttributeConverted, &_gaDisplayAttributeConverted); + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + pCategoryMgr->Release(); + + return (hr == S_OK); +} diff --git a/windows/src/DisplayAttribute/DisplayAttributeInfo.cpp b/windows/src/DisplayAttribute/DisplayAttributeInfo.cpp index ea31643b..fcea7323 100644 --- a/windows/src/DisplayAttribute/DisplayAttributeInfo.cpp +++ b/windows/src/DisplayAttribute/DisplayAttributeInfo.cpp @@ -1,218 +1,218 @@ -#include "Private.h" -#include "Globals.h" -#include "DisplayAttributeInfo.h" -#include "TfInputProcessorProfile.h" - -//+--------------------------------------------------------------------------- -// -// The registry key and values -// -//---------------------------------------------------------------------------- - -// the registry values of the custmized display attributes -const WCHAR CDisplayAttributeInfoInput::_s_szValueName[] = L"DisplayAttributeInput"; -const WCHAR CDisplayAttributeInfoConverted::_s_szValueName[] = L"DisplayAttributeConverted"; - -// The descriptions -const WCHAR CDisplayAttributeInfoInput::_s_szDescription[] = L"Metasequoia IME Text Service Display Attribute Input"; -const WCHAR CDisplayAttributeInfoConverted::_s_szDescription[] = - L"Metasequoia IME Text Service Display Attribute Converted"; - -//+--------------------------------------------------------------------------- -// -// DisplayAttribute -// -//---------------------------------------------------------------------------- - -const TF_DISPLAYATTRIBUTE CDisplayAttributeInfoInput::_s_DisplayAttribute = { - {TF_CT_NONE, 0}, // text color - {TF_CT_NONE, 0}, // background color (TF_CT_NONE => app default) - TF_LS_DOT, // underline style - FALSE, // underline boldness - {TF_CT_NONE, 0}, // underline color - TF_ATTR_INPUT // attribute info -}; - -const TF_DISPLAYATTRIBUTE CDisplayAttributeInfoConverted::_s_DisplayAttribute = { - {TF_CT_NONE, 0}, // text color - {TF_CT_NONE, 0}, // background color (TF_CT_NONE => app default) - TF_LS_NONE, // underline style - FALSE, // underline boldness - {TF_CT_NONE, 0}, // underline color - TF_ATTR_TARGET_CONVERTED // attribute info -}; - -//+--------------------------------------------------------------------------- -// -// ctor -// -//---------------------------------------------------------------------------- - -CDisplayAttributeInfo::CDisplayAttributeInfo() -{ - DllAddRef(); - - _pguid = nullptr; - _pDisplayAttribute = nullptr; - _pValueName = nullptr; - - _refCount = 1; -} - -//+--------------------------------------------------------------------------- -// -// dtor -// -//---------------------------------------------------------------------------- - -CDisplayAttributeInfo::~CDisplayAttributeInfo() -{ - DllRelease(); -} - -//+--------------------------------------------------------------------------- -// -// QueryInterface -// -//---------------------------------------------------------------------------- - -STDAPI CDisplayAttributeInfo::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - return E_INVALIDARG; - - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfDisplayAttributeInfo)) - { - *ppvObj = (ITfDisplayAttributeInfo *)this; - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -//+--------------------------------------------------------------------------- -// -// AddRef -// -//---------------------------------------------------------------------------- - -ULONG CDisplayAttributeInfo::AddRef(void) -{ - return ++_refCount; -} - -//+--------------------------------------------------------------------------- -// -// Release -// -//---------------------------------------------------------------------------- - -ULONG CDisplayAttributeInfo::Release(void) -{ - LONG cr = --_refCount; - - assert(_refCount >= 0); - - if (_refCount == 0) - { - delete this; - } - - return cr; -} - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeInfo::GetGUID -// -//---------------------------------------------------------------------------- - -STDAPI CDisplayAttributeInfo::GetGUID(_Out_ GUID *pguid) -{ - if (pguid == nullptr) - return E_INVALIDARG; - - if (_pguid == nullptr) - return E_FAIL; - - *pguid = *_pguid; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeInfo::GetDescription -// -//---------------------------------------------------------------------------- - -STDAPI CDisplayAttributeInfo::GetDescription(_Out_ BSTR *pbstrDesc) -{ - BSTR tempDesc; - - if (pbstrDesc == nullptr) - { - return E_INVALIDARG; - } - - *pbstrDesc = nullptr; - - if ((tempDesc = SysAllocString(_pDescription)) == nullptr) - { - return E_OUTOFMEMORY; - } - - *pbstrDesc = tempDesc; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeInfo::GetAttributeInfo -// -//---------------------------------------------------------------------------- - -STDAPI CDisplayAttributeInfo::GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *ptfDisplayAttr) -{ - if (ptfDisplayAttr == nullptr) - { - return E_INVALIDARG; - } - - // return the default display attribute. - *ptfDisplayAttr = *_pDisplayAttribute; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeInfo::SetAttributeInfo -// -//---------------------------------------------------------------------------- - -STDAPI CDisplayAttributeInfo::SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr) -{ - ptfDisplayAttr; - - return E_NOTIMPL; -} - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeInfo::Reset -// -//---------------------------------------------------------------------------- - -STDAPI CDisplayAttributeInfo::Reset() -{ - return SetAttributeInfo(_pDisplayAttribute); -} +#include "Private.h" +#include "Globals.h" +#include "DisplayAttributeInfo.h" +#include "TfInputProcessorProfile.h" + +//+--------------------------------------------------------------------------- +// +// The registry key and values +// +//---------------------------------------------------------------------------- + +// the registry values of the custmized display attributes +const WCHAR CDisplayAttributeInfoInput::_s_szValueName[] = L"DisplayAttributeInput"; +const WCHAR CDisplayAttributeInfoConverted::_s_szValueName[] = L"DisplayAttributeConverted"; + +// The descriptions +const WCHAR CDisplayAttributeInfoInput::_s_szDescription[] = L"Metasequoia IME Text Service Display Attribute Input"; +const WCHAR CDisplayAttributeInfoConverted::_s_szDescription[] = + L"Metasequoia IME Text Service Display Attribute Converted"; + +//+--------------------------------------------------------------------------- +// +// DisplayAttribute +// +//---------------------------------------------------------------------------- + +const TF_DISPLAYATTRIBUTE CDisplayAttributeInfoInput::_s_DisplayAttribute = { + {TF_CT_NONE, 0}, // text color + {TF_CT_NONE, 0}, // background color (TF_CT_NONE => app default) + TF_LS_DOT, // underline style + FALSE, // underline boldness + {TF_CT_NONE, 0}, // underline color + TF_ATTR_INPUT // attribute info +}; + +const TF_DISPLAYATTRIBUTE CDisplayAttributeInfoConverted::_s_DisplayAttribute = { + {TF_CT_NONE, 0}, // text color + {TF_CT_NONE, 0}, // background color (TF_CT_NONE => app default) + TF_LS_NONE, // underline style + FALSE, // underline boldness + {TF_CT_NONE, 0}, // underline color + TF_ATTR_TARGET_CONVERTED // attribute info +}; + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CDisplayAttributeInfo::CDisplayAttributeInfo() +{ + DllAddRef(); + + _pguid = nullptr; + _pDisplayAttribute = nullptr; + _pValueName = nullptr; + + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CDisplayAttributeInfo::~CDisplayAttributeInfo() +{ + DllRelease(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + return E_INVALIDARG; + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfDisplayAttributeInfo)) + { + *ppvObj = (ITfDisplayAttributeInfo *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +ULONG CDisplayAttributeInfo::AddRef(void) +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +ULONG CDisplayAttributeInfo::Release(void) +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::GetGUID +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::GetGUID(_Out_ GUID *pguid) +{ + if (pguid == nullptr) + return E_INVALIDARG; + + if (_pguid == nullptr) + return E_FAIL; + + *pguid = *_pguid; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::GetDescription +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::GetDescription(_Out_ BSTR *pbstrDesc) +{ + BSTR tempDesc; + + if (pbstrDesc == nullptr) + { + return E_INVALIDARG; + } + + *pbstrDesc = nullptr; + + if ((tempDesc = SysAllocString(_pDescription)) == nullptr) + { + return E_OUTOFMEMORY; + } + + *pbstrDesc = tempDesc; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::GetAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *ptfDisplayAttr) +{ + if (ptfDisplayAttr == nullptr) + { + return E_INVALIDARG; + } + + // return the default display attribute. + *ptfDisplayAttr = *_pDisplayAttribute; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::SetAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr) +{ + ptfDisplayAttr; + + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::Reset +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::Reset() +{ + return SetAttributeInfo(_pDisplayAttribute); +} diff --git a/windows/src/DisplayAttribute/DisplayAttributeInfo.h b/windows/src/DisplayAttribute/DisplayAttributeInfo.h index bc35a134..6b0c2c4a 100644 --- a/windows/src/DisplayAttribute/DisplayAttributeInfo.h +++ b/windows/src/DisplayAttribute/DisplayAttributeInfo.h @@ -1,82 +1,82 @@ -#pragma once - -#include "Private.h" -#include "Globals.h" - -//+--------------------------------------------------------------------------- -// -// CDisplayAttributeInfo class -// -//---------------------------------------------------------------------------- - -class CDisplayAttributeInfo : public ITfDisplayAttributeInfo -{ - public: - CDisplayAttributeInfo(); - ~CDisplayAttributeInfo(); - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfDisplayAttributeInfo - STDMETHODIMP GetGUID(_Out_ GUID *pguid); - STDMETHODIMP GetDescription(_Out_ BSTR *pbstrDesc); - STDMETHODIMP GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *pTSFDisplayAttr); - STDMETHODIMP SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr); - STDMETHODIMP Reset(); - - protected: - const GUID *_pguid; - const TF_DISPLAYATTRIBUTE *_pDisplayAttribute; - const WCHAR *_pDescription; - const WCHAR *_pValueName; - - private: - LONG _refCount; // COM ref count -}; - -//+--------------------------------------------------------------------------- -// -// CDisplayAttributeInfoInput class -// -//---------------------------------------------------------------------------- - -class CDisplayAttributeInfoInput : public CDisplayAttributeInfo -{ - public: - CDisplayAttributeInfoInput() - { - _pguid = &Global::MetasequoiaIMEGuidDisplayAttributeInput; - _pDisplayAttribute = &_s_DisplayAttribute; - _pDescription = _s_szDescription; - _pValueName = _s_szValueName; - } - - static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; - static const WCHAR _s_szDescription[]; - static const WCHAR _s_szValueName[]; -}; - -//+--------------------------------------------------------------------------- -// -// CDisplayAttributeInfoConverted class -// -//---------------------------------------------------------------------------- - -class CDisplayAttributeInfoConverted : public CDisplayAttributeInfo -{ - public: - CDisplayAttributeInfoConverted() - { - _pguid = &Global::MetasequoiaIMEGuidDisplayAttributeConverted; - _pDisplayAttribute = &_s_DisplayAttribute; - _pDescription = _s_szDescription; - _pValueName = _s_szValueName; - } - - static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; - static const WCHAR _s_szDescription[]; - static const WCHAR _s_szValueName[]; -}; +#pragma once + +#include "Private.h" +#include "Globals.h" + +//+--------------------------------------------------------------------------- +// +// CDisplayAttributeInfo class +// +//---------------------------------------------------------------------------- + +class CDisplayAttributeInfo : public ITfDisplayAttributeInfo +{ + public: + CDisplayAttributeInfo(); + ~CDisplayAttributeInfo(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfDisplayAttributeInfo + STDMETHODIMP GetGUID(_Out_ GUID *pguid); + STDMETHODIMP GetDescription(_Out_ BSTR *pbstrDesc); + STDMETHODIMP GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *pTSFDisplayAttr); + STDMETHODIMP SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr); + STDMETHODIMP Reset(); + + protected: + const GUID *_pguid; + const TF_DISPLAYATTRIBUTE *_pDisplayAttribute; + const WCHAR *_pDescription; + const WCHAR *_pValueName; + + private: + LONG _refCount; // COM ref count +}; + +//+--------------------------------------------------------------------------- +// +// CDisplayAttributeInfoInput class +// +//---------------------------------------------------------------------------- + +class CDisplayAttributeInfoInput : public CDisplayAttributeInfo +{ + public: + CDisplayAttributeInfoInput() + { + _pguid = &Global::MetasequoiaIMEGuidDisplayAttributeInput; + _pDisplayAttribute = &_s_DisplayAttribute; + _pDescription = _s_szDescription; + _pValueName = _s_szValueName; + } + + static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; + static const WCHAR _s_szDescription[]; + static const WCHAR _s_szValueName[]; +}; + +//+--------------------------------------------------------------------------- +// +// CDisplayAttributeInfoConverted class +// +//---------------------------------------------------------------------------- + +class CDisplayAttributeInfoConverted : public CDisplayAttributeInfo +{ + public: + CDisplayAttributeInfoConverted() + { + _pguid = &Global::MetasequoiaIMEGuidDisplayAttributeConverted; + _pDisplayAttribute = &_s_DisplayAttribute; + _pDescription = _s_szDescription; + _pValueName = _s_szValueName; + } + + static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; + static const WCHAR _s_szDescription[]; + static const WCHAR _s_szValueName[]; +}; diff --git a/windows/src/DisplayAttribute/DisplayAttributeProvider.cpp b/windows/src/DisplayAttribute/DisplayAttributeProvider.cpp index 443e38c0..41eefeb5 100644 --- a/windows/src/DisplayAttribute/DisplayAttributeProvider.cpp +++ b/windows/src/DisplayAttribute/DisplayAttributeProvider.cpp @@ -1,74 +1,74 @@ -#include "Private.h" -#include "Globals.h" -#include "MetasequoiaIME.h" -#include "DisplayAttributeInfo.h" -#include "EnumDisplayAttributeInfo.h" - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeProvider::EnumDisplayAttributeInfo -// -//---------------------------------------------------------------------------- - -STDAPI CMetasequoiaIME::EnumDisplayAttributeInfo(__RPC__deref_out_opt IEnumTfDisplayAttributeInfo **ppEnum) -{ - CEnumDisplayAttributeInfo *pAttributeEnum = nullptr; - - if (ppEnum == nullptr) - { - return E_INVALIDARG; - } - - *ppEnum = nullptr; - - pAttributeEnum = new (std::nothrow) CEnumDisplayAttributeInfo(); - if (pAttributeEnum == nullptr) - { - return E_OUTOFMEMORY; - } - - *ppEnum = pAttributeEnum; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// ITfDisplayAttributeProvider::GetDisplayAttributeInfo -// -//---------------------------------------------------------------------------- - -STDAPI CMetasequoiaIME::GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, - __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo) -{ - if (ppInfo == nullptr) - { - return E_INVALIDARG; - } - - *ppInfo = nullptr; - - // Which display attribute GUID? - if (IsEqualGUID(guidInfo, Global::MetasequoiaIMEGuidDisplayAttributeInput)) - { - *ppInfo = new (std::nothrow) CDisplayAttributeInfoInput(); - if ((*ppInfo) == nullptr) - { - return E_OUTOFMEMORY; - } - } - else if (IsEqualGUID(guidInfo, Global::MetasequoiaIMEGuidDisplayAttributeConverted)) - { - *ppInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); - if ((*ppInfo) == nullptr) - { - return E_OUTOFMEMORY; - } - } - else - { - return E_INVALIDARG; - } - - return S_OK; -} +#include "Private.h" +#include "Globals.h" +#include "MetasequoiaIME.h" +#include "DisplayAttributeInfo.h" +#include "EnumDisplayAttributeInfo.h" + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeProvider::EnumDisplayAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CMetasequoiaIME::EnumDisplayAttributeInfo(__RPC__deref_out_opt IEnumTfDisplayAttributeInfo **ppEnum) +{ + CEnumDisplayAttributeInfo *pAttributeEnum = nullptr; + + if (ppEnum == nullptr) + { + return E_INVALIDARG; + } + + *ppEnum = nullptr; + + pAttributeEnum = new (std::nothrow) CEnumDisplayAttributeInfo(); + if (pAttributeEnum == nullptr) + { + return E_OUTOFMEMORY; + } + + *ppEnum = pAttributeEnum; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeProvider::GetDisplayAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CMetasequoiaIME::GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, + __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo) +{ + if (ppInfo == nullptr) + { + return E_INVALIDARG; + } + + *ppInfo = nullptr; + + // Which display attribute GUID? + if (IsEqualGUID(guidInfo, Global::MetasequoiaIMEGuidDisplayAttributeInput)) + { + *ppInfo = new (std::nothrow) CDisplayAttributeInfoInput(); + if ((*ppInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else if (IsEqualGUID(guidInfo, Global::MetasequoiaIMEGuidDisplayAttributeConverted)) + { + *ppInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); + if ((*ppInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else + { + return E_INVALIDARG; + } + + return S_OK; +} diff --git a/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.cpp b/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.cpp index 803418ed..78f924c9 100644 --- a/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.cpp +++ b/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.cpp @@ -1,218 +1,218 @@ -#include "Private.h" -#include "Globals.h" -#include "MetasequoiaIME.h" -#include "DisplayAttributeInfo.h" -#include "EnumDisplayAttributeInfo.h" - -//+--------------------------------------------------------------------------- -// -// ctor -// -//---------------------------------------------------------------------------- - -CEnumDisplayAttributeInfo::CEnumDisplayAttributeInfo() -{ - DllAddRef(); - - _index = 0; - _refCount = 1; -} - -//+--------------------------------------------------------------------------- -// -// dtor -// -//---------------------------------------------------------------------------- - -CEnumDisplayAttributeInfo::~CEnumDisplayAttributeInfo() -{ - DllRelease(); -} - -//+--------------------------------------------------------------------------- -// -// QueryInterface -// -//---------------------------------------------------------------------------- - -STDAPI CEnumDisplayAttributeInfo::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - return E_INVALIDARG; - - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumTfDisplayAttributeInfo)) - { - *ppvObj = (IEnumTfDisplayAttributeInfo *)this; - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -//+--------------------------------------------------------------------------- -// -// AddRef -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CEnumDisplayAttributeInfo::AddRef() -{ - return ++_refCount; -} - -//+--------------------------------------------------------------------------- -// -// Release -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CEnumDisplayAttributeInfo::Release() -{ - LONG cr = --_refCount; - - assert(_refCount >= 0); - - if (_refCount == 0) - { - delete this; - } - - return cr; -} - -//+--------------------------------------------------------------------------- -// -// IEnumTfDisplayAttributeInfo::Clone -// -// Returns a copy of the object. -//---------------------------------------------------------------------------- - -STDAPI CEnumDisplayAttributeInfo::Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum) -{ - CEnumDisplayAttributeInfo *pClone = nullptr; - - if (ppEnum == nullptr) - { - return E_INVALIDARG; - } - - *ppEnum = nullptr; - - pClone = new (std::nothrow) CEnumDisplayAttributeInfo(); - if ((pClone) == nullptr) - { - return E_OUTOFMEMORY; - } - - // the clone should match this object's state - pClone->_index = _index; - - *ppEnum = pClone; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// IEnumTfDisplayAttributeInfo::Next -// -// Returns an array of display attribute info objects supported by this service. -//---------------------------------------------------------------------------- - -const int MAX_DISPLAY_ATTRIBUTE_INFO = 2; - -STDAPI CEnumDisplayAttributeInfo::Next(ULONG ulCount, - __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, - __RPC__out ULONG *pcFetched) -{ - ULONG fetched; - - fetched = 0; - - if (ulCount == 0) - { - return S_OK; - } - if (rgInfo == nullptr) - { - return E_INVALIDARG; - } - *rgInfo = nullptr; - - while (fetched < ulCount) - { - ITfDisplayAttributeInfo *pDisplayAttributeInfo = nullptr; - - if (_index == 0) - { - pDisplayAttributeInfo = new (std::nothrow) CDisplayAttributeInfoInput(); - if ((pDisplayAttributeInfo) == nullptr) - { - return E_OUTOFMEMORY; - } - } - else if (_index == 1) - { - pDisplayAttributeInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); - if ((pDisplayAttributeInfo) == nullptr) - { - return E_OUTOFMEMORY; - } - } - else - { - break; - } - - *rgInfo = pDisplayAttributeInfo; - rgInfo++; - fetched++; - _index++; - } - - if (pcFetched != nullptr) - { - // technically this is only legal if ulCount == 1, but we won't check - *pcFetched = fetched; - } - - return (fetched == ulCount) ? S_OK : S_FALSE; -} - -//+--------------------------------------------------------------------------- -// -// IEnumTfDisplayAttributeInfo::Reset -// -// Resets the enumeration. -//---------------------------------------------------------------------------- - -STDAPI CEnumDisplayAttributeInfo::Reset() -{ - _index = 0; - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// IEnumTfDisplayAttributeInfo::Skip -// -// Skips past objects in the enumeration. -//---------------------------------------------------------------------------- - -STDAPI CEnumDisplayAttributeInfo::Skip(ULONG ulCount) -{ - if ((ulCount + _index) > MAX_DISPLAY_ATTRIBUTE_INFO || (ulCount + _index) < ulCount) - { - _index = MAX_DISPLAY_ATTRIBUTE_INFO; - return S_FALSE; - } - _index += ulCount; - return S_OK; -} +#include "Private.h" +#include "Globals.h" +#include "MetasequoiaIME.h" +#include "DisplayAttributeInfo.h" +#include "EnumDisplayAttributeInfo.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CEnumDisplayAttributeInfo::CEnumDisplayAttributeInfo() +{ + DllAddRef(); + + _index = 0; + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CEnumDisplayAttributeInfo::~CEnumDisplayAttributeInfo() +{ + DllRelease(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + return E_INVALIDARG; + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumTfDisplayAttributeInfo)) + { + *ppvObj = (IEnumTfDisplayAttributeInfo *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEnumDisplayAttributeInfo::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEnumDisplayAttributeInfo::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Clone +// +// Returns a copy of the object. +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum) +{ + CEnumDisplayAttributeInfo *pClone = nullptr; + + if (ppEnum == nullptr) + { + return E_INVALIDARG; + } + + *ppEnum = nullptr; + + pClone = new (std::nothrow) CEnumDisplayAttributeInfo(); + if ((pClone) == nullptr) + { + return E_OUTOFMEMORY; + } + + // the clone should match this object's state + pClone->_index = _index; + + *ppEnum = pClone; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Next +// +// Returns an array of display attribute info objects supported by this service. +//---------------------------------------------------------------------------- + +const int MAX_DISPLAY_ATTRIBUTE_INFO = 2; + +STDAPI CEnumDisplayAttributeInfo::Next(ULONG ulCount, + __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, + __RPC__out ULONG *pcFetched) +{ + ULONG fetched; + + fetched = 0; + + if (ulCount == 0) + { + return S_OK; + } + if (rgInfo == nullptr) + { + return E_INVALIDARG; + } + *rgInfo = nullptr; + + while (fetched < ulCount) + { + ITfDisplayAttributeInfo *pDisplayAttributeInfo = nullptr; + + if (_index == 0) + { + pDisplayAttributeInfo = new (std::nothrow) CDisplayAttributeInfoInput(); + if ((pDisplayAttributeInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else if (_index == 1) + { + pDisplayAttributeInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); + if ((pDisplayAttributeInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else + { + break; + } + + *rgInfo = pDisplayAttributeInfo; + rgInfo++; + fetched++; + _index++; + } + + if (pcFetched != nullptr) + { + // technically this is only legal if ulCount == 1, but we won't check + *pcFetched = fetched; + } + + return (fetched == ulCount) ? S_OK : S_FALSE; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Reset +// +// Resets the enumeration. +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::Reset() +{ + _index = 0; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Skip +// +// Skips past objects in the enumeration. +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::Skip(ULONG ulCount) +{ + if ((ulCount + _index) > MAX_DISPLAY_ATTRIBUTE_INFO || (ulCount + _index) < ulCount) + { + _index = MAX_DISPLAY_ATTRIBUTE_INFO; + return S_FALSE; + } + _index += ulCount; + return S_OK; +} diff --git a/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.h b/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.h index 0925c8ad..8fe23f33 100644 --- a/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.h +++ b/windows/src/DisplayAttribute/EnumDisplayAttributeInfo.h @@ -1,32 +1,32 @@ -#pragma once - -#include "Private.h" - -//+--------------------------------------------------------------------------- -// -// CEnumDisplayAttributeInfo -// -//---------------------------------------------------------------------------- - -class CEnumDisplayAttributeInfo : public IEnumTfDisplayAttributeInfo -{ - public: - CEnumDisplayAttributeInfo(); - ~CEnumDisplayAttributeInfo(); - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // IEnumTfDisplayAttributeInfo - STDMETHODIMP Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum); - STDMETHODIMP Next(ULONG ulCount, __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, - __RPC__out ULONG *pcFetched); - STDMETHODIMP Reset(); - STDMETHODIMP Skip(ULONG ulCount); - - private: - LONG _index; // next display attribute to enum - LONG _refCount; // COM ref count -}; +#pragma once + +#include "Private.h" + +//+--------------------------------------------------------------------------- +// +// CEnumDisplayAttributeInfo +// +//---------------------------------------------------------------------------- + +class CEnumDisplayAttributeInfo : public IEnumTfDisplayAttributeInfo +{ + public: + CEnumDisplayAttributeInfo(); + ~CEnumDisplayAttributeInfo(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // IEnumTfDisplayAttributeInfo + STDMETHODIMP Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum); + STDMETHODIMP Next(ULONG ulCount, __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, + __RPC__out ULONG *pcFetched); + STDMETHODIMP Reset(); + STDMETHODIMP Skip(ULONG ulCount); + + private: + LONG _index; // next display attribute to enum + LONG _refCount; // COM ref count +}; diff --git a/windows/src/DllMain.cpp b/windows/src/DllMain.cpp index 2bca9bfe..d0ec35a3 100644 --- a/windows/src/DllMain.cpp +++ b/windows/src/DllMain.cpp @@ -1,43 +1,43 @@ -#include "Private.h" -#include "Globals.h" - -//+--------------------------------------------------------------------------- -// -// DllMain -// -//---------------------------------------------------------------------------- - -BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) -{ - pvReserved; - - switch (dwReason) - { - case DLL_PROCESS_ATTACH: - - Global::dllInstanceHandle = hInstance; - - if (!InitializeCriticalSectionAndSpinCount(&Global::CS, 0)) - { - return FALSE; - } - - break; - - case DLL_PROCESS_DETACH: - - DeleteCriticalSection(&Global::CS); - - break; - - case DLL_THREAD_ATTACH: - - break; - - case DLL_THREAD_DETACH: - - break; - } - - return TRUE; -} +#include "Private.h" +#include "Globals.h" + +//+--------------------------------------------------------------------------- +// +// DllMain +// +//---------------------------------------------------------------------------- + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) +{ + pvReserved; + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + + Global::dllInstanceHandle = hInstance; + + if (!InitializeCriticalSectionAndSpinCount(&Global::CS, 0)) + { + return FALSE; + } + + break; + + case DLL_PROCESS_DETACH: + + DeleteCriticalSection(&Global::CS); + + break; + + case DLL_THREAD_ATTACH: + + break; + + case DLL_THREAD_DETACH: + + break; + } + + return TRUE; +} diff --git a/windows/src/Edit/EditSession.cpp b/windows/src/Edit/EditSession.cpp index c88bac2e..261886cc 100644 --- a/windows/src/Edit/EditSession.cpp +++ b/windows/src/Edit/EditSession.cpp @@ -1,91 +1,91 @@ -#include "Private.h" -#include "EditSession.h" -#include "MetasequoiaIME.h" - -//+--------------------------------------------------------------------------- -// -// ctor -// -//---------------------------------------------------------------------------- - -CEditSessionBase::CEditSessionBase(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext) -{ - _refCount = 1; - _pContext = pContext; - _pContext->AddRef(); - - _pTextService = pTextService; - _pTextService->AddRef(); -} - -//+--------------------------------------------------------------------------- -// -// dtor -// -//---------------------------------------------------------------------------- - -CEditSessionBase::~CEditSessionBase() -{ - _pContext->Release(); - _pTextService->Release(); -} - -//+--------------------------------------------------------------------------- -// -// QueryInterface -// -//---------------------------------------------------------------------------- - -STDAPI CEditSessionBase::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_INVALIDARG; - } - - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfEditSession)) - { - *ppvObj = (ITfEditSession *)this; - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -//+--------------------------------------------------------------------------- -// -// AddRef -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CEditSessionBase::AddRef(void) -{ - return ++_refCount; -} - -//+--------------------------------------------------------------------------- -// -// Release -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CEditSessionBase::Release(void) -{ - LONG cr = --_refCount; - - assert(_refCount >= 0); - - if (_refCount == 0) - { - delete this; - } - - return cr; -} +#include "Private.h" +#include "EditSession.h" +#include "MetasequoiaIME.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CEditSessionBase::CEditSessionBase(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext) +{ + _refCount = 1; + _pContext = pContext; + _pContext->AddRef(); + + _pTextService = pTextService; + _pTextService->AddRef(); +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CEditSessionBase::~CEditSessionBase() +{ + _pContext->Release(); + _pTextService->Release(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CEditSessionBase::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfEditSession)) + { + *ppvObj = (ITfEditSession *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEditSessionBase::AddRef(void) +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEditSessionBase::Release(void) +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} diff --git a/windows/src/Edit/EditSession.h b/windows/src/Edit/EditSession.h index 4bfbc4e7..9b2223fe 100644 --- a/windows/src/Edit/EditSession.h +++ b/windows/src/Edit/EditSession.h @@ -1,27 +1,27 @@ -#pragma once - -#include "Private.h" - -class CMetasequoiaIME; - -class CEditSessionBase : public ITfEditSession -{ - public: - CEditSessionBase(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext); - virtual ~CEditSessionBase(); - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfEditSession - virtual STDMETHODIMP DoEditSession(TfEditCookie ec) = 0; - - protected: - ITfContext *_pContext; - CMetasequoiaIME *_pTextService; - - private: - LONG _refCount; // COM ref count -}; +#pragma once + +#include "Private.h" + +class CMetasequoiaIME; + +class CEditSessionBase : public ITfEditSession +{ + public: + CEditSessionBase(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext); + virtual ~CEditSessionBase(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfEditSession + virtual STDMETHODIMP DoEditSession(TfEditCookie ec) = 0; + + protected: + ITfContext *_pContext; + CMetasequoiaIME *_pTextService; + + private: + LONG _refCount; // COM ref count +}; diff --git a/windows/src/Edit/GetTextExtentEditSession.cpp b/windows/src/Edit/GetTextExtentEditSession.cpp index bc9fc3c6..c0b7c7ae 100644 --- a/windows/src/Edit/GetTextExtentEditSession.cpp +++ b/windows/src/Edit/GetTextExtentEditSession.cpp @@ -1,83 +1,83 @@ -#include "Private.h" -#include "EditSession.h" -#include "GetTextExtentEditSession.h" -#include "TfTextLayoutSink.h" -#include "Ipc.h" -#include "FanyDefines.h" -#include -#include -#include "../Utils/PerfTimer.h" - -//+--------------------------------------------------------------------------- -// -// ctor -// -//---------------------------------------------------------------------------- - -CGetTextExtentEditSession::CGetTextExtentEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext, - _In_ ITfContextView *pContextView, - _In_ ITfRange *pRangeComposition, - _In_ CTfTextLayoutSink *pTfTextLayoutSink) - : CEditSessionBase(pTextService, pContext) -{ - _pContextView = pContextView; - _pRangeComposition = pRangeComposition; - _pTfTextLayoutSink = pTfTextLayoutSink; -} - -//+--------------------------------------------------------------------------- -// -// ITfEditSession::DoEditSession -// -//---------------------------------------------------------------------------- - -STDAPI CGetTextExtentEditSession::DoEditSession(TfEditCookie ec) -{ - PerfTimer timer; - RECT rc = {0, 0, 0, 0}; - BOOL isClipped = TRUE; - HRESULT hr = S_OK; - double getTextExtElapsedMs = 0; - double layoutChangeElapsedMs = 0; - - PerfTimer getTextExtTimer; - hr = _pContextView->GetTextExt(ec, _pRangeComposition, &rc, &isClipped); - getTextExtElapsedMs = getTextExtTimer.ElapsedMs(); - - if (SUCCEEDED(hr)) - { - // Office may set isClipped with a usable rect; ignore only empty extents. - if (isClipped && (rc.right <= rc.left || rc.bottom <= rc.top)) - { - Global::Point[0] = 0; - Global::Point[1] = Global::INVALID_Y; - return S_OK; - } - - const POINT anchor = GetPhysicalTextAnchor(_pContextView, rc); - Global::Point[0] = anchor.x; - Global::Point[1] = anchor.y; - // This is the per-keystroke caret source. Tell the sink the session now - // has a usable anchor, so a later transient TS_E_NOLAYOUT can be answered - // by keeping this position instead of parking the window off-screen. - _pTfTextLayoutSink->_MarkAnchorValid(); - if (Global::current_process_name == Global::ZEN_BROWSER) - { - Global::firefox_like_cnt++; - if (Global::firefox_like_cnt == 3) - { - PerfTimer layoutChangeTimer; - _pTfTextLayoutSink->_LayoutChangeNotification(&rc); - layoutChangeElapsedMs = layoutChangeTimer.ElapsedMs(); - } - } - else - { - PerfTimer layoutChangeTimer; - _pTfTextLayoutSink->_LayoutChangeNotification(&rc); - layoutChangeElapsedMs = layoutChangeTimer.ElapsedMs(); - } - } - - return S_OK; -} +#include "Private.h" +#include "EditSession.h" +#include "GetTextExtentEditSession.h" +#include "TfTextLayoutSink.h" +#include "Ipc.h" +#include "FanyDefines.h" +#include +#include +#include "../Utils/PerfTimer.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CGetTextExtentEditSession::CGetTextExtentEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext, + _In_ ITfContextView *pContextView, + _In_ ITfRange *pRangeComposition, + _In_ CTfTextLayoutSink *pTfTextLayoutSink) + : CEditSessionBase(pTextService, pContext) +{ + _pContextView = pContextView; + _pRangeComposition = pRangeComposition; + _pTfTextLayoutSink = pTfTextLayoutSink; +} + +//+--------------------------------------------------------------------------- +// +// ITfEditSession::DoEditSession +// +//---------------------------------------------------------------------------- + +STDAPI CGetTextExtentEditSession::DoEditSession(TfEditCookie ec) +{ + PerfTimer timer; + RECT rc = {0, 0, 0, 0}; + BOOL isClipped = TRUE; + HRESULT hr = S_OK; + double getTextExtElapsedMs = 0; + double layoutChangeElapsedMs = 0; + + PerfTimer getTextExtTimer; + hr = _pContextView->GetTextExt(ec, _pRangeComposition, &rc, &isClipped); + getTextExtElapsedMs = getTextExtTimer.ElapsedMs(); + + if (SUCCEEDED(hr)) + { + // Office may set isClipped with a usable rect; ignore only empty extents. + if (isClipped && (rc.right <= rc.left || rc.bottom <= rc.top)) + { + Global::Point[0] = 0; + Global::Point[1] = Global::INVALID_Y; + return S_OK; + } + + const POINT anchor = GetPhysicalTextAnchor(_pContextView, rc); + Global::Point[0] = anchor.x; + Global::Point[1] = anchor.y; + // This is the per-keystroke caret source. Tell the sink the session now + // has a usable anchor, so a later transient TS_E_NOLAYOUT can be answered + // by keeping this position instead of parking the window off-screen. + _pTfTextLayoutSink->_MarkAnchorValid(); + if (Global::current_process_name == Global::ZEN_BROWSER) + { + Global::firefox_like_cnt++; + if (Global::firefox_like_cnt == 3) + { + PerfTimer layoutChangeTimer; + _pTfTextLayoutSink->_LayoutChangeNotification(&rc); + layoutChangeElapsedMs = layoutChangeTimer.ElapsedMs(); + } + } + else + { + PerfTimer layoutChangeTimer; + _pTfTextLayoutSink->_LayoutChangeNotification(&rc); + layoutChangeElapsedMs = layoutChangeTimer.ElapsedMs(); + } + } + + return S_OK; +} diff --git a/windows/src/Edit/GetTextExtentEditSession.h b/windows/src/Edit/GetTextExtentEditSession.h index cef12082..6701bd9f 100644 --- a/windows/src/Edit/GetTextExtentEditSession.h +++ b/windows/src/Edit/GetTextExtentEditSession.h @@ -1,36 +1,36 @@ -#pragma once - -#include "EditSession.h" - -class CMetasequoiaIME; -class CTfTextLayoutSink; - -////////////////////////////////////////////////////////////////////// -// -// ITfEditSession -// CEditSessionBase -// CGetTextExtentEditSession class -// -////////////////////////////////////////////////////////////////////// - -//+--------------------------------------------------------------------------- -// -// CGetTextExtentEditSession -// -//---------------------------------------------------------------------------- - -class CGetTextExtentEditSession : public CEditSessionBase -{ - public: - CGetTextExtentEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext, - _In_ ITfContextView *pContextView, _In_ ITfRange *pRangeComposition, - _In_ CTfTextLayoutSink *pTextLayoutSink); - - // ITfEditSession - STDMETHODIMP DoEditSession(TfEditCookie ec); - - private: - ITfContextView *_pContextView; - ITfRange *_pRangeComposition; - CTfTextLayoutSink *_pTfTextLayoutSink; -}; +#pragma once + +#include "EditSession.h" + +class CMetasequoiaIME; +class CTfTextLayoutSink; + +////////////////////////////////////////////////////////////////////// +// +// ITfEditSession +// CEditSessionBase +// CGetTextExtentEditSession class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// CGetTextExtentEditSession +// +//---------------------------------------------------------------------------- + +class CGetTextExtentEditSession : public CEditSessionBase +{ + public: + CGetTextExtentEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext, + _In_ ITfContextView *pContextView, _In_ ITfRange *pRangeComposition, + _In_ CTfTextLayoutSink *pTextLayoutSink); + + // ITfEditSession + STDMETHODIMP DoEditSession(TfEditCookie ec); + + private: + ITfContextView *_pContextView; + ITfRange *_pRangeComposition; + CTfTextLayoutSink *_pTfTextLayoutSink; +}; diff --git a/windows/src/Global/Globals.cpp b/windows/src/Global/Globals.cpp index d5167014..2bca66d7 100644 --- a/windows/src/Global/Globals.cpp +++ b/windows/src/Global/Globals.cpp @@ -1,535 +1,535 @@ -#include "Globals.h" -#include "Private.h" -#include "resource.h" -#include "define.h" -#include "MetasequoiaIMEBaseStructure.h" -#include -#include -#include -#include -#include -#include "FanyUtils.h" - -namespace Global -{ -HINSTANCE dllInstanceHandle; - -LONG dllRefCount = -1; - -CRITICAL_SECTION CS; -HFONT defaultlFontHandle; // Global font object we use everywhere - -//--------------------------------------------------------------------- -// MetasequoiaIME CLSID -//--------------------------------------------------------------------- -// {E3062E9A-D834-4637-8958-ED8CFA427D01} -extern const CLSID MetasequoiaIMECLSID = {0xe3062e9a, 0xd834, 0x4637, {0x89, 0x58, 0xed, 0x8c, 0xfa, 0x42, 0x7d, 0x01}}; - -//--------------------------------------------------------------------- -// Profile GUID -//--------------------------------------------------------------------- -// {4D59B1B4-D503-44AE-9259-BAD9BB2778AB} -extern const GUID MetasequoiaIMEGuidProfile = { - 0x4d59b1b4, 0xd503, 0x44ae, {0x92, 0x59, 0xba, 0xd9, 0xbb, 0x27, 0x78, 0xab}}; - -//--------------------------------------------------------------------- -// PreserveKey GUID -//--------------------------------------------------------------------- -// {34764E82-AE6D-4F71-BB3A-96799AECE466} -extern const GUID MetasequoiaIMEGuidImeModePreserveKey = { - 0x34764e82, 0xae6d, 0x4f71, {0xbb, 0x3a, 0x96, 0x79, 0x9a, 0xec, 0xe4, 0x66}}; - -// {748C1D81-246B-4849-921F-143BA2BED3F5} -extern const GUID MetasequoiaIMEGuidImeModePreserveKey02 = { - 0x748c1d81, 0x246b, 0x4849, {0x92, 0x1f, 0x14, 0x3b, 0xa2, 0xbe, 0xd3, 0xf5}}; - -// {B7E4F2A1-9C3D-4E8F-A1B2-C3D4E5F60718} -extern const GUID MetasequoiaIMEGuidImeModePreserveKey03 = { - 0xb7e4f2a1, 0x9c3d, 0x4e8f, {0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18}}; - -// {D625C0B1-5A8F-4CC4-9C65-6C536BFF2D91} -extern const GUID MetasequoiaIMEGuidEnglishInputModePreserveKey = { - 0xd625c0b1, 0x5a8f, 0x4cc4, {0x9c, 0x65, 0x6c, 0x53, 0x6b, 0xff, 0x2d, 0x91}}; - -// {4393748A-89DC-485C-A7F7-5FA232CEC70B} -extern const GUID MetasequoiaIMEGuidDoubleSingleBytePreserveKey = { - 0x4393748a, 0x89dc, 0x485c, {0xa7, 0xf7, 0x5f, 0xa2, 0x32, 0xce, 0xc7, 0x0b}}; - -// {628DDA3B-38D8-4521-BDD4-85CA38F475B8} -extern const GUID MetasequoiaIMEGuidPunctuationPreserveKey = { - 0x628dda3b, 0x38d8, 0x4521, {0xbd, 0xd4, 0x85, 0xca, 0x38, 0xf4, 0x75, 0xb8}}; - -//--------------------------------------------------------------------- -// Compartments -//--------------------------------------------------------------------- -// {851BC7CB-8395-4FA6-9C95-DB6EFC2E648E} -extern const GUID MetasequoiaIMEGuidCompartmentDoubleSingleByte = { - 0x851bc7cb, 0x8395, 0x4fa6, {0x9c, 0x95, 0xdb, 0x6e, 0xfc, 0x2e, 0x64, 0x8e}}; - -// {58DA9E0F-88B2-426F-91C8-802C9B4D9115} -extern const GUID MetasequoiaIMEGuidCompartmentPunctuation = { - 0x58da9e0f, 0x88b2, 0x426f, {0x91, 0xc8, 0x80, 0x2c, 0x9b, 0x4d, 0x91, 0x15}}; - -//--------------------------------------------------------------------- -// LanguageBars -//--------------------------------------------------------------------- - -// {94B8FD94-E918-4667-93BE-57A49D35B02D} -extern const GUID MetasequoiaIMEGuidLangBarIMEMode = { - 0x94b8fd94, 0xe918, 0x4667, {0x93, 0xbe, 0x57, 0xa4, 0x9d, 0x35, 0xb0, 0x2d}}; - -// {3E044725-9617-402E-B113-9865AD9B4F8E} -extern const GUID MetasequoiaIMEGuidLangBarDoubleSingleByte = { - 0x3e044725, 0x9617, 0x402e, {0xb1, 0x13, 0x98, 0x65, 0xad, 0x9b, 0x4f, 0x8e}}; - -// {596E7EE3-B629-4895-A5B0-C60A82B47A04} -extern const GUID MetasequoiaIMEGuidLangBarPunctuation = { - 0x596e7ee3, 0xb629, 0x4895, {0xa5, 0xb0, 0xc6, 0x0a, 0x82, 0xb4, 0x7a, 0x04}}; - -// {688746FF-BAF2-4153-93ED-96943436422F} -extern const GUID MetasequoiaIMEGuidDisplayAttributeInput = { - 0x688746ff, 0xbaf2, 0x4153, {0x93, 0xed, 0x96, 0x94, 0x34, 0x36, 0x42, 0x2f}}; - -// {1E2209EA-13CD-4550-8A8F-B352E9744DF2} -extern const GUID MetasequoiaIMEGuidDisplayAttributeConverted = { - 0x1e2209ea, 0x13cd, 0x4550, {0x8a, 0x8f, 0xb3, 0x52, 0xe9, 0x74, 0x4d, 0xf2}}; - -//--------------------------------------------------------------------- -// UI element -//--------------------------------------------------------------------- - -// {9FFF12AA-B5EE-4477-A1AA-A4BF5F7B2447} -extern const GUID MetasequoiaIMEGuidCandUIElement = { - 0x9fff12aa, 0xb5ee, 0x4477, {0xa1, 0xaa, 0xa4, 0xbf, 0x5f, 0x7b, 0x24, 0x47}}; - -//--------------------------------------------------------------------- -// Unicode byte order mark -//--------------------------------------------------------------------- -extern const WCHAR UnicodeByteOrderMark = 0xFEFF; - -//--------------------------------------------------------------------- -// dictionary table delimiter -//--------------------------------------------------------------------- -extern const WCHAR KeywordDelimiter = L'='; -extern const WCHAR StringDelimiter = L'\"'; - -//--------------------------------------------------------------------- -// defined item in setting file table [PreservedKey] section -//--------------------------------------------------------------------- -extern const WCHAR ImeModeDescription[] = L"Chinese/English input (Shift)"; -extern const WCHAR ImeModeDescription02[] = L"Chinese/English input (Ctrl+Alt+Space)"; -extern const WCHAR ImeModeDescription03[] = L"Chinese/English input (Ctrl)"; -extern const WCHAR EnglishInputModeDescription[] = L"English candidate input (Ctrl+Shift+E)"; -extern const int ImeModeOnIcoIndex = IME_MODE_ON_ICON_INDEX; -extern const int ImeModeOffIcoIndex = IME_MODE_OFF_ICON_INDEX; - -extern const WCHAR DoubleSingleByteDescription[] = L"Double/Single byte (Ctrl+Shift+Space)"; -extern const int DoubleSingleByteOnIcoIndex = IME_DOUBLE_ON_INDEX; -extern const int DoubleSingleByteOffIcoIndex = IME_DOUBLE_OFF_INDEX; - -extern const WCHAR PunctuationDescription[] = L"Chinese/English punctuation (Ctrl+.)"; -extern const int PunctuationOnIcoIndex = IME_PUNCTUATION_ON_INDEX; -extern const int PunctuationOffIcoIndex = IME_PUNCTUATION_OFF_INDEX; - -//--------------------------------------------------------------------- -// defined item in setting file table [LanguageBar] section -//--------------------------------------------------------------------- -extern const WCHAR LangbarImeModeDescription[] = L"Conversion mode"; -extern const WCHAR LangbarDoubleSingleByteDescription[] = L"Character width"; -extern const WCHAR LangbarPunctuationDescription[] = L"Punctuation"; - -//--------------------------------------------------------------------- -// defined full width characters for Double/Single byte conversion -//--------------------------------------------------------------------- -extern const WCHAR FullWidthCharTable[] = { - 0x3000, // Full width space - 0xFF01, // ! - 0xFF02, // " - 0xFF03, // # - 0xFF04, // $ - 0xFF05, // % - 0xFF06, // & - 0xFF07, // ' - 0xFF08, // ( - 0xFF09, // ) - 0xFF0A, // * - 0xFF0B, // + - 0xFF0C, // , - 0xFF0D, // - - 0xFF0E, // . - 0xFF0F, // / - - 0xFF10, // 0 - 0xFF11, // 1 - 0xFF12, // 2 - 0xFF13, // 3 - 0xFF14, // 4 - 0xFF15, // 5 - 0xFF16, // 6 - 0xFF17, // 7 - 0xFF18, // 8 - 0xFF19, // 9 - 0xFF1A, // : - 0xFF1B, // ; - 0xFF1C, // < - 0xFF1D, // = - 0xFF1E, // > - 0xFF1F, // ? - - 0xFF20, // @ - 0xFF21, // A - 0xFF22, // B - 0xFF23, // C - 0xFF24, // D - 0xFF25, // E - 0xFF26, // F - 0xFF27, // G - 0xFF28, // H - 0xFF29, // I - 0xFF2A, // J - 0xFF2B, // K - 0xFF2C, // L - 0xFF2D, // M - 0xFF2E, // N - 0xFF2F, // O - - 0xFF30, // P - 0xFF31, // Q - 0xFF32, // R - 0xFF33, // S - 0xFF34, // T - 0xFF35, // U - 0xFF36, // V - 0xFF37, // W - 0xFF38, // X - 0xFF39, // Y - 0xFF3A, // Z - 0xFF3B, // [ - 0xFF3C, // \ - 0xFF3D, // ] - 0xFF3E, // ^ - 0xFF3F, // _ - - 0xFF40, // ` - 0xFF41, // a - 0xFF42, // b - 0xFF43, // c - 0xFF44, // d - 0xFF45, // e - 0xFF46, // f - 0xFF47, // g - 0xFF48, // h - 0xFF49, // i - 0xFF4A, // j - 0xFF4B, // k - 0xFF4C, // l - 0xFF4D, // m - 0xFF4E, // n - 0xFF4F, // o - - 0xFF50, // p - 0xFF51, // q - 0xFF52, // r - 0xFF53, // s - 0xFF54, // t - 0xFF55, // u - 0xFF56, // v - 0xFF57, // w - 0xFF58, // x - 0xFF59, // y - 0xFF5A, // z - 0xFF5B, // { - 0xFF5C, // | - 0xFF5D, // } - 0xFF5E // ~ -}; - -//--------------------------------------------------------------------- -// defined punctuation characters -//--------------------------------------------------------------------- -// '<' and '>' are deliberately absent: they belong to the nest pair set up in -// SetupPunctuationPair, and GetPunctuation searches this table first. Listing them here -// would shadow the nest pair, and the nested marks would never be produced. -extern const struct _PUNCTUATION PunctuationTable[23] = { - {L'`', L"·"}, // · - {L'~', L"~"}, // ~ - {L'!', L"!"}, // ! - {L'@', L"@"}, // @ - {L'#', L"#"}, // # - {L'$', L"¥"}, // ¥ - {L'%', L"%"}, // % - {L'^', L"……"}, // …… - {L'&', L"&"}, // & - {L'*', L"*"}, // * - {L'(', L"("}, // ( - {L')', L")"}, // ) - {L'_', L"——"}, // —— - {L'[', L"【"}, // 【 - {L']', L"】"}, // 】 - {L'{', L"{"}, // { - {L'}', L"}"}, // } - {L'\\', L"、"}, // 、 - {L';', L";"}, // ; - {L':', L":"}, // : - {L',', L","}, // , - {L'.', L"。"}, // 。 - {L'?', L"?"}, // ? -}; - -// -// Will commit the highlighted candidate string with a punctuation character. -// -extern const std::unordered_set CommitWithHighlightedCandPunc = { - L'`', // - L'!', // - L'@', // - L'#', // - L'$', // - L'%', // - L'^', // - L'&', // - L'*', // - L'(', // - L')', // - L'-', // - L'_', // - L'=', // - L'+', // - L'[', // - L']', // - L'\\', // - L';', // - L':', // - L'\'', // - L'"', // - L',', // - L'<', // - L'.', // - L'>', // - L'?' // -}; - -//+--------------------------------------------------------------------------- -// -// CheckModifiers -// -//---------------------------------------------------------------------------- - -#define TF_MOD_ALLALT (TF_MOD_RALT | TF_MOD_LALT | TF_MOD_ALT) -#define TF_MOD_ALLCONTROL (TF_MOD_RCONTROL | TF_MOD_LCONTROL | TF_MOD_CONTROL) -#define TF_MOD_ALLSHIFT (TF_MOD_RSHIFT | TF_MOD_LSHIFT | TF_MOD_SHIFT) -#define TF_MOD_RLALT (TF_MOD_RALT | TF_MOD_LALT) -#define TF_MOD_RLCONTROL (TF_MOD_RCONTROL | TF_MOD_LCONTROL) -#define TF_MOD_RLSHIFT (TF_MOD_RSHIFT | TF_MOD_LSHIFT) - -#define CheckMod(m0, m1, mod) \ - if (m1 & TF_MOD_##mod##) \ - { \ - if (!(m0 & TF_MOD_##mod##)) \ - { \ - return FALSE; \ - } \ - } \ - else \ - { \ - if ((m1 ^ m0) & TF_MOD_RL##mod##) \ - { \ - return FALSE; \ - } \ - } - -BOOL CheckModifiers(UINT modCurrent, UINT mod) -{ - mod &= ~TF_MOD_ON_KEYUP; - - if (mod & TF_MOD_IGNORE_ALL_MODIFIER) - { - return TRUE; - } - - if (modCurrent == mod) - { - return TRUE; - } - - if (modCurrent && !mod) - { - return FALSE; - } - - CheckMod(modCurrent, mod, ALT); - CheckMod(modCurrent, mod, SHIFT); - CheckMod(modCurrent, mod, CONTROL); - - return TRUE; -} - -//+--------------------------------------------------------------------------- -// -// UpdateModifiers -// -// wParam - virtual-key code -// lParam - [0-15] Repeat count -// [16-23] Scan code -// [24] Extended key -// [25-28] Reserved -// [29] Context code -// [30] Previous key state -// [31] Transition state -//---------------------------------------------------------------------------- - -thread_local USHORT ModifiersValue = 0; -thread_local BOOL IsShiftKeyDownOnly = FALSE; -thread_local BOOL IsControlKeyDownOnly = FALSE; -thread_local BOOL IsAltKeyDownOnly = FALSE; -thread_local BOOL PureShiftKeyDown = FALSE; -thread_local BOOL PureShiftKeyUp = FALSE; - -BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam) -{ - // high-order bit : key down - // low-order bit : toggled - SHORT sksMenu = GetKeyState(VK_MENU); - SHORT sksCtrl = GetKeyState(VK_CONTROL); - SHORT sksShft = GetKeyState(VK_SHIFT); - - PureShiftKeyUp = FALSE; - - switch (wParam & 0xff) - { - case VK_MENU: - // is VK_MENU down? - if (sksMenu & 0x8000) - { - // is extended key? - if (lParam & 0x01000000) - { - ModifiersValue |= (TF_MOD_RALT | TF_MOD_ALT); - } - else - { - ModifiersValue |= (TF_MOD_LALT | TF_MOD_ALT); - } - - // is previous key state up? - if (!(lParam & 0x40000000)) - { - // is VK_CONTROL and VK_SHIFT up? - if (!(sksCtrl & 0x8000) && !(sksShft & 0x8000)) - { - IsAltKeyDownOnly = TRUE; - } - else - { - IsShiftKeyDownOnly = FALSE; - IsControlKeyDownOnly = FALSE; - IsAltKeyDownOnly = FALSE; - } - } - } - break; - - case VK_CONTROL: - // is VK_CONTROL down? - if (sksCtrl & 0x8000) - { - // is extended key? - if (lParam & 0x01000000) - { - ModifiersValue |= (TF_MOD_RCONTROL | TF_MOD_CONTROL); - } - else - { - ModifiersValue |= (TF_MOD_LCONTROL | TF_MOD_CONTROL); - } - - // is previous key state up? - if (!(lParam & 0x40000000)) - { - // is VK_SHIFT and VK_MENU up? - if (!(sksShft & 0x8000) && !(sksMenu & 0x8000)) - { - IsControlKeyDownOnly = TRUE; - } - else - { - IsShiftKeyDownOnly = FALSE; - IsControlKeyDownOnly = FALSE; - IsAltKeyDownOnly = FALSE; - } - } - } - break; - - case VK_SHIFT: { - // is VK_SHIFT down? - if (sksShft & 0x8000) - { - PureShiftKeyDown = TRUE; - // is scan code 0x36(right shift)? - if (((lParam >> 16) & 0x00ff) == 0x36) - { - ModifiersValue |= (TF_MOD_RSHIFT | TF_MOD_SHIFT); - } - else - { - ModifiersValue |= (TF_MOD_LSHIFT | TF_MOD_SHIFT); - } - - // is previous key state up? - if (!(lParam & 0x40000000)) - { - // is VK_MENU and VK_CONTROL up? - if (!(sksMenu & 0x8000) && !(sksCtrl & 0x8000)) - { - IsShiftKeyDownOnly = TRUE; - } - else - { - IsShiftKeyDownOnly = FALSE; - IsControlKeyDownOnly = FALSE; - IsAltKeyDownOnly = FALSE; - } - } - } - else - { - if (PureShiftKeyDown) - PureShiftKeyUp = TRUE; - } - break; - } - - default: - IsShiftKeyDownOnly = FALSE; - IsControlKeyDownOnly = FALSE; - IsAltKeyDownOnly = FALSE; - PureShiftKeyDown = FALSE; - break; - } - - if (!(sksMenu & 0x8000)) - { - ModifiersValue &= ~TF_MOD_ALLALT; - } - if (!(sksCtrl & 0x8000)) - { - ModifiersValue &= ~TF_MOD_ALLCONTROL; - } - if (!(sksShft & 0x8000)) - { - ModifiersValue &= ~TF_MOD_ALLSHIFT; - } - - return TRUE; -} - -//--------------------------------------------------------------------- -// override CompareElements -//--------------------------------------------------------------------- -BOOL CompareElements(LCID locale, const CStringRange *pElement1, const CStringRange *pElement2) -{ - return (CStringRange::Compare(locale, (CStringRange *)pElement1, (CStringRange *)pElement2) == CSTR_EQUAL) ? TRUE - : FALSE; -} -} // namespace Global +#include "Globals.h" +#include "Private.h" +#include "resource.h" +#include "define.h" +#include "MetasequoiaIMEBaseStructure.h" +#include +#include +#include +#include +#include +#include "FanyUtils.h" + +namespace Global +{ +HINSTANCE dllInstanceHandle; + +LONG dllRefCount = -1; + +CRITICAL_SECTION CS; +HFONT defaultlFontHandle; // Global font object we use everywhere + +//--------------------------------------------------------------------- +// MetasequoiaIME CLSID +//--------------------------------------------------------------------- +// {E3062E9A-D834-4637-8958-ED8CFA427D01} +extern const CLSID MetasequoiaIMECLSID = {0xe3062e9a, 0xd834, 0x4637, {0x89, 0x58, 0xed, 0x8c, 0xfa, 0x42, 0x7d, 0x01}}; + +//--------------------------------------------------------------------- +// Profile GUID +//--------------------------------------------------------------------- +// {4D59B1B4-D503-44AE-9259-BAD9BB2778AB} +extern const GUID MetasequoiaIMEGuidProfile = { + 0x4d59b1b4, 0xd503, 0x44ae, {0x92, 0x59, 0xba, 0xd9, 0xbb, 0x27, 0x78, 0xab}}; + +//--------------------------------------------------------------------- +// PreserveKey GUID +//--------------------------------------------------------------------- +// {34764E82-AE6D-4F71-BB3A-96799AECE466} +extern const GUID MetasequoiaIMEGuidImeModePreserveKey = { + 0x34764e82, 0xae6d, 0x4f71, {0xbb, 0x3a, 0x96, 0x79, 0x9a, 0xec, 0xe4, 0x66}}; + +// {748C1D81-246B-4849-921F-143BA2BED3F5} +extern const GUID MetasequoiaIMEGuidImeModePreserveKey02 = { + 0x748c1d81, 0x246b, 0x4849, {0x92, 0x1f, 0x14, 0x3b, 0xa2, 0xbe, 0xd3, 0xf5}}; + +// {B7E4F2A1-9C3D-4E8F-A1B2-C3D4E5F60718} +extern const GUID MetasequoiaIMEGuidImeModePreserveKey03 = { + 0xb7e4f2a1, 0x9c3d, 0x4e8f, {0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18}}; + +// {D625C0B1-5A8F-4CC4-9C65-6C536BFF2D91} +extern const GUID MetasequoiaIMEGuidEnglishInputModePreserveKey = { + 0xd625c0b1, 0x5a8f, 0x4cc4, {0x9c, 0x65, 0x6c, 0x53, 0x6b, 0xff, 0x2d, 0x91}}; + +// {4393748A-89DC-485C-A7F7-5FA232CEC70B} +extern const GUID MetasequoiaIMEGuidDoubleSingleBytePreserveKey = { + 0x4393748a, 0x89dc, 0x485c, {0xa7, 0xf7, 0x5f, 0xa2, 0x32, 0xce, 0xc7, 0x0b}}; + +// {628DDA3B-38D8-4521-BDD4-85CA38F475B8} +extern const GUID MetasequoiaIMEGuidPunctuationPreserveKey = { + 0x628dda3b, 0x38d8, 0x4521, {0xbd, 0xd4, 0x85, 0xca, 0x38, 0xf4, 0x75, 0xb8}}; + +//--------------------------------------------------------------------- +// Compartments +//--------------------------------------------------------------------- +// {851BC7CB-8395-4FA6-9C95-DB6EFC2E648E} +extern const GUID MetasequoiaIMEGuidCompartmentDoubleSingleByte = { + 0x851bc7cb, 0x8395, 0x4fa6, {0x9c, 0x95, 0xdb, 0x6e, 0xfc, 0x2e, 0x64, 0x8e}}; + +// {58DA9E0F-88B2-426F-91C8-802C9B4D9115} +extern const GUID MetasequoiaIMEGuidCompartmentPunctuation = { + 0x58da9e0f, 0x88b2, 0x426f, {0x91, 0xc8, 0x80, 0x2c, 0x9b, 0x4d, 0x91, 0x15}}; + +//--------------------------------------------------------------------- +// LanguageBars +//--------------------------------------------------------------------- + +// {94B8FD94-E918-4667-93BE-57A49D35B02D} +extern const GUID MetasequoiaIMEGuidLangBarIMEMode = { + 0x94b8fd94, 0xe918, 0x4667, {0x93, 0xbe, 0x57, 0xa4, 0x9d, 0x35, 0xb0, 0x2d}}; + +// {3E044725-9617-402E-B113-9865AD9B4F8E} +extern const GUID MetasequoiaIMEGuidLangBarDoubleSingleByte = { + 0x3e044725, 0x9617, 0x402e, {0xb1, 0x13, 0x98, 0x65, 0xad, 0x9b, 0x4f, 0x8e}}; + +// {596E7EE3-B629-4895-A5B0-C60A82B47A04} +extern const GUID MetasequoiaIMEGuidLangBarPunctuation = { + 0x596e7ee3, 0xb629, 0x4895, {0xa5, 0xb0, 0xc6, 0x0a, 0x82, 0xb4, 0x7a, 0x04}}; + +// {688746FF-BAF2-4153-93ED-96943436422F} +extern const GUID MetasequoiaIMEGuidDisplayAttributeInput = { + 0x688746ff, 0xbaf2, 0x4153, {0x93, 0xed, 0x96, 0x94, 0x34, 0x36, 0x42, 0x2f}}; + +// {1E2209EA-13CD-4550-8A8F-B352E9744DF2} +extern const GUID MetasequoiaIMEGuidDisplayAttributeConverted = { + 0x1e2209ea, 0x13cd, 0x4550, {0x8a, 0x8f, 0xb3, 0x52, 0xe9, 0x74, 0x4d, 0xf2}}; + +//--------------------------------------------------------------------- +// UI element +//--------------------------------------------------------------------- + +// {9FFF12AA-B5EE-4477-A1AA-A4BF5F7B2447} +extern const GUID MetasequoiaIMEGuidCandUIElement = { + 0x9fff12aa, 0xb5ee, 0x4477, {0xa1, 0xaa, 0xa4, 0xbf, 0x5f, 0x7b, 0x24, 0x47}}; + +//--------------------------------------------------------------------- +// Unicode byte order mark +//--------------------------------------------------------------------- +extern const WCHAR UnicodeByteOrderMark = 0xFEFF; + +//--------------------------------------------------------------------- +// dictionary table delimiter +//--------------------------------------------------------------------- +extern const WCHAR KeywordDelimiter = L'='; +extern const WCHAR StringDelimiter = L'\"'; + +//--------------------------------------------------------------------- +// defined item in setting file table [PreservedKey] section +//--------------------------------------------------------------------- +extern const WCHAR ImeModeDescription[] = L"Chinese/English input (Shift)"; +extern const WCHAR ImeModeDescription02[] = L"Chinese/English input (Ctrl+Alt+Space)"; +extern const WCHAR ImeModeDescription03[] = L"Chinese/English input (Ctrl)"; +extern const WCHAR EnglishInputModeDescription[] = L"English candidate input (Ctrl+Shift+E)"; +extern const int ImeModeOnIcoIndex = IME_MODE_ON_ICON_INDEX; +extern const int ImeModeOffIcoIndex = IME_MODE_OFF_ICON_INDEX; + +extern const WCHAR DoubleSingleByteDescription[] = L"Double/Single byte (Ctrl+Shift+Space)"; +extern const int DoubleSingleByteOnIcoIndex = IME_DOUBLE_ON_INDEX; +extern const int DoubleSingleByteOffIcoIndex = IME_DOUBLE_OFF_INDEX; + +extern const WCHAR PunctuationDescription[] = L"Chinese/English punctuation (Ctrl+.)"; +extern const int PunctuationOnIcoIndex = IME_PUNCTUATION_ON_INDEX; +extern const int PunctuationOffIcoIndex = IME_PUNCTUATION_OFF_INDEX; + +//--------------------------------------------------------------------- +// defined item in setting file table [LanguageBar] section +//--------------------------------------------------------------------- +extern const WCHAR LangbarImeModeDescription[] = L"Conversion mode"; +extern const WCHAR LangbarDoubleSingleByteDescription[] = L"Character width"; +extern const WCHAR LangbarPunctuationDescription[] = L"Punctuation"; + +//--------------------------------------------------------------------- +// defined full width characters for Double/Single byte conversion +//--------------------------------------------------------------------- +extern const WCHAR FullWidthCharTable[] = { + 0x3000, // Full width space + 0xFF01, // ! + 0xFF02, // " + 0xFF03, // # + 0xFF04, // $ + 0xFF05, // % + 0xFF06, // & + 0xFF07, // ' + 0xFF08, // ( + 0xFF09, // ) + 0xFF0A, // * + 0xFF0B, // + + 0xFF0C, // , + 0xFF0D, // - + 0xFF0E, // . + 0xFF0F, // / + + 0xFF10, // 0 + 0xFF11, // 1 + 0xFF12, // 2 + 0xFF13, // 3 + 0xFF14, // 4 + 0xFF15, // 5 + 0xFF16, // 6 + 0xFF17, // 7 + 0xFF18, // 8 + 0xFF19, // 9 + 0xFF1A, // : + 0xFF1B, // ; + 0xFF1C, // < + 0xFF1D, // = + 0xFF1E, // > + 0xFF1F, // ? + + 0xFF20, // @ + 0xFF21, // A + 0xFF22, // B + 0xFF23, // C + 0xFF24, // D + 0xFF25, // E + 0xFF26, // F + 0xFF27, // G + 0xFF28, // H + 0xFF29, // I + 0xFF2A, // J + 0xFF2B, // K + 0xFF2C, // L + 0xFF2D, // M + 0xFF2E, // N + 0xFF2F, // O + + 0xFF30, // P + 0xFF31, // Q + 0xFF32, // R + 0xFF33, // S + 0xFF34, // T + 0xFF35, // U + 0xFF36, // V + 0xFF37, // W + 0xFF38, // X + 0xFF39, // Y + 0xFF3A, // Z + 0xFF3B, // [ + 0xFF3C, // \ + 0xFF3D, // ] + 0xFF3E, // ^ + 0xFF3F, // _ + + 0xFF40, // ` + 0xFF41, // a + 0xFF42, // b + 0xFF43, // c + 0xFF44, // d + 0xFF45, // e + 0xFF46, // f + 0xFF47, // g + 0xFF48, // h + 0xFF49, // i + 0xFF4A, // j + 0xFF4B, // k + 0xFF4C, // l + 0xFF4D, // m + 0xFF4E, // n + 0xFF4F, // o + + 0xFF50, // p + 0xFF51, // q + 0xFF52, // r + 0xFF53, // s + 0xFF54, // t + 0xFF55, // u + 0xFF56, // v + 0xFF57, // w + 0xFF58, // x + 0xFF59, // y + 0xFF5A, // z + 0xFF5B, // { + 0xFF5C, // | + 0xFF5D, // } + 0xFF5E // ~ +}; + +//--------------------------------------------------------------------- +// defined punctuation characters +//--------------------------------------------------------------------- +// '<' and '>' are deliberately absent: they belong to the nest pair set up in +// SetupPunctuationPair, and GetPunctuation searches this table first. Listing them here +// would shadow the nest pair, and the nested marks would never be produced. +extern const struct _PUNCTUATION PunctuationTable[23] = { + {L'`', L"·"}, // · + {L'~', L"~"}, // ~ + {L'!', L"!"}, // ! + {L'@', L"@"}, // @ + {L'#', L"#"}, // # + {L'$', L"¥"}, // ¥ + {L'%', L"%"}, // % + {L'^', L"……"}, // …… + {L'&', L"&"}, // & + {L'*', L"*"}, // * + {L'(', L"("}, // ( + {L')', L")"}, // ) + {L'_', L"——"}, // —— + {L'[', L"【"}, // 【 + {L']', L"】"}, // 】 + {L'{', L"{"}, // { + {L'}', L"}"}, // } + {L'\\', L"、"}, // 、 + {L';', L";"}, // ; + {L':', L":"}, // : + {L',', L","}, // , + {L'.', L"。"}, // 。 + {L'?', L"?"}, // ? +}; + +// +// Will commit the highlighted candidate string with a punctuation character. +// +extern const std::unordered_set CommitWithHighlightedCandPunc = { + L'`', // + L'!', // + L'@', // + L'#', // + L'$', // + L'%', // + L'^', // + L'&', // + L'*', // + L'(', // + L')', // + L'-', // + L'_', // + L'=', // + L'+', // + L'[', // + L']', // + L'\\', // + L';', // + L':', // + L'\'', // + L'"', // + L',', // + L'<', // + L'.', // + L'>', // + L'?' // +}; + +//+--------------------------------------------------------------------------- +// +// CheckModifiers +// +//---------------------------------------------------------------------------- + +#define TF_MOD_ALLALT (TF_MOD_RALT | TF_MOD_LALT | TF_MOD_ALT) +#define TF_MOD_ALLCONTROL (TF_MOD_RCONTROL | TF_MOD_LCONTROL | TF_MOD_CONTROL) +#define TF_MOD_ALLSHIFT (TF_MOD_RSHIFT | TF_MOD_LSHIFT | TF_MOD_SHIFT) +#define TF_MOD_RLALT (TF_MOD_RALT | TF_MOD_LALT) +#define TF_MOD_RLCONTROL (TF_MOD_RCONTROL | TF_MOD_LCONTROL) +#define TF_MOD_RLSHIFT (TF_MOD_RSHIFT | TF_MOD_LSHIFT) + +#define CheckMod(m0, m1, mod) \ + if (m1 & TF_MOD_##mod##) \ + { \ + if (!(m0 & TF_MOD_##mod##)) \ + { \ + return FALSE; \ + } \ + } \ + else \ + { \ + if ((m1 ^ m0) & TF_MOD_RL##mod##) \ + { \ + return FALSE; \ + } \ + } + +BOOL CheckModifiers(UINT modCurrent, UINT mod) +{ + mod &= ~TF_MOD_ON_KEYUP; + + if (mod & TF_MOD_IGNORE_ALL_MODIFIER) + { + return TRUE; + } + + if (modCurrent == mod) + { + return TRUE; + } + + if (modCurrent && !mod) + { + return FALSE; + } + + CheckMod(modCurrent, mod, ALT); + CheckMod(modCurrent, mod, SHIFT); + CheckMod(modCurrent, mod, CONTROL); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// UpdateModifiers +// +// wParam - virtual-key code +// lParam - [0-15] Repeat count +// [16-23] Scan code +// [24] Extended key +// [25-28] Reserved +// [29] Context code +// [30] Previous key state +// [31] Transition state +//---------------------------------------------------------------------------- + +thread_local USHORT ModifiersValue = 0; +thread_local BOOL IsShiftKeyDownOnly = FALSE; +thread_local BOOL IsControlKeyDownOnly = FALSE; +thread_local BOOL IsAltKeyDownOnly = FALSE; +thread_local BOOL PureShiftKeyDown = FALSE; +thread_local BOOL PureShiftKeyUp = FALSE; + +BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam) +{ + // high-order bit : key down + // low-order bit : toggled + SHORT sksMenu = GetKeyState(VK_MENU); + SHORT sksCtrl = GetKeyState(VK_CONTROL); + SHORT sksShft = GetKeyState(VK_SHIFT); + + PureShiftKeyUp = FALSE; + + switch (wParam & 0xff) + { + case VK_MENU: + // is VK_MENU down? + if (sksMenu & 0x8000) + { + // is extended key? + if (lParam & 0x01000000) + { + ModifiersValue |= (TF_MOD_RALT | TF_MOD_ALT); + } + else + { + ModifiersValue |= (TF_MOD_LALT | TF_MOD_ALT); + } + + // is previous key state up? + if (!(lParam & 0x40000000)) + { + // is VK_CONTROL and VK_SHIFT up? + if (!(sksCtrl & 0x8000) && !(sksShft & 0x8000)) + { + IsAltKeyDownOnly = TRUE; + } + else + { + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + } + } + } + break; + + case VK_CONTROL: + // is VK_CONTROL down? + if (sksCtrl & 0x8000) + { + // is extended key? + if (lParam & 0x01000000) + { + ModifiersValue |= (TF_MOD_RCONTROL | TF_MOD_CONTROL); + } + else + { + ModifiersValue |= (TF_MOD_LCONTROL | TF_MOD_CONTROL); + } + + // is previous key state up? + if (!(lParam & 0x40000000)) + { + // is VK_SHIFT and VK_MENU up? + if (!(sksShft & 0x8000) && !(sksMenu & 0x8000)) + { + IsControlKeyDownOnly = TRUE; + } + else + { + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + } + } + } + break; + + case VK_SHIFT: { + // is VK_SHIFT down? + if (sksShft & 0x8000) + { + PureShiftKeyDown = TRUE; + // is scan code 0x36(right shift)? + if (((lParam >> 16) & 0x00ff) == 0x36) + { + ModifiersValue |= (TF_MOD_RSHIFT | TF_MOD_SHIFT); + } + else + { + ModifiersValue |= (TF_MOD_LSHIFT | TF_MOD_SHIFT); + } + + // is previous key state up? + if (!(lParam & 0x40000000)) + { + // is VK_MENU and VK_CONTROL up? + if (!(sksMenu & 0x8000) && !(sksCtrl & 0x8000)) + { + IsShiftKeyDownOnly = TRUE; + } + else + { + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + } + } + } + else + { + if (PureShiftKeyDown) + PureShiftKeyUp = TRUE; + } + break; + } + + default: + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + PureShiftKeyDown = FALSE; + break; + } + + if (!(sksMenu & 0x8000)) + { + ModifiersValue &= ~TF_MOD_ALLALT; + } + if (!(sksCtrl & 0x8000)) + { + ModifiersValue &= ~TF_MOD_ALLCONTROL; + } + if (!(sksShft & 0x8000)) + { + ModifiersValue &= ~TF_MOD_ALLSHIFT; + } + + return TRUE; +} + +//--------------------------------------------------------------------- +// override CompareElements +//--------------------------------------------------------------------- +BOOL CompareElements(LCID locale, const CStringRange *pElement1, const CStringRange *pElement2) +{ + return (CStringRange::Compare(locale, (CStringRange *)pElement1, (CStringRange *)pElement2) == CSTR_EQUAL) ? TRUE + : FALSE; +} +} // namespace Global diff --git a/windows/src/Global/Globals.h b/windows/src/Global/Globals.h index 82da5518..02ea1cd4 100644 --- a/windows/src/Global/Globals.h +++ b/windows/src/Global/Globals.h @@ -1,170 +1,170 @@ -#pragma once - -#include "Private.h" -#include "define.h" -#include "MetasequoiaIMEBaseStructure.h" -#include -#include -#include -#include - -void DllAddRef(); -void DllRelease(); - -using namespace Microsoft::WRL; - -namespace Global -{ -//--------------------------------------------------------------------- -// inline -//--------------------------------------------------------------------- - -inline void SafeRelease(_In_ IUnknown *punk) -{ - if (punk != nullptr) - { - punk->Release(); - } -} - -inline void QuickVariantInit(_Inout_ VARIANT *pvar) -{ - pvar->vt = VT_EMPTY; -} - -inline void QuickVariantClear(_Inout_ VARIANT *pvar) -{ - switch (pvar->vt) - { - // some ovbious VTs that don't need to call VariantClear. - case VT_EMPTY: - case VT_NULL: - case VT_I2: - case VT_I4: - case VT_R4: - case VT_R8: - case VT_CY: - case VT_DATE: - case VT_I1: - case VT_UI1: - case VT_UI2: - case VT_UI4: - case VT_I8: - case VT_UI8: - case VT_INT: - case VT_UINT: - case VT_BOOL: - break; - - // Call release for VT_UNKNOWN. - case VT_UNKNOWN: - SafeRelease(pvar->punkVal); - break; - - default: - // we call OleAut32 for other VTs. - VariantClear(pvar); - break; - } - pvar->vt = VT_EMPTY; -} - -//+--------------------------------------------------------------------------- -// -// IsTooSimilar -// -// Return TRUE if the colors cr1 and cr2 are so similar that they -// are hard to distinguish. Used for deciding to use reverse video -// selection instead of system selection colors. -// -//---------------------------------------------------------------------------- - -inline BOOL IsTooSimilar(COLORREF cr1, COLORREF cr2) -{ - if ((cr1 | cr2) & 0xFF000000) // One color and/or the other isn't RGB, so algorithm doesn't apply - { - return FALSE; - } - - LONG DeltaR = abs(GetRValue(cr1) - GetRValue(cr2)); - LONG DeltaG = abs(GetGValue(cr1) - GetGValue(cr2)); - LONG DeltaB = abs(GetBValue(cr1) - GetBValue(cr2)); - - return DeltaR + DeltaG + DeltaB < 80; -} - -//--------------------------------------------------------------------- -// extern -//--------------------------------------------------------------------- -extern HINSTANCE dllInstanceHandle; - -extern LONG dllRefCount; - -extern CRITICAL_SECTION CS; -extern HFONT defaultlFontHandle; // Global font object we use everywhere - -extern const CLSID MetasequoiaIMECLSID; -extern const GUID MetasequoiaIMEGuidProfile; -extern const GUID MetasequoiaIMEGuidImeModePreserveKey; -extern const GUID MetasequoiaIMEGuidImeModePreserveKey02; -extern const GUID MetasequoiaIMEGuidImeModePreserveKey03; -extern const GUID MetasequoiaIMEGuidEnglishInputModePreserveKey; -extern const GUID MetasequoiaIMEGuidDoubleSingleBytePreserveKey; -extern const GUID MetasequoiaIMEGuidPunctuationPreserveKey; - -LRESULT CALLBACK ThreadKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); -BOOL CheckModifiers(UINT uModCurrent, UINT uMod); -BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam); - -extern thread_local USHORT ModifiersValue; -extern thread_local BOOL IsShiftKeyDownOnly; -extern thread_local BOOL IsControlKeyDownOnly; -extern thread_local BOOL IsAltKeyDownOnly; -extern thread_local BOOL PureShiftKeyDown; -extern thread_local BOOL PureShiftKeyUp; - -extern const GUID MetasequoiaIMEGuidCompartmentDoubleSingleByte; -extern const GUID MetasequoiaIMEGuidCompartmentPunctuation; - -extern const WCHAR FullWidthCharTable[]; -extern const struct _PUNCTUATION PunctuationTable[23]; -extern const std::unordered_set CommitWithHighlightedCandPunc; - -extern const GUID MetasequoiaIMEGuidLangBarIMEMode; -extern const GUID MetasequoiaIMEGuidLangBarDoubleSingleByte; -extern const GUID MetasequoiaIMEGuidLangBarPunctuation; - -extern const GUID MetasequoiaIMEGuidDisplayAttributeInput; -extern const GUID MetasequoiaIMEGuidDisplayAttributeConverted; - -extern const GUID MetasequoiaIMEGuidCandUIElement; - -extern const WCHAR UnicodeByteOrderMark; -extern const WCHAR KeywordDelimiter; -extern const WCHAR StringDelimiter; - -extern const WCHAR ImeModeDescription[]; -extern const WCHAR ImeModeDescription02[]; -extern const WCHAR ImeModeDescription03[]; -extern const WCHAR EnglishInputModeDescription[]; -extern const int ImeModeOnIcoIndex; -extern const int ImeModeOffIcoIndex; - -extern const WCHAR DoubleSingleByteDescription[]; -extern const int DoubleSingleByteOnIcoIndex; -extern const int DoubleSingleByteOffIcoIndex; - -extern const WCHAR PunctuationDescription[]; -extern const int PunctuationOnIcoIndex; -extern const int PunctuationOffIcoIndex; - -extern const WCHAR LangbarImeModeDescription[]; -extern const WCHAR LangbarDoubleSingleByteDescription[]; -extern const WCHAR LangbarPunctuationDescription[]; - -inline std::vector WStringCandidateList; -inline std::wstring FindKeyCode; - -inline POINT PointDTO = {0, 0}; -inline std::wstring CandidateWString = L""; -} // namespace Global +#pragma once + +#include "Private.h" +#include "define.h" +#include "MetasequoiaIMEBaseStructure.h" +#include +#include +#include +#include + +void DllAddRef(); +void DllRelease(); + +using namespace Microsoft::WRL; + +namespace Global +{ +//--------------------------------------------------------------------- +// inline +//--------------------------------------------------------------------- + +inline void SafeRelease(_In_ IUnknown *punk) +{ + if (punk != nullptr) + { + punk->Release(); + } +} + +inline void QuickVariantInit(_Inout_ VARIANT *pvar) +{ + pvar->vt = VT_EMPTY; +} + +inline void QuickVariantClear(_Inout_ VARIANT *pvar) +{ + switch (pvar->vt) + { + // some ovbious VTs that don't need to call VariantClear. + case VT_EMPTY: + case VT_NULL: + case VT_I2: + case VT_I4: + case VT_R4: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_I1: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_I8: + case VT_UI8: + case VT_INT: + case VT_UINT: + case VT_BOOL: + break; + + // Call release for VT_UNKNOWN. + case VT_UNKNOWN: + SafeRelease(pvar->punkVal); + break; + + default: + // we call OleAut32 for other VTs. + VariantClear(pvar); + break; + } + pvar->vt = VT_EMPTY; +} + +//+--------------------------------------------------------------------------- +// +// IsTooSimilar +// +// Return TRUE if the colors cr1 and cr2 are so similar that they +// are hard to distinguish. Used for deciding to use reverse video +// selection instead of system selection colors. +// +//---------------------------------------------------------------------------- + +inline BOOL IsTooSimilar(COLORREF cr1, COLORREF cr2) +{ + if ((cr1 | cr2) & 0xFF000000) // One color and/or the other isn't RGB, so algorithm doesn't apply + { + return FALSE; + } + + LONG DeltaR = abs(GetRValue(cr1) - GetRValue(cr2)); + LONG DeltaG = abs(GetGValue(cr1) - GetGValue(cr2)); + LONG DeltaB = abs(GetBValue(cr1) - GetBValue(cr2)); + + return DeltaR + DeltaG + DeltaB < 80; +} + +//--------------------------------------------------------------------- +// extern +//--------------------------------------------------------------------- +extern HINSTANCE dllInstanceHandle; + +extern LONG dllRefCount; + +extern CRITICAL_SECTION CS; +extern HFONT defaultlFontHandle; // Global font object we use everywhere + +extern const CLSID MetasequoiaIMECLSID; +extern const GUID MetasequoiaIMEGuidProfile; +extern const GUID MetasequoiaIMEGuidImeModePreserveKey; +extern const GUID MetasequoiaIMEGuidImeModePreserveKey02; +extern const GUID MetasequoiaIMEGuidImeModePreserveKey03; +extern const GUID MetasequoiaIMEGuidEnglishInputModePreserveKey; +extern const GUID MetasequoiaIMEGuidDoubleSingleBytePreserveKey; +extern const GUID MetasequoiaIMEGuidPunctuationPreserveKey; + +LRESULT CALLBACK ThreadKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); +BOOL CheckModifiers(UINT uModCurrent, UINT uMod); +BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam); + +extern thread_local USHORT ModifiersValue; +extern thread_local BOOL IsShiftKeyDownOnly; +extern thread_local BOOL IsControlKeyDownOnly; +extern thread_local BOOL IsAltKeyDownOnly; +extern thread_local BOOL PureShiftKeyDown; +extern thread_local BOOL PureShiftKeyUp; + +extern const GUID MetasequoiaIMEGuidCompartmentDoubleSingleByte; +extern const GUID MetasequoiaIMEGuidCompartmentPunctuation; + +extern const WCHAR FullWidthCharTable[]; +extern const struct _PUNCTUATION PunctuationTable[23]; +extern const std::unordered_set CommitWithHighlightedCandPunc; + +extern const GUID MetasequoiaIMEGuidLangBarIMEMode; +extern const GUID MetasequoiaIMEGuidLangBarDoubleSingleByte; +extern const GUID MetasequoiaIMEGuidLangBarPunctuation; + +extern const GUID MetasequoiaIMEGuidDisplayAttributeInput; +extern const GUID MetasequoiaIMEGuidDisplayAttributeConverted; + +extern const GUID MetasequoiaIMEGuidCandUIElement; + +extern const WCHAR UnicodeByteOrderMark; +extern const WCHAR KeywordDelimiter; +extern const WCHAR StringDelimiter; + +extern const WCHAR ImeModeDescription[]; +extern const WCHAR ImeModeDescription02[]; +extern const WCHAR ImeModeDescription03[]; +extern const WCHAR EnglishInputModeDescription[]; +extern const int ImeModeOnIcoIndex; +extern const int ImeModeOffIcoIndex; + +extern const WCHAR DoubleSingleByteDescription[]; +extern const int DoubleSingleByteOnIcoIndex; +extern const int DoubleSingleByteOffIcoIndex; + +extern const WCHAR PunctuationDescription[]; +extern const int PunctuationOnIcoIndex; +extern const int PunctuationOffIcoIndex; + +extern const WCHAR LangbarImeModeDescription[]; +extern const WCHAR LangbarDoubleSingleByteDescription[]; +extern const WCHAR LangbarPunctuationDescription[]; + +inline std::vector WStringCandidateList; +inline std::wstring FindKeyCode; + +inline POINT PointDTO = {0, 0}; +inline std::wstring CandidateWString = L""; +} // namespace Global diff --git a/windows/src/Header/Define.h b/windows/src/Header/Define.h index e75d9318..99946a1e 100644 --- a/windows/src/Header/Define.h +++ b/windows/src/Header/Define.h @@ -1,73 +1,73 @@ -#pragma once -#include "resource.h" - -#define IME_NAME L"MetasequoiaImeTsf" - -#define TEXTSERVICE_MODEL L"Apartment" -#define TEXTSERVICE_LANGID MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) -#define TEXTSERVICE_ICON_INDEX -IDIS_METASEQUOIAIME -#define TEXTSERVICE_DIC L"MetasequoiaIMESimplifiedQuanPin.txt" -#define TEXTSERVICE_DIC_DB L"cutted_flyciku_with_jp.db" -#define FANYLOGFILE_ "fanydebug.log" -#define FANYLOGFILE L"fanydebug_w.log" - -#define IME_MODE_ON_ICON_INDEX IDI_IME_MODE_ON -#define IME_MODE_OFF_ICON_INDEX IDI_IME_MODE_OFF -#define IME_MODE_ON_DARK_ICON_INDEX IDI_IME_MODE_ON_DARK -#define IME_MODE_OFF_DARK_ICON_INDEX IDI_IME_MODE_OFF_DARK -#define IME_MODE_ON_JP_ICON_INDEX IDI_IME_MODE_ON_JP -#define IME_MODE_ON_JP_DARK_ICON_INDEX IDI_IME_MODE_ON_JP_DARK -#define IME_MODE_CAP_ICON_INDEX IDI_IME_MODE_CAP -#define IME_MODE_CAP_DARK_ICON_INDEX IDI_IME_MODE_CAP_DARK -#define IME_DOUBLE_ON_INDEX IDI_DOUBLE_SINGLE_BYTE_ON -#define IME_DOUBLE_OFF_INDEX IDI_DOUBLE_SINGLE_BYTE_OFF -#define IME_PUNCTUATION_ON_INDEX IDI_PUNCTUATION_ON -#define IME_PUNCTUATION_OFF_INDEX IDI_PUNCTUATION_OFF - -#define METASEQUOIAIME_FONT_DEFAULT L"Microsoft YaHei UI" -#define METASEQUOIAIME_LOCALE_DEFAULT L"zh-CN" - -//--------------------------------------------------------------------- -// defined max pinyin input length -//--------------------------------------------------------------------- -#define MAX_PINYIN_LENGTH (64) // Maximum number of characters allowed in pinyin input - -//--------------------------------------------------------------------- -// defined Candidated Window -//--------------------------------------------------------------------- -#define CANDWND_ROW_WIDTH (30) -#define CANDWND_BORDER_COLOR (RGB(0x00, 0x00, 0x00)) -#define CANDWND_BORDER_WIDTH (2) -#define CANDWND_NUM_COLOR (RGB(0xB4, 0xB4, 0xB4)) -// #define CANDWND_SELECTED_ITEM_COLOR (RGB(0xFF, 0xFF, 0xFF)) -// #define CANDWND_SELECTED_BK_COLOR (RGB(0xA6, 0xA6, 0x00)) -#define CANDWND_SELECTED_ITEM_COLOR (RGB(0x00, 0x00, 0x00)) -#define CANDWND_SELECTED_BK_COLOR (RGB(0xFF, 0xFF, 0xFF)) -#define CANDWND_ITEM_COLOR (RGB(0x00, 0x00, 0x00)) - -//--------------------------------------------------------------------- -// defined Candidated List Contants -//--------------------------------------------------------------------- -#define CANDWND_ITEM_CNT_PER_PAGE (8) // Do not exceed 9 - -//--------------------------------------------------------------------- -// defined modifier -//--------------------------------------------------------------------- -#define _TF_MOD_ON_KEYUP_SHIFT_ONLY (0x00010000 | TF_MOD_ON_KEYUP) -#define _TF_MOD_ON_KEYUP_CONTROL_ONLY (0x00020000 | TF_MOD_ON_KEYUP) -#define _TF_MOD_ON_KEYUP_ALT_ONLY (0x00040000 | TF_MOD_ON_KEYUP) - -#define CAND_WIDTH (13) // * tmMaxCharWidth - -//--------------------------------------------------------------------- -// string length of CLSID -//--------------------------------------------------------------------- -#define CLSID_STRLEN (38) // strlen("{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}") - -// -// Message define -// -// #define WM_SHOW_MAIN_WINDOW -// #define WM_HIDE_MAIN_WINDOW -// #define WM_MOVE_CANDIDATE_WINDOW +#pragma once +#include "resource.h" + +#define IME_NAME L"MetasequoiaImeTsf" + +#define TEXTSERVICE_MODEL L"Apartment" +#define TEXTSERVICE_LANGID MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) +#define TEXTSERVICE_ICON_INDEX -IDIS_METASEQUOIAIME +#define TEXTSERVICE_DIC L"MetasequoiaIMESimplifiedQuanPin.txt" +#define TEXTSERVICE_DIC_DB L"cutted_flyciku_with_jp.db" +#define FANYLOGFILE_ "fanydebug.log" +#define FANYLOGFILE L"fanydebug_w.log" + +#define IME_MODE_ON_ICON_INDEX IDI_IME_MODE_ON +#define IME_MODE_OFF_ICON_INDEX IDI_IME_MODE_OFF +#define IME_MODE_ON_DARK_ICON_INDEX IDI_IME_MODE_ON_DARK +#define IME_MODE_OFF_DARK_ICON_INDEX IDI_IME_MODE_OFF_DARK +#define IME_MODE_ON_JP_ICON_INDEX IDI_IME_MODE_ON_JP +#define IME_MODE_ON_JP_DARK_ICON_INDEX IDI_IME_MODE_ON_JP_DARK +#define IME_MODE_CAP_ICON_INDEX IDI_IME_MODE_CAP +#define IME_MODE_CAP_DARK_ICON_INDEX IDI_IME_MODE_CAP_DARK +#define IME_DOUBLE_ON_INDEX IDI_DOUBLE_SINGLE_BYTE_ON +#define IME_DOUBLE_OFF_INDEX IDI_DOUBLE_SINGLE_BYTE_OFF +#define IME_PUNCTUATION_ON_INDEX IDI_PUNCTUATION_ON +#define IME_PUNCTUATION_OFF_INDEX IDI_PUNCTUATION_OFF + +#define METASEQUOIAIME_FONT_DEFAULT L"Microsoft YaHei UI" +#define METASEQUOIAIME_LOCALE_DEFAULT L"zh-CN" + +//--------------------------------------------------------------------- +// defined max pinyin input length +//--------------------------------------------------------------------- +#define MAX_PINYIN_LENGTH (64) // Maximum number of characters allowed in pinyin input + +//--------------------------------------------------------------------- +// defined Candidated Window +//--------------------------------------------------------------------- +#define CANDWND_ROW_WIDTH (30) +#define CANDWND_BORDER_COLOR (RGB(0x00, 0x00, 0x00)) +#define CANDWND_BORDER_WIDTH (2) +#define CANDWND_NUM_COLOR (RGB(0xB4, 0xB4, 0xB4)) +// #define CANDWND_SELECTED_ITEM_COLOR (RGB(0xFF, 0xFF, 0xFF)) +// #define CANDWND_SELECTED_BK_COLOR (RGB(0xA6, 0xA6, 0x00)) +#define CANDWND_SELECTED_ITEM_COLOR (RGB(0x00, 0x00, 0x00)) +#define CANDWND_SELECTED_BK_COLOR (RGB(0xFF, 0xFF, 0xFF)) +#define CANDWND_ITEM_COLOR (RGB(0x00, 0x00, 0x00)) + +//--------------------------------------------------------------------- +// defined Candidated List Contants +//--------------------------------------------------------------------- +#define CANDWND_ITEM_CNT_PER_PAGE (8) // Do not exceed 9 + +//--------------------------------------------------------------------- +// defined modifier +//--------------------------------------------------------------------- +#define _TF_MOD_ON_KEYUP_SHIFT_ONLY (0x00010000 | TF_MOD_ON_KEYUP) +#define _TF_MOD_ON_KEYUP_CONTROL_ONLY (0x00020000 | TF_MOD_ON_KEYUP) +#define _TF_MOD_ON_KEYUP_ALT_ONLY (0x00040000 | TF_MOD_ON_KEYUP) + +#define CAND_WIDTH (13) // * tmMaxCharWidth + +//--------------------------------------------------------------------- +// string length of CLSID +//--------------------------------------------------------------------- +#define CLSID_STRLEN (38) // strlen("{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}") + +// +// Message define +// +// #define WM_SHOW_MAIN_WINDOW +// #define WM_HIDE_MAIN_WINDOW +// #define WM_MOVE_CANDIDATE_WINDOW // #define WM_SET_PARENT_HWND \ No newline at end of file diff --git a/windows/src/Header/Private.h b/windows/src/Header/Private.h index 494915b8..61451272 100644 --- a/windows/src/Header/Private.h +++ b/windows/src/Header/Private.h @@ -1,15 +1,15 @@ -#pragma once - -#include "stdafx.h" -#include "sal.h" - -#include -#include -#include - -#include -#include - -#include "initguid.h" -#include "msctf.h" -#include "ctffunc.h" +#pragma once + +#include "stdafx.h" +#include "sal.h" + +#include +#include +#include + +#include +#include + +#include "initguid.h" +#include "msctf.h" +#include "ctffunc.h" diff --git a/windows/src/Header/resource.h b/windows/src/Header/resource.h index 7bae0f97..badc0863 100644 --- a/windows/src/Header/resource.h +++ b/windows/src/Header/resource.h @@ -1,35 +1,35 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by MetasequoiaIME.rc -// -#define SUBLANG_ENGLISH_US 0x01 -#define LANG_ENGLISH 0x09 -#define IDIS_METASEQUOIAIME 12 -#define IDS_DEFAULT_FONT 13 -#define IDS_IME_MODE 20 -#define IDI_IME_MODE_ON 21 -#define IDI_IME_MODE_OFF 22 -#define IDS_DOUBLE_SINGLE_BYTE 23 -#define IDI_DOUBLE_SINGLE_BYTE_ON 24 -#define IDI_DOUBLE_SINGLE_BYTE_OFF 25 -#define IDS_PUNCTUATION 26 -#define IDI_PUNCTUATION_ON 27 -#define IDI_PUNCTUATION_OFF 28 -#define IDI_IME_MODE_ON_DARK 29 -#define IDI_IME_MODE_OFF_DARK 30 -#define IDI_IME_MODE_ON_JP 31 -#define IDI_IME_MODE_ON_JP_DARK 32 -#define IDI_IME_MODE_CAP 33 -#define IDI_IME_MODE_CAP_DARK 34 -#define IDR_VERSION2 107 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 108 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by MetasequoiaIME.rc +// +#define SUBLANG_ENGLISH_US 0x01 +#define LANG_ENGLISH 0x09 +#define IDIS_METASEQUOIAIME 12 +#define IDS_DEFAULT_FONT 13 +#define IDS_IME_MODE 20 +#define IDI_IME_MODE_ON 21 +#define IDI_IME_MODE_OFF 22 +#define IDS_DOUBLE_SINGLE_BYTE 23 +#define IDI_DOUBLE_SINGLE_BYTE_ON 24 +#define IDI_DOUBLE_SINGLE_BYTE_OFF 25 +#define IDS_PUNCTUATION 26 +#define IDI_PUNCTUATION_ON 27 +#define IDI_PUNCTUATION_OFF 28 +#define IDI_IME_MODE_ON_DARK 29 +#define IDI_IME_MODE_OFF_DARK 30 +#define IDI_IME_MODE_ON_JP 31 +#define IDI_IME_MODE_ON_JP_DARK 32 +#define IDI_IME_MODE_CAP 33 +#define IDI_IME_MODE_CAP_DARK 34 +#define IDR_VERSION2 107 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/src/Header/stdafx.h b/windows/src/Header/stdafx.h index 0615544a..720e64a9 100644 --- a/windows/src/Header/stdafx.h +++ b/windows/src/Header/stdafx.h @@ -1,2 +1,2 @@ -#pragma once -#include +#pragma once +#include diff --git a/windows/src/IME/ActiveLanguageProfileNotifySink.cpp b/windows/src/IME/ActiveLanguageProfileNotifySink.cpp index db7f6745..1a781b44 100644 --- a/windows/src/IME/ActiveLanguageProfileNotifySink.cpp +++ b/windows/src/IME/ActiveLanguageProfileNotifySink.cpp @@ -1,125 +1,125 @@ -#include "Private.h" -#include "Globals.h" -#include "Ipc.h" -#include "MetasequoiaIME.h" -#include "CompositionProcessorEngine.h" - -BOOL CMetasequoiaIME::VerifyMetasequoiaIMECLSID(_In_ REFCLSID clsid) -{ - if (IsEqualCLSID(clsid, Global::MetasequoiaIMECLSID)) - { - return TRUE; - } - return FALSE; -} - -//+--------------------------------------------------------------------------- -// -// ITfActiveLanguageProfileNotifySink::OnActivated -// -// Sink called by the framework when changes activate language profile. -//---------------------------------------------------------------------------- - -STDAPI CMetasequoiaIME::OnActivated(_In_ REFCLSID clsid, _In_ REFGUID guidProfile, _In_ BOOL isActivated) -{ - guidProfile; - - if (FALSE == VerifyMetasequoiaIMECLSID(clsid)) - { - return S_OK; - } - - if (isActivated) - { - // ActivateEx covers the first load; this callback covers later - // profile switches in an already-running host. Use the same - // ShellExecute-based wake path so packaged/UWP hosts do not pass - // their package identity to the Server. - _WakeServerIfNeeded(); - _AddTextProcessorEngine(); - } - else - { - // Profile deactivation is the earliest authoritative signal that the - // user selected another TIP. Deactivate() sends the same idempotent - // event again during teardown, but may run after the Main pipe has - // already become unavailable. - FlushNamedpipeImeDeactivation(); - } - - if (nullptr == _pCompositionProcessorEngine) - { - return S_OK; - } - - if (isActivated) - { - _pCompositionProcessorEngine->ShowAllLanguageBarIcons(); - - _pCompositionProcessorEngine->ConversionModeCompartmentUpdated(_pThreadMgr); - } - else - { - _DeleteCandidateList(FALSE, nullptr); - - _pCompositionProcessorEngine->HideAllLanguageBarIcons(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// _InitActiveLanguageProfileNotifySink -// -// Advise a active language profile notify sink. -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_InitActiveLanguageProfileNotifySink() -{ - ITfSource *pSource = nullptr; - BOOL ret = FALSE; - - if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK) - { - return ret; - } - - if (pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, (ITfActiveLanguageProfileNotifySink *)this, - &_activeLanguageProfileNotifySinkCookie) != S_OK) - { - _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; - goto Exit; - } - - ret = TRUE; - -Exit: - pSource->Release(); - return ret; -} - -//+--------------------------------------------------------------------------- -// -// _UninitActiveLanguageProfileNotifySink -// -// Unadvise a active language profile notify sink. Assumes we have advised one already. -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_UninitActiveLanguageProfileNotifySink() -{ - ITfSource *pSource = nullptr; - - if (_activeLanguageProfileNotifySinkCookie == TF_INVALID_COOKIE) - { - return; // never Advised - } - - if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK) - { - pSource->UnadviseSink(_activeLanguageProfileNotifySinkCookie); - pSource->Release(); - } - - _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; -} +#include "Private.h" +#include "Globals.h" +#include "Ipc.h" +#include "MetasequoiaIME.h" +#include "CompositionProcessorEngine.h" + +BOOL CMetasequoiaIME::VerifyMetasequoiaIMECLSID(_In_ REFCLSID clsid) +{ + if (IsEqualCLSID(clsid, Global::MetasequoiaIMECLSID)) + { + return TRUE; + } + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// ITfActiveLanguageProfileNotifySink::OnActivated +// +// Sink called by the framework when changes activate language profile. +//---------------------------------------------------------------------------- + +STDAPI CMetasequoiaIME::OnActivated(_In_ REFCLSID clsid, _In_ REFGUID guidProfile, _In_ BOOL isActivated) +{ + guidProfile; + + if (FALSE == VerifyMetasequoiaIMECLSID(clsid)) + { + return S_OK; + } + + if (isActivated) + { + // ActivateEx covers the first load; this callback covers later + // profile switches in an already-running host. Use the same + // ShellExecute-based wake path so packaged/UWP hosts do not pass + // their package identity to the Server. + _WakeServerIfNeeded(); + _AddTextProcessorEngine(); + } + else + { + // Profile deactivation is the earliest authoritative signal that the + // user selected another TIP. Deactivate() sends the same idempotent + // event again during teardown, but may run after the Main pipe has + // already become unavailable. + FlushNamedpipeImeDeactivation(); + } + + if (nullptr == _pCompositionProcessorEngine) + { + return S_OK; + } + + if (isActivated) + { + _pCompositionProcessorEngine->ShowAllLanguageBarIcons(); + + _pCompositionProcessorEngine->ConversionModeCompartmentUpdated(_pThreadMgr); + } + else + { + _DeleteCandidateList(FALSE, nullptr); + + _pCompositionProcessorEngine->HideAllLanguageBarIcons(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InitActiveLanguageProfileNotifySink +// +// Advise a active language profile notify sink. +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_InitActiveLanguageProfileNotifySink() +{ + ITfSource *pSource = nullptr; + BOOL ret = FALSE; + + if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK) + { + return ret; + } + + if (pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, (ITfActiveLanguageProfileNotifySink *)this, + &_activeLanguageProfileNotifySinkCookie) != S_OK) + { + _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; + goto Exit; + } + + ret = TRUE; + +Exit: + pSource->Release(); + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _UninitActiveLanguageProfileNotifySink +// +// Unadvise a active language profile notify sink. Assumes we have advised one already. +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_UninitActiveLanguageProfileNotifySink() +{ + ITfSource *pSource = nullptr; + + if (_activeLanguageProfileNotifySinkCookie == TF_INVALID_COOKIE) + { + return; // never Advised + } + + if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK) + { + pSource->UnadviseSink(_activeLanguageProfileNotifySinkCookie); + pSource->Release(); + } + + _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; +} diff --git a/windows/src/IME/FunctionProviderSink.cpp b/windows/src/IME/FunctionProviderSink.cpp index 8af84d27..56d68993 100644 --- a/windows/src/IME/FunctionProviderSink.cpp +++ b/windows/src/IME/FunctionProviderSink.cpp @@ -1,55 +1,55 @@ -#include "Private.h" -#include "MetasequoiaIME.h" -#include "SearchCandidateProvider.h" - -//+--------------------------------------------------------------------------- -// -// _InitFunctionProviderSink -// -//---------------------------------------------------------------------------- - -BOOL CMetasequoiaIME::_InitFunctionProviderSink() -{ - ITfSourceSingle *pSourceSingle = nullptr; - BOOL ret = FALSE; - if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) - { - IUnknown *punk = nullptr; - if (SUCCEEDED(QueryInterface(IID_IUnknown, (void **)&punk))) - { - if (SUCCEEDED(pSourceSingle->AdviseSingleSink(_tfClientId, IID_ITfFunctionProvider, punk))) - { - if (SUCCEEDED(CSearchCandidateProvider::CreateInstance(&_pITfFnSearchCandidateProvider, - (ITfTextInputProcessorEx *)this))) - { - ret = TRUE; - } - } - punk->Release(); - } - pSourceSingle->Release(); - } - return ret; -} - -//+--------------------------------------------------------------------------- -// -// _UninitFunctionProviderSink -// -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_UninitFunctionProviderSink() -{ - if (_pITfFnSearchCandidateProvider != nullptr) - { - _pITfFnSearchCandidateProvider->Release(); - _pITfFnSearchCandidateProvider = nullptr; - } - - ITfSourceSingle *pSourceSingle = nullptr; - if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) - { - pSourceSingle->UnadviseSingleSink(_tfClientId, IID_ITfFunctionProvider); - pSourceSingle->Release(); - } -} +#include "Private.h" +#include "MetasequoiaIME.h" +#include "SearchCandidateProvider.h" + +//+--------------------------------------------------------------------------- +// +// _InitFunctionProviderSink +// +//---------------------------------------------------------------------------- + +BOOL CMetasequoiaIME::_InitFunctionProviderSink() +{ + ITfSourceSingle *pSourceSingle = nullptr; + BOOL ret = FALSE; + if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) + { + IUnknown *punk = nullptr; + if (SUCCEEDED(QueryInterface(IID_IUnknown, (void **)&punk))) + { + if (SUCCEEDED(pSourceSingle->AdviseSingleSink(_tfClientId, IID_ITfFunctionProvider, punk))) + { + if (SUCCEEDED(CSearchCandidateProvider::CreateInstance(&_pITfFnSearchCandidateProvider, + (ITfTextInputProcessorEx *)this))) + { + ret = TRUE; + } + } + punk->Release(); + } + pSourceSingle->Release(); + } + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _UninitFunctionProviderSink +// +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_UninitFunctionProviderSink() +{ + if (_pITfFnSearchCandidateProvider != nullptr) + { + _pITfFnSearchCandidateProvider->Release(); + _pITfFnSearchCandidateProvider = nullptr; + } + + ITfSourceSingle *pSourceSingle = nullptr; + if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) + { + pSourceSingle->UnadviseSingleSink(_tfClientId, IID_ITfFunctionProvider); + pSourceSingle->Release(); + } +} diff --git a/windows/src/IME/MetasequoiaIME.def b/windows/src/IME/MetasequoiaIME.def index 9994ae70..116fec19 100644 --- a/windows/src/IME/MetasequoiaIME.def +++ b/windows/src/IME/MetasequoiaIME.def @@ -1,7 +1,7 @@ -LIBRARY MetasequoiaImeTsf.dll - -EXPORTS - DllGetClassObject PRIVATE - DllCanUnloadNow PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE +LIBRARY MetasequoiaImeTsf.dll + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/windows/src/IME/MetasequoiaIME.h b/windows/src/IME/MetasequoiaIME.h index 4ac32a78..7b42f78e 100644 --- a/windows/src/IME/MetasequoiaIME.h +++ b/windows/src/IME/MetasequoiaIME.h @@ -1,607 +1,607 @@ -#pragma once - -#include "KeyHandlerEditSession.h" -#include "MetasequoiaIMEBaseStructure.h" -#include "Ipc.h" -#include -#include -#include -#include -#include -#include -#include -#include - -class CLangBarItemButton; -class CCandidateListUIPresenter; -class CCompositionProcessorEngine; - -const DWORD WM_CheckGlobalCompartment = WM_USER; -const DWORD WM_ConnectNamedpipe = WM_USER + 1; -const DWORD WM_DisconnectNamedpipe = WM_USER + 2; -const DWORD WM_ConnectToTsfNamedpipe = WM_USER + 3; -const DWORD WM_IMEActivation = WM_USER + 4; -const DWORD WM_ThreadFocus = WM_USER + 5; -const DWORD WM_UpdateIMEStatus = WM_USER + 6; -const DWORD WM_UpdateDoubleSingleByte = WM_USER + 7; -const DWORD WM_UpdatePuncMode = WM_USER + 8; -const DWORD WM_CommitCandidate = WM_USER + 9; -const DWORD WM_CleanupCandidatePresenter = WM_USER + 10; -const DWORD WM_AsyncFinalizeCandidate = WM_USER + 11; -const DWORD WM_AsyncPunctuationCommit = WM_USER + 12; -const DWORD WM_AsyncNumberCandidateCommit = WM_USER + 13; -const DWORD WM_AsyncServerCandidateKey = WM_USER + 14; -const DWORD WM_IpcWorkerDisconnected = WM_USER + 15; -const DWORD WM_IpcReconnect = WM_USER + 16; -const DWORD WM_IpcSessionDirty = WM_USER + 17; -const DWORD WM_DrainDeferredKeyDown = WM_USER + 18; -const DWORD WM_InsertText = WM_USER + 19; -const DWORD WM_RefreshLanguageBarTheme = WM_USER + 20; -const DWORD WM_PairedPunctuationMoveLeft = WM_USER + 21; -const DWORD WM_ReplaceRepeatedSmartPunctuation = WM_USER + 22; -const DWORD WM_MinttyShiftRelease = WM_USER + 23; -const DWORD WM_UpdateVoiceComposition = WM_USER + 24; -const DWORD WM_CommitVoiceComposition = WM_USER + 25; -const DWORD WM_CancelVoiceComposition = WM_USER + 26; -const DWORD WM_ApplyPunctuationLock = WM_USER + 27; -constexpr ULONG_PTR SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO = 0x4D535050u; -constexpr ULONGLONG SMART_PUNCTUATION_REPEAT_INTERVAL_MS = 2000; -constexpr UINT_PTR TIMER_CONNECT_ALL_NAMEDPIPE = 1; -constexpr UINT_PTR TIMER_CONNECT_TO_TSF_NAMEDPIPE = 2; -constexpr UINT_PTR TIMER_REFRESH_LANG_BAR_THEME = 3; -constexpr UINT_PTR TIMER_DEFERRED_FOCUS_LOSS = 4; -constexpr UINT_PTR TIMER_FOCUS_STATUS_RESEND = 5; -constexpr UINT FOCUS_LOSS_DEFER_MS = 300; -// Chromium hosts fire a burst of OnSetFocus per window switch; coalesce them -// into one resend instead of one packet per callback. -constexpr UINT FOCUS_STATUS_RESEND_DELAY_MS = 50; -LRESULT CALLBACK CMetasequoiaIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); - -class CMetasequoiaIME : public ITfTextInputProcessorEx, - public ITfThreadMgrEventSink, - public ITfTextEditSink, - public ITfKeyEventSink, - public ITfCompositionSink, - public ITfDisplayAttributeProvider, - public ITfActiveLanguageProfileNotifySink, - public ITfThreadFocusSink, - public ITfFunctionProvider, - public ITfFnGetPreferredTouchKeyboardLayout -{ - friend class CCompositionProcessorEngine; - friend class CKeyHandlerEditSession; - // Needs _IsComposing() to tell a host's transient context-view teardown apart - // from a real end of composition. - friend class CCandidateListUIPresenter; - - public: - CMetasequoiaIME(); - ~CMetasequoiaIME(); - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfTextInputProcessor - STDMETHODIMP Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClientId) - { - return ActivateEx(pThreadMgr, tfClientId, 0); - } - // ITfTextInputProcessorEx - STDMETHODIMP ActivateEx(ITfThreadMgr *pThreadMgr, TfClientId tfClientId, DWORD dwFlags); - STDMETHODIMP Deactivate(); - - // ITfThreadMgrEventSink - STDMETHODIMP OnInitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr); - STDMETHODIMP OnUninitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr); - STDMETHODIMP OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDocumentMgr *pDocMgrPrevFocus); - STDMETHODIMP OnPushContext(_In_ ITfContext *pContext); - STDMETHODIMP OnPopContext(_In_ ITfContext *pContext); - - // ITfTextEditSink - STDMETHODIMP OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly, - __RPC__in_opt ITfEditRecord *pEditRecord); - - // ITfKeyEventSink - STDMETHODIMP OnSetFocus(BOOL fForeground); - STDMETHODIMP OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); - STDMETHODIMP OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); - STDMETHODIMP OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); - STDMETHODIMP OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); - STDMETHODIMP OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pIsEaten); - - // ITfCompositionSink - STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition); - - // ITfDisplayAttributeProvider - STDMETHODIMP EnumDisplayAttributeInfo(__RPC__deref_out_opt IEnumTfDisplayAttributeInfo **ppEnum); - STDMETHODIMP GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, - __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo); - - // ITfActiveLanguageProfileNotifySink - STDMETHODIMP OnActivated(_In_ REFCLSID clsid, _In_ REFGUID guidProfile, _In_ BOOL isActivated); - - // ITfThreadFocusSink - STDMETHODIMP OnSetThreadFocus(); - STDMETHODIMP OnKillThreadFocus(); - - // ITfFunctionProvider - STDMETHODIMP GetType(__RPC__out GUID *pguid); - STDMETHODIMP GetDescription(__RPC__deref_out_opt BSTR *pbstrDesc); - STDMETHODIMP GetFunction(__RPC__in REFGUID rguid, __RPC__in REFIID riid, __RPC__deref_out_opt IUnknown **ppunk); - - // ITfFunction - STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrDisplayName); - - // ITfFnGetPreferredTouchKeyboardLayout, it is the Optimized layout feature. - STDMETHODIMP GetLayout(_Out_ TKBLayoutType *ptkblayoutType, _Out_ WORD *pwPreferredLayoutId); - - // CClassFactory factory callback - static HRESULT CreateInstance(_In_ IUnknown *pUnkOuter, REFIID riid, _Outptr_ void **ppvObj); - - // utility function for thread manager. - ITfThreadMgr *_GetThreadMgr() - { - return _pThreadMgr; - } - TfClientId _GetClientId() - { - return _tfClientId; - } - bool _IsServerUnavailableFallbackActive() const; - - // functions for the composition object. - void _SetComposition(_In_ ITfComposition *pComposition); - void _TerminateComposition(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCalledFromDeactivate = FALSE); - void _SaveCompositionContext(_In_ ITfContext *pContext); - - // key event handlers for composition/candidate/phrase common objects. - HRESULT _HandleComplete(TfEditCookie ec, _In_ ITfContext *pContext); - HRESULT _HandleCompleteCommitFirst(TfEditCookie ec, _In_ ITfContext *pContext); - HRESULT _HandleCancel(TfEditCookie ec, _In_ ITfContext *pContext); - HRESULT _HandleToogleIMEMode(TfEditCookie ec, _In_ ITfContext *pContext); - HRESULT _HandleInsertText(TfEditCookie ec, _In_ ITfContext *pContext, const std::wstring &text); - HRESULT _HandleUpdateVoiceComposition(TfEditCookie ec, _In_ ITfContext *pContext, const std::wstring &text); - HRESULT _HandleCommitVoiceComposition(TfEditCookie ec, _In_ ITfContext *pContext, const std::wstring &text); - HRESULT _HandleCancelVoiceComposition(TfEditCookie ec, _In_ ITfContext *pContext); - - // key event handlers for composition object. - HRESULT _HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch, uint64_t requestId); - HRESULT _HandleCompositionFinalize(TfEditCookie ec, _In_ ITfContext *pContext, BOOL fCandidateList); - HRESULT _HandleCompositionConvert(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isWildcardSearch); - HRESULT _HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId); - HRESULT _HandleCompositionDelete(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId); - HRESULT _HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, KEYSTROKE_FUNCTION keyFunction, - uint64_t requestId = FANY_IME_NO_REQUEST_ID); - HRESULT _HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, UINT code, WCHAR wch, - uint64_t requestId, const std::wstring &prefetchedText); - // Character immediately before the caret / composition start (0 if unavailable). - WCHAR _GetPrecedingDocumentChar(TfEditCookie ec, _In_ ITfContext *pContext); - // Shadow first, document read as the fallback. See _smartPunctuationShadowChar. - WCHAR _GetPrecedingCharForSmartPunctuation(TfEditCookie ec, _In_ ITfContext *pContext); - - // Smart punctuation: backspacing the ASCII punctuation we just committed - // means that form was unwanted, so the spot stays on Chinese punctuation. - std::wstring _ResolveSmartPunctuation(WCHAR wch, WCHAR precedingChar); - bool _QueueRepeatedSmartPunctuationReplacement(WCHAR wch); - void _NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten); - void _ResetSmartPunctuationHistory(); - void _UpdateSmartPunctuationShadow(UINT code, WCHAR wch, bool isEaten); - void _InvalidateSmartPunctuationShadow(); - HRESULT _HandleCompositionDoubleSingleByte(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch); - - // key event handlers for candidate object. - HRESULT _HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId, - const std::wstring &prefetchedText); - HRESULT _HandleCandidateFinalizeForVKReturn(TfEditCookie ec, _In_ ITfContext *pContext); - HRESULT _HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId, - const std::wstring &prefetchedText); - HRESULT _HandleCandidateArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction, - uint64_t requestId = FANY_IME_NO_REQUEST_ID); - HRESULT _HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode, - uint64_t requestId, const std::wstring &prefetchedText); - - BOOL _IsSecureMode(void) - { - return (_dwActivateFlags & TF_TMAE_SECUREMODE) ? TRUE : FALSE; - } - BOOL _IsComLess(void) - { - return (_dwActivateFlags & TF_TMAE_COMLESS) ? TRUE : FALSE; - } - BOOL _IsStoreAppMode(void) - { - return (_dwActivateFlags & TF_TMF_IMMERSIVEMODE) ? TRUE : FALSE; - } - // Host requested UILess (games / fullscreen / console): only ITfUIElement - // data is allowed — never an IME-owned HWND. - BOOL _IsUiLessMode(void) - { - return (_dwActivateFlags & TF_TMF_UIELEMENTENABLEDONLY) ? TRUE : FALSE; - } - - CCompositionProcessorEngine *GetCompositionProcessorEngine() - { - return (_pCompositionProcessorEngine); - }; - - // Async TSF edit sessions may be granted after a focus transition. Bind - // every text-producing session to the exact focus token that scheduled it - // so an old worker/key message cannot commit into the next document. - uint64_t _CaptureFocusSessionToken() const; - bool _IsFocusSessionCurrent(uint64_t focusToken, _In_opt_ ITfContext *expectedContext = nullptr) const; - uint64_t _CaptureCompositionEpoch() const; - bool _IsCompositionEpochCurrent(uint64_t compositionEpoch) const; - bool _IsCompositionCurrent(_In_opt_ ITfComposition *expectedComposition) const; - static bool _IsSameComObject(_In_opt_ IUnknown *left, _In_opt_ IUnknown *right); - void _DebugCompositionRecovery(_In_z_ const WCHAR *reason, HRESULT hr) const; - bool _IsLocalSessionResetCurrent(UINT resetToken) const; - void _CompleteLocalSessionReset(UINT resetToken); - bool _IsDeferredKeyReplayCurrent(uint64_t replayToken, uint64_t focusGeneration, - _In_opt_ ITfContext *expectedContext) const; - void _CompleteDeferredKeyReplay(uint64_t replayToken); - void _RetryDeferredKeyReplay(uint64_t replayToken); - - // comless helpers - static HRESULT CMetasequoiaIME::CreateInstance(REFCLSID rclsid, REFIID riid, _Outptr_result_maybenull_ LPVOID *ppv, - _Out_opt_ HINSTANCE *phInst, BOOL isComLessMode); - static HRESULT CMetasequoiaIME::ComLessCreateInstance(REFGUID rclsid, REFIID riid, - _Outptr_result_maybenull_ void **ppv, - _Out_opt_ HINSTANCE *phInst); - static HRESULT CMetasequoiaIME::GetComModuleName(REFGUID rclsid, _Out_writes_(cchPath) WCHAR *wchPath, - DWORD cchPath); - - static void IpcWorkerThread(CMetasequoiaIME *pIME); - void _QueuePendingServerCandidate(UINT msgType, _In_z_ const WCHAR *pCandidateString); - bool _TakePendingServerCandidate(_Out_ UINT *pMsgType, _Out_ std::wstring *pCandidateString); - void _ScheduleCandidatePresenterCleanup(_In_ CCandidateListUIPresenter *pPresenter); - void _DrainPendingCandidatePresenterCleanup(); - - private: - // functions for the composition object. - HRESULT _HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, - TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId); - HRESULT _CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, - _In_ ITfContext *pContext); - HRESULT _HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId, - const std::wstring &prefetchedText); - - struct AsyncKeyRequest - { - UINT message = 0; - UINT code = 0; - WCHAR wch = L'\0'; - uint64_t requestId = UINT64_MAX; - uint64_t focusToken = 0; - uint64_t compositionEpoch = 0; - uint64_t deferredReplayToken = 0; - std::wstring prefetchedText; - }; - bool _PostAsyncKeyRequest(UINT message, UINT code, WCHAR wch, uint64_t requestId, std::wstring prefetchedText = {}, - uint64_t expectedFocusToken = 0, uint64_t expectedCompositionEpoch = 0, - uint64_t deferredReplayToken = 0); - bool _TakeAsyncKeyRequest(UINT message, UINT token, _Out_ AsyncKeyRequest &request); - struct WorkerCandidateCommit - { - std::wstring text; - uint64_t focusToken = 0; - uint64_t compositionEpoch = 0; - }; - bool _PostServerCandidateCommit(_In_z_ const WCHAR *candidateText); - bool _PostServerInsertText(_In_z_ const WCHAR *text); - bool _PostServerTextDelivery(UINT windowMessage, _In_z_ const WCHAR *text); - bool _TakeServerCandidateCommit(UINT token, _Out_ WorkerCandidateCommit &request); - void _ResetVoiceCompositionAssemble(); - void _AssembleVoiceCompositionFrame(const FanyImeNamedpipeDataToTsfWorkerThread &buf); - void _DispatchUnsolicitedVoiceText(WPARAM wParam, KEYSTROKE_FUNCTION function); - struct WorkerCompartmentSwitch - { - UINT messageType = 0; - uint64_t focusToken = 0; - uint64_t compositionEpoch = 0; - }; - bool _PostWorkerCompartmentSwitch(UINT messageType, uint64_t focusToken); - bool _TakeWorkerCompartmentSwitch(UINT token, _Out_ WorkerCompartmentSwitch &request); - void _ClearAsyncKeyRequests(); - void _ClearPendingIpcRequests(); - void _RequestLocalSessionReset(_In_opt_ ITfContext *preferredContext, UINT resetToken); - bool _CaptureWindowsTextInputHostFocusLoss(); - - struct DeferredKeyDown - { - enum class Kind - { - KeyDown, - PreservedKey, - ApplicationText - }; - - Kind kind = Kind::KeyDown; - ITfContext *context = nullptr; - WPARAM wParam = 0; - LPARAM lParam = 0; - WCHAR translatedWch = L'\0'; - UINT modifiersDown = 0; - _KEYSTROKE_STATE keyState = {}; - GUID preservedKey = {}; - uint64_t focusGeneration = 0; - ULONGLONG queuedAtMs = 0; - UINT replayAttempts = 0; - bool preservedApplied = false; - }; - enum class KeyDownDispatchResult - { - Complete, - Retry, - AwaitingCompletion - }; - bool _HasDeferredKeyBarrier() const; - bool _DeferredKeyQueueHasCapacity() const; - void _EnsureDeferredKeyProjection(); - void _ApplyDeferredKeyProjection(const _KEYSTROKE_STATE &keyState, WCHAR wch); - void _ApplyDeferredPreservedKeyProjection(REFGUID preservedKey); - bool _RefreshDeferredRecoveryPrefix(_In_ ITfContext *pContext); - void _ArmDeferredRecoveryForTransport(_In_opt_ ITfContext *pContext); - bool _ClassifyDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, _In_opt_ const WCHAR *translatedWch, - _In_opt_ const UINT *modifiersDown, _Out_ WCHAR *classifiedWch, - _Out_ UINT *classifiedCode, _Out_ _KEYSTROKE_STATE *keyState); - bool _QueueDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam, WCHAR translatedWch, - UINT modifiersDown, const _KEYSTROKE_STATE &keyState); - bool _QueueDeferredPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey); - void _ClearDeferredKeyDowns(); - void _ScheduleDeferredKeyDownDrain(); - void _DrainOneDeferredKeyDown(); - void _TryLeaveServerUnavailableFallback(); - void _WakeServerIfNeeded(); - void _NoteKeyEventIpcFailure(); - HRESULT _RequestDeferredApplicationTextEditSession(_In_ ITfContext *pContext, WCHAR wch, - uint64_t expectedFocusToken, uint64_t expectedFocusGeneration, - uint64_t deferredReplayToken); - KeyDownDispatchResult _DispatchKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam, - _Out_ BOOL *pIsEaten, _In_opt_ const WCHAR *translatedWch, - _In_opt_ const UINT *modifiersDown, - _In_opt_ const _KEYSTROKE_STATE *prevalidatedKeyState, bool canDefer, - uint64_t expectedFocusGeneration, uint64_t deferredReplayToken = 0); - void _DispatchPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey, _Out_ BOOL *pIsEaten, - uint64_t expectedFocusGeneration, bool isPrevalidated, uint64_t deferredReplayToken = 0); - - // Input-mode hotkeys (Shift/Ctrl toggle, Ctrl+Alt+Space, Ctrl+Shift+Space, - // Ctrl+., Ctrl+Shift+E) are detected from ITfKeyEventSink like - // Weasel/Rime ascii_composer, not via TSF PreserveKey. Global/admin - // shortcuts stay on the Server LL hook. - void _TrackModifierHotkeyArming(WPARAM wParam, LPARAM lParam, bool isKeyUp); - bool _MatchChordInputHotkey(WPARAM wParam, _Out_ GUID *hotkeyGuid) const; - bool _MatchModifierReleaseHotkey(WPARAM wParam, _Out_ GUID *hotkeyGuid); - bool _QueueInputHotkey(_In_ ITfContext *pContext, REFGUID hotkeyGuid, _Out_ BOOL *pIsEaten); - - // mintty exposes IME composition through the legacy IMM bridge, but some - // versions do not forward bare modifier key-up events to ITfKeyEventSink. - // Observe only this host thread and feed a missed bare-Shift release back - // into the normal deferred hotkey path. - void _InitMinttyKeyboardHook(); - void _UninitMinttyKeyboardHook(); - void _HandleMinttyShiftRelease(UINT sequence); - void _MarkMinttyShiftHandled(); - static LRESULT CALLBACK _MinttyKeyboardHookProc(int code, WPARAM wParam, LPARAM lParam); - static thread_local CMetasequoiaIME *_minttyKeyboardHookOwner; - - void _StartComposition(_In_ ITfContext *pContext); - HRESULT _EndComposition(_In_opt_ ITfContext *pContext, _In_opt_ ITfComposition *expectedComposition = nullptr, - bool bypassFocusValidation = false); - BOOL _IsComposing(); - BOOL _IsKeyboardDisabled(); - - HRESULT _AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); - HRESULT _AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); - HRESULT _InsertTextToComposition(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); - HRESULT _SetCompositionTextAndSelection(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ CStringRange *pstrAddString); - - BOOL _FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, - _Outptr_result_maybenull_ ITfRange **ppRange); - HRESULT _SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, - _In_ CStringRange *pstrAddString, BOOL exist_composing); - HRESULT _InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString, - _Outptr_ ITfRange **ppCompRange); - - HRESULT _RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition); - - // Invoke key handler edit session - HRESULT _InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags, _KEYSTROKE_STATE keyState, - uint64_t requestId, std::wstring prefetchedText = {}, UINT localResetToken = 0, - uint64_t expectedCompositionEpoch = 0, uint64_t expectedFocusToken = 0, - uint64_t deferredReplayToken = 0); - HRESULT _RequestDirectPunctuationEditSession(_In_ ITfContext *pContext, UINT code, WCHAR wch, uint64_t requestId, - std::wstring prefetchedText, uint64_t expectedFocusToken = 0, - uint64_t expectedCompositionEpoch = 0, - uint64_t deferredReplayToken = 0); - - // function for the language property - BOOL _SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext); - - // function for the display attribute - void _ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, - _In_opt_ ITfComposition *expectedComposition = nullptr); - BOOL _SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, TfGuidAtom gaDisplayAttribute); - BOOL _SetCompositionDisplayAttributesForRange(TfEditCookie ec, _In_ ITfContext *pContext, - _In_ ITfRange *pRangeComposition, TfGuidAtom gaDisplayAttribute); - BOOL _InitDisplayAttributeGuidAtom(); - - BOOL _InitThreadMgrEventSink(); - void _UninitThreadMgrEventSink(); - void _HandleFocusedContextStackChange(_In_opt_ ITfContext *changedContext); - - BOOL _InitTextEditSink(_In_opt_ ITfDocumentMgr *pDocMgr); - - void _UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus); - - BOOL _InitKeyEventSink(); - void _UninitKeyEventSink(); - - BOOL _InitActiveLanguageProfileNotifySink(); - void _UninitActiveLanguageProfileNotifySink(); - - BOOL _IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch, - _Out_opt_ _KEYSTROKE_STATE *pKeyState, _In_opt_ const WCHAR *translatedWch = nullptr, - bool freshCompositionState = false); - - BOOL _IsRangeCovered(TfEditCookie ec, _In_ ITfRange *pRangeTest, _In_ ITfRange *pRangeCover); - VOID _DeleteCandidateList(BOOL fForce, _In_opt_ ITfContext *pContext); - - WCHAR ConvertVKey(UINT code); - - BOOL _InitThreadFocusSink(); - void _UninitThreadFocusSink(); - - BOOL _InitFunctionProviderSink(); - void _UninitFunctionProviderSink(); - - BOOL _AddTextProcessorEngine(); - - void _StartThemeRegistryWatcher(); - void _StopThemeRegistryWatcher(); - void _RefreshLanguageBarThemeIcons(); - void _RequestLanguageBarCapsIconRefresh(); - - BOOL VerifyMetasequoiaIMECLSID(_In_ REFCLSID clsid); - - friend LRESULT CALLBACK CMetasequoiaIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); - - private: - ITfThreadMgr *_pThreadMgr; - TfClientId _tfClientId; - DWORD _dwActivateFlags; - - // The cookie of ThreadMgrEventSink - DWORD _threadMgrEventSinkCookie; - - ITfContext *_pTextEditSinkContext; - DWORD _textEditSinkCookie; - - // The cookie of ActiveLanguageProfileNotifySink - DWORD _activeLanguageProfileNotifySinkCookie; - - // The cookie of ThreadFocusSink - DWORD _dwThreadFocusSinkCookie; - - // Composition Processor Engine object. - CCompositionProcessorEngine *_pCompositionProcessorEngine; - - // Language bar item object. - CLangBarItemButton *_pLangBarItem; - - // the current composition object. - ITfComposition *_pComposition; - - // guidatom for the display attibute. - TfGuidAtom _gaDisplayAttributeInput; - TfGuidAtom _gaDisplayAttributeConverted; - - CANDIDATE_MODE _candidateMode; - CCandidateListUIPresenter *_pCandidateListUIPresenter; - BOOL _isCandidateWithWildcard : 1; - - // Last smart-punctuation commit, used to detect a backspace rejection. - WCHAR _smartPunctuationKey = 0; - WCHAR _smartPunctuationPrecedingChar = 0; - bool _smartPunctuationCommittedAscii = false; - bool _smartPunctuationAsciiRejected = false; - ULONGLONG _smartPunctuationCommitTick = 0; - uint64_t _smartPunctuationFocusToken = 0; - HWND _smartPunctuationForegroundWindow = nullptr; - WCHAR _pendingSmartPunctuationReplacement = 0; - uint64_t _pendingSmartPunctuationFocusToken = 0; - HWND _pendingSmartPunctuationForegroundWindow = nullptr; - ULONGLONG _pendingSmartPunctuationDeadline = 0; - - // Last character known to have reached the application. Hosts such as the - // VS Code terminal back the context with a proxy text store that only ever - // receives what this tip commits: keys they route straight to the pty leave - // no trace, so reading the document there yields nothing or stale text. - // Keys passed through to the application are tracked here instead, and the - // document read is used only while this is invalid. - WCHAR _smartPunctuationShadowChar = 0; - bool _smartPunctuationShadowValid = false; - - ITfDocumentMgr *_pDocMgrLastFocused; - - ITfContext *_pContext; - - ITfCompartment *_pSIPIMEOnOffCompartment; - DWORD _dwSIPIMEOnOffCompartmentSinkCookie; - - HWND _msgWndHandle; - HKEY _themeRegKey; - HANDLE _themeRegEvent; - std::thread *_pThemeWatcherThread; - std::atomic _stopThemeWatcher; - std::thread *_pIpcThread; - std::atomic _hToTsfWorkerThreadPipe; - std::atomic _workerPipeGeneration; - HANDLE _ipcStopEvent; - std::atomic _shouldStopIpcThread; - UINT _ipcReconnectDelayMs; - UINT _ipcConsecutiveFailures; - std::mutex _pendingCommitCandidateMutex; - std::map _pendingServerCommitMessages; - std::map _pendingWorkerSwitchMessages; - std::map _pendingAsyncKeyMessages; - std::atomic _workerCommitReady; - std::atomic _expectedWorkerFocusToken; - std::atomic _acknowledgedWorkerFocusToken; - std::atomic _compositionEpoch; - std::wstring _voiceCompositionAssemble; - UINT _voiceCompositionAssembleMsg = 0; - wchar_t _voiceCompositionAssembleGeneration = 0; - bool _voiceCompositionAssembleActive = false; - bool _voiceCompositionActive = false; - std::atomic _localSessionResetPending; - std::atomic _localSessionResetToken; - bool _localResetEditSessionQueued; - UINT _queuedLocalResetToken; - bool _focusResetPending; - bool _activationRequired; - bool _focusLostToWindowsTextInputHost; - bool _focusLossDeferPending; - bool _hasPendingServerCandidate; - UINT _pendingServerCandidateMsgType; - std::wstring _pendingServerCandidateString; - std::deque _pendingCandidatePresenterCleanup; - std::deque _deferredKeyDowns; - std::deque _deferredAppliedPrefix; - DeferredKeyDown _deferredKeyInFlight; - bool _hasDeferredKeyInFlight; - uint64_t _deferredKeyReplayToken; - uint64_t _nextDeferredKeyReplayToken; - bool _deferredKeyProjectionValid; - bool _deferredProjectedImeOpen; - bool _deferredProjectedPunctuationOpen; - bool _deferredProjectedDoubleSingleByteOpen; - size_t _deferredProjectedInputLength; - std::wstring _deferredProjectedRawInput; - size_t _deferredProjectedCaret; - bool _deferredProjectedCandidateActive; - bool _deferredProjectedUnicodeMode; - uint64_t _deferredKeyFocusGeneration; - bool _deferredKeyDrainPosted; - bool _serverUnavailableFallbackActive; - - // Bare Shift/Ctrl toggle arming (Weasel-style: release within timeout). - bool _shiftHotkeyArmed; - bool _ctrlHotkeyArmed; - std::chrono::steady_clock::time_point _modifierHotkeyExpire; - - HHOOK _minttyKeyboardHook; - BYTE _minttyShiftDownMask; - bool _minttyShiftArmed; - UINT _minttyShiftSequence; - UINT _minttyShiftHandledSequence; - uint64_t _minttyShiftFocusGeneration; - ULONGLONG _minttyShiftExpireTick; - - LONG _refCount; - - // Support the search integration - ITfFnSearchCandidateProvider *_pITfFnSearchCandidateProvider; -}; +#pragma once + +#include "KeyHandlerEditSession.h" +#include "MetasequoiaIMEBaseStructure.h" +#include "Ipc.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class CLangBarItemButton; +class CCandidateListUIPresenter; +class CCompositionProcessorEngine; + +const DWORD WM_CheckGlobalCompartment = WM_USER; +const DWORD WM_ConnectNamedpipe = WM_USER + 1; +const DWORD WM_DisconnectNamedpipe = WM_USER + 2; +const DWORD WM_ConnectToTsfNamedpipe = WM_USER + 3; +const DWORD WM_IMEActivation = WM_USER + 4; +const DWORD WM_ThreadFocus = WM_USER + 5; +const DWORD WM_UpdateIMEStatus = WM_USER + 6; +const DWORD WM_UpdateDoubleSingleByte = WM_USER + 7; +const DWORD WM_UpdatePuncMode = WM_USER + 8; +const DWORD WM_CommitCandidate = WM_USER + 9; +const DWORD WM_CleanupCandidatePresenter = WM_USER + 10; +const DWORD WM_AsyncFinalizeCandidate = WM_USER + 11; +const DWORD WM_AsyncPunctuationCommit = WM_USER + 12; +const DWORD WM_AsyncNumberCandidateCommit = WM_USER + 13; +const DWORD WM_AsyncServerCandidateKey = WM_USER + 14; +const DWORD WM_IpcWorkerDisconnected = WM_USER + 15; +const DWORD WM_IpcReconnect = WM_USER + 16; +const DWORD WM_IpcSessionDirty = WM_USER + 17; +const DWORD WM_DrainDeferredKeyDown = WM_USER + 18; +const DWORD WM_InsertText = WM_USER + 19; +const DWORD WM_RefreshLanguageBarTheme = WM_USER + 20; +const DWORD WM_PairedPunctuationMoveLeft = WM_USER + 21; +const DWORD WM_ReplaceRepeatedSmartPunctuation = WM_USER + 22; +const DWORD WM_MinttyShiftRelease = WM_USER + 23; +const DWORD WM_UpdateVoiceComposition = WM_USER + 24; +const DWORD WM_CommitVoiceComposition = WM_USER + 25; +const DWORD WM_CancelVoiceComposition = WM_USER + 26; +const DWORD WM_ApplyPunctuationLock = WM_USER + 27; +constexpr ULONG_PTR SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO = 0x4D535050u; +constexpr ULONGLONG SMART_PUNCTUATION_REPEAT_INTERVAL_MS = 2000; +constexpr UINT_PTR TIMER_CONNECT_ALL_NAMEDPIPE = 1; +constexpr UINT_PTR TIMER_CONNECT_TO_TSF_NAMEDPIPE = 2; +constexpr UINT_PTR TIMER_REFRESH_LANG_BAR_THEME = 3; +constexpr UINT_PTR TIMER_DEFERRED_FOCUS_LOSS = 4; +constexpr UINT_PTR TIMER_FOCUS_STATUS_RESEND = 5; +constexpr UINT FOCUS_LOSS_DEFER_MS = 300; +// Chromium hosts fire a burst of OnSetFocus per window switch; coalesce them +// into one resend instead of one packet per callback. +constexpr UINT FOCUS_STATUS_RESEND_DELAY_MS = 50; +LRESULT CALLBACK CMetasequoiaIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class CMetasequoiaIME : public ITfTextInputProcessorEx, + public ITfThreadMgrEventSink, + public ITfTextEditSink, + public ITfKeyEventSink, + public ITfCompositionSink, + public ITfDisplayAttributeProvider, + public ITfActiveLanguageProfileNotifySink, + public ITfThreadFocusSink, + public ITfFunctionProvider, + public ITfFnGetPreferredTouchKeyboardLayout +{ + friend class CCompositionProcessorEngine; + friend class CKeyHandlerEditSession; + // Needs _IsComposing() to tell a host's transient context-view teardown apart + // from a real end of composition. + friend class CCandidateListUIPresenter; + + public: + CMetasequoiaIME(); + ~CMetasequoiaIME(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfTextInputProcessor + STDMETHODIMP Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClientId) + { + return ActivateEx(pThreadMgr, tfClientId, 0); + } + // ITfTextInputProcessorEx + STDMETHODIMP ActivateEx(ITfThreadMgr *pThreadMgr, TfClientId tfClientId, DWORD dwFlags); + STDMETHODIMP Deactivate(); + + // ITfThreadMgrEventSink + STDMETHODIMP OnInitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr); + STDMETHODIMP OnUninitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr); + STDMETHODIMP OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDocumentMgr *pDocMgrPrevFocus); + STDMETHODIMP OnPushContext(_In_ ITfContext *pContext); + STDMETHODIMP OnPopContext(_In_ ITfContext *pContext); + + // ITfTextEditSink + STDMETHODIMP OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly, + __RPC__in_opt ITfEditRecord *pEditRecord); + + // ITfKeyEventSink + STDMETHODIMP OnSetFocus(BOOL fForeground); + STDMETHODIMP OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pIsEaten); + + // ITfCompositionSink + STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition); + + // ITfDisplayAttributeProvider + STDMETHODIMP EnumDisplayAttributeInfo(__RPC__deref_out_opt IEnumTfDisplayAttributeInfo **ppEnum); + STDMETHODIMP GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, + __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo); + + // ITfActiveLanguageProfileNotifySink + STDMETHODIMP OnActivated(_In_ REFCLSID clsid, _In_ REFGUID guidProfile, _In_ BOOL isActivated); + + // ITfThreadFocusSink + STDMETHODIMP OnSetThreadFocus(); + STDMETHODIMP OnKillThreadFocus(); + + // ITfFunctionProvider + STDMETHODIMP GetType(__RPC__out GUID *pguid); + STDMETHODIMP GetDescription(__RPC__deref_out_opt BSTR *pbstrDesc); + STDMETHODIMP GetFunction(__RPC__in REFGUID rguid, __RPC__in REFIID riid, __RPC__deref_out_opt IUnknown **ppunk); + + // ITfFunction + STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrDisplayName); + + // ITfFnGetPreferredTouchKeyboardLayout, it is the Optimized layout feature. + STDMETHODIMP GetLayout(_Out_ TKBLayoutType *ptkblayoutType, _Out_ WORD *pwPreferredLayoutId); + + // CClassFactory factory callback + static HRESULT CreateInstance(_In_ IUnknown *pUnkOuter, REFIID riid, _Outptr_ void **ppvObj); + + // utility function for thread manager. + ITfThreadMgr *_GetThreadMgr() + { + return _pThreadMgr; + } + TfClientId _GetClientId() + { + return _tfClientId; + } + bool _IsServerUnavailableFallbackActive() const; + + // functions for the composition object. + void _SetComposition(_In_ ITfComposition *pComposition); + void _TerminateComposition(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCalledFromDeactivate = FALSE); + void _SaveCompositionContext(_In_ ITfContext *pContext); + + // key event handlers for composition/candidate/phrase common objects. + HRESULT _HandleComplete(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCompleteCommitFirst(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCancel(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleToogleIMEMode(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleInsertText(TfEditCookie ec, _In_ ITfContext *pContext, const std::wstring &text); + HRESULT _HandleUpdateVoiceComposition(TfEditCookie ec, _In_ ITfContext *pContext, const std::wstring &text); + HRESULT _HandleCommitVoiceComposition(TfEditCookie ec, _In_ ITfContext *pContext, const std::wstring &text); + HRESULT _HandleCancelVoiceComposition(TfEditCookie ec, _In_ ITfContext *pContext); + + // key event handlers for composition object. + HRESULT _HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch, uint64_t requestId); + HRESULT _HandleCompositionFinalize(TfEditCookie ec, _In_ ITfContext *pContext, BOOL fCandidateList); + HRESULT _HandleCompositionConvert(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isWildcardSearch); + HRESULT _HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId); + HRESULT _HandleCompositionDelete(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId); + HRESULT _HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, KEYSTROKE_FUNCTION keyFunction, + uint64_t requestId = FANY_IME_NO_REQUEST_ID); + HRESULT _HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, UINT code, WCHAR wch, + uint64_t requestId, const std::wstring &prefetchedText); + // Character immediately before the caret / composition start (0 if unavailable). + WCHAR _GetPrecedingDocumentChar(TfEditCookie ec, _In_ ITfContext *pContext); + // Shadow first, document read as the fallback. See _smartPunctuationShadowChar. + WCHAR _GetPrecedingCharForSmartPunctuation(TfEditCookie ec, _In_ ITfContext *pContext); + + // Smart punctuation: backspacing the ASCII punctuation we just committed + // means that form was unwanted, so the spot stays on Chinese punctuation. + std::wstring _ResolveSmartPunctuation(WCHAR wch, WCHAR precedingChar); + bool _QueueRepeatedSmartPunctuationReplacement(WCHAR wch); + void _NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten); + void _ResetSmartPunctuationHistory(); + void _UpdateSmartPunctuationShadow(UINT code, WCHAR wch, bool isEaten); + void _InvalidateSmartPunctuationShadow(); + HRESULT _HandleCompositionDoubleSingleByte(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch); + + // key event handlers for candidate object. + HRESULT _HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId, + const std::wstring &prefetchedText); + HRESULT _HandleCandidateFinalizeForVKReturn(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId, + const std::wstring &prefetchedText); + HRESULT _HandleCandidateArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction, + uint64_t requestId = FANY_IME_NO_REQUEST_ID); + HRESULT _HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode, + uint64_t requestId, const std::wstring &prefetchedText); + + BOOL _IsSecureMode(void) + { + return (_dwActivateFlags & TF_TMAE_SECUREMODE) ? TRUE : FALSE; + } + BOOL _IsComLess(void) + { + return (_dwActivateFlags & TF_TMAE_COMLESS) ? TRUE : FALSE; + } + BOOL _IsStoreAppMode(void) + { + return (_dwActivateFlags & TF_TMF_IMMERSIVEMODE) ? TRUE : FALSE; + } + // Host requested UILess (games / fullscreen / console): only ITfUIElement + // data is allowed — never an IME-owned HWND. + BOOL _IsUiLessMode(void) + { + return (_dwActivateFlags & TF_TMF_UIELEMENTENABLEDONLY) ? TRUE : FALSE; + } + + CCompositionProcessorEngine *GetCompositionProcessorEngine() + { + return (_pCompositionProcessorEngine); + }; + + // Async TSF edit sessions may be granted after a focus transition. Bind + // every text-producing session to the exact focus token that scheduled it + // so an old worker/key message cannot commit into the next document. + uint64_t _CaptureFocusSessionToken() const; + bool _IsFocusSessionCurrent(uint64_t focusToken, _In_opt_ ITfContext *expectedContext = nullptr) const; + uint64_t _CaptureCompositionEpoch() const; + bool _IsCompositionEpochCurrent(uint64_t compositionEpoch) const; + bool _IsCompositionCurrent(_In_opt_ ITfComposition *expectedComposition) const; + static bool _IsSameComObject(_In_opt_ IUnknown *left, _In_opt_ IUnknown *right); + void _DebugCompositionRecovery(_In_z_ const WCHAR *reason, HRESULT hr) const; + bool _IsLocalSessionResetCurrent(UINT resetToken) const; + void _CompleteLocalSessionReset(UINT resetToken); + bool _IsDeferredKeyReplayCurrent(uint64_t replayToken, uint64_t focusGeneration, + _In_opt_ ITfContext *expectedContext) const; + void _CompleteDeferredKeyReplay(uint64_t replayToken); + void _RetryDeferredKeyReplay(uint64_t replayToken); + + // comless helpers + static HRESULT CMetasequoiaIME::CreateInstance(REFCLSID rclsid, REFIID riid, _Outptr_result_maybenull_ LPVOID *ppv, + _Out_opt_ HINSTANCE *phInst, BOOL isComLessMode); + static HRESULT CMetasequoiaIME::ComLessCreateInstance(REFGUID rclsid, REFIID riid, + _Outptr_result_maybenull_ void **ppv, + _Out_opt_ HINSTANCE *phInst); + static HRESULT CMetasequoiaIME::GetComModuleName(REFGUID rclsid, _Out_writes_(cchPath) WCHAR *wchPath, + DWORD cchPath); + + static void IpcWorkerThread(CMetasequoiaIME *pIME); + void _QueuePendingServerCandidate(UINT msgType, _In_z_ const WCHAR *pCandidateString); + bool _TakePendingServerCandidate(_Out_ UINT *pMsgType, _Out_ std::wstring *pCandidateString); + void _ScheduleCandidatePresenterCleanup(_In_ CCandidateListUIPresenter *pPresenter); + void _DrainPendingCandidatePresenterCleanup(); + + private: + // functions for the composition object. + HRESULT _HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, + TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId); + HRESULT _CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, + _In_ ITfContext *pContext); + HRESULT _HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId, + const std::wstring &prefetchedText); + + struct AsyncKeyRequest + { + UINT message = 0; + UINT code = 0; + WCHAR wch = L'\0'; + uint64_t requestId = UINT64_MAX; + uint64_t focusToken = 0; + uint64_t compositionEpoch = 0; + uint64_t deferredReplayToken = 0; + std::wstring prefetchedText; + }; + bool _PostAsyncKeyRequest(UINT message, UINT code, WCHAR wch, uint64_t requestId, std::wstring prefetchedText = {}, + uint64_t expectedFocusToken = 0, uint64_t expectedCompositionEpoch = 0, + uint64_t deferredReplayToken = 0); + bool _TakeAsyncKeyRequest(UINT message, UINT token, _Out_ AsyncKeyRequest &request); + struct WorkerCandidateCommit + { + std::wstring text; + uint64_t focusToken = 0; + uint64_t compositionEpoch = 0; + }; + bool _PostServerCandidateCommit(_In_z_ const WCHAR *candidateText); + bool _PostServerInsertText(_In_z_ const WCHAR *text); + bool _PostServerTextDelivery(UINT windowMessage, _In_z_ const WCHAR *text); + bool _TakeServerCandidateCommit(UINT token, _Out_ WorkerCandidateCommit &request); + void _ResetVoiceCompositionAssemble(); + void _AssembleVoiceCompositionFrame(const FanyImeNamedpipeDataToTsfWorkerThread &buf); + void _DispatchUnsolicitedVoiceText(WPARAM wParam, KEYSTROKE_FUNCTION function); + struct WorkerCompartmentSwitch + { + UINT messageType = 0; + uint64_t focusToken = 0; + uint64_t compositionEpoch = 0; + }; + bool _PostWorkerCompartmentSwitch(UINT messageType, uint64_t focusToken); + bool _TakeWorkerCompartmentSwitch(UINT token, _Out_ WorkerCompartmentSwitch &request); + void _ClearAsyncKeyRequests(); + void _ClearPendingIpcRequests(); + void _RequestLocalSessionReset(_In_opt_ ITfContext *preferredContext, UINT resetToken); + bool _CaptureWindowsTextInputHostFocusLoss(); + + struct DeferredKeyDown + { + enum class Kind + { + KeyDown, + PreservedKey, + ApplicationText + }; + + Kind kind = Kind::KeyDown; + ITfContext *context = nullptr; + WPARAM wParam = 0; + LPARAM lParam = 0; + WCHAR translatedWch = L'\0'; + UINT modifiersDown = 0; + _KEYSTROKE_STATE keyState = {}; + GUID preservedKey = {}; + uint64_t focusGeneration = 0; + ULONGLONG queuedAtMs = 0; + UINT replayAttempts = 0; + bool preservedApplied = false; + }; + enum class KeyDownDispatchResult + { + Complete, + Retry, + AwaitingCompletion + }; + bool _HasDeferredKeyBarrier() const; + bool _DeferredKeyQueueHasCapacity() const; + void _EnsureDeferredKeyProjection(); + void _ApplyDeferredKeyProjection(const _KEYSTROKE_STATE &keyState, WCHAR wch); + void _ApplyDeferredPreservedKeyProjection(REFGUID preservedKey); + bool _RefreshDeferredRecoveryPrefix(_In_ ITfContext *pContext); + void _ArmDeferredRecoveryForTransport(_In_opt_ ITfContext *pContext); + bool _ClassifyDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, _In_opt_ const WCHAR *translatedWch, + _In_opt_ const UINT *modifiersDown, _Out_ WCHAR *classifiedWch, + _Out_ UINT *classifiedCode, _Out_ _KEYSTROKE_STATE *keyState); + bool _QueueDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam, WCHAR translatedWch, + UINT modifiersDown, const _KEYSTROKE_STATE &keyState); + bool _QueueDeferredPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey); + void _ClearDeferredKeyDowns(); + void _ScheduleDeferredKeyDownDrain(); + void _DrainOneDeferredKeyDown(); + void _TryLeaveServerUnavailableFallback(); + void _WakeServerIfNeeded(); + void _NoteKeyEventIpcFailure(); + HRESULT _RequestDeferredApplicationTextEditSession(_In_ ITfContext *pContext, WCHAR wch, + uint64_t expectedFocusToken, uint64_t expectedFocusGeneration, + uint64_t deferredReplayToken); + KeyDownDispatchResult _DispatchKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam, + _Out_ BOOL *pIsEaten, _In_opt_ const WCHAR *translatedWch, + _In_opt_ const UINT *modifiersDown, + _In_opt_ const _KEYSTROKE_STATE *prevalidatedKeyState, bool canDefer, + uint64_t expectedFocusGeneration, uint64_t deferredReplayToken = 0); + void _DispatchPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey, _Out_ BOOL *pIsEaten, + uint64_t expectedFocusGeneration, bool isPrevalidated, uint64_t deferredReplayToken = 0); + + // Input-mode hotkeys (Shift/Ctrl toggle, Ctrl+Alt+Space, Ctrl+Shift+Space, + // Ctrl+., Ctrl+Shift+E) are detected from ITfKeyEventSink like + // Weasel/Rime ascii_composer, not via TSF PreserveKey. Global/admin + // shortcuts stay on the Server LL hook. + void _TrackModifierHotkeyArming(WPARAM wParam, LPARAM lParam, bool isKeyUp); + bool _MatchChordInputHotkey(WPARAM wParam, _Out_ GUID *hotkeyGuid) const; + bool _MatchModifierReleaseHotkey(WPARAM wParam, _Out_ GUID *hotkeyGuid); + bool _QueueInputHotkey(_In_ ITfContext *pContext, REFGUID hotkeyGuid, _Out_ BOOL *pIsEaten); + + // mintty exposes IME composition through the legacy IMM bridge, but some + // versions do not forward bare modifier key-up events to ITfKeyEventSink. + // Observe only this host thread and feed a missed bare-Shift release back + // into the normal deferred hotkey path. + void _InitMinttyKeyboardHook(); + void _UninitMinttyKeyboardHook(); + void _HandleMinttyShiftRelease(UINT sequence); + void _MarkMinttyShiftHandled(); + static LRESULT CALLBACK _MinttyKeyboardHookProc(int code, WPARAM wParam, LPARAM lParam); + static thread_local CMetasequoiaIME *_minttyKeyboardHookOwner; + + void _StartComposition(_In_ ITfContext *pContext); + HRESULT _EndComposition(_In_opt_ ITfContext *pContext, _In_opt_ ITfComposition *expectedComposition = nullptr, + bool bypassFocusValidation = false); + BOOL _IsComposing(); + BOOL _IsKeyboardDisabled(); + + HRESULT _AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); + HRESULT _AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); + HRESULT _InsertTextToComposition(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); + HRESULT _SetCompositionTextAndSelection(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ CStringRange *pstrAddString); + + BOOL _FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, + _Outptr_result_maybenull_ ITfRange **ppRange); + HRESULT _SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, + _In_ CStringRange *pstrAddString, BOOL exist_composing); + HRESULT _InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString, + _Outptr_ ITfRange **ppCompRange); + + HRESULT _RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition); + + // Invoke key handler edit session + HRESULT _InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags, _KEYSTROKE_STATE keyState, + uint64_t requestId, std::wstring prefetchedText = {}, UINT localResetToken = 0, + uint64_t expectedCompositionEpoch = 0, uint64_t expectedFocusToken = 0, + uint64_t deferredReplayToken = 0); + HRESULT _RequestDirectPunctuationEditSession(_In_ ITfContext *pContext, UINT code, WCHAR wch, uint64_t requestId, + std::wstring prefetchedText, uint64_t expectedFocusToken = 0, + uint64_t expectedCompositionEpoch = 0, + uint64_t deferredReplayToken = 0); + + // function for the language property + BOOL _SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext); + + // function for the display attribute + void _ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, + _In_opt_ ITfComposition *expectedComposition = nullptr); + BOOL _SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, TfGuidAtom gaDisplayAttribute); + BOOL _SetCompositionDisplayAttributesForRange(TfEditCookie ec, _In_ ITfContext *pContext, + _In_ ITfRange *pRangeComposition, TfGuidAtom gaDisplayAttribute); + BOOL _InitDisplayAttributeGuidAtom(); + + BOOL _InitThreadMgrEventSink(); + void _UninitThreadMgrEventSink(); + void _HandleFocusedContextStackChange(_In_opt_ ITfContext *changedContext); + + BOOL _InitTextEditSink(_In_opt_ ITfDocumentMgr *pDocMgr); + + void _UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus); + + BOOL _InitKeyEventSink(); + void _UninitKeyEventSink(); + + BOOL _InitActiveLanguageProfileNotifySink(); + void _UninitActiveLanguageProfileNotifySink(); + + BOOL _IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch, + _Out_opt_ _KEYSTROKE_STATE *pKeyState, _In_opt_ const WCHAR *translatedWch = nullptr, + bool freshCompositionState = false); + + BOOL _IsRangeCovered(TfEditCookie ec, _In_ ITfRange *pRangeTest, _In_ ITfRange *pRangeCover); + VOID _DeleteCandidateList(BOOL fForce, _In_opt_ ITfContext *pContext); + + WCHAR ConvertVKey(UINT code); + + BOOL _InitThreadFocusSink(); + void _UninitThreadFocusSink(); + + BOOL _InitFunctionProviderSink(); + void _UninitFunctionProviderSink(); + + BOOL _AddTextProcessorEngine(); + + void _StartThemeRegistryWatcher(); + void _StopThemeRegistryWatcher(); + void _RefreshLanguageBarThemeIcons(); + void _RequestLanguageBarCapsIconRefresh(); + + BOOL VerifyMetasequoiaIMECLSID(_In_ REFCLSID clsid); + + friend LRESULT CALLBACK CMetasequoiaIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + ITfThreadMgr *_pThreadMgr; + TfClientId _tfClientId; + DWORD _dwActivateFlags; + + // The cookie of ThreadMgrEventSink + DWORD _threadMgrEventSinkCookie; + + ITfContext *_pTextEditSinkContext; + DWORD _textEditSinkCookie; + + // The cookie of ActiveLanguageProfileNotifySink + DWORD _activeLanguageProfileNotifySinkCookie; + + // The cookie of ThreadFocusSink + DWORD _dwThreadFocusSinkCookie; + + // Composition Processor Engine object. + CCompositionProcessorEngine *_pCompositionProcessorEngine; + + // Language bar item object. + CLangBarItemButton *_pLangBarItem; + + // the current composition object. + ITfComposition *_pComposition; + + // guidatom for the display attibute. + TfGuidAtom _gaDisplayAttributeInput; + TfGuidAtom _gaDisplayAttributeConverted; + + CANDIDATE_MODE _candidateMode; + CCandidateListUIPresenter *_pCandidateListUIPresenter; + BOOL _isCandidateWithWildcard : 1; + + // Last smart-punctuation commit, used to detect a backspace rejection. + WCHAR _smartPunctuationKey = 0; + WCHAR _smartPunctuationPrecedingChar = 0; + bool _smartPunctuationCommittedAscii = false; + bool _smartPunctuationAsciiRejected = false; + ULONGLONG _smartPunctuationCommitTick = 0; + uint64_t _smartPunctuationFocusToken = 0; + HWND _smartPunctuationForegroundWindow = nullptr; + WCHAR _pendingSmartPunctuationReplacement = 0; + uint64_t _pendingSmartPunctuationFocusToken = 0; + HWND _pendingSmartPunctuationForegroundWindow = nullptr; + ULONGLONG _pendingSmartPunctuationDeadline = 0; + + // Last character known to have reached the application. Hosts such as the + // VS Code terminal back the context with a proxy text store that only ever + // receives what this tip commits: keys they route straight to the pty leave + // no trace, so reading the document there yields nothing or stale text. + // Keys passed through to the application are tracked here instead, and the + // document read is used only while this is invalid. + WCHAR _smartPunctuationShadowChar = 0; + bool _smartPunctuationShadowValid = false; + + ITfDocumentMgr *_pDocMgrLastFocused; + + ITfContext *_pContext; + + ITfCompartment *_pSIPIMEOnOffCompartment; + DWORD _dwSIPIMEOnOffCompartmentSinkCookie; + + HWND _msgWndHandle; + HKEY _themeRegKey; + HANDLE _themeRegEvent; + std::thread *_pThemeWatcherThread; + std::atomic _stopThemeWatcher; + std::thread *_pIpcThread; + std::atomic _hToTsfWorkerThreadPipe; + std::atomic _workerPipeGeneration; + HANDLE _ipcStopEvent; + std::atomic _shouldStopIpcThread; + UINT _ipcReconnectDelayMs; + UINT _ipcConsecutiveFailures; + std::mutex _pendingCommitCandidateMutex; + std::map _pendingServerCommitMessages; + std::map _pendingWorkerSwitchMessages; + std::map _pendingAsyncKeyMessages; + std::atomic _workerCommitReady; + std::atomic _expectedWorkerFocusToken; + std::atomic _acknowledgedWorkerFocusToken; + std::atomic _compositionEpoch; + std::wstring _voiceCompositionAssemble; + UINT _voiceCompositionAssembleMsg = 0; + wchar_t _voiceCompositionAssembleGeneration = 0; + bool _voiceCompositionAssembleActive = false; + bool _voiceCompositionActive = false; + std::atomic _localSessionResetPending; + std::atomic _localSessionResetToken; + bool _localResetEditSessionQueued; + UINT _queuedLocalResetToken; + bool _focusResetPending; + bool _activationRequired; + bool _focusLostToWindowsTextInputHost; + bool _focusLossDeferPending; + bool _hasPendingServerCandidate; + UINT _pendingServerCandidateMsgType; + std::wstring _pendingServerCandidateString; + std::deque _pendingCandidatePresenterCleanup; + std::deque _deferredKeyDowns; + std::deque _deferredAppliedPrefix; + DeferredKeyDown _deferredKeyInFlight; + bool _hasDeferredKeyInFlight; + uint64_t _deferredKeyReplayToken; + uint64_t _nextDeferredKeyReplayToken; + bool _deferredKeyProjectionValid; + bool _deferredProjectedImeOpen; + bool _deferredProjectedPunctuationOpen; + bool _deferredProjectedDoubleSingleByteOpen; + size_t _deferredProjectedInputLength; + std::wstring _deferredProjectedRawInput; + size_t _deferredProjectedCaret; + bool _deferredProjectedCandidateActive; + bool _deferredProjectedUnicodeMode; + uint64_t _deferredKeyFocusGeneration; + bool _deferredKeyDrainPosted; + bool _serverUnavailableFallbackActive; + + // Bare Shift/Ctrl toggle arming (Weasel-style: release within timeout). + bool _shiftHotkeyArmed; + bool _ctrlHotkeyArmed; + std::chrono::steady_clock::time_point _modifierHotkeyExpire; + + HHOOK _minttyKeyboardHook; + BYTE _minttyShiftDownMask; + bool _minttyShiftArmed; + UINT _minttyShiftSequence; + UINT _minttyShiftHandledSequence; + uint64_t _minttyShiftFocusGeneration; + ULONGLONG _minttyShiftExpireTick; + + LONG _refCount; + + // Support the search integration + ITfFnSearchCandidateProvider *_pITfFnSearchCandidateProvider; +}; diff --git a/windows/src/IME/MetasequoiaIME.rc b/windows/src/IME/MetasequoiaIME.rc index e80aee7a..433712ea 100644 --- a/windows/src/IME/MetasequoiaIME.rc +++ b/windows/src/IME/MetasequoiaIME.rc @@ -1,128 +1,128 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDIS_METASEQUOIAIME ICON "..\\..\\image\\msime.ico" -IDI_IME_MODE_ON ICON "..\\..\\image\\cn-light.ico" -IDI_IME_MODE_OFF ICON "..\\..\\image\\en-light.ico" -IDI_IME_MODE_ON_DARK ICON "..\\..\\image\\cn-dark.ico" -IDI_IME_MODE_OFF_DARK ICON "..\\..\\image\\en-dark.ico" -IDI_IME_MODE_ON_JP ICON "..\\..\\image\\jp-light.ico" -IDI_IME_MODE_ON_JP_DARK ICON "..\\..\\image\\jp-dark.ico" -IDI_IME_MODE_CAP ICON "..\\..\\image\\cap-light.ico" -IDI_IME_MODE_CAP_DARK ICON "..\\..\\image\\cap-dark.ico" -IDI_DOUBLE_SINGLE_BYTE_ON ICON "..\\..\\image\\DoubleSingleByteOn.ico" -IDI_DOUBLE_SINGLE_BYTE_OFF ICON "..\\..\\image\\DoubleSingleByteOff.ico" -IDI_PUNCTUATION_ON ICON "..\\..\\image\\PunctuationOn.ico" -IDI_PUNCTUATION_OFF ICON "..\\..\\image\\PunctuationOff.ico" - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -IDR_VERSION2 VERSIONINFO - FILEVERSION 0,0,17,0 - PRODUCTVERSION 0,0,17,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "MSFT" - VALUE "FileDescription", "The Metasequoia code of Windows 11 IME" - VALUE "FileVersion", "0.0.17.0" - VALUE "InternalName", "MetasequoiaIM.dll" - VALUE "LegalCopyright", "Copyright (C)" - VALUE "OriginalFilename", "MetasequoiaIM.dll" - VALUE "ProductName", "MetasequoiaIME" - VALUE "ProductVersion", "0.0.17.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDIS_METASEQUOIAIME "Chinese Simplified QuanPin (version 6.0)" - IDS_DEFAULT_FONT "Microsoft YaHei UI" -END - -STRINGTABLE -BEGIN - IDS_IME_MODE "Chinese/English input (Shift)" - IDS_DOUBLE_SINGLE_BYTE "Double/Single byte (Shift+Space)" - IDS_PUNCTUATION "Chinese/English punctuation (Ctrl+.)" -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDIS_METASEQUOIAIME ICON "..\\..\\image\\msime.ico" +IDI_IME_MODE_ON ICON "..\\..\\image\\cn-light.ico" +IDI_IME_MODE_OFF ICON "..\\..\\image\\en-light.ico" +IDI_IME_MODE_ON_DARK ICON "..\\..\\image\\cn-dark.ico" +IDI_IME_MODE_OFF_DARK ICON "..\\..\\image\\en-dark.ico" +IDI_IME_MODE_ON_JP ICON "..\\..\\image\\jp-light.ico" +IDI_IME_MODE_ON_JP_DARK ICON "..\\..\\image\\jp-dark.ico" +IDI_IME_MODE_CAP ICON "..\\..\\image\\cap-light.ico" +IDI_IME_MODE_CAP_DARK ICON "..\\..\\image\\cap-dark.ico" +IDI_DOUBLE_SINGLE_BYTE_ON ICON "..\\..\\image\\DoubleSingleByteOn.ico" +IDI_DOUBLE_SINGLE_BYTE_OFF ICON "..\\..\\image\\DoubleSingleByteOff.ico" +IDI_PUNCTUATION_ON ICON "..\\..\\image\\PunctuationOn.ico" +IDI_PUNCTUATION_OFF ICON "..\\..\\image\\PunctuationOff.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +IDR_VERSION2 VERSIONINFO + FILEVERSION 0,0,17,0 + PRODUCTVERSION 0,0,17,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "MSFT" + VALUE "FileDescription", "The Metasequoia code of Windows 11 IME" + VALUE "FileVersion", "0.0.17.0" + VALUE "InternalName", "MetasequoiaIM.dll" + VALUE "LegalCopyright", "Copyright (C)" + VALUE "OriginalFilename", "MetasequoiaIM.dll" + VALUE "ProductName", "MetasequoiaIME" + VALUE "ProductVersion", "0.0.17.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDIS_METASEQUOIAIME "Chinese Simplified QuanPin (version 6.0)" + IDS_DEFAULT_FONT "Microsoft YaHei UI" +END + +STRINGTABLE +BEGIN + IDS_IME_MODE "Chinese/English input (Shift)" + IDS_DOUBLE_SINGLE_BYTE "Double/Single byte (Shift+Space)" + IDS_PUNCTUATION "Chinese/English punctuation (Ctrl+.)" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/windows/src/IME/MetasequoiaIMEBaseStructure.cpp b/windows/src/IME/MetasequoiaIMEBaseStructure.cpp index ee9ed32f..8070f668 100644 --- a/windows/src/IME/MetasequoiaIMEBaseStructure.cpp +++ b/windows/src/IME/MetasequoiaIMEBaseStructure.cpp @@ -1,366 +1,366 @@ -#ifndef UNICODE -#define UNICODE -#endif // !UNICODE - -#ifndef _UNICODE -#define _UNICODE -#endif // !UNICODE - -#include "Globals.h" - -//--------------------------------------------------------------------- -// -// CLSIDToString -// -//--------------------------------------------------------------------- - -const BYTE GuidSymbols[] = {3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-', 8, 9, '-', 10, 11, 12, 13, 14, 15}; - -const WCHAR HexDigits[] = L"0123456789ABCDEF"; - -BOOL CLSIDToString(REFGUID refGUID, _Out_writes_(39) WCHAR *pCLSIDString) -{ - WCHAR *pTemp = pCLSIDString; - const BYTE *pBytes = (const BYTE *)&refGUID; - - DWORD j = 0; - pTemp[j++] = L'{'; - for (int i = 0; i < sizeof(GuidSymbols) && j < (CLSID_STRLEN - 2); i++) - { - if (GuidSymbols[i] == '-') - { - pTemp[j++] = L'-'; - } - else - { - pTemp[j++] = HexDigits[(pBytes[GuidSymbols[i]] & 0xF0) >> 4]; - pTemp[j++] = HexDigits[(pBytes[GuidSymbols[i]] & 0x0F)]; - } - } - - pTemp[j++] = L'}'; - pTemp[j] = L'\0'; - - return TRUE; -} - -//--------------------------------------------------------------------- -// -// SkipWhiteSpace -// -// dwBufLen - in character count -// -//--------------------------------------------------------------------- - -HRESULT SkipWhiteSpace(LCID locale, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex) -{ - DWORD_PTR index = 0; - - *pdwIndex = 0; - while (*pwszBuffer && IsSpace(locale, *pwszBuffer) && dwBufLen) - { - dwBufLen--; - pwszBuffer++; - index++; - } - - if (*pwszBuffer && dwBufLen) - { - *pdwIndex = index; - return S_OK; - } - - return E_FAIL; -} - -//--------------------------------------------------------------------- -// -// FindChar -// -// dwBufLen - in character count -// -//--------------------------------------------------------------------- - -HRESULT FindChar(WCHAR wch, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex) -{ - DWORD_PTR index = 0; - - *pdwIndex = 0; - while (*pwszBuffer && (*pwszBuffer != wch) && dwBufLen) - { - dwBufLen--; - pwszBuffer++; - index++; - } - - if (*pwszBuffer && dwBufLen) - { - *pdwIndex = index; - return S_OK; - } - - return E_FAIL; -} - -//--------------------------------------------------------------------- -// -// IsSpace -// -//--------------------------------------------------------------------- - -BOOL IsSpace(LCID locale, WCHAR wch) -{ - WORD wCharType = 0; - - GetStringTypeEx(locale, CT_CTYPE1, &wch, 1, &wCharType); - return (wCharType & C1_SPACE); -} - -CStringRange::CStringRange() -{ - _stringBufLen = 0; - _pStringBuf = nullptr; -} - -CStringRange::CStringRange(const CStringRange &sr) : _stringBufLen(0), _pStringBuf(nullptr) -{ - if (sr._stringBufLen > 0 && sr._pStringBuf != nullptr) - { - _pStringBuf = new WCHAR[sr._stringBufLen]; - _stringBufLen = sr._stringBufLen; - std::memcpy((void *)_pStringBuf, sr._pStringBuf, sr._stringBufLen * sizeof(WCHAR)); - } -} - -CStringRange::~CStringRange() -{ -} - -const WCHAR *CStringRange::Get() const -{ - return _pStringBuf; -} - -const DWORD_PTR CStringRange::GetLength() const -{ - return _stringBufLen; -} - -void CStringRange::Clear() -{ - _stringBufLen = 0; - _pStringBuf = nullptr; -} - -void CStringRange::Set(const WCHAR *pwch, DWORD_PTR dwLength) -{ - _stringBufLen = dwLength; - _pStringBuf = pwch; -} - -void CStringRange::Set(CStringRange &sr) -{ - *this = sr; -} - -CStringRange &CStringRange::operator=(const CStringRange &sr) -{ - _stringBufLen = sr._stringBufLen; - _pStringBuf = sr._pStringBuf; - return *this; -} - -void CStringRange::CharNext(_Inout_ CStringRange *pCharNext) -{ - if (!_stringBufLen) - { - pCharNext->_stringBufLen = 0; - pCharNext->_pStringBuf = nullptr; - } - else - { - pCharNext->_stringBufLen = _stringBufLen; - pCharNext->_pStringBuf = _pStringBuf; - - while (pCharNext->_stringBufLen) - { - BOOL isSurrogate = - (IS_HIGH_SURROGATE(*pCharNext->_pStringBuf) || IS_LOW_SURROGATE(*pCharNext->_pStringBuf)); - pCharNext->_stringBufLen--; - pCharNext->_pStringBuf++; - if (!isSurrogate) - { - break; - } - } - } -} - -int CStringRange::Compare(LCID locale, _In_ CStringRange *pString1, _In_ CStringRange *pString2) -{ - return CompareString(locale, NORM_IGNORECASE, pString1->Get(), (DWORD)pString1->GetLength(), pString2->Get(), - (DWORD)pString2->GetLength()); -} - -BOOL CStringRange::WildcardCompare(LCID locale, _In_ CStringRange *stringWithWildcard, _In_ CStringRange *targetString) -{ - if (stringWithWildcard->GetLength() == 0) - { - return targetString->GetLength() == 0 ? TRUE : FALSE; - } - - CStringRange stringWithWildcard_next; - CStringRange targetString_next; - stringWithWildcard->CharNext(&stringWithWildcard_next); - targetString->CharNext(&targetString_next); - - if (*stringWithWildcard->Get() == L'*') - { - return WildcardCompare(locale, &stringWithWildcard_next, targetString) || - ((targetString->GetLength() != 0) && WildcardCompare(locale, stringWithWildcard, &targetString_next)); - } - if (*stringWithWildcard->Get() == L'?') - { - return ((targetString->GetLength() != 0) && - WildcardCompare(locale, &stringWithWildcard_next, &targetString_next)); - } - - BOOL isSurrogate1 = (IS_HIGH_SURROGATE(*stringWithWildcard->Get()) || IS_LOW_SURROGATE(*stringWithWildcard->Get())); - BOOL isSurrogate2 = (IS_HIGH_SURROGATE(*targetString->Get()) || IS_LOW_SURROGATE(*targetString->Get())); - - return ((CompareString(locale, NORM_IGNORECASE, stringWithWildcard->Get(), (isSurrogate1 ? 2 : 1), - targetString->Get(), (isSurrogate2 ? 2 : 1)) == CSTR_EQUAL) && - WildcardCompare(locale, &stringWithWildcard_next, &targetString_next)); -} - -CCandidateRange::CCandidateRange(void) -{ -} - -CCandidateRange::~CCandidateRange(void) -{ -} - -BOOL CCandidateRange::IsRange(UINT vKey) -{ - DWORD value = vKey - L'0'; - if (value > 0 && value < 10) // Start from 1 - { - return TRUE; - } - - for (UINT i = 0; i < _CandidateListIndexRange.Count(); i++) - { - if (value == *_CandidateListIndexRange.GetAt(i)) - { - return TRUE; - } - else if ((VK_NUMPAD0 <= vKey) && (vKey <= VK_NUMPAD9)) - { - if ((vKey - VK_NUMPAD0) == *_CandidateListIndexRange.GetAt(i)) - { - return TRUE; - } - } - } - return FALSE; -} - -int CCandidateRange::GetIndex(UINT vKey) -{ - return 0; // Always return 0(First candidate item), start from 0 - - DWORD value = vKey - L'0'; - - for (UINT i = 0; i < _CandidateListIndexRange.Count(); i++) - { - if (value == *_CandidateListIndexRange.GetAt(i)) - { - return i; - } - else if ((VK_NUMPAD0 <= vKey) && (vKey <= VK_NUMPAD9)) - { - if ((vKey - VK_NUMPAD0) == *_CandidateListIndexRange.GetAt(i)) - { - return i; - } - } - } - return -1; -} - -CPunctuationPair::CPunctuationPair() -{ - _punctuation._Code = 0; - _punctuation._Punctuation[0] = 0; - _pairPunctuation[0] = 0; - _isPairToggle = FALSE; -} - -CPunctuationPair::CPunctuationPair( // - WCHAR code, // - const WCHAR *punctuation, // - const WCHAR *pair // -) -{ - _punctuation._Code = code; - - wcsncpy_s(_punctuation._Punctuation, _countof(_punctuation._Punctuation), punctuation, _TRUNCATE); - wcsncpy_s(_pairPunctuation, _countof(_pairPunctuation), pair, _TRUNCATE); - _isPairToggle = FALSE; -} - -CPunctuationNestPair::CPunctuationNestPair() -{ - _punctuation_begin._Code = 0; - _punctuation_begin._Punctuation[0] = 0; - _pairPunctuation_begin[0] = 0; - - _punctuation_end._Code = 0; - _punctuation_end._Punctuation[0] = 0; - _pairPunctuation_end[0] = 0; - - _nestCount = 0; -} - -CPunctuationNestPair::CPunctuationNestPair( // - WCHAR codeBegin, // - const WCHAR *punctuationBegin, // - const WCHAR *pairBegin, // - const WCHAR codeEnd, // - const WCHAR *punctuationEnd, // - const WCHAR *pairEnd // -) -{ - pairEnd; - punctuationEnd; - _punctuation_begin._Code = codeBegin; - wcsncpy_s( // - _punctuation_begin._Punctuation, // - _countof(_punctuation_begin._Punctuation), // - punctuationBegin, // - _TRUNCATE // - ); - wcsncpy_s( // - _pairPunctuation_begin, // - _countof(_pairPunctuation_begin), // - pairBegin, // - _TRUNCATE // - ); - - _punctuation_end._Code = codeEnd; - wcsncpy_s( // - _punctuation_end._Punctuation, // - _countof(_punctuation_end._Punctuation), // - punctuationEnd, // - _TRUNCATE // - ); - wcsncpy_s( // - _pairPunctuation_end, // - _countof(_pairPunctuation_end), // - pairEnd, // - _TRUNCATE // - ); - - _nestCount = 0; -} +#ifndef UNICODE +#define UNICODE +#endif // !UNICODE + +#ifndef _UNICODE +#define _UNICODE +#endif // !UNICODE + +#include "Globals.h" + +//--------------------------------------------------------------------- +// +// CLSIDToString +// +//--------------------------------------------------------------------- + +const BYTE GuidSymbols[] = {3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-', 8, 9, '-', 10, 11, 12, 13, 14, 15}; + +const WCHAR HexDigits[] = L"0123456789ABCDEF"; + +BOOL CLSIDToString(REFGUID refGUID, _Out_writes_(39) WCHAR *pCLSIDString) +{ + WCHAR *pTemp = pCLSIDString; + const BYTE *pBytes = (const BYTE *)&refGUID; + + DWORD j = 0; + pTemp[j++] = L'{'; + for (int i = 0; i < sizeof(GuidSymbols) && j < (CLSID_STRLEN - 2); i++) + { + if (GuidSymbols[i] == '-') + { + pTemp[j++] = L'-'; + } + else + { + pTemp[j++] = HexDigits[(pBytes[GuidSymbols[i]] & 0xF0) >> 4]; + pTemp[j++] = HexDigits[(pBytes[GuidSymbols[i]] & 0x0F)]; + } + } + + pTemp[j++] = L'}'; + pTemp[j] = L'\0'; + + return TRUE; +} + +//--------------------------------------------------------------------- +// +// SkipWhiteSpace +// +// dwBufLen - in character count +// +//--------------------------------------------------------------------- + +HRESULT SkipWhiteSpace(LCID locale, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex) +{ + DWORD_PTR index = 0; + + *pdwIndex = 0; + while (*pwszBuffer && IsSpace(locale, *pwszBuffer) && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + index++; + } + + if (*pwszBuffer && dwBufLen) + { + *pdwIndex = index; + return S_OK; + } + + return E_FAIL; +} + +//--------------------------------------------------------------------- +// +// FindChar +// +// dwBufLen - in character count +// +//--------------------------------------------------------------------- + +HRESULT FindChar(WCHAR wch, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex) +{ + DWORD_PTR index = 0; + + *pdwIndex = 0; + while (*pwszBuffer && (*pwszBuffer != wch) && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + index++; + } + + if (*pwszBuffer && dwBufLen) + { + *pdwIndex = index; + return S_OK; + } + + return E_FAIL; +} + +//--------------------------------------------------------------------- +// +// IsSpace +// +//--------------------------------------------------------------------- + +BOOL IsSpace(LCID locale, WCHAR wch) +{ + WORD wCharType = 0; + + GetStringTypeEx(locale, CT_CTYPE1, &wch, 1, &wCharType); + return (wCharType & C1_SPACE); +} + +CStringRange::CStringRange() +{ + _stringBufLen = 0; + _pStringBuf = nullptr; +} + +CStringRange::CStringRange(const CStringRange &sr) : _stringBufLen(0), _pStringBuf(nullptr) +{ + if (sr._stringBufLen > 0 && sr._pStringBuf != nullptr) + { + _pStringBuf = new WCHAR[sr._stringBufLen]; + _stringBufLen = sr._stringBufLen; + std::memcpy((void *)_pStringBuf, sr._pStringBuf, sr._stringBufLen * sizeof(WCHAR)); + } +} + +CStringRange::~CStringRange() +{ +} + +const WCHAR *CStringRange::Get() const +{ + return _pStringBuf; +} + +const DWORD_PTR CStringRange::GetLength() const +{ + return _stringBufLen; +} + +void CStringRange::Clear() +{ + _stringBufLen = 0; + _pStringBuf = nullptr; +} + +void CStringRange::Set(const WCHAR *pwch, DWORD_PTR dwLength) +{ + _stringBufLen = dwLength; + _pStringBuf = pwch; +} + +void CStringRange::Set(CStringRange &sr) +{ + *this = sr; +} + +CStringRange &CStringRange::operator=(const CStringRange &sr) +{ + _stringBufLen = sr._stringBufLen; + _pStringBuf = sr._pStringBuf; + return *this; +} + +void CStringRange::CharNext(_Inout_ CStringRange *pCharNext) +{ + if (!_stringBufLen) + { + pCharNext->_stringBufLen = 0; + pCharNext->_pStringBuf = nullptr; + } + else + { + pCharNext->_stringBufLen = _stringBufLen; + pCharNext->_pStringBuf = _pStringBuf; + + while (pCharNext->_stringBufLen) + { + BOOL isSurrogate = + (IS_HIGH_SURROGATE(*pCharNext->_pStringBuf) || IS_LOW_SURROGATE(*pCharNext->_pStringBuf)); + pCharNext->_stringBufLen--; + pCharNext->_pStringBuf++; + if (!isSurrogate) + { + break; + } + } + } +} + +int CStringRange::Compare(LCID locale, _In_ CStringRange *pString1, _In_ CStringRange *pString2) +{ + return CompareString(locale, NORM_IGNORECASE, pString1->Get(), (DWORD)pString1->GetLength(), pString2->Get(), + (DWORD)pString2->GetLength()); +} + +BOOL CStringRange::WildcardCompare(LCID locale, _In_ CStringRange *stringWithWildcard, _In_ CStringRange *targetString) +{ + if (stringWithWildcard->GetLength() == 0) + { + return targetString->GetLength() == 0 ? TRUE : FALSE; + } + + CStringRange stringWithWildcard_next; + CStringRange targetString_next; + stringWithWildcard->CharNext(&stringWithWildcard_next); + targetString->CharNext(&targetString_next); + + if (*stringWithWildcard->Get() == L'*') + { + return WildcardCompare(locale, &stringWithWildcard_next, targetString) || + ((targetString->GetLength() != 0) && WildcardCompare(locale, stringWithWildcard, &targetString_next)); + } + if (*stringWithWildcard->Get() == L'?') + { + return ((targetString->GetLength() != 0) && + WildcardCompare(locale, &stringWithWildcard_next, &targetString_next)); + } + + BOOL isSurrogate1 = (IS_HIGH_SURROGATE(*stringWithWildcard->Get()) || IS_LOW_SURROGATE(*stringWithWildcard->Get())); + BOOL isSurrogate2 = (IS_HIGH_SURROGATE(*targetString->Get()) || IS_LOW_SURROGATE(*targetString->Get())); + + return ((CompareString(locale, NORM_IGNORECASE, stringWithWildcard->Get(), (isSurrogate1 ? 2 : 1), + targetString->Get(), (isSurrogate2 ? 2 : 1)) == CSTR_EQUAL) && + WildcardCompare(locale, &stringWithWildcard_next, &targetString_next)); +} + +CCandidateRange::CCandidateRange(void) +{ +} + +CCandidateRange::~CCandidateRange(void) +{ +} + +BOOL CCandidateRange::IsRange(UINT vKey) +{ + DWORD value = vKey - L'0'; + if (value > 0 && value < 10) // Start from 1 + { + return TRUE; + } + + for (UINT i = 0; i < _CandidateListIndexRange.Count(); i++) + { + if (value == *_CandidateListIndexRange.GetAt(i)) + { + return TRUE; + } + else if ((VK_NUMPAD0 <= vKey) && (vKey <= VK_NUMPAD9)) + { + if ((vKey - VK_NUMPAD0) == *_CandidateListIndexRange.GetAt(i)) + { + return TRUE; + } + } + } + return FALSE; +} + +int CCandidateRange::GetIndex(UINT vKey) +{ + return 0; // Always return 0(First candidate item), start from 0 + + DWORD value = vKey - L'0'; + + for (UINT i = 0; i < _CandidateListIndexRange.Count(); i++) + { + if (value == *_CandidateListIndexRange.GetAt(i)) + { + return i; + } + else if ((VK_NUMPAD0 <= vKey) && (vKey <= VK_NUMPAD9)) + { + if ((vKey - VK_NUMPAD0) == *_CandidateListIndexRange.GetAt(i)) + { + return i; + } + } + } + return -1; +} + +CPunctuationPair::CPunctuationPair() +{ + _punctuation._Code = 0; + _punctuation._Punctuation[0] = 0; + _pairPunctuation[0] = 0; + _isPairToggle = FALSE; +} + +CPunctuationPair::CPunctuationPair( // + WCHAR code, // + const WCHAR *punctuation, // + const WCHAR *pair // +) +{ + _punctuation._Code = code; + + wcsncpy_s(_punctuation._Punctuation, _countof(_punctuation._Punctuation), punctuation, _TRUNCATE); + wcsncpy_s(_pairPunctuation, _countof(_pairPunctuation), pair, _TRUNCATE); + _isPairToggle = FALSE; +} + +CPunctuationNestPair::CPunctuationNestPair() +{ + _punctuation_begin._Code = 0; + _punctuation_begin._Punctuation[0] = 0; + _pairPunctuation_begin[0] = 0; + + _punctuation_end._Code = 0; + _punctuation_end._Punctuation[0] = 0; + _pairPunctuation_end[0] = 0; + + _nestCount = 0; +} + +CPunctuationNestPair::CPunctuationNestPair( // + WCHAR codeBegin, // + const WCHAR *punctuationBegin, // + const WCHAR *pairBegin, // + const WCHAR codeEnd, // + const WCHAR *punctuationEnd, // + const WCHAR *pairEnd // +) +{ + pairEnd; + punctuationEnd; + _punctuation_begin._Code = codeBegin; + wcsncpy_s( // + _punctuation_begin._Punctuation, // + _countof(_punctuation_begin._Punctuation), // + punctuationBegin, // + _TRUNCATE // + ); + wcsncpy_s( // + _pairPunctuation_begin, // + _countof(_pairPunctuation_begin), // + pairBegin, // + _TRUNCATE // + ); + + _punctuation_end._Code = codeEnd; + wcsncpy_s( // + _punctuation_end._Punctuation, // + _countof(_punctuation_end._Punctuation), // + punctuationEnd, // + _TRUNCATE // + ); + wcsncpy_s( // + _pairPunctuation_end, // + _countof(_pairPunctuation_end), // + pairEnd, // + _TRUNCATE // + ); + + _nestCount = 0; +} diff --git a/windows/src/IME/MetasequoiaIMEBaseStructure.h b/windows/src/IME/MetasequoiaIMEBaseStructure.h index f7df9850..4399dbad 100644 --- a/windows/src/IME/MetasequoiaIMEBaseStructure.h +++ b/windows/src/IME/MetasequoiaIMEBaseStructure.h @@ -1,284 +1,284 @@ -#pragma once - -#include "stdafx.h" -#include -#include "assert.h" -#include - -using std::endl; - -//--------------------------------------------------------------------- -// defined keyword -//--------------------------------------------------------------------- -template struct _DEFINED_KEYWORD -{ - LPCWSTR _pwszKeyword; - VALUE _value; -}; - -//--------------------------------------------------------------------- -// enum -//--------------------------------------------------------------------- -enum KEYSTROKE_CATEGORY -{ - CATEGORY_NONE = 0, - CATEGORY_COMPOSING, - CATEGORY_CANDIDATE, - CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION -}; - -enum KEYSTROKE_FUNCTION -{ - FUNCTION_NONE = 0, - FUNCTION_INPUT, - - FUNCTION_CANCEL, - FUNCTION_TOGGLE_IME_MODE, // Toggle IME mode - FUNCTION_FINALIZE_TEXTSTORE, - FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT, - FUNCTION_FINALIZE_CANDIDATELIST, - FUNCTION_FINALIZE_CANDIDATELISTForVKReturn, - FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT, - FUNCTION_CONVERT, - FUNCTION_CONVERT_WILDCARD, - FUNCTION_SELECT_BY_NUMBER, - FUNCTION_BACKSPACE, - FUNCTION_DELETE, - FUNCTION_MOVE_LEFT, - FUNCTION_MOVE_RIGHT, - FUNCTION_MOVE_UP, - FUNCTION_MOVE_DOWN, - FUNCTION_MOVE_PAGE_UP, - FUNCTION_MOVE_PAGE_DOWN, - FUNCTION_MOVE_PAGE_TOP, - FUNCTION_MOVE_PAGE_BOTTOM, - - // Function Double/Single byte - FUNCTION_DOUBLE_SINGLE_BYTE, - - // Function Punctuation - FUNCTION_PUNCTUATION, - - // Raw candidate navigation key; Server decides its configured behavior. - FUNCTION_SERVER_CANDIDATE_KEY, - - // Unsolicited insert (voice ASR via worker pipe). Append-only so existing - // ordinals stay stable across builds. - FUNCTION_INSERT_TEXT, - FUNCTION_UPDATE_VOICE_COMPOSITION, - FUNCTION_COMMIT_VOICE_COMPOSITION, - FUNCTION_CANCEL_VOICE_COMPOSITION, - FUNCTION_TOGGLE_CHARACTER_SET -}; - -//--------------------------------------------------------------------- -// candidate list -//--------------------------------------------------------------------- -enum CANDIDATE_MODE -{ - CANDIDATE_NONE = 0, - CANDIDATE_ORIGINAL, - CANDIDATE_INCREMENTAL -}; - -//--------------------------------------------------------------------- -// structure -//--------------------------------------------------------------------- -struct _KEYSTROKE_STATE -{ - KEYSTROKE_CATEGORY Category; - KEYSTROKE_FUNCTION Function; -}; - -struct _PUNCTUATION -{ - WCHAR _Code; - WCHAR _Punctuation[3]; -}; - -BOOL CLSIDToString(REFGUID refGUID, _Out_writes_(39) WCHAR *pCLSIDString); - -HRESULT SkipWhiteSpace(LCID locale, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex); -HRESULT FindChar(WCHAR wch, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex); - -BOOL IsSpace(LCID locale, WCHAR wch); - -template class CMetasequoiaImeArray -{ - typedef typename std::vector CMetasequoiaImeInnerArray; - typedef typename std::vector::iterator CMetasequoiaImeInnerIter; - - public: - CMetasequoiaImeArray() : _innerVect() - { - } - - explicit CMetasequoiaImeArray(size_t count) : _innerVect(count) - { - } - - virtual ~CMetasequoiaImeArray() - { - } - - inline T *GetAt(size_t index) - { - assert(index >= 0); - assert(index < _innerVect.size()); - - T &curT = _innerVect.at(index); - - return &(curT); - } - - inline const T *GetAt(size_t index) const - { - assert(index >= 0); - assert(index < _innerVect.size()); - - T &curT = _innerVect.at(index); - - return &(curT); - } - - void RemoveAt(size_t index) - { - assert(index >= 0); - assert(index < _innerVect.size()); - - CMetasequoiaImeInnerIter iter = _innerVect.begin(); - _innerVect.erase(iter + index); - } - - UINT Count() const - { - return static_cast(_innerVect.size()); - } - - T *Append() - { - T newT; - _innerVect.push_back(newT); - T &backT = _innerVect.back(); - - return &(backT); - } - - void reserve(size_t Count) - { - _innerVect.reserve(Count); - } - - void Clear() - { - _innerVect.clear(); - } - - private: - CMetasequoiaImeInnerArray _innerVect; -}; - -class CCandidateRange -{ - public: - CCandidateRange(void); - ~CCandidateRange(void); - - BOOL IsRange(UINT vKey); - int GetIndex(UINT vKey); - - inline int Count() const - { - return _CandidateListIndexRange.Count(); - } - inline DWORD *GetAt(int index) - { - return _CandidateListIndexRange.GetAt(index); - } - inline DWORD *Append() - { - return _CandidateListIndexRange.Append(); - } - - private: - CMetasequoiaImeArray _CandidateListIndexRange; -}; - -class CStringRange -{ - public: - CStringRange(); - CStringRange(const CStringRange &sr); - ~CStringRange(); - - const WCHAR *Get() const; - const DWORD_PTR GetLength() const; - void Clear(); - void Set(const WCHAR *pwch, DWORD_PTR dwLength); - void Set(CStringRange &sr); - CStringRange &operator=(const CStringRange &sr); - void CharNext(_Inout_ CStringRange *pCharNext); - static int Compare(LCID locale, _In_ CStringRange *pString1, _In_ CStringRange *pString2); - static BOOL WildcardCompare(LCID locale, _In_ CStringRange *stringWithWildcard, _In_ CStringRange *targetString); - std::wstring ToWString() const - { - if (!_pStringBuf || _stringBufLen == 0) - return std::wstring(); - return std::wstring(_pStringBuf, _pStringBuf + _stringBufLen); - } - - protected: - DWORD_PTR _stringBufLen; // Length is in character count. - const WCHAR *_pStringBuf; // Buffer which is not add zero terminate. -}; - -//--------------------------------------------------------------------- -// CCandidateListItem -// _ItemString - candidate string -// _FindKeyCode - tailing string -//--------------------------------------------------------------------- -struct CCandidateListItem -{ - CStringRange _ItemString; - CStringRange _FindKeyCode; - - CCandidateListItem &CCandidateListItem::operator=(const CCandidateListItem &rhs) - { - _ItemString = rhs._ItemString; // e.g. 你好 - _FindKeyCode = rhs._FindKeyCode; // e.g. nihao - return *this; - } -}; - -class CPunctuationPair -{ - public: - CPunctuationPair(); - CPunctuationPair(WCHAR code, const WCHAR *punctuation, const WCHAR *pair); - - struct _PUNCTUATION _punctuation; - WCHAR _pairPunctuation[3]; - BOOL _isPairToggle; -}; - -class CPunctuationNestPair -{ - public: - CPunctuationNestPair(); - CPunctuationNestPair( // - WCHAR codeBegin, // - const WCHAR *punctuationBegin, // - const WCHAR *pairBegin, // - const WCHAR codeEnd, // - const WCHAR *punctuationEnd, // - const WCHAR *pairEnd // - ); - - struct _PUNCTUATION _punctuation_begin; - WCHAR _pairPunctuation_begin[3]; - - struct _PUNCTUATION _punctuation_end; - WCHAR _pairPunctuation_end[3]; - - int _nestCount; -}; +#pragma once + +#include "stdafx.h" +#include +#include "assert.h" +#include + +using std::endl; + +//--------------------------------------------------------------------- +// defined keyword +//--------------------------------------------------------------------- +template struct _DEFINED_KEYWORD +{ + LPCWSTR _pwszKeyword; + VALUE _value; +}; + +//--------------------------------------------------------------------- +// enum +//--------------------------------------------------------------------- +enum KEYSTROKE_CATEGORY +{ + CATEGORY_NONE = 0, + CATEGORY_COMPOSING, + CATEGORY_CANDIDATE, + CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION +}; + +enum KEYSTROKE_FUNCTION +{ + FUNCTION_NONE = 0, + FUNCTION_INPUT, + + FUNCTION_CANCEL, + FUNCTION_TOGGLE_IME_MODE, // Toggle IME mode + FUNCTION_FINALIZE_TEXTSTORE, + FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT, + FUNCTION_FINALIZE_CANDIDATELIST, + FUNCTION_FINALIZE_CANDIDATELISTForVKReturn, + FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT, + FUNCTION_CONVERT, + FUNCTION_CONVERT_WILDCARD, + FUNCTION_SELECT_BY_NUMBER, + FUNCTION_BACKSPACE, + FUNCTION_DELETE, + FUNCTION_MOVE_LEFT, + FUNCTION_MOVE_RIGHT, + FUNCTION_MOVE_UP, + FUNCTION_MOVE_DOWN, + FUNCTION_MOVE_PAGE_UP, + FUNCTION_MOVE_PAGE_DOWN, + FUNCTION_MOVE_PAGE_TOP, + FUNCTION_MOVE_PAGE_BOTTOM, + + // Function Double/Single byte + FUNCTION_DOUBLE_SINGLE_BYTE, + + // Function Punctuation + FUNCTION_PUNCTUATION, + + // Raw candidate navigation key; Server decides its configured behavior. + FUNCTION_SERVER_CANDIDATE_KEY, + + // Unsolicited insert (voice ASR via worker pipe). Append-only so existing + // ordinals stay stable across builds. + FUNCTION_INSERT_TEXT, + FUNCTION_UPDATE_VOICE_COMPOSITION, + FUNCTION_COMMIT_VOICE_COMPOSITION, + FUNCTION_CANCEL_VOICE_COMPOSITION, + FUNCTION_TOGGLE_CHARACTER_SET +}; + +//--------------------------------------------------------------------- +// candidate list +//--------------------------------------------------------------------- +enum CANDIDATE_MODE +{ + CANDIDATE_NONE = 0, + CANDIDATE_ORIGINAL, + CANDIDATE_INCREMENTAL +}; + +//--------------------------------------------------------------------- +// structure +//--------------------------------------------------------------------- +struct _KEYSTROKE_STATE +{ + KEYSTROKE_CATEGORY Category; + KEYSTROKE_FUNCTION Function; +}; + +struct _PUNCTUATION +{ + WCHAR _Code; + WCHAR _Punctuation[3]; +}; + +BOOL CLSIDToString(REFGUID refGUID, _Out_writes_(39) WCHAR *pCLSIDString); + +HRESULT SkipWhiteSpace(LCID locale, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex); +HRESULT FindChar(WCHAR wch, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex); + +BOOL IsSpace(LCID locale, WCHAR wch); + +template class CMetasequoiaImeArray +{ + typedef typename std::vector CMetasequoiaImeInnerArray; + typedef typename std::vector::iterator CMetasequoiaImeInnerIter; + + public: + CMetasequoiaImeArray() : _innerVect() + { + } + + explicit CMetasequoiaImeArray(size_t count) : _innerVect(count) + { + } + + virtual ~CMetasequoiaImeArray() + { + } + + inline T *GetAt(size_t index) + { + assert(index >= 0); + assert(index < _innerVect.size()); + + T &curT = _innerVect.at(index); + + return &(curT); + } + + inline const T *GetAt(size_t index) const + { + assert(index >= 0); + assert(index < _innerVect.size()); + + T &curT = _innerVect.at(index); + + return &(curT); + } + + void RemoveAt(size_t index) + { + assert(index >= 0); + assert(index < _innerVect.size()); + + CMetasequoiaImeInnerIter iter = _innerVect.begin(); + _innerVect.erase(iter + index); + } + + UINT Count() const + { + return static_cast(_innerVect.size()); + } + + T *Append() + { + T newT; + _innerVect.push_back(newT); + T &backT = _innerVect.back(); + + return &(backT); + } + + void reserve(size_t Count) + { + _innerVect.reserve(Count); + } + + void Clear() + { + _innerVect.clear(); + } + + private: + CMetasequoiaImeInnerArray _innerVect; +}; + +class CCandidateRange +{ + public: + CCandidateRange(void); + ~CCandidateRange(void); + + BOOL IsRange(UINT vKey); + int GetIndex(UINT vKey); + + inline int Count() const + { + return _CandidateListIndexRange.Count(); + } + inline DWORD *GetAt(int index) + { + return _CandidateListIndexRange.GetAt(index); + } + inline DWORD *Append() + { + return _CandidateListIndexRange.Append(); + } + + private: + CMetasequoiaImeArray _CandidateListIndexRange; +}; + +class CStringRange +{ + public: + CStringRange(); + CStringRange(const CStringRange &sr); + ~CStringRange(); + + const WCHAR *Get() const; + const DWORD_PTR GetLength() const; + void Clear(); + void Set(const WCHAR *pwch, DWORD_PTR dwLength); + void Set(CStringRange &sr); + CStringRange &operator=(const CStringRange &sr); + void CharNext(_Inout_ CStringRange *pCharNext); + static int Compare(LCID locale, _In_ CStringRange *pString1, _In_ CStringRange *pString2); + static BOOL WildcardCompare(LCID locale, _In_ CStringRange *stringWithWildcard, _In_ CStringRange *targetString); + std::wstring ToWString() const + { + if (!_pStringBuf || _stringBufLen == 0) + return std::wstring(); + return std::wstring(_pStringBuf, _pStringBuf + _stringBufLen); + } + + protected: + DWORD_PTR _stringBufLen; // Length is in character count. + const WCHAR *_pStringBuf; // Buffer which is not add zero terminate. +}; + +//--------------------------------------------------------------------- +// CCandidateListItem +// _ItemString - candidate string +// _FindKeyCode - tailing string +//--------------------------------------------------------------------- +struct CCandidateListItem +{ + CStringRange _ItemString; + CStringRange _FindKeyCode; + + CCandidateListItem &CCandidateListItem::operator=(const CCandidateListItem &rhs) + { + _ItemString = rhs._ItemString; // e.g. 你好 + _FindKeyCode = rhs._FindKeyCode; // e.g. nihao + return *this; + } +}; + +class CPunctuationPair +{ + public: + CPunctuationPair(); + CPunctuationPair(WCHAR code, const WCHAR *punctuation, const WCHAR *pair); + + struct _PUNCTUATION _punctuation; + WCHAR _pairPunctuation[3]; + BOOL _isPairToggle; +}; + +class CPunctuationNestPair +{ + public: + CPunctuationNestPair(); + CPunctuationNestPair( // + WCHAR codeBegin, // + const WCHAR *punctuationBegin, // + const WCHAR *pairBegin, // + const WCHAR codeEnd, // + const WCHAR *punctuationEnd, // + const WCHAR *pairEnd // + ); + + struct _PUNCTUATION _punctuation_begin; + WCHAR _pairPunctuation_begin[3]; + + struct _PUNCTUATION _punctuation_end; + WCHAR _pairPunctuation_end[3]; + + int _nestCount; +}; diff --git a/windows/src/IME/MetasequoiaIMEStructureArray.h b/windows/src/IME/MetasequoiaIMEStructureArray.h index 98eee179..372a7106 100644 --- a/windows/src/IME/MetasequoiaIMEStructureArray.h +++ b/windows/src/IME/MetasequoiaIMEStructureArray.h @@ -1,82 +1,82 @@ -////////////////////////////////////////////////////////////////////// -// -// CMetasequoiaIMEMetasequoiaIMEStructureArray.h -// -// CMetasequoiaIMEStructureArray declaration. -// -////////////////////////////////////////////////////////////////////// - -#pragma once - -#include - -template class CMetasequoiaIMEStructureArray -{ - typedef typename std::vector value_type; - typedef const T &CONST_REF; - typedef typename value_type CMetasequoiaIMEArray; - typedef typename value_type::iterator CMetasequoiaIMEIter; - - public: - CMetasequoiaIMEStructureArray() : _imeVector() - { - } - - explicit CMetasequoiaIMEStructureArray(size_t iCount) : _imeVector(iCount) - { - } - - CMetasequoiaIMEStructureArray(size_t iCount, CONST_REF tVal) : _imeVector(iCount, tVal) - { - } - - virtual ~CMetasequoiaIMEStructureArray() - { - } - - inline CONST_REF GetAt(size_t iIndex) const - { - assert(iIndex <= _imeVector.size()); - assert(_imeVector.size() > 0); - - return _imeVector[iIndex]; - } - - inline T &GetAt(size_t iIndex) - { - assert(iIndex <= _imeVector.size()); - assert(_imeVector.size() > 0); - - return _imeVector[iIndex]; - } - - void RemoveAt(size_t iIndex, size_t iElements) - { - assert(iIndex <= _imeVector.size()); - assert(_imeVector.size() > 0); - - CMetasequoiaIMEIter beginIter = _imeVector.begin() + iIndex; - CMetasequoiaIMEIter lastIter = beginIter + iElements - 1; - - _imeVector.erase(beginIter, lastIter); - } - - size_t Count() const - { - return _imeVector.size(); - } - - void Append(const T &tVal) - { - _imeVector.push_back(tVal); - } - - void Clear() - { - _imeVector.clear(); - } - - private: - CMetasequoiaIMEArray _imeVector; // the actual array of data - CMetasequoiaIMEIter _imeIter; -}; +////////////////////////////////////////////////////////////////////// +// +// CMetasequoiaIMEMetasequoiaIMEStructureArray.h +// +// CMetasequoiaIMEStructureArray declaration. +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +template class CMetasequoiaIMEStructureArray +{ + typedef typename std::vector value_type; + typedef const T &CONST_REF; + typedef typename value_type CMetasequoiaIMEArray; + typedef typename value_type::iterator CMetasequoiaIMEIter; + + public: + CMetasequoiaIMEStructureArray() : _imeVector() + { + } + + explicit CMetasequoiaIMEStructureArray(size_t iCount) : _imeVector(iCount) + { + } + + CMetasequoiaIMEStructureArray(size_t iCount, CONST_REF tVal) : _imeVector(iCount, tVal) + { + } + + virtual ~CMetasequoiaIMEStructureArray() + { + } + + inline CONST_REF GetAt(size_t iIndex) const + { + assert(iIndex <= _imeVector.size()); + assert(_imeVector.size() > 0); + + return _imeVector[iIndex]; + } + + inline T &GetAt(size_t iIndex) + { + assert(iIndex <= _imeVector.size()); + assert(_imeVector.size() > 0); + + return _imeVector[iIndex]; + } + + void RemoveAt(size_t iIndex, size_t iElements) + { + assert(iIndex <= _imeVector.size()); + assert(_imeVector.size() > 0); + + CMetasequoiaIMEIter beginIter = _imeVector.begin() + iIndex; + CMetasequoiaIMEIter lastIter = beginIter + iElements - 1; + + _imeVector.erase(beginIter, lastIter); + } + + size_t Count() const + { + return _imeVector.size(); + } + + void Append(const T &tVal) + { + _imeVector.push_back(tVal); + } + + void Clear() + { + _imeVector.clear(); + } + + private: + CMetasequoiaIMEArray _imeVector; // the actual array of data + CMetasequoiaIMEIter _imeIter; +}; diff --git a/windows/src/Key/KeyStateCategory.cpp b/windows/src/Key/KeyStateCategory.cpp index ad86e998..e2636155 100644 --- a/windows/src/Key/KeyStateCategory.cpp +++ b/windows/src/Key/KeyStateCategory.cpp @@ -1,403 +1,403 @@ -#include "KeyStateCategory.h" -#include "Globals.h" -#include "Ipc.h" -#include "MetasequoiaIMEBaseStructure.h" -#include - -thread_local CKeyStateCategoryFactory *CKeyStateCategoryFactory::_instance = nullptr; - -CKeyStateCategoryFactory::CKeyStateCategoryFactory() -{ - _instance = nullptr; -} - -CKeyStateCategoryFactory *CKeyStateCategoryFactory::Instance() -{ - if (nullptr == _instance) - { - _instance = new (std::nothrow) CKeyStateCategoryFactory(); - } - - return _instance; -} - -CKeyStateCategory *CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, - _In_ CMetasequoiaIME *pTextService) -{ - CKeyStateCategory *pKeyState = nullptr; - - switch (keyCategory) - { - case CATEGORY_NONE: - pKeyState = new (std::nothrow) CKeyStateNull(pTextService); - break; - - case CATEGORY_COMPOSING: - pKeyState = new (std::nothrow) CKeyStateComposing(pTextService); - break; - - case CATEGORY_CANDIDATE: - pKeyState = new (std::nothrow) CKeyStateCandidate(pTextService); - break; - - default: - pKeyState = new (std::nothrow) CKeyStateNull(pTextService); - break; - } - return pKeyState; -} - -void CKeyStateCategoryFactory::Release() -{ - if (_instance) - { - delete _instance; - _instance = nullptr; - } -} - -/* -class CKeyStateCategory -*/ -CKeyStateCategory::CKeyStateCategory(_In_ CMetasequoiaIME *pTextService) -{ - _pTextService = pTextService; -} - -CKeyStateCategory::~CKeyStateCategory(void) -{ -} - -HRESULT CKeyStateCategory::KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto) -{ - switch (function) - { - case FUNCTION_INPUT: - return HandleKeyInput(dto); - - case FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT: - return HandleKeyFinalizeTextStoreAndInput(dto); - - case FUNCTION_FINALIZE_TEXTSTORE: - return HandleKeyFinalizeTextStore(dto); - - case FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT: - return HandleKeyFinalizeCandidatelistAndInput(dto); - - case FUNCTION_FINALIZE_CANDIDATELIST: - return HandleKeyFinalizeCandidatelist(dto); - case FUNCTION_FINALIZE_CANDIDATELISTForVKReturn: - return HandleKeyFinalizeCandidatelistForVKReturn(dto); - - case FUNCTION_INSERT_TEXT: - return _pTextService->_HandleInsertText(dto.ec, dto.pContext, dto.prefetchedText); - - case FUNCTION_UPDATE_VOICE_COMPOSITION: - return _pTextService->_HandleUpdateVoiceComposition(dto.ec, dto.pContext, dto.prefetchedText); - - case FUNCTION_COMMIT_VOICE_COMPOSITION: - return _pTextService->_HandleCommitVoiceComposition(dto.ec, dto.pContext, dto.prefetchedText); - - case FUNCTION_CANCEL_VOICE_COMPOSITION: - return _pTextService->_HandleCancelVoiceComposition(dto.ec, dto.pContext); - - case FUNCTION_CONVERT: { - return HandleKeyConvert(dto); - } - - case FUNCTION_CONVERT_WILDCARD: - return HandleKeyConvertWildCard(dto); - - case FUNCTION_CANCEL: - return HandleKeyCancel(dto); - - case FUNCTION_TOGGLE_IME_MODE: - return HandleKeyToogleIMEMode(dto); - - case FUNCTION_BACKSPACE: - return HandleKeyBackspace(dto); - - case FUNCTION_DELETE: - return HandleKeyDelete(dto); - - case FUNCTION_MOVE_LEFT: - case FUNCTION_MOVE_RIGHT: - return HandleKeyArrow(dto); - - case FUNCTION_MOVE_UP: - case FUNCTION_MOVE_DOWN: - case FUNCTION_MOVE_PAGE_UP: - case FUNCTION_MOVE_PAGE_DOWN: - case FUNCTION_MOVE_PAGE_TOP: - case FUNCTION_MOVE_PAGE_BOTTOM: - return HandleKeyArrow(dto); - - case FUNCTION_DOUBLE_SINGLE_BYTE: - return HandleKeyDoubleSingleByte(dto); - - case FUNCTION_PUNCTUATION: - return HandleKeyPunctuation(dto); - - case FUNCTION_SELECT_BY_NUMBER: - return HandleKeySelectByNumber(dto); - - case FUNCTION_NONE: - break; - } - return E_INVALIDARG; -} - -void CKeyStateCategory::Release() -{ - delete this; -} - -// _HandleCompositionInput -HRESULT CKeyStateCategory::HandleKeyInput(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -// HandleKeyFinalizeTextStore -HRESULT CKeyStateCategory::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} -// HandleKeyCompositionFinalizeTextStoreAndInput -HRESULT CKeyStateCategory::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -// HandleKeyCompositionFinalizeCandidatelistAndInput -HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -// HandleKeyCompositionFinalizeCandidatelist -HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -// HandleKeyCompositionFinalizeCandidatelistForVKReturn -HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -// HandleKeyConvert -HRESULT CKeyStateCategory::HandleKeyConvert(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -// HandleKeyConvertWildCard -HRESULT CKeyStateCategory::HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -//_HandleCancel -HRESULT CKeyStateCategory::HandleKeyCancel(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -HRESULT CKeyStateCategory::HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -//_HandleCompositionBackspace -HRESULT CKeyStateCategory::HandleKeyBackspace(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -//_HandleCompositionDelete -HRESULT CKeyStateCategory::HandleKeyDelete(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -//_HandleCompositionArrowKey -HRESULT CKeyStateCategory::HandleKeyArrow(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -//_HandleCompositionDoubleSingleByte -HRESULT CKeyStateCategory::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -//_HandleCompositionPunctuation -HRESULT CKeyStateCategory::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -HRESULT CKeyStateCategory::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) -{ - dto; - return E_NOTIMPL; -} - -/* -class CKeyStateComposing -*/ -CKeyStateComposing::CKeyStateComposing(_In_ CMetasequoiaIME *pTextService) : CKeyStateCategory(pTextService) -{ -} - -HRESULT CKeyStateComposing::HandleKeyInput(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); -} - -HRESULT CKeyStateComposing::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) -{ - _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE); - return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); -} - -HRESULT CKeyStateComposing::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE); -} - -HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) -{ - _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE); - return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); -} - -HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE); -} - -HRESULT CKeyStateComposing::HandleKeyConvert(KeyHandlerEditSessionDTO dto) -{ - if (dto.code == VK_SPACE) - { - return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); - } - // VK_SPACE - return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, FALSE); -} - -HRESULT CKeyStateComposing::HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, TRUE); -} - -HRESULT CKeyStateComposing::HandleKeyCancel(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCancel(dto.ec, dto.pContext); -} - -HRESULT CKeyStateComposing::HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleToogleIMEMode(dto.ec, dto.pContext); -} - -HRESULT CKeyStateComposing::HandleKeyBackspace(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionBackspace(dto.ec, dto.pContext, dto.requestId); -} - -HRESULT CKeyStateComposing::HandleKeyDelete(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionDelete(dto.ec, dto.pContext, dto.requestId); -} - -HRESULT CKeyStateComposing::HandleKeyArrow(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionArrowKey(dto.ec, dto.pContext, dto.arrowKey, dto.requestId); -} - -HRESULT CKeyStateComposing::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionDoubleSingleByte(dto.ec, dto.pContext, dto.wch); -} - -HRESULT CKeyStateComposing::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCompositionPunctuation(dto.ec, dto.pContext, dto.code, dto.wch, dto.requestId, - dto.prefetchedText); -} - -/* -class CKeyStateCandidate -*/ -CKeyStateCandidate::CKeyStateCandidate(_In_ CMetasequoiaIME *pTextService) : CKeyStateCategory(pTextService) -{ -} - -// _HandleCandidateInput -HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); -} - -// _HandleCandidateInput -HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCandidateFinalizeForVKReturn(dto.ec, dto.pContext); -} - -// HandleKeyFinalizeCandidatelistAndInput -HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) -{ - _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); - return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); -} - -//_HandleCandidateConvert -HRESULT CKeyStateCandidate::HandleKeyConvert(KeyHandlerEditSessionDTO dto) -{ - if (dto.code == VK_SPACE) - { - return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); - } - // Send candidate string to client when pressing VK_SPACE - return _pTextService->_HandleCandidateConvert(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); -} - -//_HandleCancel -HRESULT CKeyStateCandidate::HandleKeyCancel(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCancel(dto.ec, dto.pContext); -} - -//_HandleCandidateArrowKey -HRESULT CKeyStateCandidate::HandleKeyArrow(KeyHandlerEditSessionDTO dto) -{ - return _pTextService->_HandleCandidateArrowKey(dto.ec, dto.pContext, dto.arrowKey, dto.requestId); -} - -//_HandleCandidateSelectByNumber -HRESULT CKeyStateCandidate::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) -{ - // return _pTextService->_HandleCandidateSelectByNumber(dto.ec, dto.pContext, dto.code); - return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); -} +#include "KeyStateCategory.h" +#include "Globals.h" +#include "Ipc.h" +#include "MetasequoiaIMEBaseStructure.h" +#include + +thread_local CKeyStateCategoryFactory *CKeyStateCategoryFactory::_instance = nullptr; + +CKeyStateCategoryFactory::CKeyStateCategoryFactory() +{ + _instance = nullptr; +} + +CKeyStateCategoryFactory *CKeyStateCategoryFactory::Instance() +{ + if (nullptr == _instance) + { + _instance = new (std::nothrow) CKeyStateCategoryFactory(); + } + + return _instance; +} + +CKeyStateCategory *CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, + _In_ CMetasequoiaIME *pTextService) +{ + CKeyStateCategory *pKeyState = nullptr; + + switch (keyCategory) + { + case CATEGORY_NONE: + pKeyState = new (std::nothrow) CKeyStateNull(pTextService); + break; + + case CATEGORY_COMPOSING: + pKeyState = new (std::nothrow) CKeyStateComposing(pTextService); + break; + + case CATEGORY_CANDIDATE: + pKeyState = new (std::nothrow) CKeyStateCandidate(pTextService); + break; + + default: + pKeyState = new (std::nothrow) CKeyStateNull(pTextService); + break; + } + return pKeyState; +} + +void CKeyStateCategoryFactory::Release() +{ + if (_instance) + { + delete _instance; + _instance = nullptr; + } +} + +/* +class CKeyStateCategory +*/ +CKeyStateCategory::CKeyStateCategory(_In_ CMetasequoiaIME *pTextService) +{ + _pTextService = pTextService; +} + +CKeyStateCategory::~CKeyStateCategory(void) +{ +} + +HRESULT CKeyStateCategory::KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto) +{ + switch (function) + { + case FUNCTION_INPUT: + return HandleKeyInput(dto); + + case FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT: + return HandleKeyFinalizeTextStoreAndInput(dto); + + case FUNCTION_FINALIZE_TEXTSTORE: + return HandleKeyFinalizeTextStore(dto); + + case FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT: + return HandleKeyFinalizeCandidatelistAndInput(dto); + + case FUNCTION_FINALIZE_CANDIDATELIST: + return HandleKeyFinalizeCandidatelist(dto); + case FUNCTION_FINALIZE_CANDIDATELISTForVKReturn: + return HandleKeyFinalizeCandidatelistForVKReturn(dto); + + case FUNCTION_INSERT_TEXT: + return _pTextService->_HandleInsertText(dto.ec, dto.pContext, dto.prefetchedText); + + case FUNCTION_UPDATE_VOICE_COMPOSITION: + return _pTextService->_HandleUpdateVoiceComposition(dto.ec, dto.pContext, dto.prefetchedText); + + case FUNCTION_COMMIT_VOICE_COMPOSITION: + return _pTextService->_HandleCommitVoiceComposition(dto.ec, dto.pContext, dto.prefetchedText); + + case FUNCTION_CANCEL_VOICE_COMPOSITION: + return _pTextService->_HandleCancelVoiceComposition(dto.ec, dto.pContext); + + case FUNCTION_CONVERT: { + return HandleKeyConvert(dto); + } + + case FUNCTION_CONVERT_WILDCARD: + return HandleKeyConvertWildCard(dto); + + case FUNCTION_CANCEL: + return HandleKeyCancel(dto); + + case FUNCTION_TOGGLE_IME_MODE: + return HandleKeyToogleIMEMode(dto); + + case FUNCTION_BACKSPACE: + return HandleKeyBackspace(dto); + + case FUNCTION_DELETE: + return HandleKeyDelete(dto); + + case FUNCTION_MOVE_LEFT: + case FUNCTION_MOVE_RIGHT: + return HandleKeyArrow(dto); + + case FUNCTION_MOVE_UP: + case FUNCTION_MOVE_DOWN: + case FUNCTION_MOVE_PAGE_UP: + case FUNCTION_MOVE_PAGE_DOWN: + case FUNCTION_MOVE_PAGE_TOP: + case FUNCTION_MOVE_PAGE_BOTTOM: + return HandleKeyArrow(dto); + + case FUNCTION_DOUBLE_SINGLE_BYTE: + return HandleKeyDoubleSingleByte(dto); + + case FUNCTION_PUNCTUATION: + return HandleKeyPunctuation(dto); + + case FUNCTION_SELECT_BY_NUMBER: + return HandleKeySelectByNumber(dto); + + case FUNCTION_NONE: + break; + } + return E_INVALIDARG; +} + +void CKeyStateCategory::Release() +{ + delete this; +} + +// _HandleCompositionInput +HRESULT CKeyStateCategory::HandleKeyInput(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyFinalizeTextStore +HRESULT CKeyStateCategory::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} +// HandleKeyCompositionFinalizeTextStoreAndInput +HRESULT CKeyStateCategory::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyCompositionFinalizeCandidatelistAndInput +HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyCompositionFinalizeCandidatelist +HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyCompositionFinalizeCandidatelistForVKReturn +HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyConvert +HRESULT CKeyStateCategory::HandleKeyConvert(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyConvertWildCard +HRESULT CKeyStateCategory::HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCancel +HRESULT CKeyStateCategory::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +HRESULT CKeyStateCategory::HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionBackspace +HRESULT CKeyStateCategory::HandleKeyBackspace(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionDelete +HRESULT CKeyStateCategory::HandleKeyDelete(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionArrowKey +HRESULT CKeyStateCategory::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionDoubleSingleByte +HRESULT CKeyStateCategory::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionPunctuation +HRESULT CKeyStateCategory::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +HRESULT CKeyStateCategory::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +/* +class CKeyStateComposing +*/ +CKeyStateComposing::CKeyStateComposing(_In_ CMetasequoiaIME *pTextService) : CKeyStateCategory(pTextService) +{ +} + +HRESULT CKeyStateComposing::HandleKeyInput(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) +{ + _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE); + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) +{ + _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE); + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE); +} + +HRESULT CKeyStateComposing::HandleKeyConvert(KeyHandlerEditSessionDTO dto) +{ + if (dto.code == VK_SPACE) + { + return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); + } + // VK_SPACE + return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, FALSE); +} + +HRESULT CKeyStateComposing::HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, TRUE); +} + +HRESULT CKeyStateComposing::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCancel(dto.ec, dto.pContext); +} + +HRESULT CKeyStateComposing::HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleToogleIMEMode(dto.ec, dto.pContext); +} + +HRESULT CKeyStateComposing::HandleKeyBackspace(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionBackspace(dto.ec, dto.pContext, dto.requestId); +} + +HRESULT CKeyStateComposing::HandleKeyDelete(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionDelete(dto.ec, dto.pContext, dto.requestId); +} + +HRESULT CKeyStateComposing::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionArrowKey(dto.ec, dto.pContext, dto.arrowKey, dto.requestId); +} + +HRESULT CKeyStateComposing::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionDoubleSingleByte(dto.ec, dto.pContext, dto.wch); +} + +HRESULT CKeyStateComposing::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionPunctuation(dto.ec, dto.pContext, dto.code, dto.wch, dto.requestId, + dto.prefetchedText); +} + +/* +class CKeyStateCandidate +*/ +CKeyStateCandidate::CKeyStateCandidate(_In_ CMetasequoiaIME *pTextService) : CKeyStateCategory(pTextService) +{ +} + +// _HandleCandidateInput +HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); +} + +// _HandleCandidateInput +HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateFinalizeForVKReturn(dto.ec, dto.pContext); +} + +// HandleKeyFinalizeCandidatelistAndInput +HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) +{ + _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId); +} + +//_HandleCandidateConvert +HRESULT CKeyStateCandidate::HandleKeyConvert(KeyHandlerEditSessionDTO dto) +{ + if (dto.code == VK_SPACE) + { + return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); + } + // Send candidate string to client when pressing VK_SPACE + return _pTextService->_HandleCandidateConvert(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); +} + +//_HandleCancel +HRESULT CKeyStateCandidate::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCancel(dto.ec, dto.pContext); +} + +//_HandleCandidateArrowKey +HRESULT CKeyStateCandidate::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateArrowKey(dto.ec, dto.pContext, dto.arrowKey, dto.requestId); +} + +//_HandleCandidateSelectByNumber +HRESULT CKeyStateCandidate::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) +{ + // return _pTextService->_HandleCandidateSelectByNumber(dto.ec, dto.pContext, dto.code); + return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText); +} diff --git a/windows/src/Key/KeyStateCategory.h b/windows/src/Key/KeyStateCategory.h index 4ea230b1..0d0bf88e 100644 --- a/windows/src/Key/KeyStateCategory.h +++ b/windows/src/Key/KeyStateCategory.h @@ -1,274 +1,274 @@ -#pragma once -#include "Globals.h" -#include "Private.h" -#include "MetasequoiaIME.h" -#include -#include - -class CKeyStateCategory; - -class CKeyStateCategoryFactory -{ - public: - static CKeyStateCategoryFactory *Instance(); - CKeyStateCategory *MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, _In_ CMetasequoiaIME *pTextService); - void Release(); - - protected: - CKeyStateCategoryFactory(); - - private: - static thread_local CKeyStateCategoryFactory *_instance; -}; - -typedef struct KeyHandlerEditSessionDTO -{ - KeyHandlerEditSessionDTO::KeyHandlerEditSessionDTO(TfEditCookie tFEC, _In_ ITfContext *pTfContext, UINT virualCode, - WCHAR inputChar, KEYSTROKE_FUNCTION arrowKeyFunction, - uint64_t pipeRequestId, - const std::wstring &sessionPrefetchedText) - { - ec = tFEC; - pContext = pTfContext; - code = virualCode; - wch = inputChar; - arrowKey = arrowKeyFunction; - requestId = pipeRequestId; - prefetchedText = sessionPrefetchedText; - } - - TfEditCookie ec; - ITfContext *pContext; - UINT code; - WCHAR wch; - KEYSTROKE_FUNCTION arrowKey; - uint64_t requestId; - std::wstring prefetchedText; -} KeyHandlerEditSessionDTO; - -class CKeyStateCategory -{ - public: - CKeyStateCategory(_In_ CMetasequoiaIME *pTextService); - - protected: - ~CKeyStateCategory(void); - - public: - HRESULT KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto); - void Release(void); - - protected: - // HandleKeyInput - virtual HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeTextStoreAndInput - virtual HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeTextStore - virtual HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeCandidatelistAndInput - virtual HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeCandidatelist - virtual HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeCandidatelist - virtual HRESULT HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto); - - // HandleKeyConvert - virtual HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); - - // HandleKeyConvertWild - virtual HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); - - // HandleKeyCancel - virtual HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); - - // _HandleKeyToogleIMEMode - virtual HRESULT HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto); - - // HandleKeyBackspace - virtual HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); - - // HandleKeyDelete - virtual HRESULT HandleKeyDelete(KeyHandlerEditSessionDTO dto); - - // HandleKeyArrow - virtual HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); - - // HandleKeyDoubleSingleByte - virtual HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); - - // HandleKeyPunctuation - virtual HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); - - // HandleKeySelectByNumber - virtual HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); - - protected: - CMetasequoiaIME *_pTextService; -}; - -class CKeyStateComposing : public CKeyStateCategory -{ - public: - CKeyStateComposing(_In_ CMetasequoiaIME *pTextService); - - protected: - // _HandleCompositionInput - HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); - - // HandleKeyCompositionFinalizeTextStoreAndInput - HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeTextStore - HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); - - // HandleKeyCompositionFinalizeCandidatelistAndInput - HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); - - // HandleKeyCompositionFinalizeCandidatelist - HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); - - // HandleCompositionConvert - HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); - - // HandleKeyCompositionConvertWildCard - HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); - - // HandleCancel - HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); - - // HandleKeyToogleIMEMode - HRESULT HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto); - - // HandleCompositionBackspace - HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); - - // HandleCompositionDelete - HRESULT HandleKeyDelete(KeyHandlerEditSessionDTO dto); - - // HandleArrowKey - HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); - - // HandleKeyDoubleSingleByte - HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); - - // HandleKeyCompositionPunctuation - HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); -}; - -class CKeyStateCandidate : public CKeyStateCategory -{ - public: - CKeyStateCandidate(_In_ CMetasequoiaIME *pTextService); - - protected: - // HandleKeyFinalizeCandidatelist - HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); - - HRESULT HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto); - - // HandleKeyFinalizeCandidatelistAndInput - HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); - - //_HandleCandidateConvert - HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); - - //_HandleCancel - HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); - - //_HandleCandidateArrowKey - HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); - - //_HandleCandidateSelectByNumber - HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); -}; - -// degeneration class -class CKeyStateNull : public CKeyStateCategory -{ - public: - CKeyStateNull(_In_ CMetasequoiaIME *pTextService) : CKeyStateCategory(pTextService) {}; - - protected: - // _HandleNullInput - HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyInput(dto); - }; - - // HandleKeyNullFinalizeTextStoreAndInput - HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyFinalizeTextStoreAndInput(dto); - }; - - // HandleKeyFinalizeTextStore - HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyFinalizeTextStore(dto); - }; - - // HandleKeyNullFinalizeCandidatelistAndInput - HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyFinalizeCandidatelistAndInput(dto); - }; - - // HandleKeyNullFinalizeCandidatelist - HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyFinalizeCandidatelist(dto); - }; - - //_HandleNullConvert - HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyConvert(dto); - }; - - //_HandleNullCancel - HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyCancel(dto); - }; - - // HandleKeyNullConvertWild - HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyConvertWildCard(dto); - }; - - //_HandleNullBackspace - HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyBackspace(dto); - }; - - //_HandleNullArrowKey - HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyArrow(dto); - }; - - // HandleKeyDoubleSingleByte - HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyDoubleSingleByte(dto); - }; - - // HandleKeyPunctuation - HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeyPunctuation(dto); - }; - - //_HandleNullCandidateSelectByNumber - HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) - { - return __super::HandleKeySelectByNumber(dto); - }; -}; +#pragma once +#include "Globals.h" +#include "Private.h" +#include "MetasequoiaIME.h" +#include +#include + +class CKeyStateCategory; + +class CKeyStateCategoryFactory +{ + public: + static CKeyStateCategoryFactory *Instance(); + CKeyStateCategory *MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, _In_ CMetasequoiaIME *pTextService); + void Release(); + + protected: + CKeyStateCategoryFactory(); + + private: + static thread_local CKeyStateCategoryFactory *_instance; +}; + +typedef struct KeyHandlerEditSessionDTO +{ + KeyHandlerEditSessionDTO::KeyHandlerEditSessionDTO(TfEditCookie tFEC, _In_ ITfContext *pTfContext, UINT virualCode, + WCHAR inputChar, KEYSTROKE_FUNCTION arrowKeyFunction, + uint64_t pipeRequestId, + const std::wstring &sessionPrefetchedText) + { + ec = tFEC; + pContext = pTfContext; + code = virualCode; + wch = inputChar; + arrowKey = arrowKeyFunction; + requestId = pipeRequestId; + prefetchedText = sessionPrefetchedText; + } + + TfEditCookie ec; + ITfContext *pContext; + UINT code; + WCHAR wch; + KEYSTROKE_FUNCTION arrowKey; + uint64_t requestId; + std::wstring prefetchedText; +} KeyHandlerEditSessionDTO; + +class CKeyStateCategory +{ + public: + CKeyStateCategory(_In_ CMetasequoiaIME *pTextService); + + protected: + ~CKeyStateCategory(void); + + public: + HRESULT KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto); + void Release(void); + + protected: + // HandleKeyInput + virtual HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeTextStoreAndInput + virtual HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeTextStore + virtual HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelistAndInput + virtual HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelist + virtual HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelist + virtual HRESULT HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto); + + // HandleKeyConvert + virtual HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); + + // HandleKeyConvertWild + virtual HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); + + // HandleKeyCancel + virtual HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + // _HandleKeyToogleIMEMode + virtual HRESULT HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto); + + // HandleKeyBackspace + virtual HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); + + // HandleKeyDelete + virtual HRESULT HandleKeyDelete(KeyHandlerEditSessionDTO dto); + + // HandleKeyArrow + virtual HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + // HandleKeyDoubleSingleByte + virtual HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); + + // HandleKeyPunctuation + virtual HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); + + // HandleKeySelectByNumber + virtual HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); + + protected: + CMetasequoiaIME *_pTextService; +}; + +class CKeyStateComposing : public CKeyStateCategory +{ + public: + CKeyStateComposing(_In_ CMetasequoiaIME *pTextService); + + protected: + // _HandleCompositionInput + HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionFinalizeTextStoreAndInput + HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeTextStore + HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionFinalizeCandidatelistAndInput + HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionFinalizeCandidatelist + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + // HandleCompositionConvert + HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionConvertWildCard + HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); + + // HandleCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + // HandleKeyToogleIMEMode + HRESULT HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto); + + // HandleCompositionBackspace + HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); + + // HandleCompositionDelete + HRESULT HandleKeyDelete(KeyHandlerEditSessionDTO dto); + + // HandleArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + // HandleKeyDoubleSingleByte + HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionPunctuation + HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); +}; + +class CKeyStateCandidate : public CKeyStateCategory +{ + public: + CKeyStateCandidate(_In_ CMetasequoiaIME *pTextService); + + protected: + // HandleKeyFinalizeCandidatelist + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + HRESULT HandleKeyFinalizeCandidatelistForVKReturn(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelistAndInput + HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); + + //_HandleCandidateConvert + HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); + + //_HandleCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + //_HandleCandidateArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + //_HandleCandidateSelectByNumber + HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); +}; + +// degeneration class +class CKeyStateNull : public CKeyStateCategory +{ + public: + CKeyStateNull(_In_ CMetasequoiaIME *pTextService) : CKeyStateCategory(pTextService) {}; + + protected: + // _HandleNullInput + HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyInput(dto); + }; + + // HandleKeyNullFinalizeTextStoreAndInput + HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyFinalizeTextStoreAndInput(dto); + }; + + // HandleKeyFinalizeTextStore + HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyFinalizeTextStore(dto); + }; + + // HandleKeyNullFinalizeCandidatelistAndInput + HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyFinalizeCandidatelistAndInput(dto); + }; + + // HandleKeyNullFinalizeCandidatelist + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyFinalizeCandidatelist(dto); + }; + + //_HandleNullConvert + HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyConvert(dto); + }; + + //_HandleNullCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyCancel(dto); + }; + + // HandleKeyNullConvertWild + HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyConvertWildCard(dto); + }; + + //_HandleNullBackspace + HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyBackspace(dto); + }; + + //_HandleNullArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyArrow(dto); + }; + + // HandleKeyDoubleSingleByte + HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyDoubleSingleByte(dto); + }; + + // HandleKeyPunctuation + HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeyPunctuation(dto); + }; + + //_HandleNullCandidateSelectByNumber + HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) + { + return __super::HandleKeySelectByNumber(dto); + }; +}; diff --git a/windows/src/LanguageBar/LanguageBar.cpp b/windows/src/LanguageBar/LanguageBar.cpp index 2dcf793c..7e91fffd 100644 --- a/windows/src/LanguageBar/LanguageBar.cpp +++ b/windows/src/LanguageBar/LanguageBar.cpp @@ -1,874 +1,874 @@ -#ifndef UNICODE -#define UNICODE -#endif // !UNICODE - -#ifndef _UNICODE -#define _UNICODE -#endif // !UNICODE - -#include "Private.h" -#include "MetasequoiaIME.h" -#include "CompositionProcessorEngine.h" -#include "LanguageBar.h" -#include "Globals.h" -#include "Compartment.h" -#include "Ipc.h" -#include "fmt/xchar.h" - -#include - -namespace -{ -// Taskbar/system tray follows SystemUsesLightTheme (0 = dark). -bool IsSystemDarkMode() -{ - DWORD value = 1; // default to light - DWORD size = sizeof(value); - if (RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", - L"SystemUsesLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size) != ERROR_SUCCESS) - { - return false; - } - return value == 0; -} - -DWORD ResolveThemeIconIndex(DWORD lightIconIndex) -{ - if (!IsSystemDarkMode()) - { - return lightIconIndex; - } - - if (lightIconIndex == static_cast(IME_MODE_ON_ICON_INDEX)) - { - return static_cast(IME_MODE_ON_DARK_ICON_INDEX); - } - if (lightIconIndex == static_cast(IME_MODE_ON_JP_ICON_INDEX)) - { - return static_cast(IME_MODE_ON_JP_DARK_ICON_INDEX); - } - if (lightIconIndex == static_cast(IME_MODE_CAP_ICON_INDEX)) - { - return static_cast(IME_MODE_CAP_DARK_ICON_INDEX); - } - if (lightIconIndex == static_cast(IME_MODE_OFF_ICON_INDEX)) - { - return static_cast(IME_MODE_OFF_DARK_ICON_INDEX); - } - return lightIconIndex; -} - -// Design size of the mode icons for language bar / taskbar indicators (at 96 DPI). -constexpr int kSmallIconDesignSize = 16; - -// Avoids relying on USER_DEFAULT_SCREEN_DPI, which older Windows SDK headers -// targeted by this project may not provide. -constexpr int kUserDefaultScreenDpi = 96; - -using GetDpiForMonitorFn = HRESULT(WINAPI *)(HMONITOR, int /*MONITOR_DPI_TYPE*/, UINT *, UINT *); -using SetThreadDpiAwarenessCtxFn = HANDLE(WINAPI *)(HANDLE); - -// Entry points are resolved lazily and cached in these atomics instead of -// function-local statics: the project compiles with /Zc:threadSafeInit-, under -// which magic statics are NOT thread-safe, while GetIcon can be reached from -// several TSF threads of the host process. Failed lookups are not cached - they -// are cheap enough to retry on the next call. -std::atomic s_getDpiForMonitor{nullptr}; -std::atomic s_setThreadDpiAwarenessCtx{nullptr}; - -template T ResolveOnce(std::atomic &slot, T (*resolver)()) -{ - T resolved = slot.load(std::memory_order_acquire); - if (resolved == nullptr) - { - resolved = resolver(); - if (resolved != nullptr) - { - slot.store(resolved, std::memory_order_release); - } - } - return resolved; -} - -GetDpiForMonitorFn ResolveGetDpiForMonitor() -{ - // shcore.dll ships with Windows 8.1+; most hosts already have it mapped. - HMODULE shcore = ::GetModuleHandleW(L"shcore.dll"); - if (!shcore) - { - shcore = ::LoadLibraryW(L"shcore.dll"); // system DLL; intentionally kept until process exit - } - return shcore ? reinterpret_cast(::GetProcAddress(shcore, "GetDpiForMonitor")) : nullptr; -} - -SetThreadDpiAwarenessCtxFn ResolveSetThreadDpiAwarenessContext() -{ - // user32.dll is always mapped inside any host of this DLL. - HMODULE user32 = ::GetModuleHandleW(L"user32.dll"); - return user32 ? reinterpret_cast( - ::GetProcAddress(user32, "SetThreadDpiAwarenessContext")) // Win10 1607+ - : nullptr; -} -} // namespace - -//+--------------------------------------------------------------------------- -// -// CMetasequoiaIME::_UpdateLanguageBarOnSetFocus -// -//---------------------------------------------------------------------------- - -void CMetasequoiaIME::_UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus) -{ - BOOL needDisableButtons = FALSE; - - if (!pDocMgrFocus) - { - needDisableButtons = TRUE; - } - else - { - IEnumTfContexts *pEnumContext = nullptr; - - if (FAILED(pDocMgrFocus->EnumContexts(&pEnumContext)) || !pEnumContext) - { - needDisableButtons = TRUE; - } - else - { - ULONG fetched = 0; - ITfContext *pContext = nullptr; - - if (FAILED(pEnumContext->Next(1, &pContext, &fetched)) || fetched != 1) - { - needDisableButtons = TRUE; - } - - if (!pContext) - { - // context is not associated - needDisableButtons = TRUE; - } - else - { - pContext->Release(); - } - } - - if (pEnumContext) - { - pEnumContext->Release(); - } - } - - CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr; - pCompositionProcessorEngine = _pCompositionProcessorEngine; - - pCompositionProcessorEngine->SetLanguageBarStatus(TF_LBI_STATUS_DISABLED, needDisableButtons); - - // SetStatus intentionally keeps the item enabled when focus is absent so - // Firefox does not replace our mode glyph with its disabled/close glyph. - // Consequently, returning to a document may not change the status bits and - // therefore may not generate an ITfLangBarItemSink update. Always refresh - // the newly focused thread's item: Windows caches each host/thread's icon, - // and another host may have changed the shared Chinese/Japanese mode while - // this one was in the background. - if (!needDisableButtons) - { - pCompositionProcessorEngine->RefreshLanguageBarIcons(); - } -} - -//+--------------------------------------------------------------------------- -// -// CCompositionProcessorEngine::SetLanguageBarStatus -// -//---------------------------------------------------------------------------- - -VOID CCompositionProcessorEngine::SetLanguageBarStatus(DWORD status, BOOL isSet) -{ - if (_pLanguageBar_IMEMode) - { - _pLanguageBar_IMEMode->SetStatus(status, isSet); - } - if (_pLanguageBar_DoubleSingleByte) - { - _pLanguageBar_DoubleSingleByte->SetStatus(status, isSet); - } - if (_pLanguageBar_Punctuation) - { - _pLanguageBar_Punctuation->SetStatus(status, isSet); - } -} - -void CCompositionProcessorEngine::RefreshLanguageBarIcons() -{ - if (_pLanguageBar_IMEMode) - { - _pLanguageBar_IMEMode->RefreshIcon(); - } - if (_pLanguageBar_DoubleSingleByte) - { - _pLanguageBar_DoubleSingleByte->RefreshIcon(); - } - if (_pLanguageBar_Punctuation) - { - _pLanguageBar_Punctuation->RefreshIcon(); - } -} - -//+--------------------------------------------------------------------------- -// -// CLangBarItemButton::ctor -// -//---------------------------------------------------------------------------- - -CLangBarItemButton::CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, - DWORD offIconIndex, BOOL isSecureMode) -{ - DWORD bufLen = 0; - - DllAddRef(); - - // initialize TF_LANGBARITEMINFO structure. - _tfLangBarItemInfo.clsidService = Global::MetasequoiaIMECLSID; // This LangBarItem belongs to this TextService. - _tfLangBarItemInfo.guidItem = guidLangBar; // GUID of this LangBarItem. - _tfLangBarItemInfo.dwStyle = (TF_LBI_STYLE_BTN_BUTTON | TF_LBI_STYLE_SHOWNINTRAY); // This LangBar is a button type. - _tfLangBarItemInfo.ulSort = 0; // The position of this LangBar Item is not specified. - StringCchCopy(_tfLangBarItemInfo.szDescription, ARRAYSIZE(_tfLangBarItemInfo.szDescription), - description); // Set the description of this LangBar Item. - - // Initialize the sink pointer to NULL. - _pLangBarItemSink = nullptr; - - // Initialize ICON index and file name. - _onIconIndex = onIconIndex; - _offIconIndex = offIconIndex; - - // Initialize compartment. - _pCompartment = nullptr; - _pCompartmentEventSink = nullptr; - - _isAddedToLanguageBar = FALSE; - _isSecureMode = isSecureMode; - _status = 0; - - _refCount = 1; - - // Initialize Tooltip - _pTooltipText = nullptr; - if (tooltip) - { - size_t len = 0; - if (StringCchLength(tooltip, STRSAFE_MAX_CCH, &len) != S_OK) - { - len = 0; - } - bufLen = static_cast(len) + 1; - _pTooltipText = (LPCWSTR) new (std::nothrow) WCHAR[bufLen]; - if (_pTooltipText) - { - StringCchCopy((LPWSTR)_pTooltipText, bufLen, tooltip); - } - } -} - -//+--------------------------------------------------------------------------- -// -// CLangBarItemButton::dtor -// -//---------------------------------------------------------------------------- - -CLangBarItemButton::~CLangBarItemButton() -{ - DllRelease(); - CleanUp(); -} - -//+--------------------------------------------------------------------------- -// -// CLangBarItemButton::CleanUp -// -//---------------------------------------------------------------------------- - -void CLangBarItemButton::CleanUp() -{ - if (_pTooltipText) - { - delete[] _pTooltipText; - _pTooltipText = nullptr; - } - - // TSF still holds the advised _pCompartmentEventSink until _Unadvise runs, so unregister before the - // deletes below and independently of the thread manager: under COM-less activation (TF_TMAE_COMLESS) - // CoCreateInstance fails, and freeing a still-advised sink leaves msctf calling into released memory. - _UnregisterCompartment(); - - ITfThreadMgr *pThreadMgr = nullptr; - HRESULT hr = - CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void **)&pThreadMgr); - if (SUCCEEDED(hr)) - { - _RemoveItem(pThreadMgr); - pThreadMgr->Release(); - pThreadMgr = nullptr; - } - - if (_pCompartment) - { - delete _pCompartment; - _pCompartment = nullptr; - } - - if (_pCompartmentEventSink) - { - delete _pCompartmentEventSink; - _pCompartmentEventSink = nullptr; - } -} - -//+--------------------------------------------------------------------------- -// -// CLangBarItemButton::QueryInterface -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_INVALIDARG; - } - - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfLangBarItem) || - IsEqualIID(riid, IID_ITfLangBarItemButton)) - { - *ppvObj = (ITfLangBarItemButton *)this; - } - else if (IsEqualIID(riid, IID_ITfSource)) - { - *ppvObj = (ITfSource *)this; - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -//+--------------------------------------------------------------------------- -// -// CLangBarItemButton::AddRef -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CLangBarItemButton::AddRef() -{ - return ++_refCount; -} - -//+--------------------------------------------------------------------------- -// -// CLangBarItemButton::Release -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CLangBarItemButton::Release() -{ - LONG cr = --_refCount; - - assert(_refCount >= 0); - - if (_refCount == 0) - { - delete this; - } - - return cr; -} - -//+--------------------------------------------------------------------------- -// -// GetInfo -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo) -{ - _tfLangBarItemInfo.dwStyle |= TF_LBI_STYLE_SHOWNINTRAY; - *pInfo = _tfLangBarItemInfo; - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// GetStatus -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::GetStatus(_Out_ DWORD *pdwStatus) -{ - if (pdwStatus == nullptr) - { - return E_INVALIDARG; - } - - *pdwStatus = _status; - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// SetStatus -// -//---------------------------------------------------------------------------- - -void CLangBarItemButton::SetStatus(DWORD dwStatus, BOOL fSet) -{ - BOOL isChange = FALSE; - - if (fSet) - { - if (!(_status & dwStatus)) - { - // _status |= dwStatus; - _status &= ~dwStatus; - isChange = TRUE; - } - - if (isChange && _pLangBarItemSink) - { - _pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); - } - } - else - { - if (_status & dwStatus) - { - _status &= ~dwStatus; - isChange = TRUE; - } - - if (isChange && _pLangBarItemSink) - { - _pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); - } - } - - return; -} - -//+--------------------------------------------------------------------------- -// -// RefreshIcon -// -//---------------------------------------------------------------------------- - -void CLangBarItemButton::RefreshIcon() -{ - if (_pLangBarItemSink) - { - _pLangBarItemSink->OnUpdate(TF_LBI_ICON | TF_LBI_STATUS | TF_LBI_TOOLTIP); - } -} - -//+--------------------------------------------------------------------------- -// -// Show -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::Show(BOOL fShow) -{ - fShow; - if (_pLangBarItemSink) - { - _pLangBarItemSink->OnUpdate(TF_LBI_STATUS); - } - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// GetTooltipString -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::GetTooltipString(_Out_ BSTR *pbstrToolTip) -{ - *pbstrToolTip = SysAllocString(_pTooltipText); - - return (*pbstrToolTip == nullptr) ? E_OUTOFMEMORY : S_OK; -} - -//+--------------------------------------------------------------------------- -// -// OnClick -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea) -{ - if (click == TF_LBI_CLK_RIGHT) - { - SendLangbarRightClickEventToUIProcess(prcArea); - return S_OK; - } - - if (click == TF_LBI_CLK_LEFT && _pCompartment) - { - BOOL isOn = FALSE; - - _pCompartment->_GetCompartmentBOOL(isOn); - _pCompartment->_SetCompartmentBOOL(isOn ? FALSE : TRUE); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// InitMenu -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::InitMenu(_In_ ITfMenu *pMenu) -{ - pMenu; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// OnMenuSelect -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::OnMenuSelect(UINT wID) -{ - wID; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// GetIcon -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::GetIcon(_Out_ HICON *phIcon) -{ - BOOL isOn = FALSE; - - if (!_pCompartment) - { - return E_FAIL; - } - if (!phIcon) - { - return E_FAIL; - } - *phIcon = nullptr; - - _pCompartment->_GetCompartmentBOOL(isOn); - - DWORD status = 0; - GetStatus(&status); - - // GetIcon runs on the *focused application's* TSF thread, so sizing must - // not depend on that process's DPI awareness. The taskbar lays the input - // indicator out in physical pixels of its monitor (e.g. a 28px slot at - // 175%), while GetSystemMetrics(SM_CXSMICON) reports the system-DPI - // snapshot cached when the host started (e.g. 20px). The shell then has to - // upscale our returned icon into the larger slot, which is the blur of - // issue #50 (focus-dependent only because explorer happens to start at the - // correct scale). - // - // Query the primary monitor's effective DPI instead. Per MS docs, - // GetDpiForMonitor scales its answer to the caller's DPI awareness - // (PROCESS_DPI_UNAWARE -> 96, PROCESS_SYSTEM_DPI_AWARE -> system DPI, - // PROCESS_PER_MONITOR_DPI_AWARE -> the real value), so temporarily flip - // this thread to PER_MONITOR_AWARE_V2 around the query; otherwise unaware - // or system-aware hosts would still end up with the wrong size. Hosts - // lacking these APIs fall back to the legacy SM_CXSMICON path unchanged. - int desiredSize = kSmallIconDesignSize; - bool sizedByMonitorDpi = false; - if (HMONITOR mon = ::MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY)) - { - SetThreadDpiAwarenessCtxFn setCtx = - ResolveOnce(s_setThreadDpiAwarenessCtx, &ResolveSetThreadDpiAwarenessContext); - HANDLE prevCtx = nullptr; - if (setCtx) - { - // (HANDLE)(INT_PTR)-4 == DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; - // spelled out via casts to avoid requiring WINVER >= Win10 targets. - prevCtx = setCtx(reinterpret_cast(static_cast(-4))); - } - - UINT dpiX = 0; - UINT dpiY = 0; - GetDpiForMonitorFn getDpi = ResolveOnce(s_getDpiForMonitor, &ResolveGetDpiForMonitor); - if (getDpi && SUCCEEDED(getDpi(mon, 0 /* MDT_EFFECTIVE_DPI */, &dpiX, &dpiY)) && dpiX > 0) - { - desiredSize = MulDiv(kSmallIconDesignSize, static_cast(dpiX), kUserDefaultScreenDpi); - sizedByMonitorDpi = true; - } - - if (prevCtx) - { - setCtx(prevCtx); // restore the caller's thread awareness - } - } - if (!sizedByMonitorDpi) - { - // No shcore/awareness-switch available: exactly the legacy behavior. - desiredSize = GetSystemMetrics(SM_CXSMICON); - } - if (desiredSize <= 0) - { - desiredSize = kSmallIconDesignSize; - } - // UAC/secure desktop historically expects at least 24x24. - if (_isSecureMode && desiredSize < 24) - { - desiredSize = 24; - } - - DWORD lightIconIndex = (isOn && !(status & TF_LBI_STATUS_DISABLED)) ? _onIconIndex : _offIconIndex; - if (!(status & TF_LBI_STATUS_DISABLED) && _onIconIndex == static_cast(IME_MODE_ON_ICON_INDEX) && - Global::CapsLockEnabled.load(std::memory_order_relaxed)) - { - lightIconIndex = static_cast(IME_MODE_CAP_ICON_INDEX); - } - else if (isOn && !(status & TF_LBI_STATUS_DISABLED) && _onIconIndex == static_cast(IME_MODE_ON_ICON_INDEX) && - Global::JapaneseInputModeEnabled.load(std::memory_order_relaxed)) - { - lightIconIndex = static_cast(IME_MODE_ON_JP_ICON_INDEX); - } - const DWORD iconIndex = ResolveThemeIconIndex(lightIconIndex); - - if (Global::dllInstanceHandle) - { - *phIcon = reinterpret_cast( - LoadImage(Global::dllInstanceHandle, MAKEINTRESOURCE(iconIndex), IMAGE_ICON, desiredSize, desiredSize, 0)); - } - - return (*phIcon != NULL) ? S_OK : E_FAIL; -} - -//+--------------------------------------------------------------------------- -// -// GetText -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::GetText(_Out_ BSTR *pbstrText) -{ - *pbstrText = SysAllocString(_tfLangBarItemInfo.szDescription); - - return (*pbstrText == nullptr) ? E_OUTOFMEMORY : S_OK; -} - -//+--------------------------------------------------------------------------- -// -// AdviseSink -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie) -{ - // We allow only ITfLangBarItemSink interface. - if (!IsEqualIID(IID_ITfLangBarItemSink, riid)) - { - return CONNECT_E_CANNOTCONNECT; - } - - // We support only one sink once. - if (_pLangBarItemSink != nullptr) - { - return CONNECT_E_ADVISELIMIT; - } - - // Query the ITfLangBarItemSink interface and store it into _pLangBarItemSink. - if (punk == nullptr) - { - return E_INVALIDARG; - } - if (punk->QueryInterface(IID_ITfLangBarItemSink, (void **)&_pLangBarItemSink) != S_OK) - { - _pLangBarItemSink = nullptr; - return E_NOINTERFACE; - } - - // return our cookie. - *pdwCookie = _cookie; - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// UnadviseSink -// -//---------------------------------------------------------------------------- - -STDAPI CLangBarItemButton::UnadviseSink(DWORD dwCookie) -{ - // Check the given cookie. - if (dwCookie != _cookie) - { - return CONNECT_E_NOCONNECTION; - } - - // If there is nno connected sink, we just fail. - if (_pLangBarItemSink == nullptr) - { - return CONNECT_E_NOCONNECTION; - } - - _pLangBarItemSink->Release(); - _pLangBarItemSink = nullptr; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// _AddItem -// -//---------------------------------------------------------------------------- - -HRESULT CLangBarItemButton::_AddItem(_In_ ITfThreadMgr *pThreadMgr) -{ - HRESULT hr = S_OK; - ITfLangBarItemMgr *pLangBarItemMgr = nullptr; - - if (_isAddedToLanguageBar) - { - return S_OK; - } - - hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); - if (SUCCEEDED(hr)) - { - hr = pLangBarItemMgr->AddItem(this); - if (SUCCEEDED(hr)) - { - _isAddedToLanguageBar = TRUE; - } - pLangBarItemMgr->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _RemoveItem -// -//---------------------------------------------------------------------------- - -HRESULT CLangBarItemButton::_RemoveItem(_In_ ITfThreadMgr *pThreadMgr) -{ - HRESULT hr = S_OK; - ITfLangBarItemMgr *pLangBarItemMgr = nullptr; - - if (!_isAddedToLanguageBar) - { - return S_OK; - } - - hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); - if (SUCCEEDED(hr)) - { - hr = pLangBarItemMgr->RemoveItem(this); - if (SUCCEEDED(hr)) - { - _isAddedToLanguageBar = FALSE; - } - pLangBarItemMgr->Release(); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// _RegisterCompartment -// -//---------------------------------------------------------------------------- - -BOOL CLangBarItemButton::_RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, - REFGUID guidCompartment) -{ - _pCompartment = new (std::nothrow) CCompartment(pThreadMgr, tfClientId, guidCompartment); - if (_pCompartment) - { - // Advice ITfCompartmentEventSink - _pCompartmentEventSink = new (std::nothrow) CCompartmentEventSink(_CompartmentCallback, this); - if (_pCompartmentEventSink) - { - _pCompartmentEventSink->_Advise(pThreadMgr, guidCompartment); - } - else - { - delete _pCompartment; - _pCompartment = nullptr; - } - } - - return _pCompartment ? TRUE : FALSE; -} - -//+--------------------------------------------------------------------------- -// -// _UnregisterCompartment -// -//---------------------------------------------------------------------------- - -BOOL CLangBarItemButton::_UnregisterCompartment() -{ - if (_pCompartment) - { - // Unadvice ITfCompartmentEventSink - if (_pCompartmentEventSink) - { - _pCompartmentEventSink->_Unadvise(); - } - - // clear ITfCompartment - _pCompartment->_ClearCompartment(); - } - - return TRUE; -} - -//+--------------------------------------------------------------------------- -// -// _CompartmentCallback -// -//---------------------------------------------------------------------------- - -// static -HRESULT CLangBarItemButton::_CompartmentCallback(_In_ void *pv, REFGUID guidCompartment) -{ - CLangBarItemButton *fakeThis = (CLangBarItemButton *)pv; - - GUID guid = GUID_NULL; - fakeThis->_pCompartment->_GetGUID(&guid); - - if (IsEqualGUID(guid, guidCompartment)) - { - if (fakeThis->_pLangBarItemSink) - { - fakeThis->_pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); - } - } - - return S_OK; -} +#ifndef UNICODE +#define UNICODE +#endif // !UNICODE + +#ifndef _UNICODE +#define _UNICODE +#endif // !UNICODE + +#include "Private.h" +#include "MetasequoiaIME.h" +#include "CompositionProcessorEngine.h" +#include "LanguageBar.h" +#include "Globals.h" +#include "Compartment.h" +#include "Ipc.h" +#include "fmt/xchar.h" + +#include + +namespace +{ +// Taskbar/system tray follows SystemUsesLightTheme (0 = dark). +bool IsSystemDarkMode() +{ + DWORD value = 1; // default to light + DWORD size = sizeof(value); + if (RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + L"SystemUsesLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size) != ERROR_SUCCESS) + { + return false; + } + return value == 0; +} + +DWORD ResolveThemeIconIndex(DWORD lightIconIndex) +{ + if (!IsSystemDarkMode()) + { + return lightIconIndex; + } + + if (lightIconIndex == static_cast(IME_MODE_ON_ICON_INDEX)) + { + return static_cast(IME_MODE_ON_DARK_ICON_INDEX); + } + if (lightIconIndex == static_cast(IME_MODE_ON_JP_ICON_INDEX)) + { + return static_cast(IME_MODE_ON_JP_DARK_ICON_INDEX); + } + if (lightIconIndex == static_cast(IME_MODE_CAP_ICON_INDEX)) + { + return static_cast(IME_MODE_CAP_DARK_ICON_INDEX); + } + if (lightIconIndex == static_cast(IME_MODE_OFF_ICON_INDEX)) + { + return static_cast(IME_MODE_OFF_DARK_ICON_INDEX); + } + return lightIconIndex; +} + +// Design size of the mode icons for language bar / taskbar indicators (at 96 DPI). +constexpr int kSmallIconDesignSize = 16; + +// Avoids relying on USER_DEFAULT_SCREEN_DPI, which older Windows SDK headers +// targeted by this project may not provide. +constexpr int kUserDefaultScreenDpi = 96; + +using GetDpiForMonitorFn = HRESULT(WINAPI *)(HMONITOR, int /*MONITOR_DPI_TYPE*/, UINT *, UINT *); +using SetThreadDpiAwarenessCtxFn = HANDLE(WINAPI *)(HANDLE); + +// Entry points are resolved lazily and cached in these atomics instead of +// function-local statics: the project compiles with /Zc:threadSafeInit-, under +// which magic statics are NOT thread-safe, while GetIcon can be reached from +// several TSF threads of the host process. Failed lookups are not cached - they +// are cheap enough to retry on the next call. +std::atomic s_getDpiForMonitor{nullptr}; +std::atomic s_setThreadDpiAwarenessCtx{nullptr}; + +template T ResolveOnce(std::atomic &slot, T (*resolver)()) +{ + T resolved = slot.load(std::memory_order_acquire); + if (resolved == nullptr) + { + resolved = resolver(); + if (resolved != nullptr) + { + slot.store(resolved, std::memory_order_release); + } + } + return resolved; +} + +GetDpiForMonitorFn ResolveGetDpiForMonitor() +{ + // shcore.dll ships with Windows 8.1+; most hosts already have it mapped. + HMODULE shcore = ::GetModuleHandleW(L"shcore.dll"); + if (!shcore) + { + shcore = ::LoadLibraryW(L"shcore.dll"); // system DLL; intentionally kept until process exit + } + return shcore ? reinterpret_cast(::GetProcAddress(shcore, "GetDpiForMonitor")) : nullptr; +} + +SetThreadDpiAwarenessCtxFn ResolveSetThreadDpiAwarenessContext() +{ + // user32.dll is always mapped inside any host of this DLL. + HMODULE user32 = ::GetModuleHandleW(L"user32.dll"); + return user32 ? reinterpret_cast( + ::GetProcAddress(user32, "SetThreadDpiAwarenessContext")) // Win10 1607+ + : nullptr; +} +} // namespace + +//+--------------------------------------------------------------------------- +// +// CMetasequoiaIME::_UpdateLanguageBarOnSetFocus +// +//---------------------------------------------------------------------------- + +void CMetasequoiaIME::_UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus) +{ + BOOL needDisableButtons = FALSE; + + if (!pDocMgrFocus) + { + needDisableButtons = TRUE; + } + else + { + IEnumTfContexts *pEnumContext = nullptr; + + if (FAILED(pDocMgrFocus->EnumContexts(&pEnumContext)) || !pEnumContext) + { + needDisableButtons = TRUE; + } + else + { + ULONG fetched = 0; + ITfContext *pContext = nullptr; + + if (FAILED(pEnumContext->Next(1, &pContext, &fetched)) || fetched != 1) + { + needDisableButtons = TRUE; + } + + if (!pContext) + { + // context is not associated + needDisableButtons = TRUE; + } + else + { + pContext->Release(); + } + } + + if (pEnumContext) + { + pEnumContext->Release(); + } + } + + CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + pCompositionProcessorEngine->SetLanguageBarStatus(TF_LBI_STATUS_DISABLED, needDisableButtons); + + // SetStatus intentionally keeps the item enabled when focus is absent so + // Firefox does not replace our mode glyph with its disabled/close glyph. + // Consequently, returning to a document may not change the status bits and + // therefore may not generate an ITfLangBarItemSink update. Always refresh + // the newly focused thread's item: Windows caches each host/thread's icon, + // and another host may have changed the shared Chinese/Japanese mode while + // this one was in the background. + if (!needDisableButtons) + { + pCompositionProcessorEngine->RefreshLanguageBarIcons(); + } +} + +//+--------------------------------------------------------------------------- +// +// CCompositionProcessorEngine::SetLanguageBarStatus +// +//---------------------------------------------------------------------------- + +VOID CCompositionProcessorEngine::SetLanguageBarStatus(DWORD status, BOOL isSet) +{ + if (_pLanguageBar_IMEMode) + { + _pLanguageBar_IMEMode->SetStatus(status, isSet); + } + if (_pLanguageBar_DoubleSingleByte) + { + _pLanguageBar_DoubleSingleByte->SetStatus(status, isSet); + } + if (_pLanguageBar_Punctuation) + { + _pLanguageBar_Punctuation->SetStatus(status, isSet); + } +} + +void CCompositionProcessorEngine::RefreshLanguageBarIcons() +{ + if (_pLanguageBar_IMEMode) + { + _pLanguageBar_IMEMode->RefreshIcon(); + } + if (_pLanguageBar_DoubleSingleByte) + { + _pLanguageBar_DoubleSingleByte->RefreshIcon(); + } + if (_pLanguageBar_Punctuation) + { + _pLanguageBar_Punctuation->RefreshIcon(); + } +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::ctor +// +//---------------------------------------------------------------------------- + +CLangBarItemButton::CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, + DWORD offIconIndex, BOOL isSecureMode) +{ + DWORD bufLen = 0; + + DllAddRef(); + + // initialize TF_LANGBARITEMINFO structure. + _tfLangBarItemInfo.clsidService = Global::MetasequoiaIMECLSID; // This LangBarItem belongs to this TextService. + _tfLangBarItemInfo.guidItem = guidLangBar; // GUID of this LangBarItem. + _tfLangBarItemInfo.dwStyle = (TF_LBI_STYLE_BTN_BUTTON | TF_LBI_STYLE_SHOWNINTRAY); // This LangBar is a button type. + _tfLangBarItemInfo.ulSort = 0; // The position of this LangBar Item is not specified. + StringCchCopy(_tfLangBarItemInfo.szDescription, ARRAYSIZE(_tfLangBarItemInfo.szDescription), + description); // Set the description of this LangBar Item. + + // Initialize the sink pointer to NULL. + _pLangBarItemSink = nullptr; + + // Initialize ICON index and file name. + _onIconIndex = onIconIndex; + _offIconIndex = offIconIndex; + + // Initialize compartment. + _pCompartment = nullptr; + _pCompartmentEventSink = nullptr; + + _isAddedToLanguageBar = FALSE; + _isSecureMode = isSecureMode; + _status = 0; + + _refCount = 1; + + // Initialize Tooltip + _pTooltipText = nullptr; + if (tooltip) + { + size_t len = 0; + if (StringCchLength(tooltip, STRSAFE_MAX_CCH, &len) != S_OK) + { + len = 0; + } + bufLen = static_cast(len) + 1; + _pTooltipText = (LPCWSTR) new (std::nothrow) WCHAR[bufLen]; + if (_pTooltipText) + { + StringCchCopy((LPWSTR)_pTooltipText, bufLen, tooltip); + } + } +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::dtor +// +//---------------------------------------------------------------------------- + +CLangBarItemButton::~CLangBarItemButton() +{ + DllRelease(); + CleanUp(); +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::CleanUp +// +//---------------------------------------------------------------------------- + +void CLangBarItemButton::CleanUp() +{ + if (_pTooltipText) + { + delete[] _pTooltipText; + _pTooltipText = nullptr; + } + + // TSF still holds the advised _pCompartmentEventSink until _Unadvise runs, so unregister before the + // deletes below and independently of the thread manager: under COM-less activation (TF_TMAE_COMLESS) + // CoCreateInstance fails, and freeing a still-advised sink leaves msctf calling into released memory. + _UnregisterCompartment(); + + ITfThreadMgr *pThreadMgr = nullptr; + HRESULT hr = + CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void **)&pThreadMgr); + if (SUCCEEDED(hr)) + { + _RemoveItem(pThreadMgr); + pThreadMgr->Release(); + pThreadMgr = nullptr; + } + + if (_pCompartment) + { + delete _pCompartment; + _pCompartment = nullptr; + } + + if (_pCompartmentEventSink) + { + delete _pCompartmentEventSink; + _pCompartmentEventSink = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfLangBarItem) || + IsEqualIID(riid, IID_ITfLangBarItemButton)) + { + *ppvObj = (ITfLangBarItemButton *)this; + } + else if (IsEqualIID(riid, IID_ITfSource)) + { + *ppvObj = (ITfSource *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CLangBarItemButton::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CLangBarItemButton::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// GetInfo +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo) +{ + _tfLangBarItemInfo.dwStyle |= TF_LBI_STYLE_SHOWNINTRAY; + *pInfo = _tfLangBarItemInfo; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// GetStatus +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetStatus(_Out_ DWORD *pdwStatus) +{ + if (pdwStatus == nullptr) + { + return E_INVALIDARG; + } + + *pdwStatus = _status; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// SetStatus +// +//---------------------------------------------------------------------------- + +void CLangBarItemButton::SetStatus(DWORD dwStatus, BOOL fSet) +{ + BOOL isChange = FALSE; + + if (fSet) + { + if (!(_status & dwStatus)) + { + // _status |= dwStatus; + _status &= ~dwStatus; + isChange = TRUE; + } + + if (isChange && _pLangBarItemSink) + { + _pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); + } + } + else + { + if (_status & dwStatus) + { + _status &= ~dwStatus; + isChange = TRUE; + } + + if (isChange && _pLangBarItemSink) + { + _pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); + } + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// RefreshIcon +// +//---------------------------------------------------------------------------- + +void CLangBarItemButton::RefreshIcon() +{ + if (_pLangBarItemSink) + { + _pLangBarItemSink->OnUpdate(TF_LBI_ICON | TF_LBI_STATUS | TF_LBI_TOOLTIP); + } +} + +//+--------------------------------------------------------------------------- +// +// Show +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::Show(BOOL fShow) +{ + fShow; + if (_pLangBarItemSink) + { + _pLangBarItemSink->OnUpdate(TF_LBI_STATUS); + } + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// GetTooltipString +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetTooltipString(_Out_ BSTR *pbstrToolTip) +{ + *pbstrToolTip = SysAllocString(_pTooltipText); + + return (*pbstrToolTip == nullptr) ? E_OUTOFMEMORY : S_OK; +} + +//+--------------------------------------------------------------------------- +// +// OnClick +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea) +{ + if (click == TF_LBI_CLK_RIGHT) + { + SendLangbarRightClickEventToUIProcess(prcArea); + return S_OK; + } + + if (click == TF_LBI_CLK_LEFT && _pCompartment) + { + BOOL isOn = FALSE; + + _pCompartment->_GetCompartmentBOOL(isOn); + _pCompartment->_SetCompartmentBOOL(isOn ? FALSE : TRUE); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// InitMenu +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::InitMenu(_In_ ITfMenu *pMenu) +{ + pMenu; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// OnMenuSelect +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::OnMenuSelect(UINT wID) +{ + wID; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// GetIcon +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetIcon(_Out_ HICON *phIcon) +{ + BOOL isOn = FALSE; + + if (!_pCompartment) + { + return E_FAIL; + } + if (!phIcon) + { + return E_FAIL; + } + *phIcon = nullptr; + + _pCompartment->_GetCompartmentBOOL(isOn); + + DWORD status = 0; + GetStatus(&status); + + // GetIcon runs on the *focused application's* TSF thread, so sizing must + // not depend on that process's DPI awareness. The taskbar lays the input + // indicator out in physical pixels of its monitor (e.g. a 28px slot at + // 175%), while GetSystemMetrics(SM_CXSMICON) reports the system-DPI + // snapshot cached when the host started (e.g. 20px). The shell then has to + // upscale our returned icon into the larger slot, which is the blur of + // issue #50 (focus-dependent only because explorer happens to start at the + // correct scale). + // + // Query the primary monitor's effective DPI instead. Per MS docs, + // GetDpiForMonitor scales its answer to the caller's DPI awareness + // (PROCESS_DPI_UNAWARE -> 96, PROCESS_SYSTEM_DPI_AWARE -> system DPI, + // PROCESS_PER_MONITOR_DPI_AWARE -> the real value), so temporarily flip + // this thread to PER_MONITOR_AWARE_V2 around the query; otherwise unaware + // or system-aware hosts would still end up with the wrong size. Hosts + // lacking these APIs fall back to the legacy SM_CXSMICON path unchanged. + int desiredSize = kSmallIconDesignSize; + bool sizedByMonitorDpi = false; + if (HMONITOR mon = ::MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY)) + { + SetThreadDpiAwarenessCtxFn setCtx = + ResolveOnce(s_setThreadDpiAwarenessCtx, &ResolveSetThreadDpiAwarenessContext); + HANDLE prevCtx = nullptr; + if (setCtx) + { + // (HANDLE)(INT_PTR)-4 == DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + // spelled out via casts to avoid requiring WINVER >= Win10 targets. + prevCtx = setCtx(reinterpret_cast(static_cast(-4))); + } + + UINT dpiX = 0; + UINT dpiY = 0; + GetDpiForMonitorFn getDpi = ResolveOnce(s_getDpiForMonitor, &ResolveGetDpiForMonitor); + if (getDpi && SUCCEEDED(getDpi(mon, 0 /* MDT_EFFECTIVE_DPI */, &dpiX, &dpiY)) && dpiX > 0) + { + desiredSize = MulDiv(kSmallIconDesignSize, static_cast(dpiX), kUserDefaultScreenDpi); + sizedByMonitorDpi = true; + } + + if (prevCtx) + { + setCtx(prevCtx); // restore the caller's thread awareness + } + } + if (!sizedByMonitorDpi) + { + // No shcore/awareness-switch available: exactly the legacy behavior. + desiredSize = GetSystemMetrics(SM_CXSMICON); + } + if (desiredSize <= 0) + { + desiredSize = kSmallIconDesignSize; + } + // UAC/secure desktop historically expects at least 24x24. + if (_isSecureMode && desiredSize < 24) + { + desiredSize = 24; + } + + DWORD lightIconIndex = (isOn && !(status & TF_LBI_STATUS_DISABLED)) ? _onIconIndex : _offIconIndex; + if (!(status & TF_LBI_STATUS_DISABLED) && _onIconIndex == static_cast(IME_MODE_ON_ICON_INDEX) && + Global::CapsLockEnabled.load(std::memory_order_relaxed)) + { + lightIconIndex = static_cast(IME_MODE_CAP_ICON_INDEX); + } + else if (isOn && !(status & TF_LBI_STATUS_DISABLED) && _onIconIndex == static_cast(IME_MODE_ON_ICON_INDEX) && + Global::JapaneseInputModeEnabled.load(std::memory_order_relaxed)) + { + lightIconIndex = static_cast(IME_MODE_ON_JP_ICON_INDEX); + } + const DWORD iconIndex = ResolveThemeIconIndex(lightIconIndex); + + if (Global::dllInstanceHandle) + { + *phIcon = reinterpret_cast( + LoadImage(Global::dllInstanceHandle, MAKEINTRESOURCE(iconIndex), IMAGE_ICON, desiredSize, desiredSize, 0)); + } + + return (*phIcon != NULL) ? S_OK : E_FAIL; +} + +//+--------------------------------------------------------------------------- +// +// GetText +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetText(_Out_ BSTR *pbstrText) +{ + *pbstrText = SysAllocString(_tfLangBarItemInfo.szDescription); + + return (*pbstrText == nullptr) ? E_OUTOFMEMORY : S_OK; +} + +//+--------------------------------------------------------------------------- +// +// AdviseSink +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie) +{ + // We allow only ITfLangBarItemSink interface. + if (!IsEqualIID(IID_ITfLangBarItemSink, riid)) + { + return CONNECT_E_CANNOTCONNECT; + } + + // We support only one sink once. + if (_pLangBarItemSink != nullptr) + { + return CONNECT_E_ADVISELIMIT; + } + + // Query the ITfLangBarItemSink interface and store it into _pLangBarItemSink. + if (punk == nullptr) + { + return E_INVALIDARG; + } + if (punk->QueryInterface(IID_ITfLangBarItemSink, (void **)&_pLangBarItemSink) != S_OK) + { + _pLangBarItemSink = nullptr; + return E_NOINTERFACE; + } + + // return our cookie. + *pdwCookie = _cookie; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// UnadviseSink +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::UnadviseSink(DWORD dwCookie) +{ + // Check the given cookie. + if (dwCookie != _cookie) + { + return CONNECT_E_NOCONNECTION; + } + + // If there is nno connected sink, we just fail. + if (_pLangBarItemSink == nullptr) + { + return CONNECT_E_NOCONNECTION; + } + + _pLangBarItemSink->Release(); + _pLangBarItemSink = nullptr; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _AddItem +// +//---------------------------------------------------------------------------- + +HRESULT CLangBarItemButton::_AddItem(_In_ ITfThreadMgr *pThreadMgr) +{ + HRESULT hr = S_OK; + ITfLangBarItemMgr *pLangBarItemMgr = nullptr; + + if (_isAddedToLanguageBar) + { + return S_OK; + } + + hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); + if (SUCCEEDED(hr)) + { + hr = pLangBarItemMgr->AddItem(this); + if (SUCCEEDED(hr)) + { + _isAddedToLanguageBar = TRUE; + } + pLangBarItemMgr->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _RemoveItem +// +//---------------------------------------------------------------------------- + +HRESULT CLangBarItemButton::_RemoveItem(_In_ ITfThreadMgr *pThreadMgr) +{ + HRESULT hr = S_OK; + ITfLangBarItemMgr *pLangBarItemMgr = nullptr; + + if (!_isAddedToLanguageBar) + { + return S_OK; + } + + hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); + if (SUCCEEDED(hr)) + { + hr = pLangBarItemMgr->RemoveItem(this); + if (SUCCEEDED(hr)) + { + _isAddedToLanguageBar = FALSE; + } + pLangBarItemMgr->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _RegisterCompartment +// +//---------------------------------------------------------------------------- + +BOOL CLangBarItemButton::_RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, + REFGUID guidCompartment) +{ + _pCompartment = new (std::nothrow) CCompartment(pThreadMgr, tfClientId, guidCompartment); + if (_pCompartment) + { + // Advice ITfCompartmentEventSink + _pCompartmentEventSink = new (std::nothrow) CCompartmentEventSink(_CompartmentCallback, this); + if (_pCompartmentEventSink) + { + _pCompartmentEventSink->_Advise(pThreadMgr, guidCompartment); + } + else + { + delete _pCompartment; + _pCompartment = nullptr; + } + } + + return _pCompartment ? TRUE : FALSE; +} + +//+--------------------------------------------------------------------------- +// +// _UnregisterCompartment +// +//---------------------------------------------------------------------------- + +BOOL CLangBarItemButton::_UnregisterCompartment() +{ + if (_pCompartment) + { + // Unadvice ITfCompartmentEventSink + if (_pCompartmentEventSink) + { + _pCompartmentEventSink->_Unadvise(); + } + + // clear ITfCompartment + _pCompartment->_ClearCompartment(); + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _CompartmentCallback +// +//---------------------------------------------------------------------------- + +// static +HRESULT CLangBarItemButton::_CompartmentCallback(_In_ void *pv, REFGUID guidCompartment) +{ + CLangBarItemButton *fakeThis = (CLangBarItemButton *)pv; + + GUID guid = GUID_NULL; + fakeThis->_pCompartment->_GetGUID(&guid); + + if (IsEqualGUID(guid, guidCompartment)) + { + if (fakeThis->_pLangBarItemSink) + { + fakeThis->_pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); + } + } + + return S_OK; +} diff --git a/windows/src/LanguageBar/LanguageBar.h b/windows/src/LanguageBar/LanguageBar.h index 5f1d80ae..9b52fcc6 100644 --- a/windows/src/LanguageBar/LanguageBar.h +++ b/windows/src/LanguageBar/LanguageBar.h @@ -1,74 +1,74 @@ -#pragma once - -#include "Private.h" -#include "MetasequoiaIME.h" -#include "CompositionProcessorEngine.h" -#include "Globals.h" -#include "Compartment.h" - -class CCompartment; -class CCompartmentEventSink; - -class CLangBarItemButton : public ITfLangBarItemButton, public ITfSource -{ - public: - CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, DWORD offIconIndex, - BOOL isSecureMode); - ~CLangBarItemButton(); - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfLangBarItem - STDMETHODIMP GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo); - STDMETHODIMP GetStatus(_Out_ DWORD *pdwStatus); - STDMETHODIMP Show(BOOL fShow); - STDMETHODIMP GetTooltipString(_Out_ BSTR *pbstrToolTip); - - // ITfLangBarItemButton - STDMETHODIMP OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea); - STDMETHODIMP InitMenu(_In_ ITfMenu *pMenu); - STDMETHODIMP OnMenuSelect(UINT wID); - STDMETHODIMP GetIcon(_Out_ HICON *phIcon); - STDMETHODIMP GetText(_Out_ BSTR *pbstrText); - - // ITfSource - STDMETHODIMP AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie); - STDMETHODIMP UnadviseSink(DWORD dwCookie); - - // Add/Remove languagebar item - HRESULT _AddItem(_In_ ITfThreadMgr *pThreadMgr); - HRESULT _RemoveItem(_In_ ITfThreadMgr *pThreadMgr); - - // Register compartment for button On/Off switch - BOOL _RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment); - BOOL _UnregisterCompartment(); - - void CleanUp(); - - void SetStatus(DWORD dwStatus, BOOL fSet); - void RefreshIcon(); - - private: - ITfLangBarItemSink *_pLangBarItemSink; - - TF_LANGBARITEMINFO _tfLangBarItemInfo; - LPCWSTR _pTooltipText; - DWORD _onIconIndex; - DWORD _offIconIndex; - - BOOL _isAddedToLanguageBar; - BOOL _isSecureMode; - DWORD _status; - - CCompartment *_pCompartment; - CCompartmentEventSink *_pCompartmentEventSink; - static HRESULT _CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); - - // The cookie for the sink to CLangBarItemButton. - static const DWORD _cookie = 0; - - LONG _refCount; -}; +#pragma once + +#include "Private.h" +#include "MetasequoiaIME.h" +#include "CompositionProcessorEngine.h" +#include "Globals.h" +#include "Compartment.h" + +class CCompartment; +class CCompartmentEventSink; + +class CLangBarItemButton : public ITfLangBarItemButton, public ITfSource +{ + public: + CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, DWORD offIconIndex, + BOOL isSecureMode); + ~CLangBarItemButton(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfLangBarItem + STDMETHODIMP GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo); + STDMETHODIMP GetStatus(_Out_ DWORD *pdwStatus); + STDMETHODIMP Show(BOOL fShow); + STDMETHODIMP GetTooltipString(_Out_ BSTR *pbstrToolTip); + + // ITfLangBarItemButton + STDMETHODIMP OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea); + STDMETHODIMP InitMenu(_In_ ITfMenu *pMenu); + STDMETHODIMP OnMenuSelect(UINT wID); + STDMETHODIMP GetIcon(_Out_ HICON *phIcon); + STDMETHODIMP GetText(_Out_ BSTR *pbstrText); + + // ITfSource + STDMETHODIMP AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie); + STDMETHODIMP UnadviseSink(DWORD dwCookie); + + // Add/Remove languagebar item + HRESULT _AddItem(_In_ ITfThreadMgr *pThreadMgr); + HRESULT _RemoveItem(_In_ ITfThreadMgr *pThreadMgr); + + // Register compartment for button On/Off switch + BOOL _RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment); + BOOL _UnregisterCompartment(); + + void CleanUp(); + + void SetStatus(DWORD dwStatus, BOOL fSet); + void RefreshIcon(); + + private: + ITfLangBarItemSink *_pLangBarItemSink; + + TF_LANGBARITEMINFO _tfLangBarItemInfo; + LPCWSTR _pTooltipText; + DWORD _onIconIndex; + DWORD _offIconIndex; + + BOOL _isAddedToLanguageBar; + BOOL _isSecureMode; + DWORD _status; + + CCompartment *_pCompartment; + CCompartmentEventSink *_pCompartmentEventSink; + static HRESULT _CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); + + // The cookie for the sink to CLangBarItemButton. + static const DWORD _cookie = 0; + + LONG _refCount; +}; diff --git a/windows/src/Register/RegKey.cpp b/windows/src/Register/RegKey.cpp index 6489e853..ca4ee1f6 100644 --- a/windows/src/Register/RegKey.cpp +++ b/windows/src/Register/RegKey.cpp @@ -1,281 +1,281 @@ -#ifndef UNICODE -#define UNICODE -#endif // !UNICODE - -#ifndef _UNICODE -#define _UNICODE -#endif // !UNICODE - -#include "Private.h" -#include "RegKey.h" - -//--------------------------------------------------------------------- -// -// ctor -// -//--------------------------------------------------------------------- - -CRegKey::CRegKey() -{ - _keyHandle = nullptr; -} - -//--------------------------------------------------------------------- -// -// dtor -// -//--------------------------------------------------------------------- - -CRegKey::~CRegKey() -{ - Close(); -} - -//--------------------------------------------------------------------- -// -// operator -// -//--------------------------------------------------------------------- - -HKEY CRegKey::GetHKEY() -{ - return _keyHandle; -} - -//--------------------------------------------------------------------- -// -// Create -// -//--------------------------------------------------------------------- - -LONG CRegKey::Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, _In_reads_opt_(255) LPWSTR pwszClass, - DWORD dwOptions, REGSAM samDesired, _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr, - _Out_opt_ LPDWORD lpdwDisposition) -{ - DWORD disposition = 0; - HKEY keyHandle = nullptr; - - LONG res = RegCreateKeyEx(hKeyPresent, pwszKeyName, 0, pwszClass, dwOptions, samDesired, lpSecAttr, &keyHandle, - &disposition); - - if (lpdwDisposition != nullptr) - { - *lpdwDisposition = disposition; - } - - if (res == ERROR_SUCCESS) - { - Close(); - _keyHandle = keyHandle; - } - - return res; -} - -//--------------------------------------------------------------------- -// -// Open -// -//--------------------------------------------------------------------- - -LONG CRegKey::Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, REGSAM samDesired) -{ - HKEY keyHandle = nullptr; - - LONG res = RegOpenKeyEx(hKeyParent, pwszKeyName, 0, samDesired, &keyHandle); - if (res == ERROR_SUCCESS) - { - Close(); - _keyHandle = keyHandle; - } - return res; -} - -//--------------------------------------------------------------------- -// -// Close -// -//--------------------------------------------------------------------- - -LONG CRegKey::Close() -{ - LONG res = ERROR_SUCCESS; - if (_keyHandle) - { - res = RegCloseKey(_keyHandle); - _keyHandle = nullptr; - } - return res; -} - -//--------------------------------------------------------------------- -// -// DeleteSubKey -// RecurseDeleteKey -// -//--------------------------------------------------------------------- - -LONG CRegKey::DeleteSubKey(_In_ LPCWSTR pwszSubKey) -{ - return RegDeleteKey(_keyHandle, pwszSubKey); -} - -LONG CRegKey::RecurseDeleteKey(_In_ LPCWSTR pwszSubKey) -{ - CRegKey key; - LONG res = key.Open(_keyHandle, pwszSubKey, KEY_READ | KEY_WRITE); - if (res != ERROR_SUCCESS) - { - return res; - } - FILETIME time; - WCHAR subKeyName[256] = {'\0'}; - DWORD subKeyNameSize = ARRAYSIZE(subKeyName); - - while (RegEnumKeyEx(key.GetHKEY(), 0, subKeyName, &subKeyNameSize, NULL, NULL, NULL, &time) == ERROR_SUCCESS) - { - subKeyName[ARRAYSIZE(subKeyName) - 1] = L'\0'; - res = key.RecurseDeleteKey(subKeyName); - if (res != ERROR_SUCCESS) - { - return res; - } - subKeyNameSize = ARRAYSIZE(subKeyName); - } - - key.Close(); - - return DeleteSubKey(pwszSubKey); -} - -//--------------------------------------------------------------------- -// -// DeleteValue -// -//--------------------------------------------------------------------- - -LONG CRegKey::DeleteValue(_In_ LPCWSTR pwszValue) -{ - return RegDeleteValue(_keyHandle, pwszValue); -} - -//--------------------------------------------------------------------- -// -// QueryStingValue -// SetStringValue -// -//--------------------------------------------------------------------- - -LONG CRegKey::QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, - _Inout_ ULONG *pnChars) -{ - LONG res = 0; - DWORD dataType = REG_NONE; - ULONG pwszValueSize = 0; - - if (pnChars == nullptr) - { - return E_INVALIDARG; - } - - pwszValueSize = (*pnChars) * sizeof(WCHAR); - *pnChars = 0; - - res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, (LPBYTE)pwszValue, &pwszValueSize); - if (res != ERROR_SUCCESS) - { - return res; - } - if ((dataType != REG_SZ) && (dataType != REG_EXPAND_SZ)) - { - return ERROR_INVALID_DATA; - } - - *pnChars = pwszValueSize / sizeof(WCHAR); - - return ERROR_SUCCESS; -} - -LONG CRegKey::SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType) -{ - size_t lenOfValue = 0; - if (pwszValue == nullptr) - { - return ERROR_INVALID_PARAMETER; - } - - if (StringCchLength(pwszValue, STRSAFE_MAX_CCH, &lenOfValue) != S_OK) - { - return ERROR_INVALID_PARAMETER; - } - DWORD len = static_cast(lenOfValue); - return RegSetValueEx(_keyHandle, pwszValueName, NULL, dwType, (LPBYTE)pwszValue, (++len) * sizeof(WCHAR)); -} - -//--------------------------------------------------------------------- -// -// QueryDWORDValue -// SetDWORDValue -// -//--------------------------------------------------------------------- - -LONG CRegKey::QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue) -{ - LONG res = 0; - DWORD dataType = REG_NONE; - ULONG pwszValueSize = 0; - - pwszValueSize = sizeof(DWORD); - res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, (LPBYTE)(&dwValue), &pwszValueSize); - if (res != ERROR_SUCCESS) - { - return res; - } - if (dataType != REG_DWORD) - { - return ERROR_INVALID_DATA; - } - - return ERROR_SUCCESS; -} - -LONG CRegKey::SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue) -{ - return RegSetValueEx(_keyHandle, pwszValueName, NULL, REG_DWORD, (LPBYTE)(&dwValue), sizeof(DWORD)); -} - -//--------------------------------------------------------------------- -// -// QueryBinaryValue -// SetBinaryValue -// -//--------------------------------------------------------------------- - -LONG CRegKey::QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE *lpData, DWORD cbData) -{ - LONG res = 0; - DWORD dataType = REG_NONE; - ULONG pwszValueSize = 0; - - pwszValueSize = cbData; - res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, lpData, &pwszValueSize); - if (res != ERROR_SUCCESS) - { - return res; - } - if (dataType != REG_BINARY) - { - return ERROR_INVALID_DATA; - } - if (pwszValueSize != cbData) - { - return ERROR_INVALID_DATA; - } - - return ERROR_SUCCESS; -} - -LONG CRegKey::SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE *lpData, DWORD cbData) -{ - return RegSetValueEx(_keyHandle, pwszValueName, NULL, REG_BINARY, lpData, cbData); -} +#ifndef UNICODE +#define UNICODE +#endif // !UNICODE + +#ifndef _UNICODE +#define _UNICODE +#endif // !UNICODE + +#include "Private.h" +#include "RegKey.h" + +//--------------------------------------------------------------------- +// +// ctor +// +//--------------------------------------------------------------------- + +CRegKey::CRegKey() +{ + _keyHandle = nullptr; +} + +//--------------------------------------------------------------------- +// +// dtor +// +//--------------------------------------------------------------------- + +CRegKey::~CRegKey() +{ + Close(); +} + +//--------------------------------------------------------------------- +// +// operator +// +//--------------------------------------------------------------------- + +HKEY CRegKey::GetHKEY() +{ + return _keyHandle; +} + +//--------------------------------------------------------------------- +// +// Create +// +//--------------------------------------------------------------------- + +LONG CRegKey::Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, _In_reads_opt_(255) LPWSTR pwszClass, + DWORD dwOptions, REGSAM samDesired, _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr, + _Out_opt_ LPDWORD lpdwDisposition) +{ + DWORD disposition = 0; + HKEY keyHandle = nullptr; + + LONG res = RegCreateKeyEx(hKeyPresent, pwszKeyName, 0, pwszClass, dwOptions, samDesired, lpSecAttr, &keyHandle, + &disposition); + + if (lpdwDisposition != nullptr) + { + *lpdwDisposition = disposition; + } + + if (res == ERROR_SUCCESS) + { + Close(); + _keyHandle = keyHandle; + } + + return res; +} + +//--------------------------------------------------------------------- +// +// Open +// +//--------------------------------------------------------------------- + +LONG CRegKey::Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, REGSAM samDesired) +{ + HKEY keyHandle = nullptr; + + LONG res = RegOpenKeyEx(hKeyParent, pwszKeyName, 0, samDesired, &keyHandle); + if (res == ERROR_SUCCESS) + { + Close(); + _keyHandle = keyHandle; + } + return res; +} + +//--------------------------------------------------------------------- +// +// Close +// +//--------------------------------------------------------------------- + +LONG CRegKey::Close() +{ + LONG res = ERROR_SUCCESS; + if (_keyHandle) + { + res = RegCloseKey(_keyHandle); + _keyHandle = nullptr; + } + return res; +} + +//--------------------------------------------------------------------- +// +// DeleteSubKey +// RecurseDeleteKey +// +//--------------------------------------------------------------------- + +LONG CRegKey::DeleteSubKey(_In_ LPCWSTR pwszSubKey) +{ + return RegDeleteKey(_keyHandle, pwszSubKey); +} + +LONG CRegKey::RecurseDeleteKey(_In_ LPCWSTR pwszSubKey) +{ + CRegKey key; + LONG res = key.Open(_keyHandle, pwszSubKey, KEY_READ | KEY_WRITE); + if (res != ERROR_SUCCESS) + { + return res; + } + FILETIME time; + WCHAR subKeyName[256] = {'\0'}; + DWORD subKeyNameSize = ARRAYSIZE(subKeyName); + + while (RegEnumKeyEx(key.GetHKEY(), 0, subKeyName, &subKeyNameSize, NULL, NULL, NULL, &time) == ERROR_SUCCESS) + { + subKeyName[ARRAYSIZE(subKeyName) - 1] = L'\0'; + res = key.RecurseDeleteKey(subKeyName); + if (res != ERROR_SUCCESS) + { + return res; + } + subKeyNameSize = ARRAYSIZE(subKeyName); + } + + key.Close(); + + return DeleteSubKey(pwszSubKey); +} + +//--------------------------------------------------------------------- +// +// DeleteValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::DeleteValue(_In_ LPCWSTR pwszValue) +{ + return RegDeleteValue(_keyHandle, pwszValue); +} + +//--------------------------------------------------------------------- +// +// QueryStingValue +// SetStringValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, + _Inout_ ULONG *pnChars) +{ + LONG res = 0; + DWORD dataType = REG_NONE; + ULONG pwszValueSize = 0; + + if (pnChars == nullptr) + { + return E_INVALIDARG; + } + + pwszValueSize = (*pnChars) * sizeof(WCHAR); + *pnChars = 0; + + res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, (LPBYTE)pwszValue, &pwszValueSize); + if (res != ERROR_SUCCESS) + { + return res; + } + if ((dataType != REG_SZ) && (dataType != REG_EXPAND_SZ)) + { + return ERROR_INVALID_DATA; + } + + *pnChars = pwszValueSize / sizeof(WCHAR); + + return ERROR_SUCCESS; +} + +LONG CRegKey::SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType) +{ + size_t lenOfValue = 0; + if (pwszValue == nullptr) + { + return ERROR_INVALID_PARAMETER; + } + + if (StringCchLength(pwszValue, STRSAFE_MAX_CCH, &lenOfValue) != S_OK) + { + return ERROR_INVALID_PARAMETER; + } + DWORD len = static_cast(lenOfValue); + return RegSetValueEx(_keyHandle, pwszValueName, NULL, dwType, (LPBYTE)pwszValue, (++len) * sizeof(WCHAR)); +} + +//--------------------------------------------------------------------- +// +// QueryDWORDValue +// SetDWORDValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue) +{ + LONG res = 0; + DWORD dataType = REG_NONE; + ULONG pwszValueSize = 0; + + pwszValueSize = sizeof(DWORD); + res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, (LPBYTE)(&dwValue), &pwszValueSize); + if (res != ERROR_SUCCESS) + { + return res; + } + if (dataType != REG_DWORD) + { + return ERROR_INVALID_DATA; + } + + return ERROR_SUCCESS; +} + +LONG CRegKey::SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue) +{ + return RegSetValueEx(_keyHandle, pwszValueName, NULL, REG_DWORD, (LPBYTE)(&dwValue), sizeof(DWORD)); +} + +//--------------------------------------------------------------------- +// +// QueryBinaryValue +// SetBinaryValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE *lpData, DWORD cbData) +{ + LONG res = 0; + DWORD dataType = REG_NONE; + ULONG pwszValueSize = 0; + + pwszValueSize = cbData; + res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, lpData, &pwszValueSize); + if (res != ERROR_SUCCESS) + { + return res; + } + if (dataType != REG_BINARY) + { + return ERROR_INVALID_DATA; + } + if (pwszValueSize != cbData) + { + return ERROR_INVALID_DATA; + } + + return ERROR_SUCCESS; +} + +LONG CRegKey::SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE *lpData, DWORD cbData) +{ + return RegSetValueEx(_keyHandle, pwszValueName, NULL, REG_BINARY, lpData, cbData); +} diff --git a/windows/src/Register/RegKey.h b/windows/src/Register/RegKey.h index 8f6d80e7..bafe968a 100644 --- a/windows/src/Register/RegKey.h +++ b/windows/src/Register/RegKey.h @@ -1,37 +1,37 @@ -#pragma once - -#include "Private.h" - -class CRegKey -{ - public: - CRegKey(); - ~CRegKey(); - - HKEY GetHKEY(); - - LONG Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, _In_reads_opt_(255) LPWSTR pwszClass = REG_NONE, - DWORD dwOptions = REG_OPTION_NON_VOLATILE, REGSAM samDesired = KEY_READ | KEY_WRITE, - _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr = nullptr, _Out_opt_ LPDWORD lpdwDisposition = nullptr); - - LONG Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, REGSAM samDesired = KEY_READ | KEY_WRITE); - - LONG Close(); - - LONG DeleteSubKey(_In_ LPCWSTR pwszSubKey); - LONG RecurseDeleteKey(_In_ LPCWSTR pwszSubKey); - LONG DeleteValue(_In_ LPCWSTR pwszValue); - - LONG QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, - _Inout_ ULONG *pnChars); - LONG SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType = REG_SZ); - - LONG QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue); - LONG SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue); - - LONG QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE *lpData, DWORD cbData); - LONG SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE *lpData, DWORD cbData); - - private: - HKEY _keyHandle; -}; +#pragma once + +#include "Private.h" + +class CRegKey +{ + public: + CRegKey(); + ~CRegKey(); + + HKEY GetHKEY(); + + LONG Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, _In_reads_opt_(255) LPWSTR pwszClass = REG_NONE, + DWORD dwOptions = REG_OPTION_NON_VOLATILE, REGSAM samDesired = KEY_READ | KEY_WRITE, + _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr = nullptr, _Out_opt_ LPDWORD lpdwDisposition = nullptr); + + LONG Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, REGSAM samDesired = KEY_READ | KEY_WRITE); + + LONG Close(); + + LONG DeleteSubKey(_In_ LPCWSTR pwszSubKey); + LONG RecurseDeleteKey(_In_ LPCWSTR pwszSubKey); + LONG DeleteValue(_In_ LPCWSTR pwszValue); + + LONG QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, + _Inout_ ULONG *pnChars); + LONG SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType = REG_SZ); + + LONG QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue); + LONG SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue); + + LONG QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE *lpData, DWORD cbData); + LONG SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE *lpData, DWORD cbData); + + private: + HKEY _keyHandle; +}; diff --git a/windows/src/Register/Register.cpp b/windows/src/Register/Register.cpp index c30887e5..070b05b1 100644 --- a/windows/src/Register/Register.cpp +++ b/windows/src/Register/Register.cpp @@ -1,289 +1,289 @@ -#ifndef UNICODE -#define UNICODE -#endif // !UNICODE - -#ifndef _UNICODE -#define _UNICODE -#endif // !UNICODE - -#include "Private.h" -#include "Globals.h" -#include "RegKey.h" - -static const WCHAR RegInfo_Prefix_CLSID[] = L"CLSID\\"; -static const WCHAR RegInfo_Key_InProSvr32[] = L"InProcServer32"; -static const WCHAR RegInfo_Key_ThreadModel[] = L"ThreadingModel"; - -// IME text service description, will be displayed in the language menu when switching IME -static const WCHAR TEXTSERVICE_DESC[] = L"Metasequoia \u6c34\u6749\u8f93\u5165\u6cd5"; - -static const GUID SupportCategories[] = { - GUID_TFCAT_TIP_KEYBOARD, - GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, - GUID_TFCAT_TIPCAP_UIELEMENTENABLED, - GUID_TFCAT_TIPCAP_SECUREMODE, - GUID_TFCAT_TIPCAP_COMLESS, - GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, - GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, - GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, -}; -//+--------------------------------------------------------------------------- -// -// RegisterProfiles -// -//---------------------------------------------------------------------------- - -BOOL RegisterProfiles() -{ - HRESULT hr = S_FALSE; - - ITfInputProcessorProfileMgr *pITfInputProcessorProfileMgr = nullptr; - hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfileMgr, - (void **)&pITfInputProcessorProfileMgr); - if (FAILED(hr)) - { - return FALSE; - } - - WCHAR achIconFile[MAX_PATH] = {'\0'}; - DWORD cchA = 0; - cchA = GetModuleFileName(Global::dllInstanceHandle, achIconFile, MAX_PATH); - cchA = cchA >= MAX_PATH ? (MAX_PATH - 1) : cchA; - achIconFile[cchA] = '\0'; - - size_t lenOfDesc = 0; - hr = StringCchLength(TEXTSERVICE_DESC, STRSAFE_MAX_CCH, &lenOfDesc); - if (hr != S_OK) - { - goto Exit; - } - hr = pITfInputProcessorProfileMgr->RegisterProfile( - Global::MetasequoiaIMECLSID, TEXTSERVICE_LANGID, Global::MetasequoiaIMEGuidProfile, TEXTSERVICE_DESC, - static_cast(lenOfDesc), achIconFile, cchA, (UINT)TEXTSERVICE_ICON_INDEX, NULL, 0, TRUE, 0); - - if (FAILED(hr)) - { - goto Exit; - } - -Exit: - if (pITfInputProcessorProfileMgr) - { - pITfInputProcessorProfileMgr->Release(); - } - - return (hr == S_OK); -} - -//+--------------------------------------------------------------------------- -// -// UnregisterProfiles -// -//---------------------------------------------------------------------------- - -void UnregisterProfiles() -{ - HRESULT hr = S_OK; - - ITfInputProcessorProfileMgr *pITfInputProcessorProfileMgr = nullptr; - hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfileMgr, - (void **)&pITfInputProcessorProfileMgr); - if (FAILED(hr)) - { - goto Exit; - } - - hr = pITfInputProcessorProfileMgr->UnregisterProfile(Global::MetasequoiaIMECLSID, TEXTSERVICE_LANGID, - Global::MetasequoiaIMEGuidProfile, 0); - if (FAILED(hr)) - { - goto Exit; - } - -Exit: - if (pITfInputProcessorProfileMgr) - { - pITfInputProcessorProfileMgr->Release(); - } - - return; -} - -//+--------------------------------------------------------------------------- -// -// RegisterCategories -// -//---------------------------------------------------------------------------- - -BOOL RegisterCategories() -{ - ITfCategoryMgr *pCategoryMgr = nullptr; - HRESULT hr = S_OK; - - hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void **)&pCategoryMgr); - if (FAILED(hr)) - { - return FALSE; - } - - // for each (GUID guid in SupportCategories) - for (const auto &guid : SupportCategories) - { - hr = pCategoryMgr->RegisterCategory(Global::MetasequoiaIMECLSID, guid, Global::MetasequoiaIMECLSID); - } - - pCategoryMgr->Release(); - - return (hr == S_OK); -} - -//+--------------------------------------------------------------------------- -// -// UnregisterCategories -// -//---------------------------------------------------------------------------- - -void UnregisterCategories() -{ - ITfCategoryMgr *pCategoryMgr = S_OK; - HRESULT hr = S_OK; - - hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void **)&pCategoryMgr); - if (FAILED(hr)) - { - return; - } - - // for each (GUID guid in SupportCategories) - for (const auto &guid : SupportCategories) - { - pCategoryMgr->UnregisterCategory(Global::MetasequoiaIMECLSID, guid, Global::MetasequoiaIMECLSID); - } - - pCategoryMgr->Release(); - - return; -} - -//+--------------------------------------------------------------------------- -// -// RecurseDeleteKey -// -// RecurseDeleteKey is necessary because on NT RegDeleteKey doesn't work if the -// specified key has subkeys -//---------------------------------------------------------------------------- - -LONG RecurseDeleteKey(_In_ HKEY hParentKey, _In_ LPCTSTR lpszKey) -{ - HKEY regKeyHandle = nullptr; - LONG res = 0; - FILETIME time; - WCHAR stringBuffer[256] = {'\0'}; - DWORD size = ARRAYSIZE(stringBuffer); - - if (RegOpenKey(hParentKey, lpszKey, ®KeyHandle) != ERROR_SUCCESS) - { - return ERROR_SUCCESS; - } - - res = ERROR_SUCCESS; - while (RegEnumKeyEx(regKeyHandle, 0, stringBuffer, &size, NULL, NULL, NULL, &time) == ERROR_SUCCESS) - { - stringBuffer[ARRAYSIZE(stringBuffer) - 1] = '\0'; - res = RecurseDeleteKey(regKeyHandle, stringBuffer); - if (res != ERROR_SUCCESS) - { - break; - } - size = ARRAYSIZE(stringBuffer); - } - RegCloseKey(regKeyHandle); - - return res == ERROR_SUCCESS ? RegDeleteKey(hParentKey, lpszKey) : res; -} - -//+--------------------------------------------------------------------------- -// -// RegisterServer -// -//---------------------------------------------------------------------------- - -BOOL RegisterServer() -{ - DWORD copiedStringLen = 0; - HKEY regKeyHandle = nullptr; - HKEY regSubkeyHandle = nullptr; - BOOL ret = FALSE; - WCHAR achIMEKey[ARRAYSIZE(RegInfo_Prefix_CLSID) + CLSID_STRLEN] = {'\0'}; - WCHAR achFileName[MAX_PATH] = {'\0'}; - - if (!CLSIDToString(Global::MetasequoiaIMECLSID, achIMEKey + ARRAYSIZE(RegInfo_Prefix_CLSID) - 1)) - { - return FALSE; - } - - memcpy(achIMEKey, RegInfo_Prefix_CLSID, sizeof(RegInfo_Prefix_CLSID) - sizeof(WCHAR)); - - if (RegCreateKeyEx(HKEY_CLASSES_ROOT, achIMEKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®KeyHandle, - &copiedStringLen) == ERROR_SUCCESS) - { - if (RegSetValueEx(regKeyHandle, NULL, 0, REG_SZ, (const BYTE *)TEXTSERVICE_DESC, - (_countof(TEXTSERVICE_DESC)) * sizeof(WCHAR)) != ERROR_SUCCESS) - { - goto Exit; - } - - if (RegCreateKeyEx(regKeyHandle, RegInfo_Key_InProSvr32, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, - ®SubkeyHandle, &copiedStringLen) == ERROR_SUCCESS) - { - copiedStringLen = GetModuleFileNameW(Global::dllInstanceHandle, achFileName, ARRAYSIZE(achFileName)); - copiedStringLen = (copiedStringLen >= (MAX_PATH - 1)) ? MAX_PATH : (++copiedStringLen); - if (RegSetValueEx(regSubkeyHandle, NULL, 0, REG_SZ, (const BYTE *)achFileName, - (copiedStringLen) * sizeof(WCHAR)) != ERROR_SUCCESS) - { - goto Exit; - } - if (RegSetValueEx(regSubkeyHandle, RegInfo_Key_ThreadModel, 0, REG_SZ, (const BYTE *)TEXTSERVICE_MODEL, - (_countof(TEXTSERVICE_MODEL)) * sizeof(WCHAR)) != ERROR_SUCCESS) - { - goto Exit; - } - - ret = TRUE; - } - } - -Exit: - if (regSubkeyHandle) - { - RegCloseKey(regSubkeyHandle); - regSubkeyHandle = nullptr; - } - if (regKeyHandle) - { - RegCloseKey(regKeyHandle); - regKeyHandle = nullptr; - } - - return ret; -} - -//+--------------------------------------------------------------------------- -// -// UnregisterServer -// -//---------------------------------------------------------------------------- - -void UnregisterServer() -{ - WCHAR achIMEKey[ARRAYSIZE(RegInfo_Prefix_CLSID) + CLSID_STRLEN] = {'\0'}; - - if (!CLSIDToString(Global::MetasequoiaIMECLSID, achIMEKey + ARRAYSIZE(RegInfo_Prefix_CLSID) - 1)) - { - return; - } - - memcpy(achIMEKey, RegInfo_Prefix_CLSID, sizeof(RegInfo_Prefix_CLSID) - sizeof(WCHAR)); - - RecurseDeleteKey(HKEY_CLASSES_ROOT, achIMEKey); -} +#ifndef UNICODE +#define UNICODE +#endif // !UNICODE + +#ifndef _UNICODE +#define _UNICODE +#endif // !UNICODE + +#include "Private.h" +#include "Globals.h" +#include "RegKey.h" + +static const WCHAR RegInfo_Prefix_CLSID[] = L"CLSID\\"; +static const WCHAR RegInfo_Key_InProSvr32[] = L"InProcServer32"; +static const WCHAR RegInfo_Key_ThreadModel[] = L"ThreadingModel"; + +// IME text service description, will be displayed in the language menu when switching IME +static const WCHAR TEXTSERVICE_DESC[] = L"Metasequoia \u6c34\u6749\u8f93\u5165\u6cd5"; + +static const GUID SupportCategories[] = { + GUID_TFCAT_TIP_KEYBOARD, + GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, + GUID_TFCAT_TIPCAP_UIELEMENTENABLED, + GUID_TFCAT_TIPCAP_SECUREMODE, + GUID_TFCAT_TIPCAP_COMLESS, + GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, + GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, + GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, +}; +//+--------------------------------------------------------------------------- +// +// RegisterProfiles +// +//---------------------------------------------------------------------------- + +BOOL RegisterProfiles() +{ + HRESULT hr = S_FALSE; + + ITfInputProcessorProfileMgr *pITfInputProcessorProfileMgr = nullptr; + hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfileMgr, + (void **)&pITfInputProcessorProfileMgr); + if (FAILED(hr)) + { + return FALSE; + } + + WCHAR achIconFile[MAX_PATH] = {'\0'}; + DWORD cchA = 0; + cchA = GetModuleFileName(Global::dllInstanceHandle, achIconFile, MAX_PATH); + cchA = cchA >= MAX_PATH ? (MAX_PATH - 1) : cchA; + achIconFile[cchA] = '\0'; + + size_t lenOfDesc = 0; + hr = StringCchLength(TEXTSERVICE_DESC, STRSAFE_MAX_CCH, &lenOfDesc); + if (hr != S_OK) + { + goto Exit; + } + hr = pITfInputProcessorProfileMgr->RegisterProfile( + Global::MetasequoiaIMECLSID, TEXTSERVICE_LANGID, Global::MetasequoiaIMEGuidProfile, TEXTSERVICE_DESC, + static_cast(lenOfDesc), achIconFile, cchA, (UINT)TEXTSERVICE_ICON_INDEX, NULL, 0, TRUE, 0); + + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + if (pITfInputProcessorProfileMgr) + { + pITfInputProcessorProfileMgr->Release(); + } + + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// UnregisterProfiles +// +//---------------------------------------------------------------------------- + +void UnregisterProfiles() +{ + HRESULT hr = S_OK; + + ITfInputProcessorProfileMgr *pITfInputProcessorProfileMgr = nullptr; + hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfileMgr, + (void **)&pITfInputProcessorProfileMgr); + if (FAILED(hr)) + { + goto Exit; + } + + hr = pITfInputProcessorProfileMgr->UnregisterProfile(Global::MetasequoiaIMECLSID, TEXTSERVICE_LANGID, + Global::MetasequoiaIMEGuidProfile, 0); + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + if (pITfInputProcessorProfileMgr) + { + pITfInputProcessorProfileMgr->Release(); + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// RegisterCategories +// +//---------------------------------------------------------------------------- + +BOOL RegisterCategories() +{ + ITfCategoryMgr *pCategoryMgr = nullptr; + HRESULT hr = S_OK; + + hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void **)&pCategoryMgr); + if (FAILED(hr)) + { + return FALSE; + } + + // for each (GUID guid in SupportCategories) + for (const auto &guid : SupportCategories) + { + hr = pCategoryMgr->RegisterCategory(Global::MetasequoiaIMECLSID, guid, Global::MetasequoiaIMECLSID); + } + + pCategoryMgr->Release(); + + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// UnregisterCategories +// +//---------------------------------------------------------------------------- + +void UnregisterCategories() +{ + ITfCategoryMgr *pCategoryMgr = S_OK; + HRESULT hr = S_OK; + + hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void **)&pCategoryMgr); + if (FAILED(hr)) + { + return; + } + + // for each (GUID guid in SupportCategories) + for (const auto &guid : SupportCategories) + { + pCategoryMgr->UnregisterCategory(Global::MetasequoiaIMECLSID, guid, Global::MetasequoiaIMECLSID); + } + + pCategoryMgr->Release(); + + return; +} + +//+--------------------------------------------------------------------------- +// +// RecurseDeleteKey +// +// RecurseDeleteKey is necessary because on NT RegDeleteKey doesn't work if the +// specified key has subkeys +//---------------------------------------------------------------------------- + +LONG RecurseDeleteKey(_In_ HKEY hParentKey, _In_ LPCTSTR lpszKey) +{ + HKEY regKeyHandle = nullptr; + LONG res = 0; + FILETIME time; + WCHAR stringBuffer[256] = {'\0'}; + DWORD size = ARRAYSIZE(stringBuffer); + + if (RegOpenKey(hParentKey, lpszKey, ®KeyHandle) != ERROR_SUCCESS) + { + return ERROR_SUCCESS; + } + + res = ERROR_SUCCESS; + while (RegEnumKeyEx(regKeyHandle, 0, stringBuffer, &size, NULL, NULL, NULL, &time) == ERROR_SUCCESS) + { + stringBuffer[ARRAYSIZE(stringBuffer) - 1] = '\0'; + res = RecurseDeleteKey(regKeyHandle, stringBuffer); + if (res != ERROR_SUCCESS) + { + break; + } + size = ARRAYSIZE(stringBuffer); + } + RegCloseKey(regKeyHandle); + + return res == ERROR_SUCCESS ? RegDeleteKey(hParentKey, lpszKey) : res; +} + +//+--------------------------------------------------------------------------- +// +// RegisterServer +// +//---------------------------------------------------------------------------- + +BOOL RegisterServer() +{ + DWORD copiedStringLen = 0; + HKEY regKeyHandle = nullptr; + HKEY regSubkeyHandle = nullptr; + BOOL ret = FALSE; + WCHAR achIMEKey[ARRAYSIZE(RegInfo_Prefix_CLSID) + CLSID_STRLEN] = {'\0'}; + WCHAR achFileName[MAX_PATH] = {'\0'}; + + if (!CLSIDToString(Global::MetasequoiaIMECLSID, achIMEKey + ARRAYSIZE(RegInfo_Prefix_CLSID) - 1)) + { + return FALSE; + } + + memcpy(achIMEKey, RegInfo_Prefix_CLSID, sizeof(RegInfo_Prefix_CLSID) - sizeof(WCHAR)); + + if (RegCreateKeyEx(HKEY_CLASSES_ROOT, achIMEKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®KeyHandle, + &copiedStringLen) == ERROR_SUCCESS) + { + if (RegSetValueEx(regKeyHandle, NULL, 0, REG_SZ, (const BYTE *)TEXTSERVICE_DESC, + (_countof(TEXTSERVICE_DESC)) * sizeof(WCHAR)) != ERROR_SUCCESS) + { + goto Exit; + } + + if (RegCreateKeyEx(regKeyHandle, RegInfo_Key_InProSvr32, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, + ®SubkeyHandle, &copiedStringLen) == ERROR_SUCCESS) + { + copiedStringLen = GetModuleFileNameW(Global::dllInstanceHandle, achFileName, ARRAYSIZE(achFileName)); + copiedStringLen = (copiedStringLen >= (MAX_PATH - 1)) ? MAX_PATH : (++copiedStringLen); + if (RegSetValueEx(regSubkeyHandle, NULL, 0, REG_SZ, (const BYTE *)achFileName, + (copiedStringLen) * sizeof(WCHAR)) != ERROR_SUCCESS) + { + goto Exit; + } + if (RegSetValueEx(regSubkeyHandle, RegInfo_Key_ThreadModel, 0, REG_SZ, (const BYTE *)TEXTSERVICE_MODEL, + (_countof(TEXTSERVICE_MODEL)) * sizeof(WCHAR)) != ERROR_SUCCESS) + { + goto Exit; + } + + ret = TRUE; + } + } + +Exit: + if (regSubkeyHandle) + { + RegCloseKey(regSubkeyHandle); + regSubkeyHandle = nullptr; + } + if (regKeyHandle) + { + RegCloseKey(regKeyHandle); + regKeyHandle = nullptr; + } + + return ret; +} + +//+--------------------------------------------------------------------------- +// +// UnregisterServer +// +//---------------------------------------------------------------------------- + +void UnregisterServer() +{ + WCHAR achIMEKey[ARRAYSIZE(RegInfo_Prefix_CLSID) + CLSID_STRLEN] = {'\0'}; + + if (!CLSIDToString(Global::MetasequoiaIMECLSID, achIMEKey + ARRAYSIZE(RegInfo_Prefix_CLSID) - 1)) + { + return; + } + + memcpy(achIMEKey, RegInfo_Prefix_CLSID, sizeof(RegInfo_Prefix_CLSID) - sizeof(WCHAR)); + + RecurseDeleteKey(HKEY_CLASSES_ROOT, achIMEKey); +} diff --git a/windows/src/Server.cpp b/windows/src/Server.cpp index 04ed9d6f..e55be39e 100644 --- a/windows/src/Server.cpp +++ b/windows/src/Server.cpp @@ -1,276 +1,276 @@ -#include "Private.h" -#include "Globals.h" -#include "MetasequoiaIME.h" - -// from Register.cpp -BOOL RegisterProfiles(); -void UnregisterProfiles(); -BOOL RegisterCategories(); -void UnregisterCategories(); -BOOL RegisterServer(); -void UnregisterServer(); - -void FreeGlobalObjects(void); - -class CClassFactory; -static CClassFactory *classFactoryObjects[1] = {nullptr}; - -//+--------------------------------------------------------------------------- -// -// DllAddRef -// -//---------------------------------------------------------------------------- - -void DllAddRef(void) -{ - InterlockedIncrement(&Global::dllRefCount); -} - -//+--------------------------------------------------------------------------- -// -// DllRelease -// -//---------------------------------------------------------------------------- - -void DllRelease(void) -{ - if (InterlockedDecrement(&Global::dllRefCount) < 0) - { - EnterCriticalSection(&Global::CS); - - if (nullptr != classFactoryObjects[0]) - { - FreeGlobalObjects(); - } - assert(Global::dllRefCount == -1); - - LeaveCriticalSection(&Global::CS); - } -} - -//+--------------------------------------------------------------------------- -// -// CClassFactory declaration with IClassFactory Interface -// -//---------------------------------------------------------------------------- - -class CClassFactory : public IClassFactory -{ - public: - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // IClassFactory methods - STDMETHODIMP CreateInstance(_In_opt_ IUnknown *pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void **ppvObj); - STDMETHODIMP LockServer(BOOL fLock); - - // Constructor - CClassFactory(REFCLSID rclsid, HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)) - : _rclsid(rclsid) - { - _pfnCreateInstance = pfnCreateInstance; - } - - public: - REFCLSID _rclsid; - HRESULT (*_pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid, _COM_Outptr_ void **ppvObj); - - private: - CClassFactory &operator=(const CClassFactory &rhn) - { - rhn; - }; -}; - -//+--------------------------------------------------------------------------- -// -// CClassFactory::QueryInterface -// -//---------------------------------------------------------------------------- - -STDAPI CClassFactory::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) - { - *ppvObj = this; - DllAddRef(); - return NOERROR; - } - *ppvObj = nullptr; - - return E_NOINTERFACE; -} - -//+--------------------------------------------------------------------------- -// -// CClassFactory::AddRef -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CClassFactory::AddRef() -{ - DllAddRef(); - return (Global::dllRefCount + 1); -} - -//+--------------------------------------------------------------------------- -// -// CClassFactory::Release -// -//---------------------------------------------------------------------------- - -STDAPI_(ULONG) CClassFactory::Release() -{ - DllRelease(); - return (Global::dllRefCount + 1); -} - -//+--------------------------------------------------------------------------- -// -// CClassFactory::CreateInstance -// -//---------------------------------------------------------------------------- - -STDAPI CClassFactory::CreateInstance(_In_opt_ IUnknown *pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void **ppvObj) -{ - return _pfnCreateInstance(pUnkOuter, riid, ppvObj); -} - -//+--------------------------------------------------------------------------- -// -// CClassFactory::LockServer -// -//---------------------------------------------------------------------------- - -STDAPI CClassFactory::LockServer(BOOL fLock) -{ - if (fLock) - { - DllAddRef(); - } - else - { - DllRelease(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// BuildGlobalObjects -// -//---------------------------------------------------------------------------- - -void BuildGlobalObjects(void) -{ - classFactoryObjects[0] = - new (std::nothrow) CClassFactory(Global::MetasequoiaIMECLSID, CMetasequoiaIME::CreateInstance); -} - -//+--------------------------------------------------------------------------- -// -// FreeGlobalObjects -// -//---------------------------------------------------------------------------- - -void FreeGlobalObjects(void) -{ - for (int i = 0; i < ARRAYSIZE(classFactoryObjects); i++) - { - if (nullptr != classFactoryObjects[i]) - { - delete classFactoryObjects[i]; - classFactoryObjects[i] = nullptr; - } - } - - DeleteObject(Global::defaultlFontHandle); -} - -//+--------------------------------------------------------------------------- -// -// DllGetClassObject -// -//---------------------------------------------------------------------------- -_Check_return_ STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ void **ppv) -{ - if (classFactoryObjects[0] == nullptr) - { - EnterCriticalSection(&Global::CS); - - // need to check ref again after grabbing mutex - if (classFactoryObjects[0] == nullptr) - { - BuildGlobalObjects(); - } - - LeaveCriticalSection(&Global::CS); - } - - if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) - { - for (int i = 0; i < ARRAYSIZE(classFactoryObjects); i++) - { - if (nullptr != classFactoryObjects[i] && IsEqualGUID(rclsid, classFactoryObjects[i]->_rclsid)) - { - *ppv = (void *)classFactoryObjects[i]; - DllAddRef(); // class factory holds DLL ref count - return NOERROR; - } - } - } - - *ppv = nullptr; - - return CLASS_E_CLASSNOTAVAILABLE; -} - -//+--------------------------------------------------------------------------- -// -// DllCanUnloadNow -// -//---------------------------------------------------------------------------- - -STDAPI DllCanUnloadNow(void) -{ - if (Global::dllRefCount >= 0) - { - return S_FALSE; - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// DllUnregisterServer -// -//---------------------------------------------------------------------------- - -STDAPI DllUnregisterServer(void) -{ - UnregisterProfiles(); - UnregisterCategories(); - UnregisterServer(); - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// DllRegisterServer -// -//---------------------------------------------------------------------------- - -STDAPI DllRegisterServer(void) -{ - if ((!RegisterServer()) || (!RegisterProfiles()) || (!RegisterCategories())) - { - DllUnregisterServer(); - return E_FAIL; - } - return S_OK; -} +#include "Private.h" +#include "Globals.h" +#include "MetasequoiaIME.h" + +// from Register.cpp +BOOL RegisterProfiles(); +void UnregisterProfiles(); +BOOL RegisterCategories(); +void UnregisterCategories(); +BOOL RegisterServer(); +void UnregisterServer(); + +void FreeGlobalObjects(void); + +class CClassFactory; +static CClassFactory *classFactoryObjects[1] = {nullptr}; + +//+--------------------------------------------------------------------------- +// +// DllAddRef +// +//---------------------------------------------------------------------------- + +void DllAddRef(void) +{ + InterlockedIncrement(&Global::dllRefCount); +} + +//+--------------------------------------------------------------------------- +// +// DllRelease +// +//---------------------------------------------------------------------------- + +void DllRelease(void) +{ + if (InterlockedDecrement(&Global::dllRefCount) < 0) + { + EnterCriticalSection(&Global::CS); + + if (nullptr != classFactoryObjects[0]) + { + FreeGlobalObjects(); + } + assert(Global::dllRefCount == -1); + + LeaveCriticalSection(&Global::CS); + } +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory declaration with IClassFactory Interface +// +//---------------------------------------------------------------------------- + +class CClassFactory : public IClassFactory +{ + public: + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // IClassFactory methods + STDMETHODIMP CreateInstance(_In_opt_ IUnknown *pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void **ppvObj); + STDMETHODIMP LockServer(BOOL fLock); + + // Constructor + CClassFactory(REFCLSID rclsid, HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)) + : _rclsid(rclsid) + { + _pfnCreateInstance = pfnCreateInstance; + } + + public: + REFCLSID _rclsid; + HRESULT (*_pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid, _COM_Outptr_ void **ppvObj); + + private: + CClassFactory &operator=(const CClassFactory &rhn) + { + rhn; + }; +}; + +//+--------------------------------------------------------------------------- +// +// CClassFactory::QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CClassFactory::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = this; + DllAddRef(); + return NOERROR; + } + *ppvObj = nullptr; + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CClassFactory::AddRef() +{ + DllAddRef(); + return (Global::dllRefCount + 1); +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CClassFactory::Release() +{ + DllRelease(); + return (Global::dllRefCount + 1); +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::CreateInstance +// +//---------------------------------------------------------------------------- + +STDAPI CClassFactory::CreateInstance(_In_opt_ IUnknown *pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void **ppvObj) +{ + return _pfnCreateInstance(pUnkOuter, riid, ppvObj); +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::LockServer +// +//---------------------------------------------------------------------------- + +STDAPI CClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + DllAddRef(); + } + else + { + DllRelease(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// BuildGlobalObjects +// +//---------------------------------------------------------------------------- + +void BuildGlobalObjects(void) +{ + classFactoryObjects[0] = + new (std::nothrow) CClassFactory(Global::MetasequoiaIMECLSID, CMetasequoiaIME::CreateInstance); +} + +//+--------------------------------------------------------------------------- +// +// FreeGlobalObjects +// +//---------------------------------------------------------------------------- + +void FreeGlobalObjects(void) +{ + for (int i = 0; i < ARRAYSIZE(classFactoryObjects); i++) + { + if (nullptr != classFactoryObjects[i]) + { + delete classFactoryObjects[i]; + classFactoryObjects[i] = nullptr; + } + } + + DeleteObject(Global::defaultlFontHandle); +} + +//+--------------------------------------------------------------------------- +// +// DllGetClassObject +// +//---------------------------------------------------------------------------- +_Check_return_ STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ void **ppv) +{ + if (classFactoryObjects[0] == nullptr) + { + EnterCriticalSection(&Global::CS); + + // need to check ref again after grabbing mutex + if (classFactoryObjects[0] == nullptr) + { + BuildGlobalObjects(); + } + + LeaveCriticalSection(&Global::CS); + } + + if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) + { + for (int i = 0; i < ARRAYSIZE(classFactoryObjects); i++) + { + if (nullptr != classFactoryObjects[i] && IsEqualGUID(rclsid, classFactoryObjects[i]->_rclsid)) + { + *ppv = (void *)classFactoryObjects[i]; + DllAddRef(); // class factory holds DLL ref count + return NOERROR; + } + } + } + + *ppv = nullptr; + + return CLASS_E_CLASSNOTAVAILABLE; +} + +//+--------------------------------------------------------------------------- +// +// DllCanUnloadNow +// +//---------------------------------------------------------------------------- + +STDAPI DllCanUnloadNow(void) +{ + if (Global::dllRefCount >= 0) + { + return S_FALSE; + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// DllUnregisterServer +// +//---------------------------------------------------------------------------- + +STDAPI DllUnregisterServer(void) +{ + UnregisterProfiles(); + UnregisterCategories(); + UnregisterServer(); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// DllRegisterServer +// +//---------------------------------------------------------------------------- + +STDAPI DllRegisterServer(void) +{ + if ((!RegisterServer()) || (!RegisterProfiles()) || (!RegisterCategories())) + { + DllUnregisterServer(); + return E_FAIL; + } + return S_OK; +} diff --git a/windows/src/Tf/TfInputProcessorProfile.cpp b/windows/src/Tf/TfInputProcessorProfile.cpp index f502e2a4..e17dd5c4 100644 --- a/windows/src/Tf/TfInputProcessorProfile.cpp +++ b/windows/src/Tf/TfInputProcessorProfile.cpp @@ -1,49 +1,49 @@ -#include "Private.h" -#include "TfInputProcessorProfile.h" - -CTfInputProcessorProfile::CTfInputProcessorProfile() -{ - _pInputProcessorProfile = nullptr; -} - -CTfInputProcessorProfile::~CTfInputProcessorProfile() -{ - if (_pInputProcessorProfile) - { - _pInputProcessorProfile->Release(); - _pInputProcessorProfile = nullptr; - } -} - -HRESULT CTfInputProcessorProfile::CreateInstance() -{ - HRESULT hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_INPROC_SERVER, - IID_ITfInputProcessorProfiles, (void **)&_pInputProcessorProfile); - - return hr; -} - -HRESULT CTfInputProcessorProfile::GetCurrentLanguage(_Out_ LANGID *plangid) -{ - if (_pInputProcessorProfile) - { - return _pInputProcessorProfile->GetCurrentLanguage(plangid); - } - else - { - return E_FAIL; - } -} - -HRESULT CTfInputProcessorProfile::GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, - _Out_ GUID *pguidProfile) -{ - if (_pInputProcessorProfile) - { - return _pInputProcessorProfile->GetDefaultLanguageProfile(langid, catid, pclsid, pguidProfile); - } - else - { - return E_FAIL; - } -} +#include "Private.h" +#include "TfInputProcessorProfile.h" + +CTfInputProcessorProfile::CTfInputProcessorProfile() +{ + _pInputProcessorProfile = nullptr; +} + +CTfInputProcessorProfile::~CTfInputProcessorProfile() +{ + if (_pInputProcessorProfile) + { + _pInputProcessorProfile->Release(); + _pInputProcessorProfile = nullptr; + } +} + +HRESULT CTfInputProcessorProfile::CreateInstance() +{ + HRESULT hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_INPROC_SERVER, + IID_ITfInputProcessorProfiles, (void **)&_pInputProcessorProfile); + + return hr; +} + +HRESULT CTfInputProcessorProfile::GetCurrentLanguage(_Out_ LANGID *plangid) +{ + if (_pInputProcessorProfile) + { + return _pInputProcessorProfile->GetCurrentLanguage(plangid); + } + else + { + return E_FAIL; + } +} + +HRESULT CTfInputProcessorProfile::GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, + _Out_ GUID *pguidProfile) +{ + if (_pInputProcessorProfile) + { + return _pInputProcessorProfile->GetDefaultLanguageProfile(langid, catid, pclsid, pguidProfile); + } + else + { + return E_FAIL; + } +} diff --git a/windows/src/Tf/TfInputProcessorProfile.h b/windows/src/Tf/TfInputProcessorProfile.h index 77fb7461..b2db9823 100644 --- a/windows/src/Tf/TfInputProcessorProfile.h +++ b/windows/src/Tf/TfInputProcessorProfile.h @@ -1,17 +1,17 @@ -#pragma once - -#include "Private.h" - -class CTfInputProcessorProfile -{ - public: - CTfInputProcessorProfile(); - ~CTfInputProcessorProfile(); - - HRESULT CreateInstance(); - HRESULT GetCurrentLanguage(_Out_ LANGID *plangid); - HRESULT GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, _Out_ GUID *pguidProfile); - - private: - ITfInputProcessorProfiles *_pInputProcessorProfile; -}; +#pragma once + +#include "Private.h" + +class CTfInputProcessorProfile +{ + public: + CTfInputProcessorProfile(); + ~CTfInputProcessorProfile(); + + HRESULT CreateInstance(); + HRESULT GetCurrentLanguage(_Out_ LANGID *plangid); + HRESULT GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, _Out_ GUID *pguidProfile); + + private: + ITfInputProcessorProfiles *_pInputProcessorProfile; +}; diff --git a/windows/src/Tf/TfTextLayoutSink.cpp b/windows/src/Tf/TfTextLayoutSink.cpp index 9b6568e6..2c9106e1 100644 --- a/windows/src/Tf/TfTextLayoutSink.cpp +++ b/windows/src/Tf/TfTextLayoutSink.cpp @@ -1,300 +1,300 @@ -#include "FanyDefines.h" -#include "Private.h" -#include "TfTextLayoutSink.h" -#include "MetasequoiaIME.h" -#include "GetTextExtentEditSession.h" -#include -#include -#include "../Utils/PerfTimer.h" - -POINT GetPhysicalTextAnchor(_In_ ITfContextView *pContextView, _In_ const RECT &textExtent) -{ - POINT anchor = {textExtent.left, textExtent.bottom}; - HWND hostWindow = nullptr; - if (SUCCEEDED(pContextView->GetWnd(&hostWindow)) && hostWindow) - { - hostWindow = GetAncestor(hostWindow, GA_ROOT); - POINT physicalAnchor = anchor; - if (hostWindow && LogicalToPhysicalPointForPerMonitorDPI(hostWindow, &physicalAnchor)) - { - anchor = physicalAnchor; - } - } - return anchor; -} - -CTfTextLayoutSink::CTfTextLayoutSink(_In_ CMetasequoiaIME *pTextService) -{ - _pTextService = pTextService; - _pTextService->AddRef(); - - _pRangeComposition = nullptr; - _pContextDocument = nullptr; - _tfEditCookie = TF_INVALID_EDIT_COOKIE; - - _dwCookieTextLayoutSink = TF_INVALID_COOKIE; - _hasValidAnchor = false; - - _refCount = 1; - - DllAddRef(); -} - -CTfTextLayoutSink::~CTfTextLayoutSink() -{ - if (_pTextService) - { - _pTextService->Release(); - } - - DllRelease(); -} - -STDAPI CTfTextLayoutSink::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) -{ - if (ppvObj == nullptr) - { - return E_INVALIDARG; - } - - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfTextLayoutSink)) - { - *ppvObj = (ITfTextLayoutSink *)this; - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -STDAPI_(ULONG) CTfTextLayoutSink::AddRef() -{ - return ++_refCount; -} - -STDAPI_(ULONG) CTfTextLayoutSink::Release() -{ - LONG cr = --_refCount; - - assert(_refCount >= 0); - - if (_refCount == 0) - { - delete this; - } - - return cr; -} - -//+--------------------------------------------------------------------------- -// -// ITfTextLayoutSink::OnLayoutChange -// -//---------------------------------------------------------------------------- - -STDAPI CTfTextLayoutSink::OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, - _In_ ITfContextView *pContextView) -{ - PerfTimer timer; - const bool isDocumentContext = (pContext == _pContextDocument); - - // we're interested in only document context. - if (pContext != _pContextDocument) - { - return S_OK; - } - - double requestEditSessionElapsedMs = 0; - switch (lcode) - { - case TF_LC_CREATE: { - } - case TF_LC_CHANGE: { - CGetTextExtentEditSession *pEditSession = nullptr; - pEditSession = new (std::nothrow) - CGetTextExtentEditSession(_pTextService, pContext, pContextView, _pRangeComposition, this); - if (nullptr != (pEditSession)) - { - HRESULT hr = S_OK; - PerfTimer requestTimer; - pContext->RequestEditSession(_pTextService->_GetClientId(), pEditSession, TF_ES_SYNC | TF_ES_READ, &hr); - requestEditSessionElapsedMs = requestTimer.ElapsedMs(); - pEditSession->Release(); - } - } - break; - - case TF_LC_DESTROY: - _LayoutDestroyNotification(); - break; - } - return S_OK; -} - -HRESULT CTfTextLayoutSink::_StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, - _In_ ITfRange *pRangeComposition) -{ - PerfTimer timer; - _pContextDocument = pContextDocument; - _pContextDocument->AddRef(); - - _pRangeComposition = pRangeComposition; - _pRangeComposition->AddRef(); - - _tfEditCookie = ec; - _hasValidAnchor = false; - HRESULT hr = _AdviseTextLayoutSink(); - return hr; -} - -VOID CTfTextLayoutSink::_EndLayout() -{ - PerfTimer timer; - const bool hadRangeComposition = (_pRangeComposition != nullptr); - const bool hadContextDocument = (_pContextDocument != nullptr); - HRESULT unadviseHr = S_OK; - _hasValidAnchor = false; - - if (_pRangeComposition) - { - _pRangeComposition->Release(); - _pRangeComposition = nullptr; - } - - if (_pContextDocument) - { - unadviseHr = _UnadviseTextLayoutSink(); - _pContextDocument->Release(); - _pContextDocument = nullptr; - } -} - -HRESULT CTfTextLayoutSink::_AdviseTextLayoutSink() -{ - PerfTimer timer; - HRESULT hr = S_OK; - ITfSource *pSource = nullptr; - - hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); - if (FAILED(hr)) - { - return hr; - } - - hr = pSource->AdviseSink(IID_ITfTextLayoutSink, (ITfTextLayoutSink *)this, &_dwCookieTextLayoutSink); - if (FAILED(hr)) - { - pSource->Release(); - return hr; - } - - pSource->Release(); - - return hr; -} - -HRESULT CTfTextLayoutSink::_UnadviseTextLayoutSink() -{ - PerfTimer timer; - HRESULT hr = S_OK; - ITfSource *pSource = nullptr; - - if (nullptr == _pContextDocument) - { - return E_FAIL; - } - - hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); - if (FAILED(hr)) - { - return hr; - } - - hr = pSource->UnadviseSink(_dwCookieTextLayoutSink); - if (FAILED(hr)) - { - pSource->Release(); - return hr; - } - - pSource->Release(); - _dwCookieTextLayoutSink = TF_INVALID_COOKIE; - - return hr; -} - -/** - * @brief 获取 caret 的坐标 - * - * 本质上是通过 _pContextView->GetTextExt 获取 caret 坐标, - * 因此,涉及到 caret 坐标的地方不止这里,还有其他地方,是直接 - * 使用 _pContextView->GetTextExt 来获取坐标的。 - * - * @param lpRect - * @return HRESULT - */ -HRESULT CTfTextLayoutSink::_GetTextExt(_Out_ RECT *lpRect, _Out_ POINT *lpAnchor) -{ - PerfTimer timer; - HRESULT hr = S_OK; - BOOL isClipped = TRUE; - ITfContextView *pContextView = nullptr; - - hr = _pContextDocument->GetActiveView(&pContextView); - if (FAILED(hr)) - { - return hr; - } - - hr = pContextView->GetTextExt(_tfEditCookie, _pRangeComposition, lpRect, &isClipped); - - // TS_E_NOLAYOUT means "the host has not laid this range out yet, ask again", - // not "the caret is gone". Chromium-based hosts (Electron apps, browsers) - // answer it routinely while their renderer is behind, and far more often - // while the machine is busy. Adopting the off-screen sentinel here parks the - // candidate window at INVALID_Y for one frame and the next measurement pulls - // it back — that is the flicker seen under load. Report the failure so the - // caller keeps the anchor it already has; TF_LC_CHANGE delivers the real - // position as soon as layout completes. Only do this once this layout - // session has produced a good anchor, so composition start still falls back - // to the sentinel rather than reusing a stale one from another document. - if (hr == TS_E_NOLAYOUT && _hasValidAnchor) - { - pContextView->Release(); - if (Global::TsfDiagnosticLogEnabled.load(std::memory_order_relaxed)) - { - QueueTsfDiagnosticLog(L"[candidate-layout] kept last anchor across transient TS_E_NOLAYOUT"); - } - return hr; - } - - if (FAILED(hr)) - { - // Set default value to make sure the window is hidden by moving it out of the screen - lpRect->left = 0; - lpRect->bottom = Global::INVALID_Y; - *lpAnchor = {0, Global::INVALID_Y}; - } - else if (isClipped && lpRect && (lpRect->right <= lpRect->left || lpRect->bottom <= lpRect->top)) - { - // Office often reports isClipped=TRUE with a still-usable rect; only reject - // degenerate clipped extents that would anchor the candidate off-screen. - lpRect->left = 0; - lpRect->bottom = Global::INVALID_Y; - *lpAnchor = {0, Global::INVALID_Y}; - } - else - { - *lpAnchor = GetPhysicalTextAnchor(pContextView, *lpRect); - _hasValidAnchor = true; - } - pContextView->Release(); - - return S_OK; -} +#include "FanyDefines.h" +#include "Private.h" +#include "TfTextLayoutSink.h" +#include "MetasequoiaIME.h" +#include "GetTextExtentEditSession.h" +#include +#include +#include "../Utils/PerfTimer.h" + +POINT GetPhysicalTextAnchor(_In_ ITfContextView *pContextView, _In_ const RECT &textExtent) +{ + POINT anchor = {textExtent.left, textExtent.bottom}; + HWND hostWindow = nullptr; + if (SUCCEEDED(pContextView->GetWnd(&hostWindow)) && hostWindow) + { + hostWindow = GetAncestor(hostWindow, GA_ROOT); + POINT physicalAnchor = anchor; + if (hostWindow && LogicalToPhysicalPointForPerMonitorDPI(hostWindow, &physicalAnchor)) + { + anchor = physicalAnchor; + } + } + return anchor; +} + +CTfTextLayoutSink::CTfTextLayoutSink(_In_ CMetasequoiaIME *pTextService) +{ + _pTextService = pTextService; + _pTextService->AddRef(); + + _pRangeComposition = nullptr; + _pContextDocument = nullptr; + _tfEditCookie = TF_INVALID_EDIT_COOKIE; + + _dwCookieTextLayoutSink = TF_INVALID_COOKIE; + _hasValidAnchor = false; + + _refCount = 1; + + DllAddRef(); +} + +CTfTextLayoutSink::~CTfTextLayoutSink() +{ + if (_pTextService) + { + _pTextService->Release(); + } + + DllRelease(); +} + +STDAPI CTfTextLayoutSink::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfTextLayoutSink)) + { + *ppvObj = (ITfTextLayoutSink *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDAPI_(ULONG) CTfTextLayoutSink::AddRef() +{ + return ++_refCount; +} + +STDAPI_(ULONG) CTfTextLayoutSink::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// ITfTextLayoutSink::OnLayoutChange +// +//---------------------------------------------------------------------------- + +STDAPI CTfTextLayoutSink::OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, + _In_ ITfContextView *pContextView) +{ + PerfTimer timer; + const bool isDocumentContext = (pContext == _pContextDocument); + + // we're interested in only document context. + if (pContext != _pContextDocument) + { + return S_OK; + } + + double requestEditSessionElapsedMs = 0; + switch (lcode) + { + case TF_LC_CREATE: { + } + case TF_LC_CHANGE: { + CGetTextExtentEditSession *pEditSession = nullptr; + pEditSession = new (std::nothrow) + CGetTextExtentEditSession(_pTextService, pContext, pContextView, _pRangeComposition, this); + if (nullptr != (pEditSession)) + { + HRESULT hr = S_OK; + PerfTimer requestTimer; + pContext->RequestEditSession(_pTextService->_GetClientId(), pEditSession, TF_ES_SYNC | TF_ES_READ, &hr); + requestEditSessionElapsedMs = requestTimer.ElapsedMs(); + pEditSession->Release(); + } + } + break; + + case TF_LC_DESTROY: + _LayoutDestroyNotification(); + break; + } + return S_OK; +} + +HRESULT CTfTextLayoutSink::_StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, + _In_ ITfRange *pRangeComposition) +{ + PerfTimer timer; + _pContextDocument = pContextDocument; + _pContextDocument->AddRef(); + + _pRangeComposition = pRangeComposition; + _pRangeComposition->AddRef(); + + _tfEditCookie = ec; + _hasValidAnchor = false; + HRESULT hr = _AdviseTextLayoutSink(); + return hr; +} + +VOID CTfTextLayoutSink::_EndLayout() +{ + PerfTimer timer; + const bool hadRangeComposition = (_pRangeComposition != nullptr); + const bool hadContextDocument = (_pContextDocument != nullptr); + HRESULT unadviseHr = S_OK; + _hasValidAnchor = false; + + if (_pRangeComposition) + { + _pRangeComposition->Release(); + _pRangeComposition = nullptr; + } + + if (_pContextDocument) + { + unadviseHr = _UnadviseTextLayoutSink(); + _pContextDocument->Release(); + _pContextDocument = nullptr; + } +} + +HRESULT CTfTextLayoutSink::_AdviseTextLayoutSink() +{ + PerfTimer timer; + HRESULT hr = S_OK; + ITfSource *pSource = nullptr; + + hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); + if (FAILED(hr)) + { + return hr; + } + + hr = pSource->AdviseSink(IID_ITfTextLayoutSink, (ITfTextLayoutSink *)this, &_dwCookieTextLayoutSink); + if (FAILED(hr)) + { + pSource->Release(); + return hr; + } + + pSource->Release(); + + return hr; +} + +HRESULT CTfTextLayoutSink::_UnadviseTextLayoutSink() +{ + PerfTimer timer; + HRESULT hr = S_OK; + ITfSource *pSource = nullptr; + + if (nullptr == _pContextDocument) + { + return E_FAIL; + } + + hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); + if (FAILED(hr)) + { + return hr; + } + + hr = pSource->UnadviseSink(_dwCookieTextLayoutSink); + if (FAILED(hr)) + { + pSource->Release(); + return hr; + } + + pSource->Release(); + _dwCookieTextLayoutSink = TF_INVALID_COOKIE; + + return hr; +} + +/** + * @brief 获取 caret 的坐标 + * + * 本质上是通过 _pContextView->GetTextExt 获取 caret 坐标, + * 因此,涉及到 caret 坐标的地方不止这里,还有其他地方,是直接 + * 使用 _pContextView->GetTextExt 来获取坐标的。 + * + * @param lpRect + * @return HRESULT + */ +HRESULT CTfTextLayoutSink::_GetTextExt(_Out_ RECT *lpRect, _Out_ POINT *lpAnchor) +{ + PerfTimer timer; + HRESULT hr = S_OK; + BOOL isClipped = TRUE; + ITfContextView *pContextView = nullptr; + + hr = _pContextDocument->GetActiveView(&pContextView); + if (FAILED(hr)) + { + return hr; + } + + hr = pContextView->GetTextExt(_tfEditCookie, _pRangeComposition, lpRect, &isClipped); + + // TS_E_NOLAYOUT means "the host has not laid this range out yet, ask again", + // not "the caret is gone". Chromium-based hosts (Electron apps, browsers) + // answer it routinely while their renderer is behind, and far more often + // while the machine is busy. Adopting the off-screen sentinel here parks the + // candidate window at INVALID_Y for one frame and the next measurement pulls + // it back — that is the flicker seen under load. Report the failure so the + // caller keeps the anchor it already has; TF_LC_CHANGE delivers the real + // position as soon as layout completes. Only do this once this layout + // session has produced a good anchor, so composition start still falls back + // to the sentinel rather than reusing a stale one from another document. + if (hr == TS_E_NOLAYOUT && _hasValidAnchor) + { + pContextView->Release(); + if (Global::TsfDiagnosticLogEnabled.load(std::memory_order_relaxed)) + { + QueueTsfDiagnosticLog(L"[candidate-layout] kept last anchor across transient TS_E_NOLAYOUT"); + } + return hr; + } + + if (FAILED(hr)) + { + // Set default value to make sure the window is hidden by moving it out of the screen + lpRect->left = 0; + lpRect->bottom = Global::INVALID_Y; + *lpAnchor = {0, Global::INVALID_Y}; + } + else if (isClipped && lpRect && (lpRect->right <= lpRect->left || lpRect->bottom <= lpRect->top)) + { + // Office often reports isClipped=TRUE with a still-usable rect; only reject + // degenerate clipped extents that would anchor the candidate off-screen. + lpRect->left = 0; + lpRect->bottom = Global::INVALID_Y; + *lpAnchor = {0, Global::INVALID_Y}; + } + else + { + *lpAnchor = GetPhysicalTextAnchor(pContextView, *lpRect); + _hasValidAnchor = true; + } + pContextView->Release(); + + return S_OK; +} diff --git a/windows/src/Tf/TfTextLayoutSink.h b/windows/src/Tf/TfTextLayoutSink.h index 8e714205..0e8b960b 100644 --- a/windows/src/Tf/TfTextLayoutSink.h +++ b/windows/src/Tf/TfTextLayoutSink.h @@ -1,55 +1,55 @@ -#pragma once - -#include "Private.h" - -class CMetasequoiaIME; - -POINT GetPhysicalTextAnchor(_In_ ITfContextView *pContextView, _In_ const RECT &textExtent); - -class CTfTextLayoutSink : public ITfTextLayoutSink -{ - public: - CTfTextLayoutSink(_In_ CMetasequoiaIME *pTextService); - virtual ~CTfTextLayoutSink(); - - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfTextLayoutSink - STDMETHODIMP OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, _In_ ITfContextView *pContextView); - - HRESULT _StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition); - VOID _EndLayout(); - - HRESULT _GetTextExt(_Out_ RECT *lpRect, _Out_ POINT *lpAnchor); - ITfContext *_GetContextDocument() - { - return _pContextDocument; - }; - - // Callers that read the caret straight from ITfContextView (the edit-session - // path) report their success here, so _GetTextExt knows whether Global::Point - // already holds a usable anchor for this layout session. - void _MarkAnchorValid() - { - _hasValidAnchor = true; - } - - virtual VOID _LayoutChangeNotification(_In_ RECT *lpRect) = 0; - virtual VOID _LayoutDestroyNotification() = 0; - - private: - HRESULT _AdviseTextLayoutSink(); - HRESULT _UnadviseTextLayoutSink(); - - private: - ITfRange *_pRangeComposition; - ITfContext *_pContextDocument; - TfEditCookie _tfEditCookie; - CMetasequoiaIME *_pTextService; - DWORD _dwCookieTextLayoutSink; - LONG _refCount; - bool _hasValidAnchor; -}; +#pragma once + +#include "Private.h" + +class CMetasequoiaIME; + +POINT GetPhysicalTextAnchor(_In_ ITfContextView *pContextView, _In_ const RECT &textExtent); + +class CTfTextLayoutSink : public ITfTextLayoutSink +{ + public: + CTfTextLayoutSink(_In_ CMetasequoiaIME *pTextService); + virtual ~CTfTextLayoutSink(); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfTextLayoutSink + STDMETHODIMP OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, _In_ ITfContextView *pContextView); + + HRESULT _StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition); + VOID _EndLayout(); + + HRESULT _GetTextExt(_Out_ RECT *lpRect, _Out_ POINT *lpAnchor); + ITfContext *_GetContextDocument() + { + return _pContextDocument; + }; + + // Callers that read the caret straight from ITfContextView (the edit-session + // path) report their success here, so _GetTextExt knows whether Global::Point + // already holds a usable anchor for this layout session. + void _MarkAnchorValid() + { + _hasValidAnchor = true; + } + + virtual VOID _LayoutChangeNotification(_In_ RECT *lpRect) = 0; + virtual VOID _LayoutDestroyNotification() = 0; + + private: + HRESULT _AdviseTextLayoutSink(); + HRESULT _UnadviseTextLayoutSink(); + + private: + ITfRange *_pRangeComposition; + ITfContext *_pContextDocument; + TfEditCookie _tfEditCookie; + CMetasequoiaIME *_pTextService; + DWORD _dwCookieTextLayoutSink; + LONG _refCount; + bool _hasValidAnchor; +}; diff --git a/windows/src/Utils/FanyUtils.h b/windows/src/Utils/FanyUtils.h index 522ed022..bf869289 100644 --- a/windows/src/Utils/FanyUtils.h +++ b/windows/src/Utils/FanyUtils.h @@ -1,34 +1,34 @@ -#pragma once -#include -#include - -namespace FanyUtils -{ -std::string GetIMEDataDirPath(); -void SendKeys(std::wstring pinyin); -std::wstring string_to_wstring(const std::string &str); -std::string wstring_to_string(const std::wstring &wstr); -std::string to_lower_copy(const std::string &str); -std::wstring GetCurrentProcessName(); -std::string::size_type count_utf8_chars(const std::string &str); -// Read input.default_ime_mode from %LOCALAPPDATA%\metasequoiaime\config.toml. -// Returns TRUE for Chinese (default), FALSE for English. -BOOL ReadConfiguredDefaultImeModeChinese(); -// Read input.mode from the shared config. TRUE when Japanese input is active. -BOOL ReadConfiguredJapaneseInputMode(); -// Read input.punctuation_lock from shared config.toml. -// 0 = follow IME, 1 = always Chinese punctuation, 2 = always English punctuation. -int ReadConfiguredPunctuationLock(); -// Refresh the in-process lock cache from config.toml. -void RefreshPunctuationLockFromConfig(); - -struct SwitchLanguageHotkeys -{ - bool shift = true; - bool ctrl = false; - bool ctrl_alt_space = true; - bool character_set_ctrl_shift_f = true; -}; -// Read keybindings.switch_language_* from shared config.toml. -SwitchLanguageHotkeys ReadConfiguredSwitchLanguageHotkeys(); -} // namespace FanyUtils +#pragma once +#include +#include + +namespace FanyUtils +{ +std::string GetIMEDataDirPath(); +void SendKeys(std::wstring pinyin); +std::wstring string_to_wstring(const std::string &str); +std::string wstring_to_string(const std::wstring &wstr); +std::string to_lower_copy(const std::string &str); +std::wstring GetCurrentProcessName(); +std::string::size_type count_utf8_chars(const std::string &str); +// Read input.default_ime_mode from %LOCALAPPDATA%\metasequoiaime\config.toml. +// Returns TRUE for Chinese (default), FALSE for English. +BOOL ReadConfiguredDefaultImeModeChinese(); +// Read input.mode from the shared config. TRUE when Japanese input is active. +BOOL ReadConfiguredJapaneseInputMode(); +// Read input.punctuation_lock from shared config.toml. +// 0 = follow IME, 1 = always Chinese punctuation, 2 = always English punctuation. +int ReadConfiguredPunctuationLock(); +// Refresh the in-process lock cache from config.toml. +void RefreshPunctuationLockFromConfig(); + +struct SwitchLanguageHotkeys +{ + bool shift = true; + bool ctrl = false; + bool ctrl_alt_space = true; + bool character_set_ctrl_shift_f = true; +}; +// Read keybindings.switch_language_* from shared config.toml. +SwitchLanguageHotkeys ReadConfiguredSwitchLanguageHotkeys(); +} // namespace FanyUtils