【Python】【Ubuntu】キーボードの入力を拾う

# キーボードの入力を検知
検証環境: macOS(UTM Ubuntu20.04)
- /proc/bus/input/devices に載っているデバイス名(Name)と用途(Handlers)を取り出す
```
I: Bus=0003 Vendor=0627 Product=0001 Version=0001
N: Name="QEMU QEMU USB Mouse"
P: Phys=usb-0000:00:04.0-2/input0
S: Sysfs=/devices/pci0000:00/0000:00:04.0/usb1/1-2/1-2:1.0/0003:0627:0001.0002/input/input2
U: Uniq=89126-0000:00:04.0-2
H: Handlers=mouse1 event2
B: PROP=0
B: EV=17
B: KEY=1f0000 0 0 0 0
B: REL=903
B: MSC=10

I: Bus=0003 Vendor=0627 Product=0001 Version=0111
N: Name="QEMU QEMU USB Keyboard"
P: Phys=usb-0000:00:04.0-3/input0
S: Sysfs=/devices/pci0000:00/0000:00:04.0/usb1/1-3/1-3:1.0/0003:0627:0001.0003/input/input3
U: Uniq=68284-0000:00:04.0-3
H: Handlers=sysrq kbd event3 leds
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=1f
```
- Handlersがkbdの「event*」(デバイスファイル)を特定、/dev/input/ にあるので確認
- この場合だと/proc/bus/input/devicesの「kbd event3」がキーボードデバイスにあたるので/dev/input/event3を確認
```
ubuntu2004:~$ ls -la /dev/input
total 0
drwxr-xr-x  4 root root     220 Dec  4 05:17 .
drwxr-xr-x 19 root root    4020 Dec  4 05:17 ..
drwxr-xr-x  2 root root     140 Dec  4 05:17 by-id
drwxr-xr-x  2 root root     140 Dec  4 05:17 by-path
crw-rw----  1 root input 13, 64 Dec  4 05:17 event0
crw-rw----  1 root input 13, 65 Dec  4 05:17 event1
crw-rw----  1 root input 13, 66 Dec  4 05:17 event2
crw-rw----  1 root input 13, 67 Dec  4 05:17 event3
crw-rw----  1 root input 13, 63 Dec  4 05:17 mice
crw-rw----  1 root input 13, 32 Dec  4 05:17 mouse0
crw-rw----  1 root input 13, 33 Dec  4 05:17 mouse1
```
- デバイスファイルはバイナリファイルのためhexdumpなどで確認
- キーボードから「a」を入力した結果。入力が拾われていることがわかる
```
ubuntu2004:~$ sudo hexdump /dev/input/event3
0000000 3b3d 638c 0000 0000 7462 0000 0000 0000
0000010 0004 0004 0004 0007 3b3d 638c 0000 0000
0000020 7462 0000 0000 0000 0001 001e 0001 0000
0000030 3b3d 638c 0000 0000 7462 0000 0000 0000
0000040 0000 0000 0000 0000 3b3d 638c 0000 0000
0000050 d12b 0001 0000 0000 0004 0004 0004 0007
0000060 3b3d 638c 0000 0000 d12b 0001 0000 0000
0000070 0001 001e 0000 0000 3b3d 638c 0000 0000
0000080 d12b 0001 0000 0000 0000 0000 0000 0000
```
- デバイスファイルのデータの構造は、Cの構造体データ(input_event)。
```
struct input_event {
        struct timeval time;
        __u16 type;
        __u16 code;
        __s32 value;
};
```
- Pythonでバイナリデータから変換する。
```
event_bin_format = 'llHHI' # long, long, unsigned short, unsigned short, unsigned int
data = device.input_file.read(struct.calcsize(event_bin_format))
seconds, microseconds, type, code, value = struct.unpack(event_bin_format, data)
return seconds + microseconds / 1e6, type, code, value
```
- イベントを待ち行列に入れる
```
def start_reading(q: Queue, device: EventDevice):
    while True:
        # timestamp, type, code, value = read_event(device)
        ret = read_event(device)
        q.put(ret)
```
- 待ち行列への追加処理をデーモン化する
```
event_queue = Queue()
read_thread = Thread(target=start_reading, args=[event_queue, device])
read_thread.setDaemon(True)
read_thread.start()
```
- 待ち行列からイベントを取り出し続ける
```
while True:
  event = read_event_from_q(event_queue)
  print(event)
```


参考:
https://qiita.com/propella/items/a73fc333c95d14d06835    
http://www.tatapa.org/~takuo/input_subsystem/input_subsystem.html    
https://wiki.onakasuita.org/pukiwiki/?input_event