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