调试输入法,和写别的程序其实没有本质的区别,常见的办法也就是 GDB 和输出日志。但它特别的地方主要在于,你用来调试输入的程序也是输入法的客户端之一,在调试输入法的时候,如果不进行一些特别的操作,将会导致你调试用的终端 Freeze(把按键发送给了输入法,却因为程序 GDB 暂停无法获得响应导致 Freeze),也就是标题所说的先有鸡还是先有蛋的问题。
当然,要解决这个问题其实也简单,就是让你的终端不用输入法就行了。这里有几个点需要注意,常见桌面环境的终端一般是单实例,你即使启动一个新的窗口也是同一个进程,并不是重新启动一个进程。所以一般我个人会选择使用另外的终端程序。这是从终端程序禁止输入法方面来说。反过来,也可以通过命令行参数禁用 Fcitx 的前端。来阻止 Fcitx 和对应类型的程序进行通信。
先说说怎么启动一个无法使用输入法的终端。如果是 XIM 的终端,例如 xterm,alacritty,就直接用 XMODIFIERS=@im=none xterm 这样的方式启动,启动之后,在终端里面直接启动新的 fcitx 的话,就需要注意要在终端里面重新恢复 XMODIFIERS 的值为一般的值。因为根据 XMODIFIERS 的处理机制,Fcitx 会根据它启动时的 XMODIFIERS 设置对应的值。如果不进行设置的话,当前的这个终端自然也反而会可以和 Fcitx 通信了。如果是 Gtk 程序,可以设置 GTK_IM_MODULE=gtk-im-context-simple ,如果是 Qt 5程序,可以设置为 QT_IM_MODULE=compose 。
如果想要从 Fcitx 这边禁用输入法,那就需要利用 –disable 命令行参数。我常用的一般就是 fcitx5 --disable dbusfrontend,ibusfrontend
。你也可以根据你具体的需要来设置禁用对应的 Frontend。
当然,这个并不是总是特别好用的。因为这里牵扯到第二个问题,就是程序的焦点问题。在你切换到终端的时候,你用来测试输入的程序就失去了焦点,会导致程序直接进入重置状态,很有可能无法达到原本想要调试的状态。这里就可以采取第二种方法,启动一个新的 X server,从而避免焦点抢占的问题。常用的一般就是 Xephyr。
可以用如下的命令来设置启动这样的一个环境
Xephyr :1 & DISPLAY=:1 openbox & #启动一个窗口管理器 DISPLAY=:1 xterm & #启动一个调试用程序用于测试 DISPLAY=:1 gdb --args fcitx --disable dbusfrontend,ibusfrontend # 启用一个禁用 dbus 的 fcitx
这里需要注意的是,dbusfrontend 并不会因为显示服务器的隔离而无法进行输入,所以例如你要在 konsole 上进行调试,如果不禁用 dbusfrontend 的话,console 还是会收到按键。
所以如果你想要调试一个使用 dbusfrontend 的程序,就最好还用上前文所述的办法,在主显示服务器启动一个无法使用输入法的终端来进行调试。
Xephyr 不会因为它自身焦点改变而修改 Xephyr 里面运行的程序的焦点,就可以很方便的在 Xephyr 和外部终端之间来回切换调试一连串的输入行为了。
之后,你只需要在 Xephyr 里面进行键盘操作,在触发对应 gdb 断点之后,用鼠标切换到外部的终端继续进行调试即可。调试完成之后也可以用鼠标切换回到 Xephyr 里面再继续模拟另一些用户的操作。
调试来说,就还有传统的输出 Log 的方式。简单的来说用 FCITX_INFO() << item << to << output;
就可以输出日志了。和 std::cout 类似,但是优点就是可以直接支持很多模板类型,例如 STL 的容器和 Fcitx 自身数据类型的直接输出,更加的方便。