vscodeの内部ターミナルでの日本語入力問題とその対処
問題内容
自分の環境: NixOS, Wayland, Hyprland, fcitx5(とmozc) system全体の設定用flake.nixにおける関連するコード部分は以下の部分で、これはchromiumのozone layerを有効にしますよ〜って感じの環境変数。
environment.sessionVariables.NIXOS_OZONE_WL = "1";
問題内容: vscodeの内部ターミナルで日本語を入力すると、二重に入力される。 例: "あいう" を入力してEnterで確定 -> "あいうあいう"って入力されちゃう。ひえ〜。
原因究明!
ChatGPTくんとGoogle検索の結果、Waylandでのキーボード入力の処理の流れはこんなっぽい。 `なんかドライバーとか -> evdev -> libinput -> compositor -> IME -> client` ドライバー部分はまあいいとして、evdevとlibinputはめっちゃ簡単に動作確認できるらしいからちょっと見てみる。 内部ターミナルの問題でここが原因ってあんまり考えられないけど... evdevの動作確認はevtestってやつ使えばいいっぽい。 evtestを起動して、問題の箇所で"あ"を入力してみるよ。
Event: time 1745644947.193073, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70004 Event: time 1745644947.193073, type 1 (EV_KEY), code 30 (KEY_A), value 1 Event: time 1745644947.193073, -------------- SYN_REPORT ------------ Event: time 1745644947.239034, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70004 Event: time 1745644947.239034, type 1 (EV_KEY), code 30 (KEY_A), value 0 Event: time 1745644947.239034, -------------- SYN_REPORT ------------
うーん。問題なく、KEY_Aは1回だけ押されて離されてそう。最初のがKEY_Aを押したときのlogで、2回目のやつが離したときのlogっぽい。 そんじゃ次はlibinput これは、libinput debug-eventsってやつ使えばいいっぽい。
event28 KEYBOARD_KEY +6.731s *** (-1) pressed event28 KEYBOARD_KEY +6.833s *** (-1) released
また、"あ"を入力してみているんだけど、1回だけ押されて離されている。うんうん、あってるね。 evdev、libinputの後のcompositorってのは、waylandのcompositorのことで、ここでは、hyprland(の内部にある?)やつっぽい。 ここで、簡単にWaylandの解説をしておくと、これはディスプレイサーバのプロトコルで、linux kernelとmonitorの間でなんかいい感じに動いて画面を表示できるようにしてくれているやつ。 もともと、40年くらいの歴史のあるX Window Systemっていうやつが一般的にlinuxで使われるディスプレイサーバのプロトコルだったんだけど、あまりにも古いのと、なんか根本的に脆弱らしくて最近はWaylandへの移行が流行ってるっぽいよ。 compositorとvscodeの間の動作確認はWAYLAND_DEBUGみたいなノリの環境変数でできたよ。ログは消しちゃったんだけど、zwp-text-input-v3で接続しようとしてできなくて、v1使ってますみたいなログだったよ。 このtext-input-v1,v3っていう適当な名前のやつは、wayland上のクライアント(ここではvscode)とIMEとの間で使われるプロトコル。 vscodeはelectron製で、描画エンジンとしてchromiumが使われているんだけど、このchromiumとIMEとのやり取りをする部分のプロトコルがtext-input-v1とv3で、vscodeでは、v1には結構前に対応済みで、v3は最近対応したみたい。 うーんいかにも怪しい。 v3に対応したはずなのに、ログではv1使っているから、そこがミスマッチでおかしくなってるんじゃないかな? vscodeの内部のターミナル部分はxterm.jsというライブラリを利用して実装されているから、こいつをビルドしてみて、動作確認するよ。 xterm.jsの中で、キーボード入力で発火しそうな感じの関数にログを仕込んで、"あ"をうつと...
input "a" input "" compositionend "あ" input "あ"
うおお、compositionend(IME確定時の処理)の後にinputが発火している! お前か... 念の為、システムをX11にして、同じことしたり、macで同じことしたりするとcompositionendの後にinputは発火しないことが確認できた。 さらに、このlogを仕込んだxterm.jsを組み込んで、vscodeをローカルでビルドして、そいつに、 `--enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime`っていう魔法のopitonを与えて起動すると、waylandのtext-input-v1を使用してxterm.jsとやり取りをすることがわかったよ。 そんで、事前の予想通り、text-input-v1でやり取りさせると、日本語が二重に入力された。 そんでそんで、`--wayland-text-input-version=3`も追加すると、text-input-v3でやり取りをさせられることが判明! 試してみると...日本語二重になってない!うおおこれだ!
ここまでのまとめ
なんかvscodeの起動時引数に`--wayland-text-input-version=3`を追加したらいけそう。
だめでした
でもだめでした。v3にすると、ターミナル部分はたしかにちゃんと動作するんだけど、エディター部分のIMEで日本語を1文字入力した後、backspaceを入力したとき、エディター部分がめっちゃバグることがわかったよ。 v3に対応した(バグがないとは言ってない)ってことか... `https://github.com/microsoft/vscode/issues/242799`ちょっと調べたらちゃんとissue挙がってた。ガッツリOpen。
自分でビルド
てわけで、text-input-v1を使用しつつ、xterm.jsに無理やり修正を入れて、vscodeにそれを合体させてビルドするってことにしたよ。 nixosだから、共有ライブラリがないって言われまくって大変だったけど、chatgptにムチャを言って、なんとかビルド設定を作って、nixosのpackageとしてビルドさせることに成功! そのflake.nixがこれです。成し遂げた...!
{ description = "Package code-oss from VSCode-Linux-x64 directory"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/24.11"; outputs = { self, nixpkgs }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; code-oss = pkgs.stdenv.mkDerivation { name = "code-oss"; src = ./VSCode-linux-x64; nativeBuildInputs = [ pkgs.makeWrapper ]; phases = [ "installPhase" ]; installPhase = '' # Create target directory mkdir -p $out/lib/code-oss # Copy all files from source directory to output directory cp -r $src/* $out/lib/code-oss/ # Ensure the binary is executable chmod +x $out/lib/code-oss/code-oss mkdir -p $out/bin makeWrapper $out/lib/code-oss/code-oss $out/bin/code-oss \ --set LD_LIBRARY_PATH "${ pkgs.lib.makeLibraryPath [ pkgs.glib pkgs.nss pkgs.nspr pkgs.dbus pkgs.libdbusmenu pkgs.gtk3 pkgs.expat pkgs.udev pkgs.at-spi2-atk pkgs.at-spi2-core pkgs.cups pkgs.libdrm pkgs.libxkbcommon pkgs.mesa pkgs.libGL pkgs.libGLU pkgs.xorg.libxcb pkgs.xorg.libX11 pkgs.xorg.libXcomposite pkgs.xorg.libXdamage pkgs.xorg.libXext pkgs.xorg.libXfixes pkgs.xorg.libXrandr pkgs.alsa-lib pkgs.cairo pkgs.pango pkgs.libepoxy pkgs.gdk-pixbuf ] }" ''; }; in { packages.${system}.code-oss = code-oss; apps.${system}.code-oss = { type = "app"; program = "${code-oss}/bin/code-oss"; }; defaultPackage.${system} = code-oss; }; }
どこからともなく現れるx11くん
でもだめでした。開発用の実行コマンドだと、めっちゃいい感じに動くんだけど、ビルドをちゃんと行うと、なぜか一切のIME起動ができなくなるっていう問題が発生してしまったよ。 意味がわかんなかったけど、この環境下と普段のvscodeでの環境の環境変数の差分を確認したところ、`GDK_BACKEND`が、自前ビルドvscodeだと`x11`になっていることが判明した... waylandですらない!? text-input-v1を使ってくれ〜。 これの対処が全然わかんなくて、うんうん唸ってたけど、ChatGPTの†推論†を使ったら、`ELECTRON_ENABLE_WAYLAND=1`っていう環境変数が必要ではないか?っていう案をいただきました。 ほんとぅお〜?って思って試してみたら動きました、完璧に!!! す、すごい...
さようならvim、こんにちはneovim
ってわけで、自分でビルドしたvscodeを使うことでターミナルの問題は解決したんだけど、また別の問題が発生... Editor部分で日本語を入力して変換した後に確定すると、「{変換前の文字}{変換後の文字}」になるっていう別の問題が発生しちゃったよ。 よくわかんなかったんだけど、拡張機能全部offにしたら治ったから、これは拡張機能の問題だね。 そんで原因は...vimくんでした。vscodeのvim拡張機能使ってたんだけど、こいつ結構な頻度でバグるんだよね。また君かぁ。 ちょうどneovim拡張機能にも興味出てきていたから、この際思い切って乗り換えてしまうことを決意。 vimくんとバイバイして、neovimくんをお迎えしました。日本語の変なバグもなくていい感じ。
まとめ
だらだらっと無意味に長文にしてしまったけど、ようやく全部解決できてよかった! (自前ビルドのvscodeを起動するときに発生する大量のエラーが気になるけど)問題解決!!!名探偵の気分だぜ。 v3のバグが治るまでは自前vscode生活かな〜。これはこれで楽しいからいいか。