試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

XIMによる日本語入力で9文字以上の日本語が確定できない問題の対処

(2008/9/20)この記事の不具合は、日本人の開発者によってWineのバージョン1.1.5で改善され、特に修正(パッチ当て)を行うことなく長い文字列の確定が正常に行えるようになっている。これは大変嬉しい。

  1. 問題について
  2. ソースの該当部分
  3. 修正作業

問題について

XIM(X入力メソッド)経由で、日本語を8文字以内で入力し、確定させると、正常に入力領域にこれが入るのだが、9文字以上入力すると、端末に

err:keyboard:X11DRV_KeyEvent Buffer Overflow need 27!
err:keyboard:X11DRV_ToUnicodeEx Buffer Overflow need 27!

と表示されて入らない。10文字では、最後の数字部分が「30」になり、これより多くの文字を入れると、1文字あたり3ずつ値が増えていく。
UTF-8エンコーディングにおいて日本語は3バイトであることもあわせて考えると

  • XIMの入力を一時的にためておく場所がある
  • その上限は8文字分x3バイト=24バイトになっている
  • これを越えると「err:keyboard:X11DRV_KeyEvent Buffer Overflow need [数字]」と表示する

ということになる。

ソースの該当部分

Wineのソースに、該当する部分が無いかを探すと、dlls/winex11.drv/keyboard.cというファイルのX11DRV_KeyEvent()関数が怪しい*1ことが分かった。上のエラーメッセージに出ている名前とも一致している。

[引用]wine-0.9.51/dlls/winex11.drv/keyboard.c のX11DRV_KeyEvent()関数の一部

void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
{
    XKeyEvent *event = &xev->xkey;
    char Str[24];
    KeySym keysym = 0;
    WORD vkey = 0, bScan;
    DWORD dwFlags;
    int ascii_chars;
    XIC xic = X11DRV_get_ic( hwnd );
    DWORD event_time = EVENT_x11_time_to_win32_time(event->time);
    Status status = 0;

    TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
		event->type, event->window, event->state, event->keycode);

    wine_tsx11_lock();
    /* Clients should pass only KeyPress events to XmbLookupString */
    if (xic && event->type == KeyPress)
        ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), &keysym, &status);
    else
        ascii_chars = XLookupString(event, Str, sizeof(Str), &keysym, NULL);
    wine_tsx11_unlock();

    TRACE_(key)("nbyte = %d, status 0x%x\n", ascii_chars, status);

    if (status == XBufferOverflow)
        ERR("Buffer Overflow need %i!\n",ascii_chars);

    if (status == XLookupChars)
    {
        X11DRV_XIMLookupChars( Str, ascii_chars );
        return;
    }
    /* (以下略) */

この中で、24バイトにハードコーディングされている配列「Str」のサイズを、大きめの値*2に修正することで、9文字以上で確定できない問題は回避できた。
ところが、手元のSCIM + Anthyで試してみたところ、「256」にしても64文字止まり(エラーは出ない)だった。他のアプリケーションでは65文字以上入っているためWine固有と考えられ、別のところにも文字数の壁がある可能性がある。

修正作業

sedを使用した修正例(配列長は「192」にした)としては、

$ sed -i 's/\(char Str\)\[24\]/\1[192]/' dlls/winex11.drv/keyboard.c

となる。これからビルドする場合は、ソース修正後普通にビルドする。Gentooebuildでは下のような修正を追加(「Wineで複数のMIDIポートを持つALSA MIDIデバイスのポートが1つしか使えない問題に対処」の修正を含む)。

--- /usr/portage/app-emulation/wine/wine-0.9.51.ebuild.orig
+++ /usr/portage/app-emulation/wine/wine-0.9.51.ebuild
@@ -68,6 +68,10 @@
 	sed -i '/^UPDATE_DESKTOP_DATABASE/s:=.*:=true:' tools/Makefile.in
 	epatch "${FILESDIR}"/wine-gentoo-no-ssp.patch #66002
 	sed -i '/^MimeType/d' tools/wine.desktop || die #117785
+	# MIDI port name fix
+	sed -i 's/\(MultiByteToWideChar(CP_ACP, 0, snd_seq_\)client\(_info_get_name\)(cinfo)/\1port\2(pinfo)/' dlls/winealsa.drv/midi.c || die
+	# XIM keyboard fix
+	sed -i 's/\(char Str\)\[24\]/\1[192]/' dlls/winex11.drv/keyboard.c
 }
 
 config_cache() {

すでにWineがビルド済みで、winex11.drvのみをビルドする場合は「Wineのライブラリやドライバを個別にビルド(バイナリパッケージを使用している場合の修正に便利)」の要領で作業する。

使用したバージョン:

*1:24バイトのchar型配列があることや、XIMに関係する関数を呼んでいること、「Buffer Overflow need [数字]」を出力することなどから

*2:計算上は、「128」で42文字、「192」で64文字、「256」で85文字となる