touch_device.py revision 1dfb83324d02b4f82a8ee2bd32525e78197adf86
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 45 # Extract and return the device node if device_node_str is not None 46 return (device_node_str.split(':')[-1].strip().strip('"') 47 if device_node_str else None) 48 49 def get_dimensions_in_mm(self): 50 """Get the width and height in mm of the device.""" 51 (left, right, top, bottom, 52 resolution_x, resolution_y) = self.get_resolutions() 53 width = float((right - left)) / resolution_x 54 height = float((bottom - top)) / resolution_y 55 return (width, height) 56 57 def get_resolutions(self): 58 """Get the resolutions in x and y axis of the device.""" 59 return (self.axis_x.resolution, self.axis_y.resolution) 60 61 def get_edges(self): 62 """Get the left, right, top, and bottom edges of the device.""" 63 return (self.axis_x.min, self.axis_x.max, 64 self.axis_y.min, self.axis_y.max) 65 66 def parse_abs_axes(self, device_description): 67 """Prase to get information about min, max, and resolution of 68 ABS_X and ABS_Y 69 70 Example of ABS_X: 71 A: 00 0 1280 0 0 12 72 Example of ABS_y: 73 A: 01 0 1280 0 0 12 74 """ 75 pattern = 'A:\s*%s\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)' 76 pattern_x = pattern % '00' 77 pattern_y = pattern % '01' 78 cmd = 'evemu-describe %s' % self.device_node 79 if device_description is None: 80 device_description = common_util.simple_system_output(cmd) 81 axis_x = axis_y = None 82 if device_description: 83 for line in device_description.splitlines(): 84 if not axis_x: 85 result = re.search(pattern_x, line, re.I) 86 if result: 87 min_x = int(result.group(1)) 88 max_x = int(result.group(2)) 89 resolution_x = int(result.group(5)) 90 axis_x = AbsAxis(min_x, max_x, resolution_x) 91 if not axis_y: 92 result = re.search(pattern_y, line, re.I) 93 if result: 94 min_y = int(result.group(1)) 95 max_y = int(result.group(2)) 96 resolution_y = int(result.group(5)) 97 axis_y = AbsAxis(min_y, max_y, resolution_y) 98 return (axis_x, axis_y) 99 100 def pixel_to_mm(self, (pixel_x, pixel_y)): 101 """Convert the point coordinate from pixel to mm.""" 102 mm_x = float(pixel_x - self.axis_x.min) / self.axis_x.resolution 103 mm_y = float(pixel_y - self.axis_y.min) / self.axis_y.resolution 104 return (mm_x, mm_y) 105 106 def pixel_to_mm_single_axis(self, value_pixel, axis): 107 """Convert the coordinate from pixel to mm.""" 108 value_mm = float(value_pixel - axis.min) / axis.resolution 109 return value_mm 110 111 def get_dimensions(self): 112 """Get the vendor-specified dimensions of the touch device.""" 113 return (self.axis_x.max - self.axis_x.min, 114 self.axis_y.max - self.axis_y.min) 115 116 def get_display_geometry(self, screen_size, display_ratio): 117 """Get a preferred display geometry when running the test.""" 118 display_ratio = 0.8 119 dev_width, dev_height = self.get_dimensions() 120 screen_width, screen_height = screen_size 121 122 if 1.0 * screen_width / screen_height <= 1.0 * dev_width / dev_height: 123 disp_width = int(screen_width * display_ratio) 124 disp_height = int(disp_width * dev_height / dev_width) 125 disp_offset_x = 0 126 disp_offset_y = screen_height - disp_height 127 else: 128 disp_height = int(screen_height * display_ratio) 129 disp_width = int(disp_height * dev_width / dev_height) 130 disp_offset_x = 0 131 disp_offset_y = screen_height - disp_height 132 133 return (disp_width, disp_height, disp_offset_x, disp_offset_y) 134 135 def _touch_input_name_re_str(self): 136 pattern_str = ('touchpad', 'trackpad') 137 return '(?:%s)' % '|'.join(pattern_str) 138 139 def get_touch_input_dir(self): 140 """Get touch device input directory.""" 141 input_root_dir = '/sys/class/input' 142 input_dirs = glob.glob(os.path.join(input_root_dir, 'input*')) 143 re_pattern = re.compile(self._touch_input_name_re_str(), re.I) 144 for input_dir in input_dirs: 145 filename = os.path.join(input_dir, 'name') 146 if os.path.isfile(filename): 147 with open(filename) as f: 148 for line in f: 149 if re_pattern.search(line) is not None: 150 return input_dir 151 return None 152 153 def get_firmware_version(self): 154 """Probe the firmware version.""" 155 input_dir = self.get_touch_input_dir() 156 device_dir = 'device' 157 158 # Get the re search pattern for firmware_version file name 159 fw_list = ('firmware', 'fw') 160 ver_list = ('version', 'id') 161 sep_list = ('_', '-') 162 re_str = '%s%s%s' % ('(?:%s)' % '|'.join(fw_list), 163 '(?:%s)' % '|'.join(sep_list), 164 '(?:%s)' % '|'.join(ver_list)) 165 re_pattern = re.compile(re_str, re.I) 166 167 if input_dir is not None: 168 device_dir = os.path.join(input_dir, 'device', '*') 169 for f in glob.glob(device_dir): 170 if os.path.isfile(f) and re_pattern.search(f): 171 with open (f) as f: 172 for line in f: 173 return line.strip('\n') 174 return 'unknown' 175