Ubuntu 24.04 LTS で変換対象がハイライトされない問題の対策

原因
waylandの文字入力用プロトコルであるtext-input-v3が入力中の文字列(preeditと呼ばれる)のスタイリングに対応していない。waylandのリポジトリにスタイリングを実現するMRが存在するが、作業中のようでマージされるのは当分先だと思われる。

対策1:fcitx5を使う
fcitx5-mozcを使うと変換対象の文字列がハイライトされる。
設定方法
sudo apt install fcitx5-mozc
- 言語サポートアプリで「キーボード入力に使うIMシステム」を「Fcitx 5」に設定する。
理由
IMシステムをfcitx5
に設定すると以下の環境変数が設定される。
XIM=fcitx
XIM_PROGRAM=/usr/bin/fcitx
XIM_ARGS=""
GTK_IM_MODULE=fcitx
QT_IM_MODULE=fcitx
DEPENDS="fcitx"
設定ファイルは/etc/X11/xinit/xinput.d/fcitx
に配置されている。
これらの環境変数に対応するアプリではWaylandを介さず、直接アプリとIMシステムが通信しているのではないか?

対策2:GNOME Extensionで変換対象の文字列をポップアップさせる(未実装)
GNOME Extensionの中にはibusの見た目や動作を変更するものがある。そうすると、文節区切りの変更時に変換対象の文字列をポップアップさせることができる可能性がある。勉強がてら実装するかもしれない。
この拡張機能を実装する方法について、思いつく二つの方法
- 何とかしてibusからpreeditを取得する
- mozcのunix domain socketを読み取って表示する

mozc -> ibus に送信されるプレエディットの情報から変換対象を抽出できた。Looking Glassで実行するとgnome shellのログに表示される。
bhpocid = Main.inputMethod._context.connect("update-preedit-text-with-mode", (_con,text,pos,v,mode) => {
let text_str = text.get_text();
let attr_list = text.get_attributes();
let a;
for (let i=0; (a=attr_list.get(i)); ++i) {
console.log("Bunsetu:",text_str, a.get_start_index(), a.get_end_index(), v);
}
});
ログ確認コマンド
journalctl _COMM=gnome-shell --since "yyyy-m-dd"

どういう条件でどんな装飾がつくのか調査するためのコード
function attrtype(type) {
switch(type) {
case 1:
return "UNDERLINE ";
case 2:
return "FOREGROUND";
case 3:
return "BACKGROUND";
default:
return `UNKOWN ${type}`;
}
}
let bhcontext = Main.inputMethod._context;
bhpocid = bhcontext.connect("update-preedit-text-with-mode", (_con,text,pos,v,mode) => {
let text_str = text.get_text();
let text_with_cursor = text_str.slice(0,pos)+"|"+text_str.slice(pos,-1);
let attr_list = text.get_attributes();
let a;
console.log(`Bunsetu: "${text_with_cursor}" visible=${v} mode=${mode}`);
for (let i=0; (a=attr_list.get(i)); ++i) {
let start = a.get_start_index();
let end = a.get_end_index();
console.log(`Bunsetu: [*] attr_type=${attrtype(a.get_attr_type())} value=${a.get_value()} start=${start} end=${end} "${text_str.slice(start, end)}"`);
}
if (bhcontext != Main.inputMethod._context) {
console.log("LOST CONNECTION! NEED RELOAD.");
}
});
console.log("Bunsetu ID:", bhpocid);

BoxPointer.BoxPointerについて
resource:///org/gnome/shell/ui/boxpointer.js
のBoxPointer
は四角い吹き出しを表示するためのもの
BoxPointer.setPosition()
で吹き出しの指す先を指定する。
片付けるときはboxpointer.destroy()
でよさげ。gnome extensionのdisable()
にこれを書いていなかったときはポップアップが残っていたが、書くと消えるようになった。おそらく親クラスのdestroy()
が効いている。
ではそうしていた。
また、setPositionの引数にするウィジェットもdestroy()していた。

InjectionManagerは使わない方針
https://gjs.guide/extensions/topics/extension.html
InjectionManagerは既存のクラスを変更するという便利なクラス。prototypeを書き換えることで動作を変更するといったもので、理屈はわかる。しかし、サンプルが期待通りに動かない。(GUIでカレンダーや設定をトグルした際にログが出ることを期待した。しかし、実際はGUIからの操作ではログは出ず、lookingGlassから関数を呼び出したときのみログが出た。つまり書き換え自体は成功しているにも関わらず、GUIからの操作には影響がないことになる。)
ややこしいのと、ibusCandidatePopupではうまく使えなさそうな気がするのでこれは使わないことにした。

ポップアップ部分実装で参考にするコード

mozcの場合、変換候補選択中のカーソル位置は|
の位置にある。
確定文字列 | 変換中の文字列 みへんかんもじれつ
そのため、変換中の文字列の長さが変わってもカーソル位置の値は変化しない。

ポップアップと他のUIがかぶる
一時的にポップアップを非表示にしたい。
- ポップアップをクリックすると非表示になる。
- カーソル位置が変わると再度表示。

記事を書いたが、Waylandに対する誤解が含まれていた。当初、text-input-v3
が考え出されたタイミングではハイライトはcursor, anchorの間につければ良いと考えられていたようだ。何も全く考えがなかったわけではないようなのだ。GNOMEの場合ibusがanchor二相当するものを提供しないがためにその考えが機能しなかっただけのようだ。
詳しくはこのIssueを見よ。

KDEではFcitxの作者が上手いことしていた。

update-preedit-text-with-mode
を使うようになったコミット

anchor === pos にしたのはibusから直接的な情報が得られなかったからのようだ。