參考資料導航
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)來提供功能:
- 輸入法引擎服務:爲輸入法本身。
- 配置服務:管理IBus以及輸入法的設置選項。
- 控制面板服務:提供諸如語言條,候選字菜單等用戶界面。
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,如下圖所示:
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 <[email protected]></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 <[email protected]></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也需要重啓才能使設置生效。我的設置模塊方案將在後續博客中提出,若能有更好的、更穩定的設置模塊實現方法,歡迎共同探討。
對exec
、icon
、setup
等內容的存放都沒有嚴格要求,一般可以在/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
。