很久很久以前,我还没有在用 Linux 的那个时候,那会儿还在用 Windows 上的搜狗输入法。有时候经常就会好奇一件事,为什么输入法的更新日志里,经常会有「修复和XXX程序的兼容性」,「修复和YYY程序的兼容性」,难道他们不应该采用一套统一的接口吗?
几年以后当我开始在 Linux 下开发输入法之后,我终于了解了一切……
有些东西其实还是颇为为难的,我就来说一下曾经处理过的bug(有些解决,有些未解决,或者难以解决)。
1、LibreOffice 的 crash
简单来说原理就是在某个 Qt 调用 IM Module 的函数里调用某个函数可能会导致无限递归。这个相对来说是比较容易修复的,只需要把 IM Module 调用那个函数的移动到事件队列里延迟调用就可以避免递归了。但这个问题并不会在其他任何 Qt 程序中出现,相信也只是在某个 LibreOffice 的版本无意之中导致的。说实话这种问题并没有一种办法归结为是谁的“过错”,毕竟我相信相关的 API 并没有相应的文档来定义这么细节的调用是不合法的或者可能导致问题。
2、Gtk 的 Client Side Input Panel 的语言 tag 失效
Fcitx 5 有一个功能,让 Pango 优先用指定语言的字体显示文字。例如骨字在日语和中文中的字形是不一样的。Fcitx 5 中可以让日语输入法用日语字形,中文则用中文字形。Gtk 的 Client Side Input Panel 和 Fcitx 5 的 Classic UI 使用了几乎相同的代码,但是出于某种未知的原因,不知道从什么时候开始这个功能在 Gtk 的 Client Side Input Panel 就失效了。我尝试了半天最后采取了一些 Workaround 来暂时回避。
3、zwp_input_method_v1 的支持问题
Wayland 的输入法协议之前也有很多地方吐槽过了,但让我出乎意料的是 KWin 采取了非常混搭的做法使用这个协议(并非不行,只是确实很意料之外)。首先介绍一下背景,Wayland 原生输入法协议是分为两个部分的,一部分是程序和 compositor,另一部分是输入法和 compositor 之间。程序和输入法之间的协议是 text-input 系列,而输入法和 compositor 之间则是 zwp_input_method 系列。
问题是 text-input-v1,v2 是和 zwp_input_method_v1 配套设计的,而 text-input-v3 和在野的 zwp_input_method_v2 是配套设计的。当然,考虑到 wayland-protocols 里面只有 zwp_input_method_v1,以及 kwin 的这个支持是给 Maliit 用的,也是可以理解的。但 text-input-v3 的 API 变化在某些方面和 zwp_input_method_v1 就有些格格不入,总之就是凑吧凑吧凑合能用。
但我从 5.23 开始关注这个,结果到最后我给 KWin 和相关的库写了接近30个PR。每当想起这段我就在想,我好好一个输入法开发者怎么就去写 Compositor 了。
这一部分按下不表,而 text-input-v2 有一个特别的事件,为了隐藏虚拟键盘,可以请求 compositor 来隐藏。但这个功能在桌面用键盘的输入法就会导致输入窗口永久被隐藏起来。我琢磨半天想出了一个 workaround,但这个 workaround 却又反过来会导致 weston 出问题。
后来幸好我又想出了另一个 workaround 才同时支持了 weston 和 kwin。zwp_input_method_v1 已经属于很故纸堆的协议了,也不会得到今后的更新。但很多行为其实都没有明确的定义,幸而实作只有两个,因此很多实现我就变成依靠他们实现的行为来决定。
4、Chrome 的 Wayland 输入法问题
Chrome 的 Wayland 输入法一直一个浆糊的状态。首先,Ozone 早期不支持 Gtk IM Module,只支持 text-input-v1 实现了一个输入法。现在问题是,text-input-v1 支持的 Compositor 只有 Weston,也就是说支持几乎等于 0。最广泛被支持的 text-input 是 v3,KWin wlroots GNOME 都支持。
然后他们就搞了一个支持 Gtk 4 的 IM Module,但问题来了,他们的所谓支持采用了一个非正常的使用 Gtk API 的方式,导致了 Gtk 自己的 text-input-v3 实现和 Fcitx 5 的 Client Side Input Panel 都无法正常使用。后来我倒是想通了,在最新版的 Fcitx5 Gtk 里可以略微做一个检查来临时禁用不能用 Client Side Input Panel 的情形。这种情况下好歹能显示一个位置错误的输入框。
而且根据 Chrome 的架构,他们是不可能用 Client Side Input Panel 的,因为他们内部就没有真实的 Gtk 窗口来绘制(对比 Firefox,Firefox 是用 Gtk 创建的窗口)。他们唯一正常支持输入法的希望应该就是把 text-input-v1 改成 text-input-v3(或者同时支持两个)。
5、Firefox 98alpha 的输入法问题
https://bugzilla.mozilla.org/show_bug.cgi?id=1751339
这个问题倒是 Archlinux CN 的群友优先发现的,幸好发现的早,早早的就通过 bisect 定位了导致的问题。虽然,我并不能理解这个改动牵扯了什么 Gtk 内部的问题导致窗口的销毁再重建,以至于 Gtk IM Module 接收到的是一个销毁了的窗口,但反正 Revert 回去了现在没问题了。
6、神秘的 Twitter 输入框输入法问题
说起这个问题,不得不说这个问题折磨很多人很久。而且不管是在 Chrome 还是 Firefox 上都有不同的表现:https://bugzilla.mozilla.org/show_bug.cgi?id=1735227 https://bugs.chromium.org/p/chromium/issues/detail?id=952181
最后追溯追溯追溯到底,竟然追溯到了这个输入框所用的 Javascript 上 draft-js 昨天我反正顺便加了一笔我的研究,但相关的在不同平台(含非 Linux)上至少我就能找到 4 相关汇报:
https://github.com/facebook/draft-js/issues/3109
https://github.com/facebook/draft-js/issues/2227
https://github.com/facebook/draft-js/issues/1320
https://github.com/facebook/draft-js/issues/1301
我一个输入法开发者,怎么就去看 React js 了。但光看这个 bug 的时间,我怀疑已经存在了非常长的时间。不管在什么平台上,输入法用户都是挺没有话语权的。
7、SDL2 和 Fcitx 5 的输入法小问题
这个说到底还是我自己弄出来的,我几年前给 SDL 加的 Fcitx 5 支持,在传递 DBus 参数的时候,传了一个错误的 int 类型,导致访问了无关的内存,导致了发送给 Fcitx 5 的 Capability Flag 可能意外包含 Client Side Input Panel 的 Bit 导致无法正常显示输入框……而很多分发 SDL 的是不会立刻获得更新的(甚至永远不能),例如 steam 的游戏。
所以结果就是我决定在 Fcitx 5 内部加一个 Workaround 来解决这个 bug(检查某些 bit 是否有不正常的数值,如果有,则抛弃)。总之,凑合能用。
8、SDL2 的最近的其他小问题
有一个人来汇报他没法在 Dota 用输入法,结果发现是他设置的某个快捷键无效。向下研究,则是 SDL 没有发送所有按键事件给输入法。然后我测试着测试着,就发现 SDL 的 ibus 实现也有 bug(因为 Fcitx 5 支持模拟 IBus,所以我顺手这么一测……),SDL 的 Wayland 输入法也有 bug,SDL 的 Wayland 按键处理也有 bug……
然后就又给 SDL 写了 4 个 PR ……
我一个写输入法的怎么又去看 SDL 代码了。
老k好辛苦….
我现在遇到wayland下面什么没有严重到影响使用的小问题已经都懒得报bug了,“又不是不能用”
没想到开发输入法还有牵涉到这么多知识.一直以为只要实现按键序列和字符的查表就可以了呢.
作者您好,我正在开发一个触屏用的虚拟键盘,需要把候选结果输出到任意第三方应用的文本框里(即当前输入焦点处),因为我并没有采用任何一种输入法框架,现在遇到了难题,我参考了onboard的做法,利用X11 的XTestFakeKeyEvent等接口实现了这个功能,但文字数量比较多的情况下,如5个汉字以上,输出到焦点处的时候汉字的顺序就会打乱,加上时间延时也不能完全能解决。并且在不同的系统里出现的机率也不太一样。我还尝试过用粘贴板、Ctrl+Shift+U 等方式来实现,但都有些问题无法通用。综上原因我想请教您是否可以利用 GTK IM 、 XIM 、 ibus 、 fcitx 的接口来实现这个独立的功能 ?
你可以写个 fcitx 的插件,通信随便挑你喜欢的方式。接到你虚拟键盘发送的内容,就调用 fcitx 的 commit string 给 fcitx 当前活动的 input context