On-The-Spot
On-The-Spot の入力スタイルを使用する場合、入力中のテキストを、自分のウィンドウ内で表示して描画することができます。
XIMPreeditCallbacks のスタイルを選択し、XIC 作成時に、XNPreeditAttributes で各コールバック関数を設定する必要があります。
XIMPreeditCallbacks のスタイルを選択し、XIC 作成時に、XNPreeditAttributes で各コールバック関数を設定する必要があります。
プログラム
On-The-Spot で入力メソッドからの入力を処理します。
入力メソッドが On-The-Spot に対応していない場合は、実行できません。
<24-im3.c>
入力メソッドが On-The-Spot に対応していない場合は、実行できません。
<24-im3.c>
#include <stdio.h> #include <X11/Xlib.h> #include "util.h" /* 前編集開始 */ static int _preedit_start(XIC ic,XPointer client_data,XPointer call_data) { printf("{start}\n"); //前編集文字列の最大サイズ (-1 で制限なし) return -1; } /* 前編集終了 */ static void _preedit_done(XIC ic,XPointer client_data,XPointer call_data) { printf("{done}\n"); } /* 前編集描画 */ static void _preedit_draw(XIC ic,XPointer client_data,XIMPreeditDrawCallbackStruct *p) { XIMFeedback *fb; int i; printf("--- {draw} ---\n" "caret: %d\n" "chg_first: %d\n" "chg_length: %d\n", p->caret, p->chg_first, p->chg_length); if(!p->text) printf("text: NULL\n"); else { printf("[text]\n" "length: %d\n" "is_wchar: %d\n", p->text->length, p->text->encoding_is_wchar); if(p->text->string.multi_byte) { if(p->text->encoding_is_wchar) printf("string: '%ls'\n", p->text->string.wide_char); else printf("string: '%s'\n", p->text->string.multi_byte); } if(p->text->feedback) { fb = p->text->feedback; printf("feedback:"); for(i = 0; i < p->text->length; i++) printf(" 0x%lx", *(fb++)); printf("\n"); } } printf("--------\n"); } /* 前編集カーソル */ static void _preedit_caret(XIC ic,XPointer client_data,XIMPreeditCaretCallbackStruct *p) { printf("{caret}\n" "position: %d\n" "direction: %d\n" "style: %d\n", p->position, p->direction, p->style); } /* XIC 作成 */ static XIC _create_xic(XIM im,XIMStyle style,Window win) { XIC ic; XIMCallback cb[4]; XVaNestedList valist; cb[0].callback = (XIMProc)_preedit_start; cb[1].callback = (XIMProc)_preedit_done; cb[2].callback = (XIMProc)_preedit_draw; cb[3].callback = (XIMProc)_preedit_caret; valist = XVaCreateNestedList(0, XNPreeditStartCallback, cb, XNPreeditDoneCallback, cb + 1, XNPreeditDrawCallback, cb + 2, XNPreeditCaretCallback, cb + 3, (void *)0); //XIC 作成 ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, win, XNFocusWindow, win, XNPreeditAttributes, valist, (void *)0); XFree(valist); return ic; } int main(int argc,char **argv) { Display *disp; XSetWindowAttributes attr; Window win; XEvent ev; XIM im = None; XIC ic = None; XIMStyle style; unsigned long mask; disp = XOpenDisplay(NULL); if(!disp) return 1; init_locale(); //IM 開く im = XOpenIM(disp, NULL, NULL, NULL); if(!im) { printf("failed XOpenIM\n"); goto END; } style = get_xim_style(im, XIMPreeditCallbacks); if(!style) { printf("unsupported XIMPreeditCallbacks\n"); goto END; } //ウィンドウ作成 attr.background_pixel = 0; attr.event_mask = ButtonPressMask | FocusChangeMask | KeyPressMask | KeyReleaseMask; win = XCreateWindow(disp, DefaultRootWindow(disp), 0, 0, 200, 200, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWBackPixel | CWEventMask, &attr); printf("window: 0x%lx\n", win); //IC 作成 ic = _create_xic(im, style, win); if(!ic) goto END; //イベントマスク mask = 0; XGetICValues(ic, XNFilterEvents, &mask, (void *)0); XSelectInput(disp, win, mask | attr.event_mask); //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); if(XFilterEvent(&ev, None)) continue; switch(ev.type) { case KeyPress: printf("[KeyPress] keycode(%d)\n", ev.xkey.keycode); put_xic_text(&ev, ic); break; case KeyRelease: printf("[KeyRelease] keycode(%d)\n", ev.xkey.keycode); case FocusIn: XSetICFocus(ic); break; case FocusOut: XUnsetICFocus(ic); break; case ButtonPress: goto END; } } // END: if(ic) XDestroyIC(ic); if(im) XCloseIM(im); XCloseDisplay(disp); return 0; }
コールバック関数の設定
XCreateIC() 時に、XNPreeditAttributes で前編集に関する属性を設定します。
値は、XVaCreateNestedList() で作成した XVaNestedList 型 (void *) を指定します。
各コールバックの値は、XIMCallback 構造体のポインタで指定します。
client_data は、callback 関数の2番目の引数に渡されます。今回は client_data は使わないので、設定していません。
値は、XVaCreateNestedList() で作成した XVaNestedList 型 (void *) を指定します。
typedef void (*XIMProc)(XIM, XPointer, XPointer); typedef struct { XPointer client_data; //関数に渡される値 XIMProc callback; //関数 } XIMCallback;
各コールバックの値は、XIMCallback 構造体のポインタで指定します。
client_data は、callback 関数の2番目の引数に渡されます。今回は client_data は使わないので、設定していません。
XNPreeditStartCallback
前編集が開始された時 (最初の文字が入力された時) に来る関数です。
戻り値で、前編集文字列の最大サイズを指定できます。-1 の場合は、制限なしとなります。
戻り値で、前編集文字列の最大サイズを指定できます。-1 の場合は、制限なしとなります。
XNPreeditDoneCallback
前編集が終了した時 (すべてのテキストが確定した時や、キャンセルされた時) に来る関数です。
XNPreeditDrawCallback
前編集のテキストが変化する時や、各文字の描画スタイルが変更される時に来る関数です。
XNPreeditCaretCallback
前編集内で文字カーソルを移動する必要がある時に来る関数です。
ただし、この関数は全く来ない場合があります。その場合、draw 関数の方でカーソル位置を判断します。
ただし、この関数は全く来ない場合があります。その場合、draw 関数の方でカーソル位置を判断します。
前編集描画のコールバック関数
XIMPreeditDrawCallbackStruct 構造体に、関連する情報が設定されています。
text, feedback, text->string のポインタは、NULL の場合があるので、注意してください。
上記のテキスト編集を行った後、文字カーソル位置を、caret の文字位置に移動させます。
最初に 'a' を入力すると、前編集が開始されます。
chg_length == 0 なので、chg_first (0) の位置に 'あ' を挿入することになります。
文字を挿入した後、caret == 1 で、文字カーソルの位置を1文字の位置に移動します ('あ' の後にカーソルが来る)。
その後 Delete キーを押すと、text == NULL で chg_length == 1 なので、chg_first (0) から1文字を削除します。
文字カーソル位置は caret == 0 で、先頭位置になります。
この場合、編集中の文字がなくなるので、前編集が終了します。
その後 'a' 'a' と続けて入力した場合、chg_first (0) から1文字分のテキストを、text の文字列 ('ああ' の2文字) に置き換えます。
これは、すでにある 'あ' の文字を削除して、'ああ' に置き換えるということです。
text->encoding_is_wchar が True の場合、文字列はワイド文字列で、False の場合はマルチバイト文字列です。
入力メソッドのロケールによる文字列となります。
text->length は文字数です (マルチバイト文字列の場合、バイト数ではない)。
typedef struct _XIMPreeditDrawCallbackStruct { int caret; //カーソルの文字位置 int chg_first; //変更される文字位置 int chg_length; //変更される文字数 XIMText *text; } XIMPreeditDrawCallbackStruct; typedef struct _XIMText { unsigned short length; //文字数 XIMFeedback *feedback; Bool encoding_is_wchar; //string がワイド文字列か union { char *multi_byte; wchar_t *wide_char; } string; } XIMText; typedef unsigned long XIMFeedback;
text, feedback, text->string のポインタは、NULL の場合があるので、注意してください。
テキストの変更
- <text != NULL で chg_length != 0 の場合>
chg_first 文字位置から chg_length 文字分のテキストを、text の文字列に置き換えます。 - <text != NULL で chg_length == 0 の場合>
chg_first 文字位置に、text の文字列を挿入します。 - <text == NULL で chg_length != 0 の場合>
chg_first 文字位置から chg_length 文字分のテキストを削除します。
上記のテキスト編集を行った後、文字カーソル位置を、caret の文字位置に移動させます。
例
# 'a' を入力した場合 caret: 1 chg_first: 0 chg_length: 0 [text] length: 1 is_wchar: 0 string: 'あ' feedback: 0x2 # DEL キーで1文字削除した場合 caret: 0 chg_first: 0 chg_length: 1 text: NULL # 'a' の後に 'a' を入力した場合 caret: 2 chg_first: 0 chg_length: 1 [text] length: 2 is_wchar: 0 string: 'ああ' feedback: 0x2 0x2
最初に 'a' を入力すると、前編集が開始されます。
chg_length == 0 なので、chg_first (0) の位置に 'あ' を挿入することになります。
文字を挿入した後、caret == 1 で、文字カーソルの位置を1文字の位置に移動します ('あ' の後にカーソルが来る)。
その後 Delete キーを押すと、text == NULL で chg_length == 1 なので、chg_first (0) から1文字を削除します。
文字カーソル位置は caret == 0 で、先頭位置になります。
この場合、編集中の文字がなくなるので、前編集が終了します。
その後 'a' 'a' と続けて入力した場合、chg_first (0) から1文字分のテキストを、text の文字列 ('ああ' の2文字) に置き換えます。
これは、すでにある 'あ' の文字を削除して、'ああ' に置き換えるということです。
text->encoding_is_wchar が True の場合、文字列はワイド文字列で、False の場合はマルチバイト文字列です。
入力メソッドのロケールによる文字列となります。
text->length は文字数です (マルチバイト文字列の場合、バイト数ではない)。
各文字の描画スタイル
前編集テキストは、変換の状況によって、一部の文字を強調表示する必要があります。
各文字のスタイルは、text->feedback の値で判断できます。
text->feedback == NULL の場合、すべての文字は通常のスタイルとして扱います。
NULL でない場合は、text->length 個の配列となり、string の各文字に対応します。
なお、string のポインタ (multi_byte or wide_char) が NULL の場合は、テキストの変更がなく、chg_first の位置から text->length 文字数分の各文字のスタイルのみを変更することになります。
XIMReverse は、前景色と背景色を反転。
XIMUnderline は、下線。
XIMHighlight, XIMPrimary, XIMSecondary, XIMTertiary は、XIMReverse・XIMUnderline とは別の形で強調表示を行います。
例えば、「かいとうしてまつ」と入力して変換した場合、「回答して」が反転、「待つ」が下線になります。
この状態で右矢印キーを押すと、変換対象が「待つ」に切り替わります。
先頭から6文字が「回答して待つ」に置き換わり (結果として変わらない)、カーソル位置は「待つ」の先頭位置に変わります。
また、feedback の値が変化しています。
入力メソッドによって動作は異なるかもしれませんが、このように、string に対して、常に現在の前編集テキストすべてが指定されているような場合は、内部で文字列を保持する必要はなく、string の文字列を表示すればよいことになります。
後は、feedback の値に基づいて、各文字ごとにスタイルを変えて描画します。
各文字のスタイルは、text->feedback の値で判断できます。
text->feedback == NULL の場合、すべての文字は通常のスタイルとして扱います。
NULL でない場合は、text->length 個の配列となり、string の各文字に対応します。
なお、string のポインタ (multi_byte or wide_char) が NULL の場合は、テキストの変更がなく、chg_first の位置から text->length 文字数分の各文字のスタイルのみを変更することになります。
#define XIMReverse 1L #define XIMUnderline (1L<<1) #define XIMHighlight (1L<<2) #define XIMPrimary (1L<<5) #define XIMSecondary (1L<<6) #define XIMTertiary (1L<<7) #define XIMVisibleToForward (1L<<8) #define XIMVisibleToBackward (1L<<9) #define XIMVisibleToCenter (1L<<10)
XIMReverse は、前景色と背景色を反転。
XIMUnderline は、下線。
XIMHighlight, XIMPrimary, XIMSecondary, XIMTertiary は、XIMReverse・XIMUnderline とは別の形で強調表示を行います。
例
caret: 0 chg_first: 0 chg_length: 8 [text] length: 6 is_wchar: 0 string: '回答して待つ' feedback: 0x1 0x1 0x1 0x1 0x2 0x2
例えば、「かいとうしてまつ」と入力して変換した場合、「回答して」が反転、「待つ」が下線になります。
この状態で右矢印キーを押すと、変換対象が「待つ」に切り替わります。
caret: 4 chg_first: 0 chg_length: 6 [text] length: 6 is_wchar: 0 string: '回答して待つ' feedback: 0x2 0x2 0x2 0x2 0x1 0x1
先頭から6文字が「回答して待つ」に置き換わり (結果として変わらない)、カーソル位置は「待つ」の先頭位置に変わります。
また、feedback の値が変化しています。
入力メソッドによって動作は異なるかもしれませんが、このように、string に対して、常に現在の前編集テキストすべてが指定されているような場合は、内部で文字列を保持する必要はなく、string の文字列を表示すればよいことになります。
後は、feedback の値に基づいて、各文字ごとにスタイルを変えて描画します。