tags: #linux #events #keyboard #mouse #x11 # How to work out which /dev/input is which **make a threaded python program which has one thread per input, and prints out an event along with the `/dev/input/` node it came from.** Look in `/dev/input/by-path` See [stack exchange question](https://unix.stackexchange.com/questions/340430/dev-input-what-exactly-is-this) So if you are running X11, you can do ```bash $ xinput ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ SynPS/2 Synaptics TouchPad id=13 [slave pointer (2)] ⎜ ↳ TPPS/2 IBM TrackPoint id=14 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Power Button id=6 [slave keyboard (3)] ↳ Video Bus id=7 [slave keyboard (3)] ↳ Sleep Button id=8 [slave keyboard (3)] ↳ Integrated Camera: Integrated C id=11 [slave keyboard (3)] ↳ AT Translated Set 2 keyboard id=12 [slave keyboard (3)] ↳ ThinkPad Extra Buttons ``` ```plaintext /sys/class/input -- symlinks to folders in /sys/devices, e.g. /sys/class/input/input32 => /sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input36 ``` See ExampleSysClassInput1 # Event Codes See https://www.kernel.org/doc/html/v4.13/input/event-codes.html # How to parse /dev/input/etc See [stack overflow question](https://stackoverflow.com/questions/5060710/format-of-dev-input-event) See http://www.kernel.org/doc/Documentation/input/input.txt for documentation on the format. ```c struct input_event { struct timeval time; unsigned short type; unsigned short code; unsigned int value; }; ``` ## Python modules ### evdev ```python #!/usr/bin/env python3 from evdev import InputDevice from select import select import sys try: dev = InputDevice(sys.argv[1]) except IndexError: print(f"{sys.argv[0]} /dev/input/eventX") exit(1) while True: r,w,x = select([dev], [], []) for event in dev.read(): print(event) # event at 1337427573.061822, code 01, type 02, val 01 # event at 1337427573.061846, code 00, type 00, val 00 ``` ## Code ### Sample /dev/input parser in Python ```python #!/usr/bin/python import struct import time import sys infile_path = "/dev/input/event" + (sys.argv[1] if len(sys.argv) > 1 else "0") """ FORMAT represents the format used by linux kernel input event struct See https://github.com/torvalds/linux/blob/v5.5-rc5/include/uapi/linux/input.h#L28 Stands for: long int, long int, unsigned short, unsigned short, unsigned int """ FORMAT = 'llHHI' EVENT_SIZE = struct.calcsize(FORMAT) #open file in binary mode in_file = open(infile_path, "rb") event = in_file.read(EVENT_SIZE) while event: (tv_sec, tv_usec, type, code, value) = struct.unpack(FORMAT, event) if type != 0 or code != 0 or value != 0: print("Event type %u, code %u, value %u at %d.%d" % \ (type, code, value, tv_sec, tv_usec)) else: # Events with code, type and value == 0 are "separator" events print("===========================================") event = in_file.read(EVENT_SIZE) in_file.close() ``` ## Threaded Event Listener See [here](/aw/lang/python/PythonThreading1) for more on threading. ```python #!/usr/bin/python import struct import time import sys from glob import glob from threading import Thread import socket def send_message(host,port,message): with socket.socket(socket.AF_INET,socket.SOCK_DGRAM) as s: s.connect(("localhost",4005)) s.send(message) def main(): threads = {} args = sys.argv[1:] if len(args) > 0: nodes = [] for arg in args: if arg.isnumeric(): nodes.append(f"/dev/input/event{arg}") elif "/" in arg: nodes.append(arg) else: nodes.append(f"/dev/input/{arg}") else: nodes = glob("/dev/input/event*")+glob("/dev/input/mouse*")+glob("/dev/input/mice") print(nodes) for node in nodes: print(node) threads[node] = start_thread(node) def start_thread(node): print(node) thread = Thread(target=listen,args=(node,)) thread.start() return thread def listen(node): try: listen1(node) except KeyboardInterrupt: print("Ctrl-C") exit(0) def listen1(node): """ FORMAT represents the format used by linux kernel input event struct See https://github.com/torvalds/linux/blob/v5.5-rc5/include/uapi/linux/input.h#L28 Stands for: long int, long int, unsigned short, unsigned short, unsigned int """ FORMAT = 'llHHI' EVENT_SIZE = struct.calcsize(FORMAT) #open file in binary mode in_file = open(node, "rb") event = in_file.read(EVENT_SIZE) while event: (tv_sec, tv_usec, ty, code, value) = struct.unpack(FORMAT, event) if ty != 0 or code != 0 or value != 0: print("%s: Event type %u, code %u, value %u at %d.%d" % \ (node, ty, code, value, tv_sec, tv_usec)) msg = "%s,%u,%u,%u@%d.%d" % (node, ty, code, value, tv_sec, tv_usec) else: # Events with code, type and value == 0 are "separator" events print("===========================================") event = in_file.read(EVENT_SIZE) in_file.close() if __name__ == "__main__": main() ```