Linuxで安いマクロキーボードを使う（失敗→成功）

安価な赤軸のマクロキーボード、東莞市世興盛電子科技 (MKESPN) の[K806](http://cn.mkespnhk.com/ProDetail.aspx?ProId=34) (30fa:1340) をアリエクで2個928円で買ったのでLinuxで使えるようにします。
![aliexpress-macro-keyboard-order.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109174/681177a5-3a66-90cb-4a66-e9f4a58bb15f.png)

なおK806は公式ドライバもありますが、Windows向けであるため今回は使わないこととします。また他のマクロキーボードではSayoDevice/Lichifitには[公式Webアプリ](https://sayodevice.com/)が、ch57xベースのもの（Vendor ID: 0x1189）には[ch57x-keyboard-tool](https://github.com/kriomant/ch57x-keyboard-tool)があるようで、お金があるならそちらの方が良い気がします。

が兎にも角にもK806を使えるようにしていきます。まずはキーボードのXデバイスIDを取得して環境変数 $DEVICE_ID に設定します。
```
vipper36@ubuntu-desk:~$ xinput list | grep INSTANT\ USB\ GAMING | grep keyboard
    ↳ INSTANT USB GAMING MOUSE  Keyboard      	id=30	[slave  keyboard (3)]
vipper36@ubuntu-desk:~$ DEVICE_ID=`xinput list | grep INSTANT\ USB\ GAMING | grep keyboard | grep -o 'id=[0-9]*' | cut -d= -f2`
```

xevで調べたK806の標準キーコードは 87, 88, 89, 83, 84, 85, 79, 80 とテンキー互換になっているようです。試しに独自のXkbのキーレイアウトを定義しようとしてみましたが何か上手く行かなかったのでちょっと横着します。まずは下で現在K806に使われているキーマップを引っ張ってきます。
```
xkbcomp -i $DEVICE_ID $DISPLAY k806.xkb
```

次にk806.xkbの一部を以下のように改変してマクロキーに割り当てます。
```:k806.xkb
    key  <KP7> {         [      XF86Macro7,      XF86Macro7 ] };
    key  <KP8> {         [      XF86Macro8,      XF86Macro8 ] };
    key  <KP9> {         [        KP_Prior,            KP_9 ] };
    key <KPSU> {
        type= "CTRL+ALT",
        symbols[Group1]= [     KP_Subtract,     KP_Subtract,     KP_Subtract,     KP_Subtract,  XF86Prev_VMode ]
    };
    key  <KP4> {         [      XF86Macro4,      XF86Macro4 ] };
    key  <KP5> {         [      XF86Macro5,      XF86Macro5 ] };
    key  <KP6> {         [      XF86Macro6,      XF86Macro6 ] };
    key <KPAD> {
        type= "CTRL+ALT",
        symbols[Group1]= [          KP_Add,          KP_Add,          KP_Add,          KP_Add,  XF86Next_VMode ]
    };
    key  <KP1> {         [      XF86Macro1,      XF86Macro1 ] };
    key  <KP2> {         [      XF86Macro2,      XF86Macro2 ] };
    key  <KP3> {         [      XF86Macro3,      XF86Macro3 ] };
    key  <KP0> {         [       KP_Insert,            KP_0 ] };
    key <KPDL> {         [       KP_Delete,      KP_Decimal ] };
```

その次にそれをXkbにセットします。
```
vipper36@ubuntu-desk:~$ xkbcomp -i $DEVICE_ID k806.xkb $DISPLAY
```

設定が上手く行ったらそれをデスクトップ起動時に自動設定するようにします。
```sh:~/.config/autostart/k806.desktop
[Desktop Entry]
Name=K806
GenericName=
Comment=
Exec=sh -c "env DEVICE_ID=$(xinput list | grep 'INSTANT USB GAMING' | grep keyboard | grep -o 'id=[0-9]*' | cut -d= -f2) sh -c '[ -n \\"$DEVICE_ID\\" ] && [ -f ~/k806.xkb ] && xkbcomp -i \\"$DEVICE_ID\\" ~/k806.xkb $DISPLAY'"
Terminal=false
Type=Application
X-GNOME-Autostart-enabled=true
```

最後にそのマクロキーをショートカットキーに設定します。MATEデスクトップ環境では以下から設定できます。
```
vipper36@ubuntu-desk:~$ mate-keybinding-properties
```

…ダメでした。Mateではどうやら0xffffffを超えるキーコードがショートカットに設定できないようです…（XF86Macro1〜XF86Macro8は0x10081290〜0x10081297）。仕方がないのでキーをXF86Macro1〜XF86Macro8ではなくF21〜F28 (0xffd2〜0xffd9) にマップしてお茶を濁します。

これでやっとフォーカスを変更する`wmctrl -x -a pluma.Pluma`、`wmctrl -x -a mate-terminal.Mate-terminal`、`wmctrl -x -a caja.Caja`（ウインドウIDは`wmctrl -lx`で取得）や、オーディオ出力先を変更する`pactl set-default-sink <シンク名>`（シンク名は`pactl list sinks|grep 名前`で取得）などがセットできるようになりました。

…が、何故か上手く行く時と上手く行かない時があります。上手く行く時はテンキーの方が使えなくなるので原因はどうやらkeycodeの衝突のようです。一瞬行き詰まったかと思いましたが、keysymの設定ではなく別のキーコードへとリダイレクトすれば上手く行くようです。具体的にはk806.xkbの一部を以下のように改変しました:

```:k806.xkb
    key  <KP7> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<FK16>), RedirectKey(key=<FK16>)] // XF86Launch7
    };
    key  <KP8> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<FK17>), RedirectKey(key=<FK17>)] // XF86Launch8
    };
    key  <KP9> {         [        KP_Prior,            KP_9 ] };
    key <KPSU> {
        type= "CTRL+ALT",
        symbols[Group1]= [     KP_Subtract,     KP_Subtract,     KP_Subtract,     KP_Subtract,  XF86Prev_VMode ]
    };
    key  <KP4> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<I211>), RedirectKey(key=<I211>)] // XF86Launch4
    };
    key  <KP5> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<FK14>), RedirectKey(key=<FK14>)] // XF86Launch5
    };
    key  <KP6> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<FK15>), RedirectKey(key=<FK15>)] // XF86Launch6
    };
    key <KPAD> {
        type= "CTRL+ALT",
        symbols[Group1]= [          KP_Add,          KP_Add,          KP_Add,          KP_Add,  XF86Next_VMode ]
    };
    key  <KP1> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<I156>), RedirectKey(key=<I156>)] // XF86Launch1
    };
    key  <KP2> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<I157>), RedirectKey(key=<I157>)] // XF86Launch2
    };
    key  <KP3> {
        symbols[Group1]= [      NoSymbol,      NoSymbol ],
        actions[Group1] = [RedirectKey(key=<I210>), RedirectKey(key=<I210>)] // XF86Launch3
    };
```
これでやっとちゃんと動きました。長かった…。

# 追記
[こちらの記事](https://zenn.dev/tmtms/articles/202109-remap-key)を参考にキーの変換をXよりも下の階層でやってみた。

evdevとxkbのマッピングはここにあるようです: /usr/share/X11/xkb/keycodes/evdev 。

```:/etc/udev/hwdb.d/10-k806.hwdb
evdev:name:INSTANT USB GAMING MOUSE  Keyboard:dmi:bvn*:bvr*:bd*:svn*:pn*
 KEYBOARD_KEY_70059=prog1
 KEYBOARD_KEY_7005a=prog2
 KEYBOARD_KEY_7005b=prog3
 KEYBOARD_KEY_7005c=prog4
 KEYBOARD_KEY_7005d=f14
 KEYBOARD_KEY_7005e=f15
 KEYBOARD_KEY_7005f=f16
 KEYBOARD_KEY_70060=f17
```

```sh
$ sudo systemd-hwdb update && sudo udevadm trigger
```

それと音声出力の切り替えもIDが変わると動かなくなるのでこちらにしました:
```
sh -c "pactl set-default-sink `LANG=C pactl list sinks|grep -o "Name:.*" | grep <名前の一部>| cut -d: -f2`"
```