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