問題点: 複数ストロークのコマンド入力
Emacs21ではXIMをサポートするようになりましたが、Emacsのコマンド入力形態がXIMの機構と相性が悪いという点ではXEmacsと変わりません。
例えば、図1のようにリージョンを選択してコピーする場合を考えます。日本語入力がオンの状態でC-space
でリージョンの選択を開始したとします†1。カーソルを目的の場所に移動してM-w
を入力すると、ESC
はEmacsに渡りますが、w
は図2のように前編集文字列としてXIMサーバ側に渡ります。
図1: 日本語入力オンの状態でリージョンを選択している状態
図2: 図1の状態から
M-w
(ESC
の次にw
)を入力した状態
そのほか、M-<
の入力でも図3のような状態になってしまいます。
図3:
M-<
(ESC
の次に<
)を入力した状態
このように基本的なコマンドを入力する度に日本語入力を一旦オフにしなければならないわけですから、XIMの利用が普及しないのも当たり前かもしれません。もちろん、M-w
を「ESC
の次にw
」ではなく、「Alt
を押しながらw
」で入力すれば、このようなことは起きないかも知れません†2。では、C-x b
などの複数ストロークのコマンドはどうすればいいのでしょうか。
†1 日本語入力がオンの状態でC-space
を入力できない、あるいはそもそもC-space
で日本語入力のオン・オフを切り替えるXIMサーバを使っているなどの問題については、ここでは扱いません。別のXIMサーバを使うか、そのXIMサーバの設定を変更すれば解決すると思います。
†2 XIMサーバによってはAlt
を押しながら入力しても、Emacs(XIMクライアント)に渡さないものもあります。それどころか、日本語入力がオンのときは、Tab
やReturn
などの入力を制御コード(0x07や0x0d)として渡すものもあります。
解決案: EmacsがXIMサーバを制御する
ユーザが複数ストロークのコマンドを入力している途中かどうかを、Emacsは知っています。コマンドの入力が途中のときは日本語入力をオフに(状態を保存)して、コマンドの入力が完了したら日本語入力をオンにする(状態を元に戻す)ようにXIMを制御すれば、前述の問題は解決するはずです。例えば、ESC
やC-x
が押されたら日本語入力をオフにして、コマンドの入力完了かC-g
の入力で日本語入力をオンにするわけです。なぜ、こんな簡単なことが実装されていないのでしょうか†3。
次のパッチに修正の例を示します(FreeBSD上のEmacs 21.2で動作を確認しました)。なお、XEmacs 21.1についてはこちらを参照してください†4。
*** src/xterm.c Sat Mar 16 19:34:56 2002
--- src.new/xterm.c Fri Dec 20 16:21:30 2002
***************
*** 9954,9959 ****
--- 9954,9962 ----
XNextEvent (dpyinfo->display, &event);
#ifdef HAVE_X_I18N
+ #if 1
+ if (this_command_key_count == 0)
+ #endif
{
/* Filter events for the current X input method.
XFilterEvent returns non-zero if the input method has
なお、この修正方法はかなり強引な方法です。たった1行を追加しただけで簡単そうにみえますが、本当にちゃんと直すにはもっといろいろな苦労が必要になりそうです。
†4 Emacsのソースを読んでからXEmacsのソースを読むと、非常に読みづらいです。
日本語入力のオン・オフを制御する
XIMの仕様では、XIMクライアント(Emacsなど)が自発的に日本語入力のオン・オフを制御するにはXSetICValues()を使用することになっています。例として、日本語入力をオンにするコードを次に示します。
XVaNestedList list;
list = XVaCreateNestedList(0, XNPreeditState, XIMPreeditEnable, NULL);
if (XSetICValues(ic, XNPreeditAttributes, list, NULL) != NULL)
warnx("XSetICValues() failed. (XNPreeditState not supported.)");
XFree(list);
XNPreeditState
というXIC属性はR6から定義されましたが、多くのXIMサーバはこれをサポートしていません。例えば、Kinput2はv3.1(2002年10月)からサポートするようになったばかりです。
サポートしているXIC属性を調べる
XIMサーバがどのXIC属性をサポートしているのかを、XIMクライアントはXGetIMValues
で調べることができます。コードの例を次に示します。
XIMValuesList *vl;
if (XGetIMValues(im, XNQueryICValuesList, &vl, NULL) != NULL) {
warnx("XGetIMValues() failed. (XNQueryICValuesList not supported.)");
return;
}
for (int k = 0; k < vl->count_values; ++k)
printf("supported_values[%d]: %s\n", k, vl->supported_values[k]);
XFree(vl);
ところが、R6.6(とそれ以前のリリース)の実装にはバグがあり、このコードを実行するとコアダンプします(不定のアドレスをfree()
で解放してしまうバグなので、そのときコアダンプしなくても、いずれはクラッシュします)。XFree86では4.2.0から修正されているようです(ただし、メモリリークするバグが残っています)。
したがって、現状ではそれぞれのXIC属性に対してXGetICValues()
またはXSetICValues()
を使用して、NULL
が返ってくればサポートされている、返ってこなければサポートされていないと判断するしかなさそうです。