text-input
unstable の「text-input-unstable-v3.xml」のプロトコルを解説します。
これは、日本語入力など、入力メソッドからのテキストを受け付けるためのものです。
入力メソッドの管理はサーバー側が行い、サーバーは入力メソッドから受け取ったデータを元に、各クライアントへデータを送る仕組みとなっています。
これは、日本語入力など、入力メソッドからのテキストを受け付けるためのものです。
入力メソッドの管理はサーバー側が行い、サーバーは入力メソッドから受け取ったデータを元に、各クライアントへデータを送る仕組みとなっています。
実行環境
2019年12月時点では、GNOME が対応しています。
KDE では実装されていませんでした。
GNOME の設定パネルを開いて、「地域と言語」を選択します。
「入力ソース」のリストの部分で「+」を押して、「日本語」を選択。
その中の「日本語 (Anthy)」または「日本語 (かな漢字)」を選択して、「追加」ボタンを押します。
リスト内に、選択したものが追加されているので、それをドラッグして先頭に持ってきます。
これで設定は終了です。
ステータスバーのアイコンから選択すれば、入力メソッドを切り替えられます。
場合によっては、入力モードも選択する必要があります。
入力が切り替わらない場合は、「Super (Windows キー) + Space」で切り替えるか、ステータスバーのアイコンから選択してください。
なお、入力メソッドの切り替えだけでなく、入力モードの切り替えも必要な場合があります。
KDE では実装されていませんでした。
GNOME での実行環境
パッケージ
まず、以下のパッケージが必要なので、インストールされていない場合は、インストールします。gnome-control-center | GNOME の設定パネル。 入力メソッドの設定を行うのに必要。 |
---|---|
ibus | ibus の入力メソッドフレームワーク。 GNOME は ibus に対応している。 |
ibus-anthy ibus-kkc | ibus の日本語入力。 好きなものを一つ使ってください。 |
GNOME での設定
※ GNOME 上で上記パッケージを新規インストールした場合は、一度ログアウトして、再ログインしてください。GNOME の設定パネルを開いて、「地域と言語」を選択します。
「入力ソース」のリストの部分で「+」を押して、「日本語」を選択。
その中の「日本語 (Anthy)」または「日本語 (かな漢字)」を選択して、「追加」ボタンを押します。
リスト内に、選択したものが追加されているので、それをドラッグして先頭に持ってきます。
これで設定は終了です。
ステータスバーのアイコンから選択すれば、入力メソッドを切り替えられます。
場合によっては、入力モードも選択する必要があります。
切り替え
GTK+ などのアプリケーション上では、「半角/全角」キーで、直接入力/日本語入力を切り替えられますが、ツールキットを使わずに独自で作られたクライアントの場合、「半角/全角」キーでは切り替えられません。入力が切り替わらない場合は、「Super (Windows キー) + Space」で切り替えるか、ステータスバーのアイコンから選択してください。
なお、入力メソッドの切り替えだけでなく、入力モードの切り替えも必要な場合があります。
プログラム
$ cc -o test p02_textinput.c client.c imagebuf.c \ text-input-unstable-v3-protocol.c -lwayland-client -lrt
scanner で、「text-input-unstable-v3-client-protocol.h」「text-input-unstable-v3-protocol.c」を生成してください。
p02_textinput.c
/****************************** * text-input-unstable-v3 ******************************/ #include <stdio.h> #include <string.h> #include <linux/input.h> #include <wayland-client.h> #include "text-input-unstable-v3-client-protocol.h" #include "client.h" #include "imagebuf.h" //------------- struct zwp_text_input_manager_v3 *g_input_manager = NULL; struct zwp_text_input_v3 *g_text_input; #define INPUTBOX_X 10 #define INPUTBOX_Y 10 #define INPUTBOX_W 200 #define INPUTBOX_H 60 //------------- //----------------------- // zwp_text_input_v3 //----------------------- /* enter */ static void _input_enter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { printf("text_input # enter\n"); zwp_text_input_v3_enable(text_input); zwp_text_input_v3_set_cursor_rectangle(text_input, INPUTBOX_X, INPUTBOX_Y, INPUTBOX_W, INPUTBOX_H); zwp_text_input_v3_commit(text_input); } /* leave */ static void _input_leave(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { printf("text_input # leave\n"); zwp_text_input_v3_disable(text_input); zwp_text_input_v3_commit(text_input); } /* preedit_string */ static void _input_preedit_string(void *data, struct zwp_text_input_v3 *text_input, const char *text, int32_t cursor_begin, int32_t cursor_end) { printf("text_input # preedit_string | text:\"%s\", cursor_begin:%d, cursor_end:%d\n", text, cursor_begin, cursor_end); } /* commit_string */ static void _input_commit_string(void *data, struct zwp_text_input_v3 *text_input, const char *text) { printf("text_input # commit_string | text:\"%s\"\n", text); } /* delete_surrounding_text */ static void _input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *text_input, uint32_t before_length, uint32_t after_length) { printf("text_input # delete_surrounding_text | before_length:%u, after_length:%u\n", before_length, after_length); } /* done */ static void _input_done(void *data, struct zwp_text_input_v3 *text_input, uint32_t serial) { printf("text_input # done | serial:%u\n", serial); #if 0 //周囲のテキストセット zwp_text_input_v3_set_surrounding_text(text_input, "[text]", 1, 5); zwp_text_input_v3_set_text_change_cause(text_input, ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD); zwp_text_input_v3_commit(text_input); #endif } static const struct zwp_text_input_v3_listener g_text_input_listener = { _input_enter, _input_leave, _input_preedit_string, _input_commit_string, _input_delete_surrounding_text, _input_done }; //----------------------- // wl_keyboard //----------------------- static void _keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { } static void _keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { printf("wl_keyboard # enter\n"); } static void _keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { printf("wl_keyboard # leave\n"); } /* キー */ static void _keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { Client *p = (Client *)data; printf("wl_keyboard # key | %s, key:%u\n", (state == WL_KEYBOARD_KEY_STATE_PRESSED)? "press":"release", key); if(state != WL_KEYBOARD_KEY_STATE_PRESSED) return; switch(key) { //ESC キーで終了 case KEY_ESC: p->finish_loop = 1; break; } } static void _keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { } static void _keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay) { } static const struct wl_keyboard_listener g_keyboard_listener = { _keyboard_keymap, _keyboard_enter, _keyboard_leave, _keyboard_key, _keyboard_modifiers, _keyboard_repeat_info }; //------------------------ static void _registry_global( void *data,struct wl_registry *reg,uint32_t id,const char *name,uint32_t ver) { if(strcmp(name, "zwp_text_input_manager_v3") == 0) { g_input_manager = wl_registry_bind(reg, id, &zwp_text_input_manager_v3_interface, 1); } } //--------------- int main(void) { Client *p; Window *win; p = Client_new(0); p->init_flags = INIT_FLAGS_SEAT | INIT_FLAGS_KEYBOARD; p->keyboard_listener = &g_keyboard_listener; p->registry_global = _registry_global; Client_init(p); if(!g_input_manager) { printf("[!] not found 'zwp_text_input_manager_v3'\n"); Client_destroy(p); return 1; } //zwp_text_input g_text_input = zwp_text_input_manager_v3_get_text_input( g_input_manager, p->seat); zwp_text_input_v3_add_listener(g_text_input, &g_text_input_listener, p); //ウィンドウ win = Window_create(p, 256, 256, NULL); ImageBuf_fill(win->img, 0xffff0000); ImageBuf_box(win->img, INPUTBOX_X, INPUTBOX_Y, INPUTBOX_W, INPUTBOX_H, 0xff000000); Window_update(win); // Client_loop_simple(p); //解放 Window_destroy(win); zwp_text_input_v3_destroy(g_text_input); zwp_text_input_manager_v3_destroy(g_input_manager); Client_destroy(p); return 0; }
操作方法
ESC キーで終了します。入力メソッドが有効な状態であれば、そのままテキストを入力できます。
(ウィンドウ上にテキストの表示は行いませんので、イベントの出力だけ確認してください)
zwp_text_input_manager_v3
まず、zwp_text_input_manager_v3 をバインドします。
zwp_text_input_manager_v3 と wl_seat が作成できたら、zwp_text_input_v3 を作成し、ハンドラを設定します。
zwp_text_input_manager_v3 と wl_seat が作成できたら、zwp_text_input_v3 を作成し、ハンドラを設定します。
## zwp_text_input_manager_v3 破棄 void zwp_text_input_manager_v3_destroy( struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3); ## zwp_text_input_v3 作成 struct zwp_text_input_v3 *zwp_text_input_manager_v3_get_text_input( struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3, struct wl_seat *seat); ## ハンドラ設定 int zwp_text_input_v3_add_listener( struct zwp_text_input_v3 *zwp_text_input_v3, const struct zwp_text_input_v3_listener *listener, void *data); ## zwp_text_input_v3 破棄 void zwp_text_input_v3_destroy( struct zwp_text_input_v3 *zwp_text_input_v3);
zwp_text_input_v3 のイベント
void (*enter)(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, struct wl_surface *surface); void (*leave)(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, struct wl_surface *surface); void (*preedit_string)(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end); void (*commit_string)(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text); void (*delete_surrounding_text)(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t before_length, uint32_t after_length); void (*done)(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t serial);
enter
指定サーフェスにテキスト入力フォーカスが来た時。
入力メソッドからの入力を有効にするなら、ここで zwp_text_input_v3_enable() を実行します。
入力メソッドからの入力を有効にするなら、ここで zwp_text_input_v3_enable() を実行します。
leave
指定サーフェスから入力フォーカスが離れた時。
zwp_text_input_v3_disable() で、テキスト入力を無効にします。
zwp_text_input_v3_disable() で、テキスト入力を無効にします。
preedit_string
新しい編集前テキストが来た時。
ここでは、前回取得した編集前テキストを削除して、置き換えます。
クライアントで実際にテキストの表示処理などを行うのは done イベントが来た時なので、送られてきた情報を一旦保存しておきます。
text は UTF-8 文字列。
cursor_begin, cursor_end は、カーソルの先頭と終端の位置。
text の先頭からのバイト数です。
両方が -1 なら、カーソルは非表示にします。
両方が同じ値ならカーソルは線で表示し、異なる値であれば、間の文字列は強調表示します。
ここでは、前回取得した編集前テキストを削除して、置き換えます。
クライアントで実際にテキストの表示処理などを行うのは done イベントが来た時なので、送られてきた情報を一旦保存しておきます。
text は UTF-8 文字列。
cursor_begin, cursor_end は、カーソルの先頭と終端の位置。
text の先頭からのバイト数です。
両方が -1 なら、カーソルは非表示にします。
両方が同じ値ならカーソルは線で表示し、異なる値であれば、間の文字列は強調表示します。
commit_string
確定した文字列が来ます。
delete_surrounding_text
周囲のテキストを削除する必要がある時に来ます。
before_length, after_length は、現在のカーソル位置からの前後のバイト数。
before_length, after_length は、現在のカーソル位置からの前後のバイト数。
done
preedit_string, commit_string, delete_surrounding_text イベントの変更を確定させる時に来ます。
クライアントは、この done イベントが来た時に、送られてきた情報を元にテキストを操作したり、表示したりします。
引数の serial は、このオブジェクトに対する zwp_text_input_v3_commit() の総実行回数と等しくなければなりません。
クライアントは、この done イベントが来た時に、送られてきた情報を元にテキストを操作したり、表示したりします。
引数の serial は、このオブジェクトに対する zwp_text_input_v3_commit() の総実行回数と等しくなければなりません。
処理
各操作については、関数リファレンスの方をご覧ください。
zwp_text_input_v3 の状態変更については、すべて zwp_text_input_v3_commit() で変更を適用させる必要があります。
zwp_text_input_v3_set_cursor_rectangle() で、エディタ部分の範囲を指定しています。
この範囲に重ならない位置に、変換候補ウィンドウが表示されます。
範囲が設定されていない場合は、適当な位置に表示されます。
GNOME の場合は、範囲の左右中央、下端の位置に表示されました。
入力メソッドが有効な間は、入力メソッド側で処理されたキー押しイベントは発生しませんが、キー離しイベントは発生しています。
ここでは、入力ごとに編集前テキストが変化し、カーソルは終端位置に移動しています。
※ プロトコルの説明では、カーソル位置は「バイト単位」となっていましたが、GNOME では「文字単位」になっているようです (2019年12月時点)
変換が確定されたら、commit_string イベントが来るので、done イベント時に、確定テキストを処理します。
確定後は、編集前テキストはない状態になります。
zwp_text_input_v3 の状態変更については、すべて zwp_text_input_v3_commit() で変更を適用させる必要があります。
enter と leave
static void _input_enter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { printf("text_input # enter\n"); zwp_text_input_v3_enable(text_input); zwp_text_input_v3_set_cursor_rectangle(text_input, INPUTBOX_X, INPUTBOX_Y, INPUTBOX_W, INPUTBOX_H); zwp_text_input_v3_commit(text_input); } static void _input_leave(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { printf("text_input # leave\n"); zwp_text_input_v3_disable(text_input); zwp_text_input_v3_commit(text_input); }
zwp_text_input_v3_set_cursor_rectangle() で、エディタ部分の範囲を指定しています。
この範囲に重ならない位置に、変換候補ウィンドウが表示されます。
範囲が設定されていない場合は、適当な位置に表示されます。
GNOME の場合は、範囲の左右中央、下端の位置に表示されました。
イベント
text_input # preedit_string | text:"k", cursor_begin:1, cursor_end:1 text_input # done | serial:3 wl_keyboard # key | release, key:37 text_input # preedit_string | text:"か", cursor_begin:1, cursor_end:1 text_input # done | serial:4 wl_keyboard # key | release, key:30 text_input # preedit_string | text:"かn", cursor_begin:2, cursor_end:2 text_input # done | serial:5 wl_keyboard # key | release, key:49 text_input # preedit_string | text:"かん", cursor_begin:2, cursor_end:2 text_input # done | serial:6 wl_keyboard # key | release, key:49 text_input # preedit_string | text:"かんj", cursor_begin:3, cursor_end:3 text_input # done | serial:7 text_input # preedit_string | text:"かんじ", cursor_begin:3, cursor_end:3 text_input # done | serial:8 wl_keyboard # key | release, key:36 wl_keyboard # key | release, key:23 ▼ 変換 text_input # preedit_string | text:"漢字", cursor_begin:0, cursor_end:0 text_input # done | serial:9 wl_keyboard # key | release, key:57 ▼ 変換候補から選択 text_input # preedit_string | text:"感じ", cursor_begin:0, cursor_end:0 text_input # done | serial:10 wl_keyboard # key | release, key:57 text_input # preedit_string | text:"感じ", cursor_begin:0, cursor_end:0 text_input # done | serial:11 ▼ 確定 text_input # preedit_string | text:"(null)", cursor_begin:0, cursor_end:0 text_input # commit_string | text:"感じ" text_input # preedit_string | text:"(null)", cursor_begin:0, cursor_end:0 text_input # done | serial:25
入力メソッドが有効な間は、入力メソッド側で処理されたキー押しイベントは発生しませんが、キー離しイベントは発生しています。
ここでは、入力ごとに編集前テキストが変化し、カーソルは終端位置に移動しています。
※ プロトコルの説明では、カーソル位置は「バイト単位」となっていましたが、GNOME では「文字単位」になっているようです (2019年12月時点)
変換が確定されたら、commit_string イベントが来るので、done イベント時に、確定テキストを処理します。
確定後は、編集前テキストはない状態になります。
surrounding_text
GNOME 上では、surrounding_text (周囲のテキスト) の処理が確認できなかったので、ここでは省略します。