ibus输入法开发记录:(一)概览

参考资料导航

ibus源代码:https://github.com/phuang/ibus

ibus-pinyin源代码:https://github.com/phuang/ibus-pinyin

ibus-tmpl源代码:https://github.com/phuang/ibus-tmpl。此代码构件了一个极为简易的输入法引擎,适用于作为ibus自定义输入法引擎开发的框架使参考使用

ibus开发手册:https://ibus.github.io/docs/ibus-1.5/index.html

Python GTK3.0在线参考手册:https://lazka.github.io/pgi-docs/#Gtk-3.0/classes.html

GTK开发手册:ubuntu用户可下载devhelp进行查阅。

编写ibus引擎(C语言)时需要结合GTK手册及ibus手册,十分注意调用的接口返回的字符串等资源是否需要手动释放

ibus简介

ibus全称Intelligent Input Bus,是类UNIX操作系统下的开源免费多语言输入法框架,遵循GPL协议。因为它采用了总线(Bus)式的架构,所以命名为Bus。它具有易用、直觉的全功能输入法用户界面,为输入法开发者提供一个统一的接口及库,符合来自不同地域,文化的用户需求。

ibus是用C及Python开发的,它本身是一个GTK应用程序,作为守护程序ibus-daemon运行于系统,接入其他部件提供具体的功能实现。IBus主要透过下列三种服务(Service)来提供功能:

  1. 输入法引擎服务:为输入法本身。
  2. 配置服务:管理IBus以及输入法的设置选项。
  3. 控制面板服务:提供诸如语言条,候选字菜单等用户界面。
    ibus使用D-Bus实现ibus-daemon服务与IM客户端(像是console, gedit, firefox等具体应用程序)之间的通讯服务,它能够支持XIM协议及GTK IM模块以及Qt IM模块。ibus-daemon通过服务的注册以及发送D-Bus消息来管理服务及IM客户端。

ibus自定义输入法开发简介

每一个自定义开发的ibus输入法都是一个独立的应用程序,它作为“组件(component)”接入到ibus服务,由内部的“引擎(engine)”提供输入功能。输入法引擎接收ibus捕获的事件,通过实现ibus指定的接口提供键盘事件过滤、翻页、焦点变化响应等基本输入功能,并调取ibus的接口完成面板的重绘、配置读取、结果上屏等关键操作。

组件和引擎接入

不同的输入法以组件component的形式注册接入ibus-daemon服务,一个组件可以包含多个输入法引擎。一般地,ibus从其指定目录下寻找可加载组件的xml配置文件,在ibus启动时将加载目录下存在的所有组件并向ibus完成注册。在ubuntu16.04中,该目录为/usr/share/ibus/component,如下图所示:
compinent目录
xml配置文件的内容一般如下(以下内容取自输入法ibus-tmpl的配置文件:enchant.xml):

<?xml version="1.0" encoding="utf-8"?>
<!-- filename: enchant.xml -->
<component>
	<name>org.freedesktop.IBus.Enchant</name>
	<description>Enchant Component</description>
	<exec>/usr/local/libexec/ibus-engine-enchant --ibus</exec>
	<version>1.2.99.20191118</version>
	<author>Peng Huang &lt;[email protected]&gt;</author>
	<license>GPL</license>
	<homepage>http://code.google.com/p/ibus</homepage>
	<textdomain>ibus-enchant</textdomain>

	<engines>
		<engine>
			<name>enchant</name>
			<language>ko</language>
			<license>GPL</license>
			<author>Peng Huang &lt;[email protected]&gt;</author>
			<icon>/usr/local/share/ibus-tmpl/icons/ibus-enchant.svg</icon>
			<layout>us</layout>
			<longname>Enchant</longname>
			<description>Enchant Input Method</description>
			<rank>0</rank>
		</engine>
	</engines>

</component>

其中比较重要的标签内容如下:
<component>下的<name>:组件的名字,内容与可执行程序中。ibus_bus_request_name函数需要的第二个参数对应,用于可执行程序向ibus请求当前注册的组件。
<exec>:xml被载入后将执行<exec>标签内部的指令,一般这里写入的是我们的ibus输入法的可执行程序或启动脚本(绝对路径)。
<engines>下的<name>:引擎的名字,内容与可执行程序ibus_factory_add_engine函数需要的第二个参数对应,用于绑定定义于配置文件中的引擎基本信息及我们的代码实现的引擎类,使ibus输入法在切换到该引擎时能够正确调用我们自定义的方法。
<icon>:用于显示到托盘的输入法图标(绝对路径)。

另外在<engine>下可添加一个<setup>标签,内容包含启动设置的应用程序或脚本所在的路径,在实现中通过ibus_engine_desc_get_setup接口获取内容,启动设置程序。这种方法是参考ibus-pinyin的做法,设置程序独立于组件应用程序,由python gtk实现,两者通过ibus的config模块通信。但这种机制下的ibus输入法在原有逻辑上无法实现设置的及时生效或更丰富的功能,ibus-pinyin也需要重启才能使设置生效。我的设置模块方案将在后续博客中提出,若能有更好的、更稳定的设置模块实现方法,欢迎共同探讨。

execiconsetup等内容的存放都没有严格要求,一般可以在/usr/lib//usr/share/home目录下建立自己的目录,存放可执行程序及必要的配置文件。

组件初始化

参考ibus-tmpl的代码,ibus自定义组件初始化步骤大致如下:

static void
init (void)
{
    // 连接ibus
    ibus_init ();

    bus = ibus_bus_new ();
    g_object_ref_sink (bus);
    g_signal_connect (bus, "disconnected", G_CALLBACK (ibus_disconnected_cb), NULL);
	
    factory = ibus_factory_new (ibus_bus_get_connection (bus));
    g_object_ref_sink (factory);
    
    // 向ibus添加引擎
    ibus_factory_add_engine (factory, "enchant", IBUS_TYPE_ENCHANT_ENGINE);
	
	// ibus为真即程序启动时携带--ibus的参数
    if (ibus) {
        // 获取ibus组件
        ibus_bus_request_name (bus, "org.freedesktop.IBus.Enchant", 0);
    }
    else {
    	// 不读取xml信息,手动构建component和engine的情形
        IBusComponent *component;

        component = ibus_component_new ("org.freedesktop.IBus.Enchant",
                                        "Enchant",
                                        "0.1.0",
                                        "GPL",
                                        "Peng Huang <[email protected]>",
                                        "http://code.google.com/p/ibus/",
                                        "",
                                        "ibus-tmpl");
        ibus_component_add_engine (component,
                                   ibus_engine_desc_new ("enchant",
                                                         "Enchant",
                                                         "Enchant",
                                                         "ko",
                                                         "GPL",
                                                         "Peng Huang <[email protected]>",
                                                         PKGDATADIR"/icons/ibus-enchant.svg",
                                                         "us"));
        ibus_bus_register_component (bus, component);
    }
}

int main(int argc, char **argv)
{
    GError *error = NULL;
    GOptionContext *context;

    /* Parse the command line */
    context = g_option_context_new ("- ibus template engine");
    g_option_context_add_main_entries (context, entries, "ibus-tmpl");

	// 读取命令行参数的“--ibus”,非必要,作者这么写大概是用于区分debug模式
    if (!g_option_context_parse (context, &argc, &argv, &error)) {
      g_print ("Option parsing failed: %s\n", error->message);
      g_error_free (error);
      return (-1);
    }

    /* Go */
    init ();
    ibus_main ();
}

ibus程序调试

参考ibus-pinyin的写法,以上代码的else分支中手动构建component的情况可能是作者用于分离直接启动的debug模式的,可从命令行启动和测试输入法,较为方便。但实际开发中,尤其是牵扯到config模块后,这种调试方法变得极难使用,所以我一般采用的调试方法如下:
编写用于debug的启动脚本代替xml中<exec>中的可执行程序:

<!-- filename: myime.xml -->
<exec>/usr/local/libexec/ibus-engine-myime-debug --ibus</exec>
#!/bin/sh
# ibus-engine-myime-debug
time=$(date "+%Y%m%d%H%M%S")
exec /usr/share/ibus-myime/bin/ibus_engine_myime --ibus > ${HOME}/ibuslog$time 2>&1

组件可执行程序内的打印均使用fflush进行强制刷新,输入法启动后,通过tail -f ibuslog查看日志即可。

此外,若组件程序需要指定环境变量,也可以通过启动脚本使用export

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章