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