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