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