Skip to content

Instantly share code, notes, and snippets.

@jigpu
Last active January 30, 2023 20:15
Show Gist options
  • Save jigpu/65535216a6c010b1ad0d3e99d20b9005 to your computer and use it in GitHub Desktop.
Save jigpu/65535216a6c010b1ad0d3e99d20b9005 to your computer and use it in GitHub Desktop.
Convert binary Linux kernel input events (e.g. from /dev/input/eventN) into a human-readable list of events.
#!/usr/bin/env python
# Convert binary Linux kernel input events (e.g. from /dev/input/eventN)
# into a human-readable list of events. Takes input from stdin.
#
# Only supports a limited number of event types/codes, but can be extended
# without much effort.
#
# Usage: `sudo cat /dev/input/event<N> | ./event-bin-to-text.py`
#
# ************
#
# The following is an example of how to dump events from a Wacom stylus to
# a temporary file and then replay them through this script. First, we need
# to know what device node to capture from. The first two commands below
# find the X11 device number (20) for our stylus, and then find the kernel
# device node associated with it (/dev/input/event26). After that, we use
# `cat` to dump the events from the device to a temporary file for later
# replay. We move the pen around a little and then press CTRL+C to quit
# the dump. Finally, we `cat` the saved dump into this script for review.
#
# ~~~
# $ xinput list | grep stylus
# ⎜ ↳ Wacom Intuos Pro M Pen stylus id=20 [slave pointer (2)]
#
# $ xinput list-props 20 | grep "Device Node"
# Device Node (263): "/dev/input/event26"
#
# $ sudo cat /dev/input/event26 > /tmp/dump.bin
# ^C
#
# $ cat /tmp/dump.bin | ./event-bin-to-text.py
# 1675109136.477377 3 0 14832 --- EV_ABS ABS_X 14832
# 1675109136.477377 3 1 58394 --- EV_ABS ABS_Y 58394
# 1675109136.477377 3 26 -28 --- EV_ABS TILT_X -28
# 1675109136.477377 3 27 27 --- EV_ABS TILT_Y 27
# 1675109136.477377 3 25 40 --- EV_ABS ??? 40
# 1675109136.477377 1 320 1 --- EV_KEY BTN_TOOL_PEN 1
# 1675109136.477377 3 40 2114 --- EV_ABS ABS_MISC 2114
# 1675109136.477377 0 0 0 --- EV_SYN SYN_REPORT 0 ### dt = ??? ms
# 1675109136.483354 3 0 14875 --- EV_ABS ABS_X 14875
# 1675109136.483354 3 1 58415 --- EV_ABS ABS_Y 58415
# 1675109136.483354 3 27 26 --- EV_ABS TILT_Y 26
# 1675109136.483354 3 25 39 --- EV_ABS ??? 39
# 1675109136.483354 0 0 0 --- EV_SYN SYN_REPORT 0 ### dt = 5.977 ms
# 1675109136.487371 3 0 14990 --- EV_ABS ABS_X 14990
# 1675109136.487371 3 1 58487 --- EV_ABS ABS_Y 58487
# 1675109136.487371 3 24 1971 --- EV_ABS ??? 1971
# 1675109136.487371 1 330 1 --- EV_KEY ??? 1
# 1675109136.487371 4 0 1736442911 --- ??? ??? 1736442911
# 1675109136.487371 0 0 0 --- EV_SYN SYN_REPORT 0 ### dt = 4.017 ms
# ~~~
#
import struct
import sys
event_fmt = "<QQHHi"
event_len = struct.calcsize(event_fmt)
event_unpack = struct.Struct(event_fmt).unpack_from
last_time_micros = None
def calculate_dt_millis(current_s, current_us):
global last_time_micros
now_micros = current_s * 1000000 + current_us
if last_time_micros is None:
last_time_micros = now_micros
return None
dt_micros = now_micros - last_time_micros
last_time_micros = now_micros
return dt_micros / 1000
def decode_name(typeval, codeval):
return lookup_dict.get((typeval, codeval), "???")
def parse_and_dump_one(event):
s = event_unpack(event)
type_name = decode_name(s[2], None)
code_name = decode_name(s[2], s[3])
is_syn_report = s[2] == 0 and s[3] == 0
if is_syn_report:
dt_millis = calculate_dt_millis(s[0], s[1])
if dt_millis is None: dt_millis = "???"
else:
dt_millis = None
fmt_str = "{}.{:06}\t{}\t{}\t{:>10}\t---\t{:>8}\t{:>12}\t{:>10}"
print_str = fmt_str.format(*s, type_name, code_name, s[4])
if dt_millis is not None:
print_str += "\t### dt = {} ms".format(dt_millis)
print(print_str)
def read_one(stream):
event = b''
while len(event) != event_len:
nbytes = event_len - len(event)
if nbytes > 3: nbytes = 3
data = stream.read(nbytes)
if not data: return None
event += data
return event
def main():
while True:
event = read_one(sys.stdin.buffer)
if event is None: break
parse_and_dump_one(event)
# https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/input-event-codes.h
lookup_dict = {
(0, None): "EV_SYN",
(0, 0x00): "SYN_REPORT",
(1, None): "EV_KEY",
(1, 0x140): "BTN_TOOL_PEN",
(3, None): "EV_ABS",
(3, 0x00): "ABS_X",
(3, 0x01): "ABS_Y",
(3, 0x1A): "TILT_X",
(3, 0x1B): "TILT_Y",
(3, 0x28): "ABS_MISC",
(3, 0x29): "ABS_MISC+1",
(3, 0x2A): "ABS_MISC+2",
(3, 0x2B): "ABS_MISC+3",
(3, 0x2C): "ABS_MISC+4",
}
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment