NetBSD 10.0で複数キーボードを別配列で使う

はじめに

この記事はNetBSD Advent Calendar 2023の8日目の記事です。

2019年のAdvent Calender 15日めに書いた、NetBSDで複数キーボードを使うについて、NetBSD 10.0では同じ方法で素直に設定出来ないことが分かりました。

これはNetBSD 10.0(以降)での設定方法についての覚書き+αです。

なお、NetBSD 10.0は2023/12/6現在、まだRC1です。

NetBSDでのWscons keyboardとX

2019年の記事では以下の様にして異なるキー配列を同時に使えるようにしていました。

NetBSD 10.0でのキーボードの認識

NetBSD 10.0以降でも上記は基本的に変りません。ですが、この設定をしたNetBSD 9からupdateした環境では上記の設定ではうまく機能しませんでした。

最初設定がこわれたかと思ったのですがそうではありませんでした。

とりあえず状況を整理します。

まずコンソールでは以下に設定しています。

xorg.confでは以下の様に設定しています。

Section "ServerLayout"
        Identifier     "X.org Configured"
        Screen      0  "Screen0" 0 0
        InputDevice    "Mouse0" "CorePointer"
        InputDevice    "Keyboard0" "CoreKeyboard"
        InputDevice    "Keyboard1" "CoreKeyboard"
EndSection
(中略)
Section "InputDevice"
        Identifier  "Keyboard0"
        Driver      "kbd"
        Option      "Protocol" "wskbd"
        Option      "Device" "/dev/wskbd0"
        Option      "XkbModel" "jp109"
        Option      "XkbLayout" "jp"
        Option      "XkbOptions" "ctrl:nocaps"
EndSection

Section "InputDevice"
        Identifier  "Keyboard1"
        Driver      "kbd"
        Option      "Protocol" "wskbd"
        Option      "Device" "/dev/wskbd1"
        Option      "XkbModel" "pc105"
        Option      "XkbLayout" "us"
        Option      "XkbOptions" "ctrl:nocaps"
EndSection

xorg.confはこのままでNetBSD10に(ユーザランド含め)バージョンアップし、この状態でXを起動すると、xorg.confのkeyboard設定に関わらず、xinputでは1つのキーボードになり、配列はPC内蔵キーボードもUS配列になってしまいました。

% xinput
⎡ Virtual core pointer                          id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ /dev/wsmouse                              id=7    [slave  pointer  (2)]
⎣ Virtual core keyboard                         id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ /dev/wskbd                                id=6    [slave  keyboard (3)]
%

また、ctrl:nocapsとしてCapsLockキー無効に設定しているのに、しっかりとCapsLockキーが有効になっており、かつ、外付けUSキーボード側は入れ替えをしていなかったためか、ノートPC内蔵キーボードのAの左のキーがCapsLockになってしまいました。

もちろんX起動後にsetxkbmapやxmodmapで変更はできるのですが、いかんせんいくつキーボードを繋げていても1つになってしまい、個々のキーに対して設定することができません。「日本語キーボードでもUS配列で使えばいいだけじゃん」と思われるでしょうが、一部日本語キーボードにしか存在しないキーを要求された場合に変なキーコンビネーションを割り当てるよりもこちらの方が確実なのであえてこうしていたのですが(苦労すれば何とかなる、というのはその通りです)。

このままではNetBSD 10.0にしたことでとても使いにくくなってしまいます。

原因

しばらく調べていくと何となく原因が分かってきました。

NetBSD 10.0に含まれるxorgではwsmux(4)をFreeBSDのhaldやLinuxのudevによる動的コンフィグレーションデバイスとしてみなすようになっているようです。

これらは動的コンフィグレーションとなっていて、例えばキーボードの配列指定のような設定はxorg.confでは行わず、動的コンフィグレーションマネージャがデバイスの情報を通知することで行われるようです(「ようです」というのはソース上で正確に確認したわけではなく、また、これらをサポートするOSを私が良く分かっていないためです)。 本来はXserverに対してデバイスの変更(物理的な接続や切断等)が検出された場合に動的にデバイスを作成したり、適切に設定するべきもの、だと思います。

しかしwsmux(4)は、OSのカーネルレベルで複数のデバイスを1つにまとめており、wsmuxを使うプログラムに対して特に動的な通知をすることも、そういうインターフェースもなく、複数のキーボードが繋がるwsmuxは単に1つのキーボードとしてしか見えません。結果としてX上のX inputデバイスとしては1つしかありません。

そのため、いくらwsmuxに実際は2つのキーボードが存在し、且つ、それらが別々のキーマップ設定をしていてもwsmuxは1つのキーボードとして認識され、wsmuxを通して得られる単一のマッピングがされているとしか認識することができません(ついでに言えば、その得られる単一のマッピングが何になるかは実はよく分かりません。最後に設定したwskbdのものになるような気がしますが確認できていません。なんというか、「時の運」に思えます)。

xinputでキーボードデバイスが1つしか存在しないのはその証拠とも言えます。結果として各キーボード毎にどんな設定をしようが、/dev/wskbd(=/dev/wsmux1)から得られる情報だけで起動し、その時に得られた情報だけでXの入力キーボードデバイスが作成されていることになります。

これは私の様に複数のデバイスをそれぞれ設定している場合には非常に困ったことになります。私みたいに配列が異なるキーボードをそれぞれ毎に異なる配列で使いたい、というのは特殊かもしれませんが、各種USBデバイスでは特殊なボタンを持ち、且つ、それらをキーボードであると認識させる場合もたくさんあります(私の持っているUSBタブレットのショートカットキーもそうなっています)。その場合はそれぞれにキー割り当てしたいと思うのは当然でしょう。

このままでは本当に使い物になりません。

回避策

さて、実はまともに機能するとは思えないwsmux(4)ベースの全然動的ではない動的コンフィグレーションを無効にして、従来通りの動作にすることができます。

xorg.confのトップレベルに以下を追加します。

Section "ServerFlags"
        Option "AutoAddDevices" "False"
EndSection

これを設定すれば、動的コンフィグレーションは抑止されます。その結果、従来通り複数のキーボードを静的にxorg.confに記載してあればその通りに動作します。nocapsの設定もわざわざX起動後にxmodmapで設定しなくても最初から有効にできます。

簡単ですね。

NetBSD 9.xまででxorg.confに各種設定をしてX起動時の設定をしている場合にはこの設定を加えるのがおすすめです。

本質的解決策の妄想

さて、とりあえず従来の動作にはできたのですが理想には程遠い状態です。

理想的な動作としては以下のようなものだと思います。

おそらくFreeBSDのhaldやLinuxのudevでは当たり前に出来ているのでしょう。

NetBSDでやるとすれば以下の用になるでしょうか。

  1. devpubd(8)でwskbdの変更を検出する
  2. wskbdに実際に接続されているデバイス情報を取得する(ukbd0等)
  3. 必要であればデバイスに接続された物理的なハードウェアモデル情報を得る
  4. ハードウェアモデルに応じて設定したい情報を設定ファイル等に保存しておき、その内容を反映する
  5. Xが動いていたらxinputへデバイス登録or有効化や削除or無効化を行う

ただし、現在のNetBSDでは1はできても2でwskbdの実際のデバイスを知るのがものすごく大変(attach driverを逆順にたどる必要がある)、3でもukbd等の実際のデバイスを特定するのもその接続元のusb bus→uhubからたどらないとわからない、5のXへの動的変更処理を追加しなければならない等とても大変そう。

妄想としては現在カーネル内で完結しユーザ側からの情報取得や制御が著しく制限されているwscons仮想デバイス管理の一部をユーザ側に開示するIFと、USBのような多種モデルが存在するデバイスを簡単に区別出来るようなIFを追加し、それを元に管理デーモンを作成、X inputデバイスはこれらのIFを用いて情報を適時反映するようなものを作成する必要があるんじゃないかなぁ、という気がします。それはFreeBSDのhald相当じゃないの?と言われるかもしれませんが、そうかもしれません。


この記事は2023/12/8におおしまやが書きました。