日本語、韓国語、中国語のように文字数が多いまたは文字の組み合わせが必要な言語をリナックス1で入力するためには、特殊なIMEが必要です。WindowsやmacOSではこのようなIMEがオペレーティングシステムに基本的に統合されているため、ユーザーが別途気にする必要はありません。一方、リナックスでは使用するディストリビューションによってIMEを別途インストールして設定する必要がある場合が多いです。

リナックス環境で非西欧圏の言語を入力する際、IMEに関連する問題が発生することがしばしばあります。開発者がこのような問題を効果的に解決するためには、リナックスのIMEシステムについての理解が必要です。

この記事では、リナックスのIMEシステムを構成する様々な要素の役割および相互作用の方式、動作過程を詳しく見ていきます。

1. IMEとInput Method Framework

通常、IMEはInput Method Framework上で動作します。 Input Method Frameworkは様々なIMEを管理し統合する上位ソフトウェアで、代表的な例としてIBusFcitxがあります。

IME自体は一般的にInput Method Frameworkとは別にインストールする必要があります。 例えば、Fcitxベースの韓国語IMEはfcitx5-hangul、IBusベースの日本語IMEはibus-mozcなどのパッケージをインストールする必要があります。

Input Method Frameworkはユーザーの設定に従って、アプリケーションのキー入力をアクティブなIMEが処理するよう仲介する役割を果たします。 この記事では便宜上、Input Method Frameworkを区別せずにIMEと呼ぶことにします。

Input Method FrameworkとIME

IMEとInput Method Framework

2. 通信プロトコルおよびアプリケーション

IMEは基本的にクライアント-サーバー構造で設計されています。 アプリケーションはIMEデーモンと様々なIPC(プロセス間通信)方式を通じて通信し、入力を処理することができます。 この過程で使用されるプロトコルは二種類に分けることができます。一つはIMEと独立して動作する標準プロトコルであり、もう一つはIME開発者が独自に開発した固有のプロトコルです。 アプリケーションはこれらの通信方式の中から一つを選択して動作します。

標準プロトコル

クライアントとIME間の通信に使用される標準プロトコルは次の通りです。

X Input Method (XIM)

XIMは1990年代にX Consortiumが制定したプロトコルで、X11サーバーを通じてIMEとクライアントが通信する方法を定義したものです。 作られてから30年以上経った古いプロトコルのため、最近ではあまり使用されていませんが、互換性のためにほとんどのIMEがサポートしています。 X11ベースですが、Wayland環境でもX11との互換性レイヤーであるXWaylandを使用する場合、XIMプロトコルを使用することができます。

IMEをインストールする際によく設定する次の環境変数は、XIMプロトコルで使用するIMEを指定します。

export XMODIFIERS=@im=ibus

Wayland

X11を代替するために設計されたディスプレイプロトコルであるWaylandもIMEのためのプロトコルを含んでいます。 WaylandのIMEプロトコルは2回も変更され、最新バージョンであるtext-input-unstable-v3もまだunstableが付いている状態です。 しかし、現時点でtext-input-unstable-v3は事実上Waylandの入力プロトコル標準に近い形で使用されています。 特徴的な点として、XIMがただX11サーバーを通じてアプリケーションとIMEが通信する方式であるのに対し、text-input-unstable-v3は間にWaylandコンポジターが直接関与する構造となっています。

独自のプロトコル

IBus、Fcitx、UimなどほとんどのIMEは標準プロトコル以外にも独自の通信プロトコルを実装しています。 IBusとFcitxの独自プロトコルはD-BusというリナックスのIPCシステム上で通信し、Uimは直接ソケットを使用します。 これらの独自プロトコルおよびクライアントは主に後述するIMモジュールのために使用されます。

それでは標準プロトコルが存在するにもかかわらず、なぜ独自プロトコルが作られたのか疑問が生じます。 これについて推測すると、おそらく次のような過程があったと考えられます。

  1. 1990年代に開発されたXIMは基本的な入力機能のみをサポートし、現代的な入力方式(周辺テキスト参照など)をサポートしていなかった
  2. 各IME開発チーム(IBus、Fcitx、Uimなど)はXIMの限界を克服するために独自のプロトコルを開発した
  3. その後Waylandが登場し、入力プロトコルが3つも作成された
  4. 結果的に複数のプロトコルが乱立することになり、アプリケーション開発者は様々なプロトコルをサポートする負担が生じた

IMEを使用するアプリケーション開発

アプリケーション開発者がIMEに関連する考慮をする必要があるケースは多くありません。 多くの場合、GTKやQtのようなウィジェットツールキットがIMEとの連携をバックグラウンドで自動的に処理するためです。 そのため、ウィジェットツールキットでGtkEntryQLineEditのような入力フィールドを使用する際、開発者はIMEに関連する詳細に気を配る必要はありません。

ウィジェットツールキットは通常、XIMやtext-input-unstable-v3のような標準プロトコルを自体的にサポートし、 IMEの独自プロトコルを実装したコードは通常モジュール/プラグイン方式で読み込みます。これをIMモジュールと呼びます。 IMEをインストールしたことがあれば、次のようにウィジェットツールキットが使用するIMモジュールを指定する環境変数を設定した経験があるでしょう。

export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx

自前でUIを実装したアプリケーションの場合

すべてのアプリケーションがウィジェットツールキットが提供する入力フィールドを使用するわけではありません。 ウェブブラウザのように自前でUIを実装し描画するアプリケーションは、標準IMEプロトコルを直接実装するか、ウィジェットツールキットが提供するGtk.IMContextQInputMethodのようなIMEを抽象化したインターフェース/クラスを利用して独自のUI要素の入力を処理します。

要約

前述したIMEの通信プロトコルおよびアプリケーションの構造を一目で見ると次のようになります。

IMEシステム通信構造の概観

IMEシステム通信構造の概観

3. IMEの大まかな動作過程

IMEが実際のキー入力から画面出力までの全体過程でどのように動作するかをプログラミングの観点から見ていきましょう。 この過程は通常GTK、Qtなどのウィジェットツールキットによって実行されます。

理解を助けるためにJavascriptベースの疑似コードを使用しました。

Context作成

Contextは一つのテキスト入力状態(組み合わせ中のテキスト、カーソル位置など)を表します。 IMEはContextを利用して複数の入力フィールド、アプリケーションの入力状態を別々のオブジェクトとして維持します。

// Pseudocode
const inputContext = new InputContext();

カーソル位置設定

入力候補リスト

入力候補リスト

日本語や中国語を入力したり、韓国語で漢字キーを使用したりすると、上のように入力テキスト候補が表示されます。 このUIはIMEから表示されるもので、アプリケーションはIMEがこれを適切な位置に表示できるよう、現在の入力カーソルの画面上の座標を送信する必要があります。 また、ウィンドウ移動などの理由により入力フィールドの位置が移動するたびに更新が必要です。

// Pseudocode
inputContext.setCursorLocation(x, y, width, height);

キー入力送信

IMEは一般的に直接ディスプレイサーバーからキー入力を受け取らないため、アプリケーションはIMEにキー入力イベントを送信する必要があります。2 IMEはキー入力を受け取り、そのキーの処理の可否を返します。通常、組み合わせが必要ないキーにはfalse値が返されます。 例えば、日本語IMEで数字、記号など組み合わせなしで直接入力可能なキーがこれに該当します。 このような場合、一般的にアプリケーションは元のキーがそのまま入力されるように処理します。

// Pseudocode
function onKeyPress(keyCode) {
  const isKeyProcessed = inputContext.processKey(keyCode);
  // ...
}

Preeditイベント

Preeditテキスト例

Preeditテキスト例

テキストを入力していると、まだ確定していない文字の下に下線が表示されるのを観察できます。 このようにまだ入力中だが確定されていないテキストをPreeditテキストと呼びます。 IMEはPreeditテキストが更新されるとアプリケーションにイベントを送信し、これによりユーザーはまだ確定されていないテキストをリアルタイムで見ながら編集することができます。

// Pseudocode
function onPreeditTextUpdate(preeditText) {
  // ...
}

// Add an event listener
inputContext.setOnPreeditTextUpdate(onPreeditTextUpdate);

Commitイベント

Commit例

Commit例

ユーザーが入力中だったPreeditテキストを最終確定してアプリケーションに渡すことをコミット(Commit)と言います。 コミットされたテキストはもはやIMEが管理する特殊な状態ではなくなり、下線が消えます。 コミットが発生する条件は入力する言語の特性および使用するIMEによって異なりますが、一般的にはEnterキーを押したとき、マウスでウィンドウをクリックしたとき、カーソルを移動したときに発生します。

// Pseudocode
function onCommit(commitText) {
  // ...
}

// Add an event listener
inputContext.setOnCommit(onCommit);

テストしてみる

以下のウェブサイトを通じて直接Preedit更新、Commitを確認することができます。

参考


  1. 一般的なX11、Wayland、Freedesktopなどの技術を使用するGNU/Linuxディストリビューション ↩︎

  2. ただし、Waylandのtext-input-unstable-v3プロトコルはコンポジター側がキー入力をIMEに送信し、組み合わせ結果のみをアプリケーションに送る方式を使用するため、アプリケーションのキー入力送信は必要ありません。 ↩︎