Home | History | Annotate | Download | only in input
      1 #!/usr/bin/env python
      2 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 # Description:
      7 #
      8 # Class for handling linux 'evdev' input devices.
      9 #
     10 # Provides evtest-like functionality if run from the command line:
     11 # $ input_device.py -d /dev/input/event6
     12 
     13 """ Read properties and events of a linux input device. """
     14 
     15 import array
     16 import copy
     17 import fcntl
     18 import os.path
     19 import re
     20 import select
     21 import struct
     22 import time
     23 
     24 from collections import OrderedDict
     25 
     26 from linux_input import *
     27 
     28 
     29 # The regular expression of possible keyboard types.
     30 KEYBOARD_TYPES = '(keyboard|chromeos-ec-i2c|cros-ec-spi|cros-ec-i2c|cros_ec)'
     31 
     32 _DEVICE_INFO_FILE = '/proc/bus/input/devices'
     33 
     34 
     35 class Valuator:
     36     """ A Valuator just stores a value """
     37     def __init__(self):
     38         self.value = 0
     39 
     40 class SwValuator(Valuator):
     41     """ A Valuator used for EV_SW (switch) events """
     42     def __init__(self, value):
     43         self.value = value
     44 
     45 class AbsValuator(Valuator):
     46     """
     47     An AbsValuator, used for EV_ABS events stores a value as well as other
     48     properties of the corresponding absolute axis.
     49     """
     50     def __init__(self, value, min_value, max_value, fuzz, flat, resolution):
     51         self.value = value
     52         self.min = min_value
     53         self.max = max_value
     54         self.fuzz = fuzz
     55         self.flat = flat
     56         self.resolution = resolution
     57 
     58 
     59 class InputEvent:
     60     """
     61     Linux evdev input event
     62 
     63     An input event has the following fields which can be accessed as public
     64     properties of this class:
     65         tv_sec
     66         tv_usec
     67         type
     68         code
     69         value
     70     """
     71     def __init__(self, tv_sec=0, tv_usec=0, type=0, code=0, value=0):
     72         self.format = input_event_t
     73         self.format_size = struct.calcsize(self.format)
     74         (self.tv_sec, self.tv_usec, self.type, self.code,
     75          self.value) = (tv_sec, tv_usec, type, code, value)
     76 
     77     def read(self, stream):
     78         """ Read an input event from the provided stream and unpack it. """
     79         packed = stream.read(self.format_size)
     80         (self.tv_sec, self.tv_usec, self.type, self.code,
     81          self.value) = struct.unpack(self.format, packed)
     82 
     83     def write(self, stream):
     84         """ Pack an input event and write it to the provided stream. """
     85         packed = struct.pack(self.format, self.tv_sec, self.tv_usec, self.type,
     86                              self.code, self.value)
     87         stream.write(packed)
     88         stream.flush()
     89 
     90     def __str__(self):
     91         t = EV_TYPES.get(self.type, self.type)
     92         if self.type in EV_STRINGS:
     93             c = EV_STRINGS[self.type].get(self.code, self.code)
     94         else:
     95             c = self.code
     96         return ('%d.%06d: %s[%s] = %d' %
     97                 (self.tv_sec, self.tv_usec, t, c, self.value))
     98 
     99 
    100 class InputDevice:
    101     """
    102     Linux evdev input device
    103 
    104     A linux kernel "evdev" device sends a stream of "input events".
    105     These events are grouped together into "input reports", which is a set of
    106     input events ending in a single EV_SYN/SYN_REPORT event.
    107 
    108     Each input event is tagged with a type and a code.
    109     A given input device supports a subset of the possible types, and for
    110     each type it supports a subset of the possible codes for that type.
    111 
    112     The device maintains a "valuator" for each supported type/code pairs.
    113     There are two types of "valuators":
    114        Normal valuators represent just a value.
    115        Absolute valuators are only for EV_ABS events. They have more fields:
    116            value, minimum, maximum, resolution, fuzz, flatness
    117     Note: Relative and Absolute "Valuators" are also often called relative
    118           and absolute axis, respectively.
    119 
    120     The evdev protocol is stateful.  Input events are only sent when the values
    121     of a valuator actually changes.  This dramatically reduces the stream of
    122     events emenating from the kernel.
    123 
    124     Multitouch devices are a special case.  There are two types of multitouch
    125     devices defined in the kernel:
    126         Multitouch type "A" (MT-A) devices:
    127             In each input report, the device sends an unordered list of all
    128             active contacts.  The data for each active contact is separated
    129             in the input report by an EV_SYN/SYN_MT_REPORT event.
    130             Thus, the MT-A contact event stream is not stateful.
    131             Note: MT-A is not currently supported by this class.
    132 
    133         Multitouch type "B" (MT-B) devices:
    134             The device maintains a list of slots, where each slot contains a
    135             single contact.  In each input report, the device only sends
    136             information about the slots that have changed.
    137             Thus, the MT-B contact event stream is stateful.
    138             When reporting multiple slots, the EV_ABS/MT_SLOT valuator is used
    139             to indicate the 'current' slot for which subsequent EV_ABS/ABS_MT_*
    140             valuator events apply.
    141             An inactive slot has EV_ABS/ABS_MT_TRACKING_ID == -1
    142             Active slots have EV_ABS/ABS_MT_TRACKING_ID >= 0
    143 
    144     Besides maintaining the set of supported ABS_MT valuators in the supported
    145     valuator list, a array of slots is also maintained.  Each slot has its own
    146     unique copy of just the supported ABS_MT valuators.  This represents the
    147     current state of that slot.
    148     """
    149 
    150     def __init__(self, path, ev_syn_cb=None):
    151         """
    152         Constructor opens the device file and probes its properties.
    153 
    154         Note: The device file is left open when the constructor exits.
    155         """
    156         self.path = path
    157         self.ev_syn_cb = ev_syn_cb
    158         self.events = {}     # dict { ev_type : dict { ev_code : Valuator } }
    159         self.mt_slots = []   # [ dict { mt_code : AbsValuator } ] * |MT-B slots|
    160 
    161         # Open the device node, and use ioctls to probe its properties
    162         self.f = None
    163         self.f = open(path, 'rb+', buffering=0)
    164         self._ioctl_version()
    165         self._ioctl_id()
    166         self._ioctl_name()
    167         for t in self._ioctl_types():
    168             self._ioctl_codes(t)
    169         self._setup_mt_slots()
    170 
    171     def __del__(self):
    172         """
    173         Deconstructor closes the device file, if it is open.
    174         """
    175         if self.f and not self.f.closed:
    176             self.f.close()
    177 
    178     def process_event(self, ev):
    179         """
    180         Processes an incoming input event.
    181 
    182         Returns True for EV_SYN/SYN_REPORT events to indicate that a complete
    183         input report has been received.
    184 
    185         Returns False for other events.
    186 
    187         Events not supported by this device are silently ignored.
    188 
    189         For MT events, updates the slot valuator value for the current slot.
    190         If current slot is the 'primary' slot, also updates the events entry.
    191 
    192         For all other events, updates the corresponding valuator value.
    193         """
    194         if ev.type == EV_SYN and ev.code == SYN_REPORT:
    195             return True
    196         elif ev.type not in self.events or ev.code not in self.events[ev.type]:
    197             return False
    198         elif self.is_mt_b() and ev.type == EV_ABS and ev.code in ABS_MT_RANGE:
    199             # TODO: Handle MT-A
    200             slot = self._get_current_slot()
    201             slot[ev.code].value = ev.value
    202             # if the current slot is the "primary" slot,
    203             # update the events[] entry, too.
    204             if slot == self._get_mt_primary_slot():
    205                 self.events[ev.type][ev.code].value = ev.value
    206         else:
    207             self.events[ev.type][ev.code].value = ev.value
    208         return False
    209 
    210     def _ioctl_version(self):
    211         """ Queries device file for version information. """
    212         # Version is a 32-bit integer, which encodes 8-bit major version,
    213         # 8-bit minor version and 16-bit revision.
    214         version = array.array('I', [0])
    215         fcntl.ioctl(self.f, EVIOCGVERSION, version, 1)
    216         self.version = (version[0] >> 16, (version[0] >> 8) & 0xff,
    217                         version[0] & 0xff)
    218 
    219     def _ioctl_id(self):
    220         """ Queries device file for input device identification. """
    221         # struct input_id is 4 __u16
    222         gid = array.array('H', [0] * 4)
    223         fcntl.ioctl(self.f, EVIOCGID, gid, 1)
    224         self.id_bus = gid[ID_BUS]
    225         self.id_vendor = gid[ID_VENDOR]
    226         self.id_product = gid[ID_PRODUCT]
    227         self.id_version = gid[ID_VERSION]
    228 
    229     def _ioctl_name(self):
    230         """ Queries device file for the device name. """
    231         # Device name is a C-string up to 255 bytes in length.
    232         name_len = 255
    233         name = array.array('B', [0] * name_len)
    234         name_len = fcntl.ioctl(self.f, EVIOCGNAME(name_len), name, 1)
    235         self.name = name[0:name_len-1].tostring()
    236 
    237     def _ioctl_get_switch(self, sw):
    238         """
    239         Queries device file for current value of all switches and returns
    240         a boolean indicating whether the switch sw is set.
    241         """
    242         size = SW_CNT / 8    # Buffer size of one __u16
    243         buf = array.array('H', [0])
    244         fcntl.ioctl(self.f, EVIOCGSW(size), buf)
    245         return SwValuator(((buf[0] >> sw) & 0x01) == 1)
    246 
    247     def _ioctl_absinfo(self, axis):
    248         """
    249         Queries device file for absinfo structure for given absolute axis.
    250         """
    251         # struct input_absinfo is 6 __s32
    252         a = array.array('i', [0] * 6)
    253         fcntl.ioctl(self.f, EVIOCGABS(axis), a, 1)
    254         return AbsValuator(a[0], a[1], a[2], a[3], a[4], a[5])
    255 
    256     def _ioctl_codes(self, ev_type):
    257         """
    258         Queries device file for supported event codes for given event type.
    259         """
    260         self.events[ev_type] = {}
    261         if ev_type not in EV_SIZES:
    262             return
    263 
    264         size = EV_SIZES[ev_type] / 8    # Convert bits to bytes
    265         ev_code = array.array('B', [0] * size)
    266         try:
    267             count = fcntl.ioctl(self.f, EVIOCGBIT(ev_type, size), ev_code, 1)
    268             for c in range(count * 8):
    269                 if test_bit(c, ev_code):
    270                     if ev_type == EV_ABS:
    271                         self.events[ev_type][c] = self._ioctl_absinfo(c)
    272                     elif ev_type == EV_SW:
    273                         self.events[ev_type][c] = self._ioctl_get_switch(c)
    274                     else:
    275                         self.events[ev_type][c] = Valuator()
    276         except IOError as (errno, strerror):
    277             # Errno 22 signifies that this event type has no event codes.
    278             if errno != 22:
    279                 raise
    280 
    281     def _ioctl_types(self):
    282         """ Queries device file for supported event types. """
    283         ev_types = array.array('B', [0] * (EV_CNT / 8))
    284         fcntl.ioctl(self.f, EVIOCGBIT(EV_SYN, EV_CNT / 8), ev_types, 1)
    285         types  = []
    286         for t in range(EV_CNT):
    287             if test_bit(t, ev_types):
    288                 types.append(t)
    289         return types
    290 
    291     def _convert_slot_index_to_slot_id(self, index):
    292         """ Convert a slot index in self.mt_slots to its slot id. """
    293         return self.abs_mt_slot.min + index
    294 
    295     def _ioctl_mt_slots(self):
    296         """Query mt slots values using ioctl.
    297 
    298         The ioctl buffer argument should be binary equivalent to
    299         struct input_mt_request_layout {
    300             __u32 code;
    301             __s32 values[num_slots];
    302 
    303         Note that the slots information returned by EVIOCGMTSLOTS
    304         corresponds to the slot ids ranging from abs_mt_slot.min to
    305         abs_mt_slot.max which is not necessarily the same as the
    306         slot indexes ranging from 0 to num_slots - 1 in self.mt_slots.
    307         We need to map between the slot index and the slot id correctly.
    308         };
    309         """
    310         # Iterate through the absolute mt events that are supported.
    311         for c in range(ABS_MT_FIRST, ABS_MT_LAST):
    312             if c not in self.events[EV_ABS]:
    313                 continue
    314             # Sync with evdev kernel driver for the specified code c.
    315             mt_slot_info = array.array('i', [c] + [0] * self.num_slots)
    316             mt_slot_info_len = (self.num_slots + 1) * mt_slot_info.itemsize
    317             fcntl.ioctl(self.f, EVIOCGMTSLOTS(mt_slot_info_len), mt_slot_info)
    318             values = mt_slot_info[1:]
    319             for slot_index in range(self.num_slots):
    320                 slot_id = self._convert_slot_index_to_slot_id(slot_index)
    321                 self.mt_slots[slot_index][c].value = values[slot_id]
    322 
    323     def _setup_mt_slots(self):
    324         """
    325         Sets up the device's mt_slots array.
    326 
    327         Each element of the mt_slots array is initialized as a deepcopy of a
    328         dict containing all of the MT valuators from the events dict.
    329         """
    330         # TODO(djkurtz): MT-A
    331         if not self.is_mt_b():
    332             return
    333         ev_abs = self.events[EV_ABS]
    334         # Create dict containing just the MT valuators
    335         mt_abs_info = dict((axis, ev_abs[axis])
    336                            for axis in ev_abs
    337                            if axis in ABS_MT_RANGE)
    338 
    339         # Initialize TRACKING_ID to -1
    340         mt_abs_info[ABS_MT_TRACKING_ID].value = -1
    341 
    342         # Make a copy of mt_abs_info for each MT slot
    343         self.abs_mt_slot = ev_abs[ABS_MT_SLOT]
    344         self.num_slots = self.abs_mt_slot.max - self.abs_mt_slot.min + 1
    345         for s in range(self.num_slots):
    346             self.mt_slots.append(copy.deepcopy(mt_abs_info))
    347 
    348         self._ioctl_mt_slots()
    349 
    350     def get_current_slot_id(self):
    351         """
    352         Return the current slot id.
    353         """
    354         if not self.is_mt_b():
    355             return None
    356         return self.events[EV_ABS][ABS_MT_SLOT].value
    357 
    358     def _get_current_slot(self):
    359         """
    360         Returns the current slot, as indicated by the last ABS_MT_SLOT event.
    361         """
    362         current_slot_id = self.get_current_slot_id()
    363         if current_slot_id is None:
    364             return None
    365         return self.mt_slots[current_slot_id]
    366 
    367     def _get_tid(self, slot):
    368         """ Returns the tracking_id for the given MT slot. """
    369         return slot[ABS_MT_TRACKING_ID].value
    370 
    371     def _get_mt_valid_slots(self):
    372         """
    373         Returns a list of valid slots.
    374 
    375         A valid slot is a slot whose tracking_id != -1.
    376         """
    377         return [s for s in self.mt_slots if self._get_tid(s) != -1]
    378 
    379     def _get_mt_primary_slot(self):
    380         """
    381         Returns the "primary" MT-B slot.
    382 
    383         The "primary" MT-B slot is arbitrarily chosen as the slot with lowest
    384         tracking_id (> -1).  It is used to make an MT-B device look like
    385         single-touch (ST) device.
    386         """
    387         slot = None
    388         for s in self.mt_slots:
    389             tid = self._get_tid(s)
    390             if tid < 0:
    391                 continue
    392             if not slot or tid < self._get_tid(slot):
    393                 slot = s
    394         return slot
    395 
    396     def _code_if_mt(self, type, code):
    397         """
    398         Returns MT-equivalent event code for certain specific event codes
    399         """
    400         if type != EV_ABS:
    401             return code
    402         elif code == ABS_X:
    403             return  ABS_MT_POSITION_X
    404         elif code == ABS_Y:
    405             return ABS_MT_POSITION_Y
    406         elif code == ABS_PRESSURE:
    407             return ABS_MT_PRESSURE
    408         elif code == ABS_TOOL_WIDTH:
    409             return ABS_TOUCH_MAJOR
    410         else:
    411             return code
    412 
    413     def _get_valuator(self, type, code):
    414         """ Returns Valuator for given event type and code """
    415         if (not type in self.events) or (not code in self.events[type]):
    416             return None
    417         if type == EV_ABS:
    418             code = self._code_if_mt(type, code)
    419         return self.events[type][code]
    420 
    421     def _get_value(self, type, code):
    422         """
    423         Returns the value of the valuator with the give event (type, code).
    424         """
    425         axis = self._get_valuator(type, code)
    426         if not axis:
    427             return None
    428         return axis.value
    429 
    430     def _get_min(self, type, code):
    431         """
    432         Returns the min value of the valuator with the give event (type, code).
    433 
    434         Note: Only AbsValuators (EV_ABS) have max values.
    435         """
    436         axis = self._get_valuator(type, code)
    437         if not axis:
    438             return None
    439         return axis.min
    440 
    441     def _get_max(self, type, code):
    442         """
    443         Returns the min value of the valuator with the give event (type, code).
    444 
    445         Note: Only AbsValuators (EV_ABS) have max values.
    446         """
    447         axis = self._get_valuator(type, code)
    448         if not axis:
    449             return None
    450         return axis.max
    451 
    452     """ Public accessors """
    453 
    454     def get_num_fingers(self):
    455         if self.is_mt_b():
    456             return len(self._get_mt_valid_slots())
    457         elif self.is_mt_a():
    458             return 0  # TODO(djkurtz): MT-A
    459         else:  # Single-Touch case
    460             if not self._get_value(EV_KEY, BTN_TOUCH) == 1:
    461                 return 0
    462             elif self._get_value(EV_KEY, BTN_TOOL_TRIPLETAP) == 1:
    463                 return 3
    464             elif self._get_value(EV_KEY, BTN_TOOL_DOUBLETAP) == 1:
    465                 return 2
    466             elif self._get_value(EV_KEY, BTN_TOOL_FINGER) == 1:
    467                 return 1
    468             else:
    469                 return 0
    470 
    471     def get_x(self):
    472         return self._get_value(EV_ABS, ABS_X)
    473 
    474     def get_x_min(self):
    475         return self._get_min(EV_ABS, ABS_X)
    476 
    477     def get_x_max(self):
    478         return self._get_max(EV_ABS, ABS_X)
    479 
    480     def get_y(self):
    481         return self._get_value(EV_ABS, ABS_Y)
    482 
    483     def get_y_min(self):
    484         return self._get_min(EV_ABS, ABS_Y)
    485 
    486     def get_y_max(self):
    487         return self._get_max(EV_ABS, ABS_Y)
    488 
    489     def get_pressure(self):
    490         return self._get_value(EV_ABS, ABS_PRESSURE)
    491 
    492     def get_pressure_min(self):
    493         return self._get_min(EV_ABS, ABS_PRESSURE)
    494 
    495     def get_pressure_max(self):
    496         return self._get_max(EV_ABS, ABS_PRESSURE)
    497 
    498     def get_left(self):
    499         return int(self._get_value(EV_KEY, BTN_LEFT) == 1)
    500 
    501     def get_right(self):
    502         return int(self._get_value(EV_KEY, BTN_RIGHT) == 1)
    503 
    504     def get_middle(self):
    505         return int(self._get_value(EV_KEY, BTN_MIDDLE) == 1)
    506 
    507     def get_microphone_insert(self):
    508         return self._get_value(EV_SW, SW_MICROPHONE_INSERT)
    509 
    510     def get_headphone_insert(self):
    511         return self._get_value(EV_SW, SW_HEADPHONE_INSERT)
    512 
    513     def get_lineout_insert(self):
    514         return self._get_value(EV_SW, SW_LINEOUT_INSERT)
    515 
    516     def is_touchpad(self):
    517         return ((EV_KEY in self.events) and
    518                 (BTN_TOOL_FINGER in self.events[EV_KEY]) and
    519                 (EV_ABS in self.events))
    520 
    521     def is_keyboard(self):
    522         return ((EV_KEY in self.events) and
    523                 (KEY_F2 in self.events[EV_KEY]))
    524 
    525     def is_touchscreen(self):
    526         return ((EV_KEY in self.events) and
    527                 (BTN_TOUCH in self.events[EV_KEY]) and
    528                 (not BTN_TOOL_FINGER in self.events[EV_KEY]) and
    529                 (EV_ABS in self.events))
    530 
    531     def is_mt_b(self):
    532         return self.is_mt() and ABS_MT_SLOT in self.events[EV_ABS]
    533 
    534     def is_mt_a(self):
    535         return self.is_mt() and ABS_MT_SLOT not in self.events[EV_ABS]
    536 
    537     def is_mt(self):
    538         return (EV_ABS in self.events and
    539                 (set(self.events[EV_ABS]) & set(ABS_MT_RANGE)))
    540 
    541     def is_hp_jack(self):
    542         return (EV_SW in self.events and
    543                 SW_HEADPHONE_INSERT in self.events[EV_SW])
    544 
    545     def is_mic_jack(self):
    546         return (EV_SW in self.events and
    547                 SW_MICROPHONE_INSERT in self.events[EV_SW])
    548 
    549     def is_audio_jack(self):
    550         return (EV_SW in self.events and
    551                 ((SW_HEADPHONE_INSERT in self.events[EV_SW]) or
    552                  (SW_MICROPHONE_INSERT in self.events[EV_SW] or
    553                  (SW_LINEOUT_INSERT in self.events[EV_SW]))))
    554 
    555     """ Debug helper print functions """
    556 
    557     def print_abs_info(self, axis):
    558         if EV_ABS in self.events and axis in self.events[EV_ABS]:
    559             a = self.events[EV_ABS][axis]
    560             print '      Value       %6d' % a.value
    561             print '      Min         %6d' % a.min
    562             print '      Max         %6d' % a.max
    563             if a.fuzz != 0:
    564                 print '      Fuzz        %6d' % a.fuzz
    565             if a.flat != 0:
    566                 print '      Flat        %6d' % a.flat
    567             if a.resolution != 0:
    568                 print '      Resolution  %6d' % a.resolution
    569 
    570     def print_props(self):
    571         print ('Input driver Version: %d.%d.%d' %
    572                (self.version[0], self.version[1], self.version[2]))
    573         print ('Input device ID: bus %x vendor %x product %x version %x' %
    574                (self.id_bus, self.id_vendor, self.id_product, self.id_version))
    575         print 'Input device name: "%s"' % (self.name)
    576         for t in self.events:
    577             print '  Event type %d (%s)' % (t, EV_TYPES.get(t, '?'))
    578             for c in self.events[t]:
    579                 if (t in EV_STRINGS):
    580                     code = EV_STRINGS[t].get(c, '?')
    581                     print '    Event code %s (%d)' % (code, c)
    582                 else:
    583                     print '    Event code (%d)' % (c)
    584                 self.print_abs_info(c)
    585 
    586     def get_slots(self):
    587         """ Get those slots with positive tracking IDs. """
    588         slot_dict = OrderedDict()
    589         for slot_index in range(self.num_slots):
    590             slot = self.mt_slots[slot_index]
    591             if self._get_tid(slot) == -1:
    592                 continue
    593             slot_id = self._convert_slot_index_to_slot_id(slot_index)
    594             slot_dict[slot_id] = slot
    595         return slot_dict
    596 
    597     def print_slots(self):
    598         slot_dict = self.get_slots()
    599         for slot_id, slot in slot_dict.items():
    600             print 'slot #%d' % slot_id
    601             for a in slot:
    602                 abs = EV_STRINGS[EV_ABS].get(a, '?')
    603                 print '  %s = %6d' % (abs, slot[a].value)
    604 
    605 
    606 def print_report(device):
    607     print '----- EV_SYN -----'
    608     if device.is_touchpad():
    609         f = device.get_num_fingers()
    610         if f == 0:
    611             return
    612         x = device.get_x()
    613         y = device.get_y()
    614         z = device.get_pressure()
    615         l = device.get_left()
    616         print 'Left=%d Fingers=%d X=%d Y=%d Pressure=%d' % (l, f, x, y, z)
    617         if device.is_mt():
    618             device.print_slots()
    619 
    620 
    621 def get_device_node(device_type):
    622     """Get the keyboard device node through device info file.
    623 
    624     Example of the keyboard device information looks like
    625 
    626     I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
    627     N: Name="AT Translated Set 2 keyboard"
    628     P: Phys=isa0060/serio0/input0
    629     S: Sysfs=/devices/platform/i8042/serio0/input/input5
    630     U: Uniq=
    631     H: Handlers=sysrq kbd event5
    632     """
    633     device_node = None
    634     device_found = None
    635     device_pattern = re.compile('N: Name=.*%s' % device_type, re.I)
    636     event_number_pattern = re.compile(r'H: Handlers=.*event(\d?)', re.I)
    637     with open(_DEVICE_INFO_FILE) as info:
    638         for line in info:
    639             if device_found:
    640                 result = event_number_pattern.search(line)
    641                 if result:
    642                     event_number = int(result.group(1))
    643                     device_node = '/dev/input/event%d' % event_number
    644                     break
    645             else:
    646                 device_found = device_pattern.search(line)
    647     return device_node
    648 
    649 
    650 if __name__ == "__main__":
    651     from optparse import OptionParser
    652     import glob
    653     parser = OptionParser()
    654 
    655     parser.add_option("-a", "--audio_jack", action="store_true",
    656                       dest="audio_jack", default=False,
    657                       help="Find and use all audio jacks")
    658     parser.add_option("-d", "--devpath", dest="devpath",
    659                       default="/dev/input/event0",
    660                       help="device path (/dev/input/event0)")
    661     parser.add_option("-q", "--quiet", action="store_false", dest="verbose",
    662                       default=True, help="print less messages to stdout")
    663     parser.add_option("-t", "--touchpad", action="store_true", dest="touchpad",
    664                       default=False, help="Find and use first touchpad device")
    665     (options, args) = parser.parse_args()
    666 
    667     # TODO: Use gudev to detect touchpad
    668     devices = []
    669     if options.touchpad:
    670         for path in glob.glob('/dev/input/event*'):
    671             device = InputDevice(path)
    672             if device.is_touchpad():
    673                 print 'Using touchpad %s.' % path
    674                 options.devpath = path
    675                 devices.append(device)
    676                 break
    677         else:
    678             print 'No touchpad found!'
    679             exit()
    680     elif options.audio_jack:
    681         for path in glob.glob('/dev/input/event*'):
    682             device = InputDevice(path)
    683             if device.is_audio_jack():
    684                 devices.append(device)
    685         device = None
    686     elif os.path.exists(options.devpath):
    687         print 'Using %s.' % options.devpath
    688         devices.append(InputDevice(options.devpath))
    689     else:
    690         print '%s does not exist.' % options.devpath
    691         exit()
    692 
    693     for device in devices:
    694         device.print_props()
    695         if device.is_touchpad():
    696             print ('x: (%d,%d), y: (%d,%d), z: (%d, %d)' %
    697                    (device.get_x_min(), device.get_x_max(),
    698                     device.get_y_min(), device.get_y_max(),
    699                     device.get_pressure_min(), device.get_pressure_max()))
    700             device.print_slots()
    701             print 'Number of fingers: %d' % device.get_num_fingers()
    702             print 'Current slot id: %d' % device.get_current_slot_id()
    703     print '------------------'
    704     print
    705 
    706     ev = InputEvent()
    707     while True:
    708         _rl, _, _ = select.select([d.f for d in devices], [], [])
    709         for fd in _rl:
    710             # Lookup for the device which owns fd.
    711             device = [d for d in devices if d.f == fd][0]
    712             try:
    713                 ev.read(fd)
    714             except KeyboardInterrupt:
    715                 exit()
    716             is_syn = device.process_event(ev)
    717             print ev
    718             if is_syn:
    719                 print_report(device)
    720