|
○ はじめに
LECTURE 5 で説明した文字列描画と LECTURE 10 で説明したキーボードイベントを使えば、英数字のようなシングルバイト文字をキーボードから入力して表示するアプリケーションを作成することができます( テキストフィールド、etc. )。しかし、これだけでは日本語文字列を入力することはできません。日本語のようなマルチバイト文字列を入力として受け取るには、別の方法を使わなければなりません。
ここでは XIM ( X Input Method protocol ) を用いて日本語文字列(マルチバイト文字列)を入力する方法について説明していきます。
○ 日本語入力メソッド/アプリケーション/ユーザ間のやりとり
通常 Linux® ( Unix ) で日本語を入力する場合は、まず kinput2 などの漢字変換入力プログラム ( IM ) を起動し、キーボードから漢字変換前の日本語文字列を入力、変換を行い最終的に入力文字列を確定するという手順となります。この時の IM とアプリケーションのやりとりは以下のようになります。
- アプリケーションはキー入力を IM に渡す
- 入力メソッドは受け取ったキー入力を受け取り、前編集文字列を表示する
- 変換指示を受け取ると、IM は漢字変換サーバ(通常は別プロセス)とやりとりし、 変換中の表示文字列を受け取って表示する
- 入力文字列が確定されると、IM は確定文字列をアプリケーションに渡しアプリ ケーションがこれを表示する
つまり、日本語入力を行うためには IM とのやりとりを実装しなければならないのです。
IM とのやりとりには、Kinput2 プロトコルのように IM 独自のプロトコルも存在しますが、ここでは X ウィンドウシステムのリリース 5 から 用意された XIM ( X Input Method protocol ) という国際化テキスト入力のためのプロトコルに従います。
IM とユーザとのやりとりに関しては、次のスクリーン領域および対話形式が XIM 仕様として定義されています。
スクリーン領域領域名 | 説明 |
---|---|
状態表示領域 | 入力メソッドの内部状態を表示するための出力領域 |
前編集領域 | 変換確定前の入力文字列を表示するための領域 |
補助領域 | 入力メソッドが必要とする一時ウィンドウ |
対話形式
形式名 | 説明 |
---|---|
ルートウィンドウ型 | 入力メソッドはデータをアプリケーションとは別個のウィンドウ (ルートウィンドウの子ウィンドウ)に表示する |
off-the-spot 型 | 前編集中のデータをアプリケーションウィンドウのある領域に表示する。 領域はアプリケーションの方で指定する |
over-the-spot 型 | 前編集中のデータを、現在の挿入点に重ねて置かれた独立ウィンドウに表示する |
on-the-spot 型 | 入力メソッドは、前編集中のデータを表示するようにアプリケーションに指示する (この場合、前編集データを表示するのはアプリケーションとなる) |
アプリケーション側は上の対話形式の中から一つを選び、それに必要な前編集領域および状態表示領域を用意しなければいけません。on-the-spot 型 についてはさらに入力メソッドが呼び出すコールバック関数を用意する必要があります。
本ページでは、ルートウィンドウ型および off-the-spot 型について取り挙げます。over-the-spot 型および on-the-spot 型に関してはみなさんの方で勉強してみてください(単に僕の勉強不足です。すみませんm(__)m )。
○ サンプルプログラム ( ルートウィンドウ型 )
ルートウィンドウ型という対話形式で日本語文字列を入力、ウィンドウに表示するサンプルプログラム xim_root.c です。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。
実行して仮名漢字変換用の IM を起動すると、下の様に仮名漢字変換入力用のウィンドウが表示されます。このウィンドウは、ルートウィンドウの子ウィンドウとなります。このウィンドウで文字列を入力、変換を確定するとその確定文字列が上のウィンドウに表示されます。
--- xim_root.c ---
1 /* 2 * ルートウィンドウ型(別ウィンドウに仮名漢字変換文字列 3 * を表示するタイプ)のサンプルプログラム 4 */ 5 6 #include <X11/Xlib.h> 7 #include <X11/Xlocale.h> 8 #include <X11/Xutil.h> 9 #include <X11/keysym.h> 10 #include <stdio.h> 11 12 13 #define BORDER 2 /* ボーダの幅 */ 14 #define COL 30 /* 30列 */ 15 #define ROW 20 /* 20行 */ 16 17 static Display* dpy; /* ディスプレイ */ 18 static int screen; /* スクリーン */ 19 static Window root; /* ルートウィンドウ */ 20 static Window win; /* 表示するウィンドウ */ 21 static int win_w, win_h; /* ウィンドウの幅と高さ */ 22 static unsigned long black, white; /* 黒、白のピクセル値 */ 23 static GC gc; /* グラフィックスコンテキスト */ 24 static XFontSet fs; /* フォントセット */ 25 static int font_w; /* 文字幅 */ 26 static int font_h; /* 文字高 */ 27 static int ascent; /* 文字のアセント */ 28 static int descent; /* 文字のディセント */ 29 static XIM im; /* インプットメソッド */ 30 static XIC ic; /* インプットコンテキスト */ 31 static Atom protocol; /* ウィンドウマネージャ用変数 */ 32 33 34 static int xim_setlocale(); 35 static void xim_init(); 36 static void xim_create_fontset(); 37 static void xim_create_window(); 38 static int xim_init_inputmethod(); 39 static void xim_event_loop(); 40 static void xim_draw( XEvent* event ); 41 static void xim_close(); 42 43 44 int 45 main( void ) 46 { 47 /* インプットメソッドが必要とするイベント */ 48 unsigned long fevent; 49 50 if ( xim_setlocale() == -1 ) { 51 return 1; 52 } 53 54 xim_init(); 55 xim_create_fontset(); 56 xim_create_window(); 57 58 /* インプットメソッドの初期化 */ 59 if ( xim_init_inputmethod() == -1 ) { 60 return 1; 61 } 62 63 /* ウィンドウ破壊イベントを受け取る */ 64 protocol = XInternAtom( dpy, "WM_DELETE_WINDOW", True ); 65 XSetWMProtocols( dpy, win, &protocol, 1 ); 66 67 /* インプットメソッドが必要とするイベントを問い合わせる */ 68 XGetICValues( ic, XNFilterEvents, &fevent, NULL ); 69 70 XSelectInput( dpy, win, 71 ExposureMask | KeyPressMask | FocusChangeMask | fevent ); 72 XMapWindow( dpy, win ); 73 74 xim_event_loop(); 75 xim_close(); 76 77 return 0; 78 } 79 80 81 static int 82 xim_setlocale( void ) 83 { 84 if ( setlocale( LC_CTYPE, "" ) == NULL ) { 85 printf( "Can't set locale.\n" ); 86 return -1; 87 } 88 89 if ( ! XSupportsLocale() ) { 90 printf( "Current locale is not supported\n" ); 91 return -1; 92 } 93 94 /* ユーザ指定のモディファイアを連結する */ 95 if ( XSetLocaleModifiers( "" ) == NULL ) { 96 printf( "Can't set locale modifiers\n" ); 97 return -1; 98 } 99 100 return 0; 101 } 102 103 104 static void 105 xim_init( void ) 106 { 107 dpy = XOpenDisplay( "" ); 108 109 root = DefaultRootWindow( dpy ); 110 screen = DefaultScreen( dpy ); 111 white = WhitePixel( dpy, screen ); 112 black = BlackPixel( dpy, screen ); 113 } 114 115 116 static void 117 xim_create_fontset( void ) 118 { 119 XFontSetExtents* fs_extents; 120 int missing_count; 121 char** missing_list; 122 char* def_string; 123 124 125 fs = XCreateFontSet( dpy, "-*-fixed-medium-r-normal--14-*-*-*", 126 &missing_list, &missing_count, &def_string ); 127 128 XFreeStringList( missing_list ); 129 130 /* フォント情報の取得 */ 131 fs_extents = XExtentsOfFontSet(fs); 132 133 font_w = fs_extents->max_logical_extent.width; 134 font_h = fs_extents->max_logical_extent.height; 135 ascent = - fs_extents->max_logical_extent.y; 136 descent = fs_extents->max_logical_extent.height - ascent; 137 138 /* ウィンドウ幅と高さを設定 */ 139 win_w = font_w * COL; 140 win_h = font_h * ROW; 141 } 142 143 144 static void 145 xim_create_window( void ) 146 { 147 win = XCreateSimpleWindow( dpy, root, 148 0, 0, win_w, win_h, BORDER, black, white ); 149 150 gc = XCreateGC( dpy, win, 0, NULL ); 151 XSetBackground( dpy, gc, white ); 152 XSetForeground( dpy, gc, black ); 153 } 154 155 156 static int 157 xim_init_inputmethod( void ) 158 { 159 /* インプットメソッドをオープンする */ 160 if ( ( im = XOpenIM( dpy, NULL, NULL, NULL ) ) == NULL ) { 161 printf( "Couldn't open input method.\n" ); 162 return -1; 163 } 164 165 166 /* インプットコンテキストの作成 */ 167 ic = XCreateIC( im, 168 XNInputStyle, 169 XIMPreeditNothing | XIMStatusNothing, 170 XNClientWindow, 171 win, 172 NULL ); 173 174 if ( ic == NULL ) { 175 printf( "Couldn't create input context.\n" ); 176 XCloseIM( im ); 177 return -1; 178 } 179 180 return 0; 181 } 182 183 184 static void 185 xim_event_loop( void ) 186 { 187 XEvent event; 188 189 while( 1 ) { 190 XNextEvent( dpy, &event ); 191 192 /* インプットメソッドにイベントを渡す */ 193 if( XFilterEvent( &event, ( Window )NULL ) == True ) { 194 /* イベントはフィルタリングされた */ 195 continue; 196 } 197 198 switch( event.type ) { 199 /* 200 * Expose イベントを用意していないので、ウィンドウが 201 * 隠れた場合などには描画内容は失われる。 202 */ 203 case KeyPress : 204 xim_draw( &event ); 205 break; 206 207 case FocusIn : 208 XSetICFocus( ic ); 209 break; 210 211 case FocusOut : 212 XUnsetICFocus( ic ); 213 break; 214 215 case ClientMessage: 216 if ( event.xclient.data.l[0] == protocol ) { 217 return; 218 } 219 220 break; 221 } 222 } 223 } 224 225 226 static void 227 xim_draw( XEvent* event ) 228 { 229 Status status; 230 KeySym keysym; 231 int cnt = 0; 232 233 /* COL 列の 2 バイト文字分の領域を 1 行の最大文字数とする */ 234 char buffer[ COL * 2 ]; 235 236 static int row = 0; 237 static int col = 0; 238 239 240 /* 入力文字列の取得 */ 241 cnt = XmbLookupString( ic, ( XKeyPressedEvent* )event, 242 buffer, sizeof( buffer ), &keysym, &status ); 243 244 /* 245 * この改行処理を下の if 文より先に行わないと、そちらにイベントを 246 * 拾われて改行処理が行われないので注意 247 */ 248 if ( status == XLookupBoth && keysym == XK_Return ) { 249 /* 最下段に到達したら、先頭行に戻る */ 250 if ( ++ row >= ROW ) { 251 row = 0; 252 } 253 col = 0; 254 255 /* 新しい行をクリアする */ 256 XClearArea( dpy, win, 0, font_h * row, win_w, font_h, False ); 257 } 258 else if ( status == XLookupChars || status == XLookupBoth ) { 259 /* 260 * 入力文字(文字列)の描画 261 * 262 * 描画原点の x 座標を決める際に、font_w / 2 という計算を 263 * しているが、これは 1 バイト文字の幅に相当する。日本語 264 * 文字(文字列)が入力された場合には 2 バイトずつ char の 265 * 値を受け取るので、現在の col に font_w / 2 をかければ 266 * 次に描画すべき描画原点の x 座標を決定できる。 267 */ 268 XmbDrawString( dpy, win, fs, gc, 269 ( font_w / 2 ) * col, font_h * row + ascent, 270 buffer, cnt ); 271 272 col += cnt; 273 } 274 275 /* 276 * status が XBufferOverflow の場合は、用意したバッファに文字列 277 * を格納しきれなかったということなので、この場合は充分なバッファ 278 * を用意した上で再度 XmbLookupString() を実行する必要がある。 279 */ 280 } 281 282 283 static void 284 xim_close( void ) 285 { 286 printf( "Exit application.\n" ); 287 288 XDestroyIC( ic ); 289 XCloseIM( im ); 290 XFreeGC( dpy, gc ); 291 XDestroyWindow( dpy, win ); 292 XCloseDisplay( dpy ); 293 } 294 295 296 /* End of xim_root.c */
○ サンプルプログラム (ルートウィンドウ型) の説明
それでは、サンプルプログラムについて説明していきます。
- 13 〜 15 行目
ここではウィンドウサイズを定義するのではなく、20 行、30 列の文字列を表示できる領域とし てウィンドウの大きさを定義しています。なおこの数値は全角文字の表示数を表しています。
- 17 〜 31 行目
xctrl.c と同様に処理を関数単位で切り分けているため、関数間で共有する変数を static 変数 として宣言しています。25、26 行の font_w、font_h はそれぞれ文字幅、文字高さを格納する変数 です。
文字を描画する際に指定する位置座標は、文字に設定されている描画原点です。ところでこの描 画原点は、font_w および font_h で表される長方形の頂点ではありません (そういう場合もあるか もしれませんが)。ascent および descent はそれぞれ、文字高さのうち描画原点より上 / 下 の部 分の値を指します。
XIM 型変数の im および XIC 型の変数 ic は、それぞれ IM とやりとりするために必要な情報 を格納するためのものです。
- 44 〜 78 行目 ( main() )
xim_root.c の main 関数です。おおまかな処理は、
- ロケールの確立 ( xim_setlocale() )
- 初期処理 ( xim_init() )
- フォントセットの生成、およびフォント情報の取得 ( xim_create_fontset() )
- ウィンドウ / GC の作成 ( xim_create_window() )
- IM の初期化 ( xim_init_inputmethod() )
- ウィンドウ破壊イベントを受け取るための、WM_PROTOCOLS プロパティの設定
- IM が必要とするイベントの問い合わせ ( XGetICValues() )
- イベントの選択 ( XSelectInput() )
- ウィンドウのマップ ( XMapWindow() )
- イベントループ ( xim_event_loop() )
- 終了処理 ( xim_close() )
となります。ウィンドウ破壊イベントに関しては、LECTURE 13 ウィンドウマネージャとのやりとりの時に詳しく説明しますので、ここではそういう処理を やっているんだとくらいに思っていて下さい。
IM は自分が必要とするイベントをアプリケーションから受け取り、イベントに応じて動作します。 そのためには、アプリケーションは XSelectInput() でイベントマスクを設定する際に、IM が必 要とする全てのイベントも設定しなければなりません。IM が必要とするイベントを知るには XGetICValues() を使って IC に設定された属性を取得します。
char* XGetICValues( XIC ic, ... ); ... : XIC の属性値を取得するための可変長引数リストを指定
XGetICValues() の可変長引数には、取得したい属性の名前と取得した属性値を格納するための変 数へのポインタを指定します。引数の終わりには NULL を設定します。ここでは IM が必要とす るイベントマスクを取得するために、XNFilterEvents を指定して unsigned long 型の変数 fevent を格納先のバッファとして指定しています。
エラーが発生しなかった場合、XGetICValues() は返り値として NULL を返します。何らかのエラ ーが発生した場合には、設定できなかった最初の引数の名前が返されます。
- 81 〜 101 行目 ( xim_setlocale() )
LECTURE 11 で説明したロケール関連の処理に加えて、 XSetLocaleModifiers() という関数を実行しています。
char* XSetLocaleModifiers( char* modifier_list ); modifier_list : モディファイアを指定
引数を "" とすることで、環境変数 XMODIFIERS に "@im=kinput2" のように設定された IM の 指定を読み込むことができます ( kinput2 を使用する場合 )。"@im=kinput2" という表現につ いては、XOpenIM() の説明の際に再度ふれます。
- 116 〜 141 行目 ( xim_create_fontset() )
LECTURE 11 で説明したフォント作成処理に加え、 XExtentsOfFontSet() を使ってフォント幅などのフォント情報を取得しています。指定したフォ ントセットで描画される文字のうち、最大の領域を占める文字についての情報を XFontSetExtents 型構造体変数として返します。
XFontSetExtents* XExtentsOfFontSet( XFontSet font_set ); typedef struct { XRectangle max_ink_extent; XRectangle max_logical_extent; } XFontSetExtents;
max_logical_extent の方は、描画した文字が完全に囲まれる長方形領域の情報が格納されます。 max_locical_extent.x および max_logical_extent.y には、それぞれ描画文字の描画原点から 見た左上角の位置が格納されます。
これらを使って、フォント幅および高さを取得しています。また、 ( - max_logical_extent.y ) が ascent ( 画面上方向が y 軸の正方向であるため)、 font_h - ascent が descent となります。最後に フォント幅、高さと文字表示数を使ってウィ ンドウのサイズを決定しています。
- 156 〜 181 行目 ( xim_init_inputmethod() )
IM の通信するための初期処理を行っています。IM によってマルチバイト文字列を入力する場 合には、まず IM に接続しなければなりません。これには XOpenIM() を使います。
XIM XOpenIM( Display* dpy, XrmDatabase db, char* res_name, char* res_class ); db : IM がリソースの検索に使うリソースデータベース res_name : アプリケーションの完全なリソース名 res_class : アプリケーションの完全なクラス名
この関数を実行すると、現在のロケールに適する IM との接続が確立されその接続のハンドル として XIM 型の変数が返されます。IM との接続に失敗した場合は、NULL が返ります。なお、 XIM 型の内容は公開されていません。引数は IM がリソースを検索する際に使用するためのも のですが、xim_root.c では全て NULL としています( 勉強不足のため、NULL だとどうなるの か、どういう場合に NULL 以外を指定するのかについてはまだ僕は分かってませんm(__)m )。
XOpenIM() がオープンするデフォルトの IM はロケールで決定されますが、各ロケールに対し て複数の入力メソッドがあることも考えられます。このため、デフォルトの IM を設定するた めに "im" という名前のロケールモディファイアが用意されています。前述のように環境変数 XMODIFIERS に "@im=input_method_name" と設定することで、希望の入力メソッドが使用され ます。
XOpenIM() によって IM との接続が確立されましたが、IM に対する領域や対話形式の設定、お よびテキスト入出力そのものを行うには、IM のオープンに引き続いて入力コンテキスト( IC ) を作成しなければなりません。IC は、GC が描画に関する属性情報を管理しているのと同様に 国際化テキストの入出力に関する属性情報を管理しています。IC の属性によって IM の挙動や 外観が決定されます。
X 入力コンテキスト ( XIC ) を作成するには XCreateIC() を使います。作成に失敗した場合 は NULL が返ります。
XIC XCreateIC( XIM im, ... ); im : XOpenIM によって接続が確立された IM のハンドラ ... : XIC の属性を設定するための可変長引数リスト
可変長引数リストには属性名と属性値の組を設定し、リストの最後には NULL を設定します。 xim_root.c では、XNInputStyle という属性に対して XIMPreeditNothing および XIMStatusNothing という属性値を、XNClientWindow という属性に対して表示のウィンドウを 指定していることになります。
ここに指定できる属性名およびその内容について、いくつか下表に示します。
定数名 説明 XNInputStyle 入力コンテキストに対して入力メソッドが使用する対話型を指定する ( XIMStyle )。 ここに XIMPreeditNothing および XIMStatusNothing を指定するとルートウィンドウ型の対話形式となる。 XNFontSet 入力メソッドが前編集や状態表示ウィンドウでの文字列描画に使用する フォントセットを指定する ( XFontSet )。 XNClientWindow 入力メソッドが前編集領域および状態表示領域として使用するウィンドウを指定する ( Window )。 XNForeground 入力メソッドが使用する前景ピクセル値を指定する( unsigned long ) XNBackground 入力メソッドが使用する背景ピクセル値を指定する( unsigned long )
- 184 〜 223 行目 ( xim_event_loop() )
主処理を行うイベントループです。XNextEvent() を使ってイベントを読み込んだ後、イベン トの種類によって処理を振り分ける前に XFilterEvent() という関数を実行しています。
Bool XFilterEvent( XEvent* event, Window win ); event : フィルタするイベントを指定 win : フィルタが適用されるウィンドウを指定
IM はキーストローク以外の X イベントを受け取る必要があります( 領域の書き換えのためな ど色々)。そのため、アプリケーション側で処理する前に全てのイベントを IM に渡さなければ なりません。このために XFilterEvent() を使います。
この関数が True を返した場合、イベントは IM によって処理されたのでアプリケーション側 ではこのイベントの処理を行ってはいけません。False を返した場合に限って、イベントの種 類によって処理を振り分けます。
- KeyPress イベント時
226 〜 280 行目の xim_draw() で、入力文字の描画処理を行っています。
KeyPress イベントを受け取った場合、何のキーが押されたのかを判定する必要があります。 英数字の場合は XLookupString() を使いますが、日本語のようなマルチバイト入力の場合は XmbLookupString() を使います。
int XmbLookupString( XIC ic, XKeyPressedEvent event, char* buffer_return, int bytes_buffer, KeySym* keysym_return, Status* status_return ); buffer_return : IM から、構成された(変換後の)マルチバイト文字列が返される bytes_buffer : リターンバッファの容量を指定 keysym_return : NULL でない場合は、イベントから計算された KeySym が返される status_return : 返却情報の種類を示す値が返される
buffer_return に格納された文字列のバイト数が返ります。なお、status_return に格納さ れる値は下表の通りです。
定数名 説明 XLookupBoth KeySym と文字列の両方が返された XLookupChars 入力メソッドにより入力文字列が構成されている XLookupKeySym 文字列ではなく KeySym が返されている XLookupNone 正しい入力が今のところ構成されていない XBufferOverflow 返されるべき入力文字列がバッファの大きさよりも長い。 この場合、関数の返り値が必要なバイト数となるので、 その大きさのバッファを確保して再読み込みを行う xim_root.c では、改行が入力された場合 ( XK_Return という KeySym と改行文字が返され るため、status_return は XLookupBoth となる ) には文字列の表示位置を下にずらしてそ の行を背景色でクリア ( XClearArea() ) しています。また、半角英数字が入力された場合 ( status_return は XLookupBoth ) やマルチバイト文字列が入力された場合( status_return は XLookupChars ) には、現在の表示位置を計算して XmbDrawString() を使って文字列を描 画しています。
なお、xim_root.c では status_return が XBufferOverflow である場合を扱っていません が、この場合は用意したバッファに文字列を格納しきれなかったということなので、充分な バッファを用意した上で再度 XmbLookupString() を実行する必要があります。
- FocusIn / FocusOut イベント時
FocusIn / FocusOut イベントによってウィンドウのフォーカス状態を得たら、それに合わせ て IC のフォーカスを設定する必要があります。そうすることで、IM がフォーカス状態に応 じた編集領域の描画などを行うことが可能となるからです。
IC のフォーカス状態は XSetICFocus() / XUnsetICFocus() を使います。
void XSetICFocus( XIC ic ); void XUnsetICFocus( XIC ic );
- ClientMesage イベント時
LECTURE 13 で説明しますが、ウィンドウマネージャから ウィンドウ破壊要求を受け取った場合は、終了処理を行います。
- KeyPress イベント時
- 283 〜 293 行目 ( xim_close() )
終了処理を行います。IC の解放処理用の XDestroyIC()、IM の解放処理用の XCloseIM() が 新しく出てきています。
XDestroyIC( XIC ic ); XCloseIM( XIM im );
以上でルートウィンドウ型サンプルプログラムの説明は終りです。
○ サンプルプログラム ( off-the-spot 型 )
ルートウィンドウ型のコードの一部を書き換えるだけで、簡単に off-the-spot 型のコードが得られます。xim_root.c を書き換えて off-the-spot 型にした xim_offspot.c について、書き換えられた関数 ( xim_create_font_set()、xim_init_inputmethod() ) のみを示します。
off-the-spot 型の場合、仮名漢字変換入力領域がクライアントウィンドウの一部に確保されます。
--- xim_offspot.c ( 一部 ) ---
... static void xim_create_fontset( void ) { XFontSetExtents* fs_extents; int missing_count; char** missing_list; char* def_string; fs = XCreateFontSet( dpy, "-*-fixed-medium-r-normal--14-*-*-*", &missing_list, &missing_count, &def_string ); XFreeStringList( missing_list ); /* フォント情報の取得 */ fs_extents = XExtentsOfFontSet(fs); font_w = fs_extents->max_logical_extent.width; font_h = fs_extents->max_logical_extent.height; ascent = - fs_extents->max_logical_extent.y; descent = fs_extents->max_logical_extent.height - ascent; /* * ウィンドウ幅と高さを設定 * IM の状態領域および前編集領域を確保するため、 * xim_root.c より 2 行分高さを広げる */ win_w = font_w * COL; win_h = font_h * ( ROW + 2 ); } ... static int xim_init_inputmethod( void ) { /* 前編集領域および状態領域を指す矩形 */ XRectangle preedit_rect, status_rect; /* 前編集領域および状態領域の属性を指定するためのリスト */ XVaNestedList preedit_attr, status_attr; /* インプットメソッドをオープンする */ if ( ( im = XOpenIM( dpy, NULL, NULL, NULL ) ) == NULL ) { printf( "Couldn't open input method.\n" ); return -1; } /* 前編集領域属性の設定 */ preedit_rect.x = 0; preedit_rect.y = font_h * ROW; preedit_rect.width = win_w; preedit_rect.height = font_h; preedit_attr = XVaCreateNestedList( ( int )NULL, XNArea, &preedit_rect, XNFontSet, fs, NULL ); status_rect.x = 0; status_rect.y = font_h * ( ROW + 1 ); status_rect.width = win_w; status_rect.height = font_h; status_attr = XVaCreateNestedList( ( int )NULL, XNArea, &status_rect, XNFontSet, fs, NULL ); /* インプットコンテキストの作成 */ ic = XCreateIC( im, XNInputStyle, XIMPreeditArea | XIMStatusArea, XNClientWindow, win, XNPreeditAttributes, preedit_attr, XNStatusAttributes, status_attr, NULL ); XFree( preedit_attr ); XFree( status_attr ); if ( ic == NULL ) { printf( "Couldn't create input context.\n" ); XCloseIM( im ); return -1; } return 0; } ...
○ off-the-spot 型のルートウィンドウ型との違い
off-the-spot 型の実装がルートウィンドウ型と異なる点は、前編集領域および状態領域についての情報を設定すること、またその 2 つの領域を考慮してウィンドウのサイズを決定しなければならないということです。
まず xim_create_window() のウィンドウサイズを計算している部分で、高さを 2 行分大きくしています。これは、前編集領域および状態領域の 2 行分を増やしたことに相当します。次に xim_init_inputmethod() において、前編集領域および状態領域がウィンドウのどの領域を使用するかを XRectangle 型変数に設定した後に、XVaCreateNestedList() を使って属性リストを作成しています。
XVaNestedList XVaCreateNestedList( int dummy, ... ); dummy : 実際には使用されないダミーの引数を指定 ... : 可変長引数リストの指定 (*)XVaNestedList に関しては typedef void* XVaNestedList; として定義されている。
可変長リストを使用して、領域を設定した XRectangle 型変数を XNArea 属性に、作成したフォントセットを XNFontSet 属性に設定しています。
また XCreateIC を使って IC を作成するに際して、XNInputStyle 属性に XIMPreeditArea および XIMStatusArea の論理和を設定します。そして新しく出てきた XNPreeditAttributes および XNStatusAttributes 属性に、XVaCreateNestedList を使って作った属性リストをそれぞれ設定します。なお、不要になった属性リストは XFree() を使って解放します。
XFree( void* data );
以上の手続きを行うだけで、ルートウィンドウ型から簡単に off-the-spot 型を作成することができます。