これはNetBSD Advent Calendar 2019の15日めが空いていたので埋めた記事です。
複数の異なる配列のキーボードを同時に繋げてNetBSDで使う方法について書きます。
多くの機種用のNetBSDではwscons(4)フレームワークという抽象化レイヤに準拠するデバイスが採用されており、キーボードやマウスはwsmux(4)に接続され、通常は概ね何の苦労もなく複数の物理デバイスを同時に接続し同時に使うことができます。
ただし、すべて同一の配列のキーボードを使う限りにおいて。
例えばノートPC本体内蔵(AT/PS2接続)の日本語キーボードに対し、/etc/wscons.confに
encoding jp
とでも設定しておけば、接続したキーボードをすべてjp配列として扱えるようになります。しかし、外付けUSBキーボードとしてUS配列(101キーボードとか呼ばれるもの)のUSBキーボードを接続すると、こちらもJP配列がマッピングされてしまいます。この状態ではどんなに頑張ってもそのままでは外付けキーボード側から'\’'|'(JP配列でBSキーの左)や、'_'(JP配列で右SHIFTキーの左)を入力できなくなってしまいます。なぜならこれらのキーは、JPキーボードにしか存在しないキーコードを返してくるからUSキーボードでは入力のしようがないのでした。
ではということで、先のwscons.confの設定でus配列にすると本体内蔵のキーボードもUS配列になってしまいます(尤もそれで入力できないキーは無いためある意味それはそれで印字を気にしなければまったく問題なく使えますが)。
ではどうするかというと、wsmux(4)経由ではなくwskbd(4)として認識された/dev/wskbd? に対してキーコードを設定します。
まず、USBキーボードを接続した際に以下の用に出力されます。
uhidev3 at uhub6 port 3 configuration 1 interface 0 uhidev3: vendor 04d9 (0x4d9) USB Keyboard (0x125), rev 1.10/1.11, addr 4, iclass 3/1 ukbd0 at uhidev3 wskbd1 at ukbd0 mux 1
wskbd1として認識されていることがわかります。このデバイスは/dev/wskbd1というスペシャルデバイスを使ってwsconsctl(8)コマンドで個別にキーマップを指定できます(操作にはroot権限が必要です)。
# wsconsctl -k -f /dev/wskbd1 -w encoding=us
これで外付けキーボード側はUS配列、本体側はJP配列になりました。
なお、wsconsctl(8)に-fでデバイスをつけないと、wsmux(4)である/dev/wskbdに対して指定され全てのキーボードが対象になってしまうので注意が必要です(/etc/wscons.confでの設定はこのwsmux(4)に対して行われてしまうため、個別の設定はそのままではできません)。
これの難点は以下の通り。
などでしょうか。まぁできないよりはマシと言ったところです。
次はテキストコンソールではなくX Window System上です。wscons(4)での設定はX環境には(ほとんど)引き継がれません。
NetBSDに標準採用されているamd64やi386等のXserverはX11R7ベースで、キーボード入力まわりはxf86-input-keyboardモジュールが使われています。このモジュールは特別に設定しないかぎり、/dev/wskbdを使って動きます(=wsmux)。そのため、wsmux経由で接続された複数のキーボードはXserverからは1つにしか見えません。実際、xinput(1)コマンドで確認すると、keyboardは1つしかありません。
% xinput ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ Mouse0 id=6 ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Keyboard0 id=7 [slave keyboard (3)]
NetBSD標準のXserver(xorg)では通常/etc/X11/xorg.confの設定を見て動きます(なければ起動時に標準的な構成を仮定します)。xorg.confはX -configure等で作成しますが、通常は以下のようなxorg.confが作成されます。
Section "ServerLayout" Identifier "X.org Configured" Screen 0 "Screen0" 0 0 InputDevice "Mouse0" "CorePointer" InputDevice "Keyboard0" "CoreKeyboard" EndSection (中略) Section "InputDevice" Identifier "Keyboard0" Driver "kbd" EndSection
見ての通り、InputDeviceのキーボードはKeyboard0しか無く、デバイスの設定もDriver "kbd"があるのみです。この場合、Xのkbdドライバはプロトコルとして"Standard"が選択され、デバイスは"/dev/wskbd"が仮定されます。
しかし、wskbd側で内蔵のキーボードと外付けキーボードのそれぞれに配列を指定したにもかかわらず、Xserver上で上記の場合、キー配列はすべてUSになってしまいます。これは一体どういうことか。
実はxf86-input-keyboard(kbdドライバ)は、標準(Standard)では/dev/wskbdに対し ioctl(WSKBDIO_SETMODE)を発行しWSKBD_RAWを設定します。 つまり、wskbd(4)のキーマップではなくキーコードを直接扱い、X側でキーマップを独自に管理します。このため、wskbd(4)にどんなキーマップを発行しようが無視され、Xでのキー設定が使われるのでした。
しかもキーボードデバイスは前述のようにwsmux(4)経由のKeyboard0しか存在しません。困った。これではキーボード単位で異なる配置ができない。
先ほどのxf86-input-keyboardのソースを見ればわかりますが、実はこれ、wskbdそのまま扱う方法が存在します。そもそもWSKBD_RAWを指定できるのは、カーネル側ドライバがその機能を持っている場合に限られます(ただしamd64等ではGENERICで有効)。
そうでない場合は、Protocol wskbdが使用可能で、その場合は/dev/wskbd?のデバイス毎にkeyboardとmapが指定できます。さっそくやってみます。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" EndSection Section "InputDevice" Identifier "Keyboard1" Driver "kbd" Option "Protocol" "wskbd" Option "Device" "/dev/wskbd1" Option "XkbModel" "pc105" Option "XkbLayout" "us" EndSection
見ての通り。/dev/wskbd0 (内蔵のキーボード)をKeyboard0とし、Protocolにwskbdを設定、キー配列はJPでjp109モデルを選択。/dev/wskbd1(外付けのUSBキーボード)をKeyboard1とし同じくwskbdで配列はUS、モデルをpc105に設定します。
この設定をし、外付けのキーボードを接続した上でX serverを立ち上げると、見事に思ったようにノートPCキーボードではJP配列、外付けキーボードではUS配列になっています。
なお、起動時でなくともキー配列は変更できます。2ステップ必要ですが、以下の用にします。
まず、xinput(1)コマンドを使いX上で接続状況を調べます。
% xinput ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ Mouse0 id=6 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Keyboard1 id=9 [slave keyboard (3)] ↳ Keyboard0 id=8 [slave keyboard (3)]
これで変更したいキーボード、例えば外付けUSBキーボードならKeyboard1のidを調べます。ここでは9です。 これを使って、setxkbmap(4)で配列やモデルを指定します。
% setxkbmap -device 9 -layout jp -model jp109
外付けでいつもと違う日本語USBキーボードを接続し、しかも日本語配列で使いたい、といった場合はこう設定すれば良いです。
これでNetBSDでも内蔵と外付けキーボードを両方とも同時に接続し、それぞれ別々のキー配列を任意に割り当てるようになりました。めでたしめでたし!
んが、ちょっとまって。
Xで上記の設定をしたら、確かに内蔵キーボード、日本語配列になってるんだけど、ちょっと変。具体的には
…なんと日本語配列設定時にUSキーボードでを繋げて「キーが無くて物理的に入力できなかったキー」が、日本語配列キーボードで物理的にキーが存在するにもかかわらず入力できないようになってしまっている!? どうなってんの?
はい、 このパッチを当ててxf86-input-keyboardを作り直しましょう(build)。
簡単に言えば、X側で独自に割り当てるキーマップの「PS2接続キーボードの場合」に使うテーブルにこれらJP独自のキーが登録されておらずこれらのキーが押されても全部Unknown、つまり「そんなキーコードは知らん」になってたんですね。なんだかなぁ。
じつはこれ、USBキーボード側ではちゃんとマップされてるんですが、以前はやっぱりダメで修正されているんですね。 で、その報告したの自分だったりします…すっかり忘れてました。てゆーか同件無いかちゃんと見直せよ→俺。
さてキーマップ修正はさておき、これ以外にProtocol wskbdを使うと色々と注意事項というかちょっと使い辛いじゃんという点がいくつかあります。
Linuxではたぶんxinput(1)の操作だけで簡単にできそうな、複数のキーボードをそれぞれ別の配列で同時に使う、ということを、非常に制限は多いものの何とかNetBSDでもできることを確認しました。
正直なところ、非常に面倒です。キーマップ設定不足というバグは想定外でしたが(これ書き始めてから気づいた。それで当初予定した時期にこの記事あげられなかった)、ハードウェアの動的な構成変更に非常に弱いのはNetBSD全般を通して感じます。まぁ今まで何年どころか十何年もそのまま、ということは誰もそういう使い方をしていなかったんだろうなぁ、ということで需要はないのかもしれませんが。 こういったところ、今後改善されるといいなぁ、と、Native NetBSDをDesktopに使う私は思うのでした。