Python bindings to the Linux input handling subsystem for reading input events and creating virtual input devices
—
Event classes and utilities for handling input events from Linux input devices. This includes the core event representation, specialized event types, and utilities for event categorization and processing.
The fundamental InputEvent class represents all input events from the Linux kernel.
class InputEvent:
def __init__(self, sec: int, usec: int, type: int, code: int, value: int) -> None:
"""
Initialize an input event.
Parameters:
- sec: Seconds since epoch when event occurred
- usec: Microsecond portion of timestamp
- type: Event type (EV_KEY, EV_REL, EV_ABS, etc.)
- code: Event code specific to the event type
- value: Event value
"""
def timestamp(self) -> float:
"""
Return event timestamp as float seconds since epoch.
Returns:
Combined sec + usec as floating point timestamp
"""
# Attributes
sec: int # Seconds since epoch
usec: int # Microseconds portion
type: int # Event type (EV_*)
code: int # Event code
value: int # Event valueSpecialized class for keyboard and button events with enhanced key state information.
class KeyEvent:
# Class constants for key states
key_up: int = 0x0 # Key released
key_down: int = 0x1 # Key pressed
key_hold: int = 0x2 # Key held (repeat)
def __init__(self, event: InputEvent, allow_unknown: bool = False) -> None:
"""
Create KeyEvent from InputEvent.
Parameters:
- event: InputEvent with type EV_KEY
- allow_unknown: If False, raise KeyError for unknown key codes.
If True, use hex value for unknown keys.
Raises:
- KeyError: If key code is unknown and allow_unknown=False
"""
# Attributes
scancode: int # Raw scan code from event.code
keycode: str # Human-readable key name (e.g., "KEY_A", "BTN_LEFT")
keystate: int # Key state (key_up, key_down, or key_hold)
event: InputEvent # Reference to original InputEventEvents for relative positioning devices like mice and trackballs.
class RelEvent:
def __init__(self, event: InputEvent) -> None:
"""
Create RelEvent from InputEvent with type EV_REL.
Parameters:
- event: InputEvent with type EV_REL
"""
# Attributes
event: InputEvent # Reference to original InputEventEvents for absolute positioning devices like touchscreens and joysticks.
class AbsEvent:
def __init__(self, event: InputEvent) -> None:
"""
Create AbsEvent from InputEvent with type EV_ABS.
Parameters:
- event: InputEvent with type EV_ABS
"""
# Attributes
event: InputEvent # Reference to original InputEventEvents that mark boundaries between logical input events.
class SynEvent:
def __init__(self, event: InputEvent) -> None:
"""
Create SynEvent from InputEvent with type EV_SYN.
Parameters:
- event: InputEvent with type EV_SYN
"""
# Attributes
event: InputEvent # Reference to original InputEventDictionary mapping event types to their corresponding event classes.
event_factory: Dict[int, Type] = {
# Maps event type codes to event classes
# EV_KEY -> KeyEvent
# EV_REL -> RelEvent
# EV_ABS -> AbsEvent
# EV_SYN -> SynEvent
}Utilities for converting generic InputEvent objects to specialized event types.
def categorize(event: InputEvent) -> Union[InputEvent, KeyEvent, RelEvent, AbsEvent, SynEvent]:
"""
Categorize InputEvent according to its type.
Uses event_factory dictionary to map event types to specialized classes.
If event type is not found in factory, returns original InputEvent unchanged.
Parameters:
- event: InputEvent to categorize
Returns:
Specialized event object (KeyEvent, RelEvent, etc.) or original InputEvent
"""from evdev import InputDevice, categorize, ecodes
device = InputDevice('/dev/input/event0')
for event in device.read_loop():
# Categorize event based on type
categorized_event = categorize(event)
if isinstance(categorized_event, KeyEvent):
print(f"Key event: {categorized_event.keycode} -> {categorized_event.keystate}")
elif isinstance(categorized_event, RelEvent):
print(f"Relative event: code={event.code}, value={event.value}")
elif isinstance(categorized_event, AbsEvent):
print(f"Absolute event: code={event.code}, value={event.value}")
else:
print(f"Other event: {event}")from evdev import InputDevice, categorize, ecodes, KeyEvent
device = InputDevice('/dev/input/event0')
for event in device.read_loop():
if event.type == ecodes.EV_KEY:
key_event = categorize(event)
if key_event.keystate == KeyEvent.key_down:
print(f"Key pressed: {key_event.keycode}")
elif key_event.keystate == KeyEvent.key_up:
print(f"Key released: {key_event.keycode}")
elif key_event.keystate == KeyEvent.key_hold:
print(f"Key held: {key_event.keycode}")from evdev import InputDevice, categorize, ecodes
device = InputDevice('/dev/input/event0')
def process_events():
for event in device.read_loop():
if event.type == ecodes.EV_KEY:
# Process keyboard/button events
key_event = categorize(event)
handle_key_event(key_event)
elif event.type == ecodes.EV_REL:
# Process mouse movement events
rel_event = categorize(event)
handle_mouse_movement(rel_event)
elif event.type == ecodes.EV_ABS:
# Process absolute positioning events
abs_event = categorize(event)
handle_absolute_positioning(abs_event)
elif event.type == ecodes.EV_SYN:
# Synchronization event - marks end of event sequence
handle_sync_event(event)
def handle_key_event(key_event):
print(f"Key: {key_event.keycode}, State: {key_event.keystate}")
def handle_mouse_movement(rel_event):
event = rel_event.event
if event.code == ecodes.REL_X:
print(f"Mouse X movement: {event.value}")
elif event.code == ecodes.REL_Y:
print(f"Mouse Y movement: {event.value}")
def handle_absolute_positioning(abs_event):
event = abs_event.event
if event.code == ecodes.ABS_X:
print(f"Touch X position: {event.value}")
elif event.code == ecodes.ABS_Y:
print(f"Touch Y position: {event.value}")
def handle_sync_event(event):
if event.code == ecodes.SYN_REPORT:
print("End of event sequence")
process_events()from evdev import InputDevice, categorize
import time
device = InputDevice('/dev/input/event0')
start_time = time.time()
for event in device.read_loop():
# Get event timestamp
event_time = event.timestamp()
relative_time = event_time - start_time
categorized = categorize(event)
print(f"[{relative_time:.3f}s] {categorized}")
# Alternative: access timestamp components directly
print(f"Event at {event.sec}.{event.usec:06d}")from evdev import InputDevice, categorize, ecodes, KeyEvent
device = InputDevice('/dev/input/event0')
for event in device.read_loop():
if event.type == ecodes.EV_KEY:
try:
# Default behavior - raises KeyError for unknown keys
key_event = KeyEvent(event)
print(f"Known key: {key_event.keycode}")
except KeyError:
# Handle unknown key codes
key_event = KeyEvent(event, allow_unknown=True)
print(f"Unknown key: {key_event.keycode} (code: {key_event.scancode})")from evdev import InputDevice, categorize
from collections import deque
device = InputDevice('/dev/input/event0')
event_buffer = deque(maxlen=100) # Keep last 100 events
def process_buffered_events():
while True:
# Read all available events
events = device.read()
# Add to buffer and categorize
for event in events:
categorized = categorize(event)
event_buffer.append(categorized)
# Process buffer
while event_buffer:
event = event_buffer.popleft()
handle_event(event)
def handle_event(event):
if isinstance(event, KeyEvent):
print(f"Buffered key event: {event.keycode}")
else:
print(f"Buffered event: {event}")
process_buffered_events()Install with Tessl CLI
npx tessl i tessl/pypi-evdev