• 作成:
  • 更新:

WSLgでWaylandネイティブでDvorak入力を行い、勝手にQWERTYになるのに対策し、日本語入力を行う

問題

WSLgでDvorak入力しようとすると様々なストレスに見舞われます。

support customized keyboard layout (with KLC file) · Issue #173 · microsoft/wslg

を見て色々と試してみたんですが、 WSLgのwestonは初回のweston.ini設定のキーボード配列を保持しません。

ログを見ると途中で、

convert_rdp_keyboard_to_xkb_rule_names: matching model=pc105 layout=us variant=(null) options=(null)

のような動作が行われており、 Windows側のキーボード配列にXのキーボード配列を合わせているようです。

多分新しいキーボード接続したときに物理的なキーボードに合わせるようにしてるんでしょうが…

日本語設定で日本語変換しながらハードウェア的にはUSキーボードを使っているけれど、 Windows10(20H2)で、外部ツールなしでDvorak配列を使う - Qiita のようにレジストリを弄っていてDvorak入力を行っている自分のような人間にはこれはとても不便です。

起動時にweston.iniを読み込んでwestonを再起動させてもQWERTYに戻ってしまうのでWaylandではDvorakで入力出来ませんし、 setxkbmap us dvorak.profileとかで実行してXWayland経由で動かせば辛うじてDvorakで入力出来ますが、普通に使っているだけなのに頻繁にDvorakからQWERTYに変化してしまいます。

まあXWaylandで動かせば良いかと思って時たまQWERTYに変化してしまったら新しいシェルを立ち上げることでしばらくは放置していたのですが、 mikutterが確定して妙な挙動をすることを発見しました。

mikutterのpostboxに日本語を入力すると、他のウィンドウをクリックするなどでフォーカスを移そうとしても一回失敗して、もう一度クリックしないと移動しないというものです。 Keyhacでのフォーカス移動キーも2回押さないといけません。 ibusでもfcitxでも同じ問題があります。

しかしWaylandネイティブセッションだとこの問題は起きません。

これは割とストレスに関わるので今一度改善策を考えました。

改善策の前に

WSLgはデフォルトではHiDPI向けの設定をしておらず、 4Kモニタで使うととても読めたものではありません。

調べると、 C:\Users\[your user name]\.wslgconfig の、 WESTON_RDP_DEBUG_DESKTOP_SCALING_FACTOR=100 を弄れば良いとか出てくるんですが、全く効かないことは前に確認済みです。

これがダメなら入力どころでは無いので調べました。

調べたところ、 mikutterのデフォルトであるUIスケーリングの自動値が何故か0.96とかになっていて、これを1.5倍にして、

export GDK_SCALE=2
export GDK_DPI_SCALE=2

とか設定するとちょうど良くなることが分かりました。

xremapはだめ

いっそのことWeston側にこちらがDvorakであることを伝えることを諦めて、全部キー変換してやれば良いと思いました。

Linuxネイティブでは、 mooz/xkeysnail: Yet another keyboard remapping tool for X environment を使っているのですが、これはWaylandに対応していません。

k0kubun/xremap: Dynamic key remapper for X11 and Wayland とかのWayland対応のキーリマッパを使おうかと思ったのですが、

Error: Failed to prepare an output device: No such file or directory (os error 2)

とか吐いてうんともすんとも言わないですね。

まあデバイスファイルにinputデバイス無いですからね… RDPを介して接続しているわけで。

ということは他のキーリマッパを使ってもダメでしょう。

Keyhacで全部変換する

どうしようかなと困惑していましたが、よく考えてみると既にWindows側で動く、 Keyhac というキーリマッパを使っていることに気が付きました。

これでWSLgを使っているウィンドウに対して全部書き換えてしまえば良いのでは?

そんな手間でもないしとりあえず書き換えてみました。

# 文字のリテラル表現と、Keyhacの特殊なキー表現の対応リスト。
keyhac_literal_special_source = [
    ("[", "OpenBracket"),
    ("]", "CloseBracket"),
    ("\\", "BackSlash"),
    ("`", "BackQuote"),
    ("'", "Quote"),
    (",", "Comma"),
    (".", "Period"),
    ("/", "Slash"),
    ("-", "Minus"),
    ("+", "Plus"),
    (";", "Semicolon"),
]


def keyhac_literal_special(literal: str):
    """リテラル表現をKeyhacの表現に変換する。"""
    return next((t[1] for t in keyhac_literal_special_source if t[0] == literal), None)


def keyhac_special_literal(special: str):
    """Keyhacの表現をリテラル表現に変換する。"""
    return next((t[0] for t in keyhac_literal_special_source if t[1] == special), None)


# USキーボードでDvorakとQwertyで差分が生じそうなリスト。
# リテラル表現。
# Dvorak的には=と+は=をプレーンとするが、Keyhac的には+がプレーン。
dvorak = "[]\\`',.pyfgcrl/+aoeuidhtns-;qjkxbmwvz"
qwerty = "-+\\`qwertyuiop[]asdfghjkl;'zxcvbnm,./"


def d2q(key: str) -> str:
    """
    Dvorak to Qwerty.
    """
    literal = keyhac_special_literal(key) or key
    try:
        fi = dvorak.index(literal)
        to = qwerty[fi]
        return keyhac_literal_special(to) or to
    except ValueError:
        return key


def q2d(key: str) -> str:
    """
    Qwerty to Dvorak.
    """
    literal = keyhac_special_literal(key) or key
    try:
        fi = qwerty.index(literal)
        to = dvorak[fi]
        return keyhac_literal_special(to) or to
    except ValueError:
        return key


process_name_of_linux = [
    "mstsc.exe",  # WSLg
    "msrdc.exe",  # WSLg
    "XWin.exe",  # Cygwin/X
    "XWin_MobaX.exe",  # MobaXterm/X
    "XWin_MobaX_1.16.3.exe",  # MobaXterm/X
    "XWin_Cygwin_1.14.5.exe",  # MobaXterm/X
    "XWin_Cygwin_1.16.3.exe",  # MobaXterm/X
    "Xming.exe",  # Xming
    "vcxsrv.exe",  # VcXsrv
    "GWSL_vcxsrv.exe",  # GWSL
    "GWSL_vcxsrv_lowdpi.exe",  # GWSL
    "X410.exe",  # X410
    "Xpra-Launcher.exe",  # Xpra
]


def check_func_linux(window) -> bool:
    """WSLのプロセスらしいものを検出します。"""
    return window.getProcessName() in process_name_of_linux

def set_keymap_dvorak_for_linux(_, keymap_window) -> None:
    """WSLg向けに全てDvorakに変換する。"""
    for key in dvorak:
        f = keyhac_literal_special(key) or key
        t = q2d(key)
        keymap_window[f] = t
        prefixs = ["S", "C", "A"]
        # "C-M-a"みたいなprefix全て合成する。
        for p in [
            "-".join(c)
            for n in range(1, len(prefixs) + 1)
            for c in itertools.combinations(prefixs, n)
        ]:
            keymap_window[p + "-" + f] = p + "-" + t

みたいな感じです。

完全なものは https://github.com/ncaq/keyhac-config/blob/842702bceacb543811b87790db21fc451615545e/config.py を閲覧してください。

成功しました。これでキーボード配列が勝手にいじられる不快感に悩まされることはなくなるでしょう。 xkbの設定の方はQWERTYに戻しておきましょう。何故か中途半端に再起動しても残ってたりするので。

Waylandだとfcitxでもibusでも変換ウィンドウが遠い彼方に行ってしまう

しかし変換ウィンドウがfcitxを使ってもibusを使っても、左上の遠い彼方に向かってしまうことが分かりました。

1モニタ使いなら我慢できるかもしれませんが、自分がmikutterを使っているのは右モニタです。それで変換結果が左のモニタに表示されるのはたまったものではありません。

fctix4だからダメなのかと思ってfctix5を使ってみようと思いましたが、

I2022-10-20 20:58:04.090235 inputcontextmanager.cpp:318] All display connections are gone, exit now.

とか言って起動しません。

エラーメッセージで調べてみたら、 Fcitx5 crashes when window focus changed from LibreOffice · Issue #515 · fcitx/fcitx5 が出てきたので、まだここで行われた修正はUbuntuのfctix5のバージョンには取り込まれて無いので、提案に従ってflatpakでの最新バージョンをインストールして、 flatpak run org.fcitx.Fcitx5 -rdと実行してみましたが、お変わりありません。

流石にこれはfctix5側じゃなくてWSLg側のバグだろうなと思うのでfctix5側に伝えることはありませんでした。

Waylandを無効化したfctix5だとWaylandでも動く

さてじゃあキーホードの勝手切り替えを無効化したことだけを成果にXWaylandに戻るしか無いのかなと思いながら周辺issueを眺めていると、 Unable to boot fcitx5(input method) · Issue #117 · microsoft/wslg のようにfcitx5 --disable=waylandのようにWaylandを無効化すれば起動するって色んなところに書いてますね。

でもWaylandのアプリケーションのために実行したいのにWayland無効化したらダメだよなあ、一応試してみるけど… と思ってこれで試してみたら動きました。

釈然としない。

変換フォントが小さいのはfcitx5のクラシックUI設定のところから編集しましょう。

ちらつきはリアルタイム変換を無効化することでカバー

何故かmozcの変換ウィンドウが一瞬だけ出て消える挙動になっているのでちらつきが発生します。リアルタイム変換を無効化することで少し不便ですが解決しました。

追記: 別にリアルタイム変換を無効化する必要はない

単にタイプのタイミングに合わせてウィンドウが出たり消えたりするだけでした。慣れればそんなに気にならない。それでも従来のようにmozcのレンダラーに任せれば一回消えたりしないでちらつかず変換可能だったのですが。 fcitxではなくmozcのレンダラーを使いたい。

しかしmozcのレンダラーはGTK2なのと、そもそもWaylandでの変換ウィンドウに問題があるのでfcitx5をわざわざ使っているのでそれをすると本末転倒な気もしますね。

UbuntuのリポジトリにあるMozcは現在開発中のものと比較してバージョンがやや古く、Xセッションで使用している候補ウィンドウ(GTK2 Mozcレンダラー)をWaylandセッションで使用すると正しく表示されないため、このようなことになっています。

第689回 Ubuntu 21.10でFcitx 5を使用する | gihyo.jp