# Copyright 2017 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging import os from autotest_lib.client.common_lib import error from autotest_lib.client.cros.graphics import graphics_utils from autotest_lib.client.cros.input_playback import input_playback _CLICK_EVENTS = '/tmp/click_events' _CLICK_TEMPLATE = 'click_events.template' _PREFIX_RESOLUTION = 'RESOLUTION' _PREFIX_POSITION = 'POSITION' _STYLUS_DEVICE = 'stylus' _STYLUS_PROPERTY = '/tmp/stylus.prop' _STYLUS_TEMPLATE = 'stylus.prop.template' class Stylus(object): """An emulated stylus device used for UI automation.""" def __init__(self): """Prepare an emulated stylus device based on the internal display.""" self.dirname = os.path.dirname(__file__) width, height = graphics_utils.get_internal_resolution() logging.info('internal display W = %d H = %d ', width, height) # Skip the test if there is no internal display if width == -1: raise error.TestNAError('No internal display') # Enlarge resolution of the emulated stylus. self.width = width * 10 self.height = height * 10 stylus_template = os.path.join(self.dirname, _STYLUS_TEMPLATE) self.replace_with_prefix(stylus_template, _STYLUS_PROPERTY, _PREFIX_RESOLUTION, self.width, self.height) # Create an emulated stylus device. self.stylus = input_playback.InputPlayback() self.stylus.emulate(input_type=_STYLUS_DEVICE, property_file=_STYLUS_PROPERTY) self.stylus.find_connected_inputs() def replace_with_prefix(self, in_file, out_file, prefix, x_value, y_value): """Substitute with the real positions and write to an output file. Replace the keywords in template file with the real values and save the results into a file. @param in_file: the template file containing keywords for substitution. @param out_file: the generated file after substitution. @param prefix: the prefix of the keywords for substituion. @param x_value: the target value of X. @param y_value: the target value of Y. """ with open(in_file) as infile: content = infile.readlines() with open(out_file, 'w') as outfile: for line in content: if line.find(prefix + '_X') > 0: line = line.replace(prefix + '_X', str(x_value)) x_value += 1 else: if line.find(prefix + '_Y') > 0: line = line.replace(prefix + '_Y', str(y_value)) y_value += 1 outfile.write(line) def click(self, position_x, position_y): """Click the point(x,y) on the emulated stylus panel. @param position_x: the X position of the click point. @param position_y: the Y position of the click point. """ click_template = os.path.join(self.dirname, _CLICK_TEMPLATE) self.replace_with_prefix(click_template, _CLICK_EVENTS, _PREFIX_POSITION, position_x * 10, position_y * 10) self.stylus.blocking_playback(_CLICK_EVENTS, input_type=_STYLUS_DEVICE) def click_with_percentage(self, percent_x, percent_y): """Click a point based on the percentage of the display. @param percent_x: the percentage of X position over display width. @param percent_y: the percentage of Y position over display height. """ position_x = int(percent_x * self.width / 10) position_y = int(percent_y * self.height / 10) self.click(position_x, position_y) def close(self): """Clean up the files/handles created in the class.""" if self.stylus: self.stylus.close() if os.path.exists(_STYLUS_PROPERTY): os.remove(_STYLUS_PROPERTY) if os.path.exists(_CLICK_EVENTS): os.remove(_CLICK_EVENTS) def __enter__(self): """Allow usage in 'with' statements.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Release resources on completion of a 'with' statement.""" self.close()