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