欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]自己动手修改mac百度输入法的一个问题

[原创]自己动手修改mac百度输入法的一个问题

Mac百度输入法修正过程
在苹果系统上,百度输入法还算是不错的。由于个人原因,面对电脑的主要时间还是在输入代码,所以主要输入的还是英文,而不是中文。输入英文可以两种方式,一种是包括百度在内的大多数输入法支持按shift切换状态,这适合输入中文中夹的一些英文词,或短时间使用。另一种就是直接切到英文输入法状态,这更适合类似输入代码这样的长时间使用行为。
可是,百度输入法有个比较严重的bug。在shift切换到的百度英文输入状态下,如果切换到另一个使用直接英文的窗口,再切换回来,shift状态就会丢失,输入的会是中文。当我一堆程序是英文,一堆是百度,一切换百度的输入状态,所有用百度的进程全变中文,搞得我很多时候想输入英文却在中文状态,工作思路被打断,很恼火。
要解决这一bug,第一个想法自然是反馈意见,希望官方来解决。于是我打开百度输入法设置里的反馈界面,提交意见。

不幸的是,几个星期过去了,也没有任何动静。这是可以理解的,估计这些反馈消息,根本没有人看。百度现在正换着高管,也不知道有没有影响到员工们的工作心态。
既然百度指望不上,那就自己动手吧。
首先搞了个小APP来调试,发现APP没有加载任何百度相关的dylib。这说明mac跟windows对输入法的套路不一样,windows输入法都是直接加载到进程里的,而mac是通过IPC请求来输入的。windows的好处是效率高,而mac的好处是安全隔离。由于输入法本身不需要追求那么高的效率,所以这一点上,mac胜。
呃,说远了,说回来。既然不加载dylib,那就找进程呗。很容易就找到了:


呃,怎么会在临时目录里呢?去文件系统上找找。发现在 /Library/Input Methods/BaiduIM.app下。看来是系统拷过去的。
接下来就是lldb attach调试结合ida反汇编。找到了按shift切换中英文的逻辑所在

 frame #3: 0x0000000102ea58a0 BaiduIM`-[BaiduIMProcesser switchChineseEnglishState] + 93

 frame #4: 0x0000000102ea5a3e BaiduIM`-[BaiduIMProcesser onShiftHandle:] + 251

 frame #5: 0x0000000102ea49ab BaiduIM`-[BaiduIMProcesser handleEvent:client:] + 2046

也就是说,在应用程序中按下按键时,百度会收到 handleEvent:client:事件,然后如果是shift,就切换中英文。同时也可以在这个函数的反汇编中看到,client参数并没有用起来,所以百度根本没有区分当前是哪个进程要输入文本,shift的中英文切换是全系统共用的。如果所有程序一起用百度输入法,要输英文就按shift,这样可能会稍微好用一点,但这种方法对我是不适合的。
在切换处下断,继续使用其它功能,发现还有两个情况会引发中英文切换。一个是从开着百度输入法的程序切换到没开的程序时:

 frame #0: 0x0000000102ec95f6 BaiduIM`-[BaiduIMStateBar changeInputMode:]

 frame #1: 0x0000000102ec8ac0 BaiduIM`-[BaiduIMStateBar resetShortcutBarState] + 198

 frame #2: 0x0000000102e9fc78 BaiduIM`-[BaiduIMInputHook hidePalettes] + 223

此时系统会调用hidePalettes方法,来隐藏百度已经显示的一些输入法浮条。百度就乘机切换到中文状态(什么鬼?)。
另一个是从没开百度的程序切到开着百度的程序时:

 frame #0: 0x0000000102ec95f6 BaiduIM`-[BaiduIMStateBar changeInputMode:]

 frame #1: 0x0000000102ec95b8 BaiduIM`-[BaiduIMStateBar showStateBar] + 451

 frame #2: 0x0000000102e9fb6c BaiduIM`-[BaiduIMInputHook setValue:forTag:client:] + 373

此时系统会激活百度输入法,百度又乘机切换到中文(黑人问号脸???)。
有了这两玩意,shift状态不丢就怪了。这样,我们就把前文提到的BUG的原因搞清楚了。
知道了BUG的原因,就可以想出解决办法了。既然所有key是在 handleEvent:client: 事件中处理的,那么我们hook这个事件,根据 pid 选择当前应有的状态,不就可以根据进程来确定要输入英文还是中文了吗。
伪代码应该是这样的:

char Hooked_handleEvent(NSEvent *event, id sender)

{

    int pid = getCurrentPid(sender);

    struct ime_state state;

    if (get_state_for_pid(pid, state) && state != get_baidu_state()) 

        set_baidu_state(state);

    char rv = old_handleEvent(event, sender);

    set_state_for_pid(pid, get_baidu_state());

    return rv;

}


分析一下这个伪代码本质:根据pid来确定应有的状态(只有一个进程刚打开时,找不到状态,才由百度自己来确定)。然后让百度处理。再后,由于在处理过程中,百度可能切换了状态,我们把状态跟pid再绑定。这样,在不同程序间切换时,每个程序都有自己的独立状态。挺好,符合我的要求。
objc中的hook还是挺好做的,用类似于mobile substrate的代码,可以轻松写出一个dylib,用来hook掉函数-[BaiduIMProcesser handleEvent:client:]。
接下来就是怎么让百度加载这个dylib。这个也好办,给BaiduIM增加一个dylid的load command就好了。用optool解决:

cp '/Library/Input Methods/BaiduIM.app/Contents/MacOS/BaiduIM' .

optool install -c load -p "@executable_path/fixbdwb.dylib" -t BaiduIM

sudo cp BaiduIM '/Library/Input Methods/BaiduIM.app/Contents/MacOS/BaiduIM'

sudo cp fixbdwb.dylib '/Library/Input Methods/BaiduIM.app/Contents/MacOS/'


改掉了/Library中的,临时文件夹中的还需要改:只要注销重新登录,系统就重新拷过去了。再试试,发现之前的bug消失了,终于符合我的要求了。
本方案有一个不足之处,现在只能用shift来切换中英状态了,点击状态条上的小图标切换中英文失效了。暂不折腾了,我能接受。
本方案还有一个不足之处,由于方案用pid而不是窗口ID来确定状态,会使得同进程的窗口状态相互影响。比如,打开两个文本编辑,由于它们只是一个进程的两个窗口,在一个文本编辑里切换输入法状态,会改变另一个文本编辑的状态。这个问题也不再折腾了,我还能接受。
【全文完】

上传的附件 fixbdwb.dylib

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功