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_lid(self):
    532         return ((EV_SW in self.events) and
    533                 (SW_LID in self.events[EV_SW]))
    534 
    535     def is_mt_b(self):
    536         return self.is_mt() and ABS_MT_SLOT in self.events[EV_ABS]
    537 
    538     def is_mt_a(self):
    539         return self.is_mt() and ABS_MT_SLOT not in self.events[EV_ABS]
    540 
    541     def is_mt(self):
    542         return (EV_ABS in self.events and
    543                 (set(self.events[EV_ABS]) & set(ABS_MT_RANGE)))
    544 
    545     def is_hp_jack(self):
    546         return (EV_SW in self.events and
    547                 SW_HEADPHONE_INSERT in self.events[EV_SW])
    548 
    549     def is_mic_jack(self):
    550         return (EV_SW in self.events and
    551                 SW_MICROPHONE_INSERT in self.events[EV_SW])
    552 
    553     def is_audio_jack(self):
    554         return (EV_SW in self.events and
    555                 ((SW_HEADPHONE_INSERT in self.events[EV_SW]) or
    556                  (SW_MICROPHONE_INSERT in self.events[EV_SW] or
    557                  (SW_LINEOUT_INSERT in self.events[EV_SW]))))
    558 
    559     """ Debug helper print functions """
    560 
    561     def print_abs_info(self, axis):
    562         if EV_ABS in self.events and axis in self.events[EV_ABS]:
    563             a = self.events[EV_ABS][axis]
    564             print '      Value       %6d' % a.value
    565             print '      Min         %6d' % a.min
    566             print '      Max         %6d' % a.max
    567             if a.fuzz != 0:
    568                 print '      Fuzz        %6d' % a.fuzz
    569             if a.flat != 0:
    570                 print '      Flat        %6d' % a.flat
    571             if a.resolution != 0:
    572                 print '      Resolution  %6d' % a.resolution
    573 
    574     def print_props(self):
    575         print ('Input driver Version: %d.%d.%d' %
    576                (self.version[0], self.version[1], self.version[2]))
    577         print ('Input device ID: bus %x vendor %x product %x version %x' %
    578                (self.id_bus, self.id_vendor, self.id_product, self.id_version))
    579         print 'Input device name: "%s"' % (self.name)
    580         for t in self.events:
    581             print '  Event type %d (%s)' % (t, EV_TYPES.get(t, '?'))
    582             for c in self.events[t]:
    583                 if (t in EV_STRINGS):
    584                     code = EV_STRINGS[t].get(c, '?')
    585                     print '    Event code %s (%d)' % (code, c)
    586                 else:
    587                     print '    Event code (%d)' % (c)
    588                 self.print_abs_info(c)
    589 
    590     def get_slots(self):
    591         """ Get those slots with positive tracking IDs. """
    592         slot_dict = OrderedDict()
    593         for slot_index in range(self.num_slots):
    594             slot = self.mt_slots[slot_index]
    595             if self._get_tid(slot) == -1:
    596                 continue
    597             slot_id = self._convert_slot_index_to_slot_id(slot_index)
    598             slot_dict[slot_id] = slot
    599         return slot_dict
    600 
    601     def print_slots(self):
    602         slot_dict = self.get_slots()
    603         for slot_id, slot in slot_dict.items():
    604             print 'slot #%d' % slot_id
    605             for a in slot:
    606                 abs = EV_STRINGS[EV_ABS].get(a, '?')
    607                 print '  %s = %6d' % (abs, slot[a].value)
    608 
    609 
    610 def print_report(device):
    611     print '----- EV_SYN -----'
    612     if device.is_touchpad():
    613         f = device.get_num_fingers()
    614         if f == 0:
    615             return
    616         x = device.get_x()
    617         y = device.get_y()
    618         z = device.get_pressure()
    619         l = device.get_left()
    620         print 'Left=%d Fingers=%d X=%d Y=%d Pressure=%d' % (l, f, x, y, z)
    621         if device.is_mt():
    622             device.print_slots()
    623 
    624 
    625 def get_device_node(device_type):
    626     """Get the keyboard device node through device info file.
    627 
    628     Example of the keyboard device information looks like
    629 
    630     I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
    631     N: Name="AT Translated Set 2 keyboard"
    632     P: Phys=isa0060/serio0/input0
    633     S: Sysfs=/devices/platform/i8042/serio0/input/input5
    634     U: Uniq=
    635     H: Handlers=sysrq kbd event5
    636     """
    637     device_node = None
    638     device_found = None
    639     device_pattern = re.compile('N: Name=.*%s' % device_type, re.I)
    640     event_number_pattern = re.compile(r'H: Handlers=.*event(\d?)', re.I)
    641     with open(_DEVICE_INFO_FILE) as info:
    642         for line in info:
    643             if device_found:
    644                 result = event_number_pattern.search(line)
    645                 if result:
    646                     event_number = int(result.group(1))
    647                     device_node = '/dev/input/event%d' % event_number
    648                     break
    649             else:
    650                 device_found = device_pattern.search(line)
    651     return device_node
    652 
    653 
    654 if __name__ == "__main__":
    655     from optparse import OptionParser
    656     import glob
    657     parser = OptionParser()
    658 
    659     parser.add_option("-a", "--audio_jack", action="store_true",
    660                       dest="audio_jack", default=False,
    661                       help="Find and use all audio jacks")
    662     parser.add_option("-d", "--devpath", dest="devpath",
    663                       default="/dev/input/event0",
    664                       help="device path (/dev/input/event0)")
    665     parser.add_option("-q", "--quiet", action="store_false", dest="verbose",
    666                       default=True, help="print less messages to stdout")
    667     parser.add_option("-t", "--touchpad", action="store_true", dest="touchpad",
    668                       default=False, help="Find and use first touchpad device")
    669     (options, args) = parser.parse_args()
    670 
    671     # TODO: Use gudev to detect touchpad
    672     devices = []
    673     if options.touchpad:
    674         for path in glob.glob('/dev/input/event*'):
    675             device = InputDevice(path)
    676             if device.is_touchpad():
    677                 print 'Using touchpad %s.' % path
    678                 options.devpath = path
    679                 devices.append(device)
    680                 break
    681         else:
    682             print 'No touchpad found!'
    683             exit()
    684     elif options.audio_jack:
    685         for path in glob.glob('/dev/input/event*'):
    686             device = InputDevice(path)
    687             if device.is_audio_jack():
    688                 devices.append(device)
    689         device = None
    690     elif os.path.exists(options.devpath):
    691         print 'Using %s.' % options.devpath
    692         devices.append(InputDevice(options.devpath))
    693     else:
    694         print '%s does not exist.' % options.devpath
    695         exit()
    696 
    697     for device in devices:
    698         device.print_props()
    699         if device.is_touchpad():
    700             print ('x: (%d,%d), y: (%d,%d), z: (%d, %d)' %
    701                    (device.get_x_min(), device.get_x_max(),
    702                     device.get_y_min(), device.get_y_max(),
    703                     device.get_pressure_min(), device.get_pressure_max()))
    704             device.print_slots()
    705             print 'Number of fingers: %d' % device.get_num_fingers()
    706             print 'Current slot id: %d' % device.get_current_slot_id()
    707     print '------------------'
    708     print
    709 
    710     ev = InputEvent()
    711     while True:
    712         _rl, _, _ = select.select([d.f for d in devices], [], [])
    713         for fd in _rl:
    714             # Lookup for the device which owns fd.
    715             device = [d for d in devices if d.f == fd][0]
    716             try:
    717                 ev.read(fd)
    718             except KeyboardInterrupt:
    719                 exit()
    720             is_syn = device.process_event(ev)
    721             print ev
    722             if is_syn:
    723                 print_report(device)
    724