Poky是一个简洁的嵌入式Linux图形环境。在poky中实现中文显示比较容易,主要是设置区域和增加中文字体。实现中文输入就要复杂一些。我尝试过移植scim或fcitx,但都碰到同样的问题:poky环境的xim用不起来。我写了一个IMdkit的测试程序,在调用IMOpenIM后,我登记的回调函数收不到任何XIM呼叫。我想这个问题可能是poky对xlib的简化引起的。
为了回避xim问题,我改用Gtk的IMContext接口。输入法引擎基本上照搬fcitx,重写了接口和显示部分。另外还写了一个GTK的IM模块。
程序构架如下图所示。
GTK的IM模块派生自GtkIMContext接口。它负责把编辑窗口的输入法事件传递给输入法程序,并将输入法程序返回的文本用"commit"信号提交给编辑框。输入法程序接收IM模块传来的输入法事件,显示预编辑信息,并将用户选择的文本发送给IM模块。
输入法程序可以分成三块:
三个部分用一些简单的接口代码连接起来,基本上可以独立测试。例如我先在windows的VC环境调好输入法引擎。然后调好IM模块和输入法程序接口部分的通信。最后才是绘制界面。
IM模块和输入法程序用socket通信。IM模块通过GTK的io channel接收socket数据。输入法程序用select同时接收X事件和通过socket传来的输入法事件。
中文输入需要显示预编辑输入和候选字词。根据这些输入信息的显示位置,XIM总结了4种输入法风格:
在Windows中,通常只有输入法感知的程序才能实现on-the-spot,即客户程序要处理输入法信号。在GTK中,GtkIMContext提供了预编辑接口,所以由IM模块和输入法程序配合就可以实现on-the-spot,不需要客户程序直接参与。
不过,为了简单起见,我还是使用了over-the-spot的风格。这样只需要重载5个接口:
static void mb_im_context_class_init (MbIMContextClass *klass) { GtkIMContextClass *context_class = GTK_IM_CONTEXT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); context_class->set_client_window = mb_im_context_set_client_window; context_class->focus_in = mb_im_context_focus_in; context_class->focus_out = mb_im_context_focus_out; context_class->filter_keypress = mb_im_context_filter_keypress; context_class->set_cursor_location = mb_im_context_set_cursor_location; }
在focus_in和focus_out显示或隐藏输入法窗口。set_client_window和set_cursor_location收集光标位置,传给输入法程序。 filter_keypress将按键信息传给输入法程序。
字母和数字按键都可以异步处理,即这些按键都交给输入法程序,不需要编辑窗口处理。有的按键需要同步处理。例如退格键,首先交给输入法程序,如果输入法程序认为这个键未处理,就要再返还给编辑窗口处理。因此对于此类按键,im模块要同步等待输入法程序的响应。
scim的框架有78597行代码,拼音输入模块有13917行代码。 fcitx有28542行代码,其中IMdkit库有10000行代码,调用IMdkit库的xim.c有800行代码。我写的IM模块有842行代码,输入法程序有7230行代码,其中服务器接口部分只有291行代码。虽然我的程序是实验性的,但这也说明使用GtkIMContext接口比使用XIM接口要简单一点。
但基于GtkIMContext的输入法只能用于GTK程序,在兼容性上要弱于基于XIM的输入法。所以以后还是可以尝试一下重新构建poky,去掉影响xim功能的简化。