From 997fdfd9cb27ebbf3a8f998cdefae3274eb5deb9 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 10 Sep 2026 12:04:23 +0900 Subject: [PATCH] chore: settle line endings in .gitattributes (#310) * chore: settle line endings in .gitattributes Nothing declared line endings, so each file kept whatever the contributor's core.autocrlf produced when it was added: 382 text files ended up LF and 50 CRLF. The CRLF ones are the hazard. Editing one on macOS or Linux writes LF lines into it, and clang-format rejects a file that mixes both -- which is how `quality / Workflow validation` went red on KeyStateCategory.h, where three lines out of a CRLF file had arrived as LF. #309 converted those three lines back and left the other 49 files loaded. The repository now declares LF and the working trees are renormalized to match, so the failure mode is gone rather than deferred to whichever of those files is edited next. The four binary types are declared rather than left to git's heuristic, which decides from the first 8000 bytes alone. user_dict.dat is caught by that sample today only because the 40-byte seed is almost entirely NUL. Verified: the 56 renormalized files are byte-identical to their old contents once CR is stripped, no text file in the index still carries CRLF, and scripts/format.sh --check passes. * chore: skip the line-ending renormalization in blame The commit before this one rewrote every line of 56 files without changing a character of them, which is what this file exists to hide. --- .git-blame-ignore-revs | 3 + .gitattributes | 14 + windows/.clang-format | 12 +- windows/.vscode/settings.json | 18 +- windows/Dictionary/.gitattributes | 6 +- windows/assets/helpcode.txt | 16516 ++++++++-------- windows/scripts/lcompile.ps1 | 130 +- windows/src/Candidate/EnumTfCandidates.cpp | 244 +- windows/src/Candidate/EnumTfCandidates.h | 66 +- .../src/Candidate/SearchCandidateProvider.cpp | 384 +- .../src/Candidate/SearchCandidateProvider.h | 88 +- windows/src/Candidate/TipCandidateList.cpp | 340 +- windows/src/Candidate/TipCandidateList.h | 70 +- windows/src/Candidate/TipCandidateString.cpp | 210 +- windows/src/Candidate/TipCandidateString.h | 62 +- windows/src/Compartment/Compartment.cpp | 726 +- windows/src/Compartment/Compartment.h | 112 +- windows/src/Composition/Composition.cpp | 1894 +- .../Composition/CompositionProcessorEngine.h | 580 +- windows/src/Composition/StartComposition.cpp | 258 +- .../src/DisplayAttribute/DisplayAttribute.cpp | 272 +- .../DisplayAttribute/DisplayAttributeInfo.cpp | 436 +- .../DisplayAttribute/DisplayAttributeInfo.h | 164 +- .../DisplayAttributeProvider.cpp | 148 +- .../EnumDisplayAttributeInfo.cpp | 436 +- .../EnumDisplayAttributeInfo.h | 64 +- windows/src/DllMain.cpp | 86 +- windows/src/Edit/EditSession.cpp | 182 +- windows/src/Edit/EditSession.h | 54 +- windows/src/Edit/GetTextExtentEditSession.cpp | 166 +- windows/src/Edit/GetTextExtentEditSession.h | 72 +- windows/src/Global/Globals.cpp | 1070 +- windows/src/Global/Globals.h | 340 +- windows/src/Header/Define.h | 144 +- windows/src/Header/Private.h | 30 +- windows/src/Header/resource.h | 70 +- windows/src/Header/stdafx.h | 4 +- .../IME/ActiveLanguageProfileNotifySink.cpp | 250 +- windows/src/IME/FunctionProviderSink.cpp | 110 +- windows/src/IME/MetasequoiaIME.def | 14 +- windows/src/IME/MetasequoiaIME.h | 1214 +- windows/src/IME/MetasequoiaIME.rc | 256 +- .../src/IME/MetasequoiaIMEBaseStructure.cpp | 732 +- windows/src/IME/MetasequoiaIMEBaseStructure.h | 568 +- .../src/IME/MetasequoiaIMEStructureArray.h | 164 +- windows/src/Key/KeyStateCategory.cpp | 806 +- windows/src/Key/KeyStateCategory.h | 548 +- windows/src/LanguageBar/LanguageBar.cpp | 1748 +- windows/src/LanguageBar/LanguageBar.h | 148 +- windows/src/Register/RegKey.cpp | 562 +- windows/src/Register/RegKey.h | 74 +- windows/src/Register/Register.cpp | 578 +- windows/src/Server.cpp | 552 +- windows/src/Tf/TfInputProcessorProfile.cpp | 98 +- windows/src/Tf/TfInputProcessorProfile.h | 34 +- windows/src/Tf/TfTextLayoutSink.cpp | 600 +- windows/src/Tf/TfTextLayoutSink.h | 110 +- windows/src/Utils/FanyUtils.h | 68 +- 58 files changed, 17361 insertions(+), 17344 deletions(-) create mode 100644 .gitattributes 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