touch_device.py revision 51b95395336cc1e887d17acab137fcd450b7bfdc
1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Touch device module provides some touch device related attributes.""" 6 7import collections 8import glob 9import os 10import re 11 12import common_util 13 14 15# Define AbsAxis class with axis attributes: min, max, and resolution 16AbsAxis = collections.namedtuple('AbsAxis', ['min', 'max', 'resolution']) 17 18 19class TouchDevice: 20 """A class about touch device properties.""" 21 def __init__(self, device_node=None, is_touchscreen=False, 22 device_description=None): 23 """If the device_description is provided (i.e., not None), it is 24 used to create a mocked device for testing purpose. 25 """ 26 self.device_node = (device_node if device_node 27 else self.get_device_node(is_touchscreen)) 28 self.axis_x, self.axis_y = self.parse_abs_axes(device_description) 29 30 def get_device_node(self, is_touchscreen): 31 """Get the touch device node through xinput 32 Touchscreens have a different device name, so this 33 chooses between them. Otherwise they are the same. 34 35 The resulting string looks like /dev/input/event8 36 """ 37 cmd = '/opt/google/' 38 if is_touchscreen: 39 cmd = os.path.join(cmd, 'touchscreen/tscontrol') 40 else: 41 cmd = os.path.join(cmd, 'touchpad/tpcontrol') 42 cmd += ' status | grep "Device Node"' 43 device_node_str = common_util.simple_system_output(cmd) 44 device_node = device_node_str.split(':')[-1].strip().strip('"') 45 return device_node 46 47 def get_dimensions_in_mm(self): 48 """Get the width and height in mm of the device.""" 49 (left, right, top, bottom, 50 resolution_x, resolution_y) = self.get_resolutions() 51 width = float((right - left)) / resolution_x 52 height = float((bottom - top)) / resolution_y 53 return (width, height) 54 55 def get_resolutions(self): 56 """Get the resolutions in x and y axis of the device.""" 57 return (self.axis_x.resolution, self.axis_y.resolution) 58 59 def get_edges(self): 60 """Get the left, right, top, and bottom edges of the device.""" 61 return (self.axis_x.min, self.axis_x.max, 62 self.axis_y.min, self.axis_y.max) 63 64 def parse_abs_axes(self, device_description): 65 """Prase to get information about min, max, and resolution of 66 ABS_X and ABS_Y 67 68 Example of ABS_X: 69 A: 00 0 1280 0 0 12 70 Example of ABS_y: 71 A: 01 0 1280 0 0 12 72 """ 73 pattern = 'A:\s*%s\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)' 74 pattern_x = pattern % '00' 75 pattern_y = pattern % '01' 76 cmd = 'evemu-describe %s' % self.device_node 77 if device_description is None: 78 device_description = common_util.simple_system_output(cmd) 79 axis_x = axis_y = None 80 if device_description: 81 for line in device_description.splitlines(): 82 if not axis_x: 83 result = re.search(pattern_x, line, re.I) 84 if result: 85 min_x = int(result.group(1)) 86 max_x = int(result.group(2)) 87 resolution_x = int(result.group(5)) 88 axis_x = AbsAxis(min_x, max_x, resolution_x) 89 if not axis_y: 90 result = re.search(pattern_y, line, re.I) 91 if result: 92 min_y = int(result.group(1)) 93 max_y = int(result.group(2)) 94 resolution_y = int(result.group(5)) 95 axis_y = AbsAxis(min_y, max_y, resolution_y) 96 return (axis_x, axis_y) 97 98 def pixel_to_mm(self, (pixel_x, pixel_y)): 99 """Convert the point coordinate from pixel to mm.""" 100 mm_x = float(pixel_x - self.axis_x.min) / self.axis_x.resolution 101 mm_y = float(pixel_y - self.axis_y.min) / self.axis_y.resolution 102 return (mm_x, mm_y) 103 104 def pixel_to_mm_single_axis(self, value_pixel, axis): 105 """Convert the coordinate from pixel to mm.""" 106 value_mm = float(value_pixel - axis.min) / axis.resolution 107 return value_mm 108 109 def get_dimensions(self): 110 """Get the vendor-specified dimensions of the touch device.""" 111 return (self.axis_x.max - self.axis_x.min, 112 self.axis_y.max - self.axis_y.min) 113 114 def get_display_geometry(self, screen_size, display_ratio): 115 """Get a preferred display geometry when running the test.""" 116 display_ratio = 0.8 117 dev_width, dev_height = self.get_dimensions() 118 screen_width, screen_height = screen_size 119 120 if 1.0 * screen_width / screen_height <= 1.0 * dev_width / dev_height: 121 disp_width = int(screen_width * display_ratio) 122 disp_height = int(disp_width * dev_height / dev_width) 123 disp_offset_x = 0 124 disp_offset_y = screen_height - disp_height 125 else: 126 disp_height = int(screen_height * display_ratio) 127 disp_width = int(disp_height * dev_width / dev_height) 128 disp_offset_x = 0 129 disp_offset_y = screen_height - disp_height 130 131 return (disp_width, disp_height, disp_offset_x, disp_offset_y) 132 133 def _touch_input_name_re_str(self): 134 pattern_str = ('touchpad', 'trackpad') 135 return '(?:%s)' % '|'.join(pattern_str) 136 137 def get_touch_input_dir(self): 138 """Get touch device input directory.""" 139 input_root_dir = '/sys/class/input' 140 input_dirs = glob.glob(os.path.join(input_root_dir, 'input*')) 141 re_pattern = re.compile(self._touch_input_name_re_str(), re.I) 142 for input_dir in input_dirs: 143 filename = os.path.join(input_dir, 'name') 144 if os.path.isfile(filename): 145 with open(filename) as f: 146 for line in f: 147 if re_pattern.search(line) is not None: 148 return input_dir 149 return None 150 151 def get_firmware_version(self): 152 """Probe the firmware version.""" 153 input_dir = self.get_touch_input_dir() 154 device_dir = 'device' 155 156 # Get the re search pattern for firmware_version file name 157 fw_list = ('firmware', 'fw') 158 ver_list = ('version', 'id') 159 sep_list = ('_', '-') 160 re_str = '%s%s%s' % ('(?:%s)' % '|'.join(fw_list), 161 '(?:%s)' % '|'.join(sep_list), 162 '(?:%s)' % '|'.join(ver_list)) 163 re_pattern = re.compile(re_str, re.I) 164 165 if input_dir is not None: 166 device_dir = os.path.join(input_dir, 'device', '*') 167 for f in glob.glob(device_dir): 168 if os.path.isfile(f) and re_pattern.search(f): 169 with open (f) as f: 170 for line in f: 171 return line.strip('\n') 172 return 'unknown' 173