193 lines
5.8 KiB
Python
193 lines
5.8 KiB
Python
"""
|
|
This module provides the :class:`InputEvent` class, which closely
|
|
resembles the ``input_event`` struct defined in ``linux/input.h``:
|
|
|
|
.. code-block:: c
|
|
|
|
struct input_event {
|
|
struct timeval time;
|
|
__u16 type;
|
|
__u16 code;
|
|
__s32 value;
|
|
};
|
|
|
|
This module also defines several :class:`InputEvent` sub-classes that
|
|
know more about the different types of events (key, abs, rel etc). The
|
|
:data:`event_factory` dictionary maps event types to these classes.
|
|
|
|
Assuming you use the :func:`evdev.util.categorize()` function to
|
|
categorize events according to their type, adding or replacing a class
|
|
for a specific event type becomes a matter of modifying
|
|
:data:`event_factory`.
|
|
|
|
All classes in this module have reasonable ``str()`` and ``repr()``
|
|
methods::
|
|
|
|
>>> print(event)
|
|
event at 1337197425.477827, code 04, type 04, val 458792
|
|
>>> print(repr(event))
|
|
InputEvent(1337197425L, 477827L, 4, 4, 458792L)
|
|
|
|
>>> print(key_event)
|
|
key event at 1337197425.477835, 28 (KEY_ENTER), up
|
|
>>> print(repr(key_event))
|
|
KeyEvent(InputEvent(1337197425L, 477835L, 1, 28, 0L))
|
|
"""
|
|
|
|
# event type descriptions have been taken mot-a-mot from:
|
|
# http://www.kernel.org/doc/Documentation/input/event-codes.txt
|
|
|
|
# pylint: disable=no-name-in-module
|
|
from typing import Final
|
|
from .ecodes import ABS, EV_ABS, EV_KEY, EV_REL, EV_SYN, KEY, REL, SYN, keys
|
|
|
|
|
|
class InputEvent:
|
|
"""A generic input event."""
|
|
|
|
__slots__ = "sec", "usec", "type", "code", "value"
|
|
|
|
def __init__(self, sec, usec, type, code, value):
|
|
#: Time in seconds since epoch at which event occurred.
|
|
self.sec: int = sec
|
|
|
|
#: Microsecond portion of the timestamp.
|
|
self.usec: int = usec
|
|
|
|
#: Event type - one of ``ecodes.EV_*``.
|
|
self.type: int = type
|
|
|
|
#: Event code related to the event type.
|
|
self.code: int = code
|
|
|
|
#: Event value related to the event type.
|
|
self.value: int = value
|
|
|
|
def timestamp(self) -> float:
|
|
"""Return event timestamp as a float."""
|
|
return self.sec + (self.usec / 1000000.0)
|
|
|
|
def __str__(self):
|
|
msg = "event at {:f}, code {:02d}, type {:02d}, val {:02d}"
|
|
return msg.format(self.timestamp(), self.code, self.type, self.value)
|
|
|
|
def __repr__(self):
|
|
msg = "{}({!r}, {!r}, {!r}, {!r}, {!r})"
|
|
return msg.format(self.__class__.__name__, self.sec, self.usec, self.type, self.code, self.value)
|
|
|
|
|
|
class KeyEvent:
|
|
"""An event generated by a keyboard, button or other key-like devices."""
|
|
|
|
key_up: Final[int] = 0x0
|
|
key_down: Final[int] = 0x1
|
|
key_hold: Final[int] = 0x2
|
|
|
|
__slots__ = "scancode", "keycode", "keystate", "event"
|
|
|
|
def __init__(self, event: InputEvent, allow_unknown: bool = False):
|
|
"""
|
|
The ``allow_unknown`` argument determines what to do in the event of an event code
|
|
for which a key code cannot be found. If ``False`` a ``KeyError`` will be raised.
|
|
If ``True`` the keycode will be set to the hex value of the event code.
|
|
"""
|
|
|
|
self.scancode: int = event.code
|
|
|
|
if event.value == 0:
|
|
self.keystate = KeyEvent.key_up
|
|
elif event.value == 2:
|
|
self.keystate = KeyEvent.key_hold
|
|
elif event.value == 1:
|
|
self.keystate = KeyEvent.key_down
|
|
|
|
try:
|
|
self.keycode = keys[event.code]
|
|
except KeyError:
|
|
if allow_unknown:
|
|
self.keycode = "0x{:02X}".format(event.code)
|
|
else:
|
|
raise
|
|
|
|
#: Reference to an :class:`InputEvent` instance.
|
|
self.event: InputEvent = event
|
|
|
|
def __str__(self):
|
|
try:
|
|
ks = ("up", "down", "hold")[self.keystate]
|
|
except IndexError:
|
|
ks = "unknown"
|
|
|
|
msg = "key event at {:f}, {} ({}), {}"
|
|
return msg.format(self.event.timestamp(), self.scancode, self.keycode, ks)
|
|
|
|
def __repr__(self):
|
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
|
|
|
|
class RelEvent:
|
|
"""A relative axis event (e.g moving the mouse 5 units to the left)."""
|
|
|
|
__slots__ = "event"
|
|
|
|
def __init__(self, event: InputEvent):
|
|
#: Reference to an :class:`InputEvent` instance.
|
|
self.event: InputEvent = event
|
|
|
|
def __str__(self):
|
|
msg = "relative axis event at {:f}, {}"
|
|
return msg.format(self.event.timestamp(), REL[self.event.code])
|
|
|
|
def __repr__(self):
|
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
|
|
|
|
class AbsEvent:
|
|
"""An absolute axis event (e.g the coordinates of a tap on a touchscreen)."""
|
|
|
|
__slots__ = "event"
|
|
|
|
def __init__(self, event: InputEvent):
|
|
#: Reference to an :class:`InputEvent` instance.
|
|
self.event: InputEvent = event
|
|
|
|
def __str__(self):
|
|
msg = "absolute axis event at {:f}, {}"
|
|
return msg.format(self.event.timestamp(), ABS[self.event.code])
|
|
|
|
def __repr__(self):
|
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
|
|
|
|
class SynEvent:
|
|
"""
|
|
A synchronization event. Used as markers to separate events. Events may be
|
|
separated in time or in space, such as with the multitouch protocol.
|
|
"""
|
|
|
|
__slots__ = "event"
|
|
|
|
def __init__(self, event: InputEvent):
|
|
#: Reference to an :class:`InputEvent` instance.
|
|
self.event: InputEvent = event
|
|
|
|
def __str__(self):
|
|
msg = "synchronization event at {:f}, {}"
|
|
return msg.format(self.event.timestamp(), SYN[self.event.code])
|
|
|
|
def __repr__(self):
|
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
|
|
|
|
#: A mapping of event types to :class:`InputEvent` sub-classes. Used
|
|
#: by :func:`evdev.util.categorize()`
|
|
event_factory = {
|
|
EV_KEY: KeyEvent,
|
|
EV_REL: RelEvent,
|
|
EV_ABS: AbsEvent,
|
|
EV_SYN: SynEvent,
|
|
}
|
|
|
|
|
|
__all__ = ("InputEvent", "KeyEvent", "RelEvent", "SynEvent", "AbsEvent", "event_factory")
|