# Linux 鍵盤按鍵重新定義 (Keyboard Remapping)
在windows使用無線鍵盤時,為了支援特殊功能(ex.自定義巨集、開啟特殊程式等),鍵盤製造商通常會將按鍵映射至非不常用的鍵,再以驅動程式去觸發特定的功能。但是,通常製造商並不會特地再為Linux寫個驅動程式。因此,使用者在轉入Linux後,有可能會遇到一些按鍵功能錯亂的問題。
## 查詢鍵盤鍵號
* 在 terminal 輸入 `sudo apt install evtest` 安裝測試軟體
* 在 terminal 輸入 `sudo evtest` 開始程式 ( 需要 sudo 權限 ):
```shell
❯ sudo evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: Lid Switch
/dev/input/event1: Sleep Button
/dev/input/event2: Power Button
/dev/input/event3: Power Button
/dev/input/event4: AT Translated Set 2 keyboard
/dev/input/event5: SynPS/2 Synaptics TouchPad
/dev/input/event6: MSI WMI hotkeys
/dev/input/event7: Video Bus
/dev/input/event8: Video Bus
/dev/input/event9: HDA Intel PCH Mic
/dev/input/event10: HDA Intel PCH Headphone
/dev/input/event11: HDA Intel PCH HDMI/DP,pcm=3
/dev/input/event12: HDA Intel PCH HDMI/DP,pcm=7
/dev/input/event13: HDA Intel PCH HDMI/DP,pcm=8
/dev/input/event14: HDA Intel PCH HDMI/DP,pcm=9
/dev/input/event15: HDA Intel PCH HDMI/DP,pcm=10
/dev/input/event16: Smart Smart Wireless Device
/dev/input/event17: BisonCam, NB Pro: BisonCam, NB
/dev/input/event18: Logitech M585/M590
/dev/input/event19: Logitech K370s/K375s
/dev/input/event20: Logitech K370s/K375s
/dev/input/event21: Smart Smart Wireless Device
Select the device event number [0-21]:
```
輸入後,會列出所有電腦連接的設備,選擇要檢測的裝置代碼。以本次為例,外接鍵盤的代號為16,因此輸入16並按下Enter繼續。
```shell
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x5ac product 0x24f version 0x101
Input device name: "Smart Smart Wireless Device"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 1 (KEY_ESC)
Event code 2 (KEY_1)
Event code 3 (KEY_2)
Event code 4 (KEY_3)
Event code 5 (KEY_4)
Event code 6 (KEY_5)
Event code 7 (KEY_6)
Event code 8 (KEY_7)
Event code 9 (KEY_8)
... (許多該裝置接受的功能)
Properties:
Testing ... (interrupt to exit)
```
需要注意 **device ID**,在之後按鍵映射會需要裝置資訊
> Input device ID: bus 0x3 vendor 0x5ac product 0x24f version 0x101
之後,按下按鍵後,即會顯示觸發的事件:
```shell
Testing ... (interrupt to exit)
Event: time 1645019936.697282, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0
Event: time 1645019936.697282, -------------- SYN_REPORT ------------
Event: time 1645019943.306744, type 1 (EV_KEY), code 224 (KEY_BRIGHTNESSDOWN), value 1
Event: time 1645019943.306744, -------------- SYN_REPORT ------------
Event: time 1645019943.388729, type 1 (EV_KEY), code 224 (KEY_BRIGHTNESSDOWN), value 0
Event: time 1645019943.388729, -------------- SYN_REPORT ------------
Event: time 1645019945.955961, type 1 (EV_KEY), code 225 (KEY_BRIGHTNESSUP), value 1
Event: time 1645019945.955961, -------------- SYN_REPORT ------------
Event: time 1645019946.040973, type 1 (EV_KEY), code 225 (KEY_BRIGHTNESSUP), value 0
Event: time 1645019946.040973, -------------- SYN_REPORT ------------
Event: time 1645019947.686291, type 1 (EV_KEY), code 120 (KEY_SCALE), value 1
Event: time 1645019947.686291, -------------- SYN_REPORT ------------
Event: time 1645019947.777109, type 1 (EV_KEY), code 120 (KEY_SCALE), value 0
Event: time 1645019947.777109, -------------- SYN_REPORT ------------
Event: time 1645019948.371157, type 1 (EV_KEY), code 204 (KEY_DASHBOARD), value 1
Event: time 1645019948.371157, -------------- SYN_REPORT ------------
Event: time 1645019948.464234, type 1 (EV_KEY), code 204 (KEY_DASHBOARD), value 0
Event: time 1645019948.464234, -------------- SYN_REPORT ------------
Event: time 1645019948.996276, type 1 (EV_KEY), code 229 (KEY_KBDILLUMDOWN), value 1
Event: time 1645019948.996276, -------------- SYN_REPORT ------------
Event: time 1645019949.059168, type 1 (EV_KEY), code 229 (KEY_KBDILLUMDOWN), value 0
Event: time 1645019949.059168, -------------- SYN_REPORT ------------
Event: time 1645019949.566315, type 1 (EV_KEY), code 230 (KEY_KBDILLUMUP), value 1
Event: time 1645019949.566315, -------------- SYN_REPORT ------------
Event: time 1645019949.626316, type 1 (EV_KEY), code 230 (KEY_KBDILLUMUP), value 0
Event: time 1645019949.626316, -------------- SYN_REPORT ------------
Event: time 1645019950.193237, type 1 (EV_KEY), code 165 (KEY_PREVIOUSSONG), value 1
Event: time 1645019950.193237, -------------- SYN_REPORT ------------
Event: time 1645019950.250342, type 1 (EV_KEY), code 165 (KEY_PREVIOUSSONG), value 0
Event: time 1645019950.250342, -------------- SYN_REPORT ------------
Event: time 1645019951.013508, type 1 (EV_KEY), code 164 (KEY_PLAYPAUSE), value 1
Event: time 1645019951.013508, -------------- SYN_REPORT ------------
Event: time 1645019951.107293, type 1 (EV_KEY), code 164 (KEY_PLAYPAUSE), value 0
Event: time 1645019951.107293, -------------- SYN_REPORT ------------
Event: time 1645019951.978592, type 1 (EV_KEY), code 163 (KEY_NEXTSONG), value 1
Event: time 1645019951.978592, -------------- SYN_REPORT ------------
Event: time 1645019952.101364, type 1 (EV_KEY), code 163 (KEY_NEXTSONG), value 0
Event: time 1645019952.101364, -------------- SYN_REPORT ------------
Event: time 1645019952.736541, type 1 (EV_KEY), code 113 (KEY_MUTE), value 1
Event: time 1645019952.736541, -------------- SYN_REPORT ------------
Event: time 1645019952.830548, type 1 (EV_KEY), code 113 (KEY_MUTE), value 0
Event: time 1645019952.830548, -------------- SYN_REPORT ------------
Event: time 1645019953.596707, type 1 (EV_KEY), code 114 (KEY_VOLUMEDOWN), value 1
Event: time 1645019953.596707, -------------- SYN_REPORT ------------
Event: time 1645019953.687521, type 1 (EV_KEY), code 114 (KEY_VOLUMEDOWN), value 0
Event: time 1645019953.687521, -------------- SYN_REPORT ------------
Event: time 1645019955.750886, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 1
Event: time 1645019955.750886, -------------- SYN_REPORT ------------
Event: time 1645019955.835668, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 0
Event: time 1645019955.835668, -------------- SYN_REPORT ------------
Event: time 1645019970.354675, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e0
Event: time 1645019970.354675, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 1
Event: time 1645019970.354675, -------------- SYN_REPORT ------------
Event: time 1645019970.482813, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70006
Event: time 1645019970.482813, type 1 (EV_KEY), code 46 (KEY_C), value 1
Event: time 1645019970.482813, -------------- SYN_REPORT ------------
```
以下列事件為例:
> Event: time 1645019970.482813, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70006
需要注意的是 **scan code** (0x70006),及 **key code** (KEY_C 中的 C),若想知道所有的 key code ,可以至`/usr/include/linux/input-event-codes.h`查詢。
>If evtest does not show any events even though the device is being
used, the device may be grabbed by a process (EVIOCGRAB). This is
usually the case when debugging a synaptics device from within X. VT
switching to a TTY or shutting down the X server terminates this grab
and synaptics devices can be debugged.
>
> The following command shows the processes with an open file descriptor
on the device:
>
> `fuser -v /dev/input/eventX`
## 重新映射按鍵(hwdb & udev)
```
# 90-custom-keyboard.hwdb
## AT keyboard
# evdev:atkbd:dmi:bvn*:bvr*:bd*:svn<vendor>:pn<product>:pvr*
## driver device name
# evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
## Generic input devices (also USB keyboards) identified by the usb kernel modalias
evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<modalias>
KEYBOARD_KEY_<scancode>=<keycode>
```
* 用文字編輯器在 `/etc/udev/hwdb.d` 建立名為 `90-custom-keyboard.hwdb` 的檔案
* 用 `evdev` 選擇要更改的設備,有下列三種格式:
* `evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<modalias>`
由上述可知
> Input device ID: bus 0x3 vendor 0x5ac product 0x24f version 0x101
* `bus_id`: 0x3
* `vendor_id`: 0x5ac
* `product_id`: 0x24f
* `version`: 0x101
因此值為
`evdev:input:b0003v05ACp024Fe0101`
:warning: id 值須為 **四位數、16進制、英文大寫**
* `evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*`
由上述可知
> Input device name: "Smart Smart Wireless Device"
* `input device name`: Smart Smart Wireless Device
因此值為
`evdev:name:Smart Smart Wireless Device`
* `evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr*`
此指令為AT鍵盤使用
* 輸入要更改的scancode及對應的keycode
## 更新鍵盤映射表
* 在 Terminal 下 `systemd-hwdb update` 指令即可更新。
* reboot
## 參考
[man hwdb](http://manpages.ubuntu.com/manpages/bionic/zh_TW/man7/hwdb.7.html)
[Remap keys for specific device on ubuntu](https://madoshakalaka.github.io/2019/09/25/remap-keys-for-specific-device-on-ubuntu.html)
[Remap keyboard keys in GNU/Linux](https://www.rigacci.org/wiki/doku.php/doc/appunti/linux/sa/remap_keyboard_keys)
[Linux keymapping with udev hwdb](https://yulistic.gitlab.io/2017/12/linux-keymapping-with-udev-hwdb/)
[Map scancodes to keycodes (简体中文)](https://wiki.archlinux.org/title/Map_scancodes_to_keycodes_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87))
[ArchLinux 鍵盤對映:交換 CapsLock 和 Ctrl](https://www.gushiciku.cn/pl/2hbp/zh-tw)