blob: 0d67f77f6903361bf3598a0328d2ea211e34f283 [file] [log] [blame]
# Copyright (c) 2011 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.
''' A module for extracting trackpad device file properties '''
import logging
import os
import constants
import trackpad_util
from autotest_lib.client.bin import utils
from autotest_lib.client.bin.input.linux_input import *
from autotest_lib.client.common_lib import error
from trackpad_util import read_trackpad_test_conf, get_trackpad_device_file
# Declare NOP as a instance containing NOP related constants
NOP = constants.NOP()
class TrackpadDevice:
''' Management of the trackpad device file
It is used
(1) to extract trackpad device file properties such as device event
playback times, and
(2) to play back the device packets. The device file is usually
'/dev/input/event*'.
'''
playback_program_default = 'evemu-play'
playback_program_emerged = '/usr/bin/evemu-play'
DEVICE_TIME_FILE = '/tmp/time.out'
def __init__(self):
self._init_event_type_code()
self._init_event_structure()
self._set_dev_type_and_code()
if self._get_trackpad_driver() == 'synaptics':
self._ungrab_device()
self.trackpad_device_file, msg = get_trackpad_device_file()
if self.trackpad_device_file is None:
raise error.TestError(msg)
logging.info(msg)
if os.path.isfile(self.playback_program_emerged):
self.playback_program = self.playback_program_emerged
else:
self.playback_program = self.playback_program_default
logging.info('Trackpad device playback program: %s' %
self.playback_program)
def _init_event_type_code(self):
''' Initialize event type and code '''
self.ev_format = ev_format = '%04x'
# Event types
self.EV_SYN = ev_format % EV_SYN
self.EV_KEY = ev_format % EV_KEY
self.EV_ABS = ev_format % EV_ABS
# Event codes for synchronization event
self.SYN_REPORT = ev_format % SYN_REPORT
# Event codes for absolute axes
self.ABS_MT_SLOT = ev_format % ABS_MT_SLOT
self.ABS_MT_TRACKING_ID = ev_format % ABS_MT_TRACKING_ID
# Event codes for keys and buttons
self.BTN_MOUSE = ev_format % BTN_MOUSE
self.BTN_TOUCH = ev_format % BTN_TOUCH
self.BTN_TOOL_FINGER = ev_format % BTN_TOOL_FINGER
self.BTN_TOOL_DOUBLETAP = ev_format % BTN_TOOL_DOUBLETAP
self.BTN_TOOL_TRIPLETAP = ev_format % BTN_TOOL_TRIPLETAP
self.BTN_TOOL_QUADTAP = ev_format % BTN_TOOL_QUADTAP
# Event codes for keys and buttons
self.ABS_X = ev_format % ABS_X
self.ABS_Y = ev_format % ABS_Y
self.ABS_PRESSURE = ev_format % ABS_PRESSURE
self.ABS_MT_SLOT = ev_format % ABS_MT_SLOT
self.ABS_MT_POSITION_X = ev_format % ABS_MT_POSITION_X
self.ABS_MT_POSITION_Y = ev_format % ABS_MT_POSITION_Y
self.ABS_MT_TRACKING_ID = ev_format % ABS_MT_TRACKING_ID
self.ABS_MT_PRESSURE = ev_format % ABS_MT_PRESSURE
def _init_event_structure(self):
ev_type_code = '%s %s'
ev_struct = '%s %s %d'
self.finger_on = ev_struct % (self.EV_KEY, self.BTN_TOUCH, 1)
self.finger_off = ev_struct % (self.EV_KEY, self.BTN_TOUCH, 0)
self.mouse_click_press = ev_struct % (self.EV_KEY, self.BTN_MOUSE, 1)
self.mouse_click_release = ev_struct % (self.EV_KEY, self.BTN_MOUSE, 0)
self.one_finger_on = ev_struct % (self.EV_KEY, self.BTN_TOOL_FINGER, 1)
self.one_finger_off = ev_struct % (self.EV_KEY, self.BTN_TOOL_FINGER, 0)
self.two_fingers_on = ev_struct % (self.EV_KEY,
self.BTN_TOOL_DOUBLETAP, 1)
self.two_fingers_off = ev_struct % (self.EV_KEY,
self.BTN_TOOL_DOUBLETAP, 0)
self.three_fingers_on = ev_struct % (self.EV_KEY,
self.BTN_TOOL_TRIPLETAP, 1)
self.three_fingers_off = ev_struct % (self.EV_KEY,
self.BTN_TOOL_TRIPLETAP, 0)
self.four_fingers_on = ev_struct % (self.EV_KEY,
self.BTN_TOOL_QUADTAP, 1)
self.four_fingers_off = ev_struct % (self.EV_KEY,
self.BTN_TOOL_QUADTAP, 0)
self.tracking_id = ev_type_code % (self.EV_ABS, self.ABS_MT_TRACKING_ID)
self.slot = ev_type_code % (self.EV_ABS, self.ABS_MT_SLOT)
self.abs_mt_x = ev_type_code % (self.EV_ABS, self.ABS_MT_POSITION_X)
self.abs_mt_y = ev_type_code % (self.EV_ABS, self.ABS_MT_POSITION_Y)
self.abs_mt_z = ev_type_code % (self.EV_ABS, self.ABS_MT_PRESSURE)
self.abs_x = ev_type_code % (self.EV_ABS, self.ABS_X)
self.abs_y = ev_type_code % (self.EV_ABS, self.ABS_Y)
self.abs_z = ev_type_code % (self.EV_ABS, self.ABS_PRESSURE)
self.ev_syn = ev_type_code % (self.EV_SYN, self.SYN_REPORT)
def _finger_i_on_MTB(self, i):
''' The i-th finger touches the trackpad in MTB protocol '''
return '%s %s %d' % (self.EV_ABS, self.ABS_MT_SLOT, i - 1)
def _set_dev_type_and_code(self):
''' Set device code
Device event types and codes are imported from linux_input.
'''
ev_code_x = self.ev_format % ABS_MT_POSITION_X
ev_code_y = self.ev_format % ABS_MT_POSITION_Y
self.ev_code_dict = {'left': (ev_code_x,),
'right': (ev_code_x,),
'up': (ev_code_y,),
'down': (ev_code_y,),
None: (ev_code_x, ev_code_y)}
# MTA: not supported at this time
# MTB: the default MT type to use
self.second_finger_on_MTB = self._finger_i_on_MTB(2)
self.finger_off_MTB = '%s %s -1' % (self.EV_ABS,
self.ABS_MT_TRACKING_ID)
finger_MTB = '{0} %s {1}'
finger_on_MTB = finger_MTB.format(self.EV_KEY, 1)
finger_off_MTB = finger_MTB.format(self.EV_KEY, 0)
self.dev_event = {
NOP.FINGER1_LANDED: finger_on_MTB % self.BTN_TOOL_FINGER,
NOP.FINGER1_LIFTED: finger_off_MTB % self.BTN_TOOL_FINGER,
NOP.FINGER2_LANDED: finger_on_MTB % self.BTN_TOOL_DOUBLETAP,
NOP.FINGER2_LIFTED: finger_off_MTB % self.BTN_TOOL_DOUBLETAP,
NOP.FINGER3_LANDED: finger_on_MTB % self.BTN_TOOL_TRIPLETAP,
NOP.FINGER3_LIFTED: finger_off_MTB % self.BTN_TOOL_TRIPLETAP,
NOP.DEVICE_MOUSE_CLICK_PRESS: self.mouse_click_press,
NOP.DEVICE_MOUSE_CLICK_RELEASE: self.mouse_click_release,
NOP.FINGER_ON: self.finger_on,
NOP.FINGER_OFF: self.finger_off,
NOP.ONE_FINGER_ON: self.one_finger_on,
NOP.TWO_FINGERS_ON: self.two_fingers_on,
NOP.THREE_FINGERS_ON: self.three_fingers_on,
NOP.FOUR_FINGERS_ON: self.four_fingers_on,
}
def _extract_playback_time(self, line):
''' Extract the actual event playback time from the line '''
return int(float(line.split('playback ')[1]) * 1000)
def find_all_event_time(self, ev_str):
''' Find the timestamps of all occurences of an event '''
with open(TrackpadDevice.DEVICE_TIME_FILE) as f:
time_file = f.read().splitlines()
ev = self.dev_event[ev_str]
time_list = []
for line in time_file:
if ev in line:
time_list.append(self._extract_playback_time(line))
return time_list
def _find_event_time(self, ev_seq):
''' Match the events in ev_seq against the device time file, and
return the timestamp of the first matched device events.
'''
ev_seq_len = len(ev_seq)
if ev_seq_len == 0:
return None
with open(TrackpadDevice.DEVICE_TIME_FILE) as f:
time_file = f.read().splitlines()
ev = ev_seq.pop(0)
for line in time_file:
if ev in line:
if len(ev_seq) == 0:
return self._extract_playback_time(line)
ev = ev_seq.pop(0)
return None
def get_finger_time(self, ev_str):
''' Derive the device playback time of a specified event '''
ev_seq_MTB = [self.dev_event[ev_str]]
ev_time = self._find_event_time(ev_seq_MTB)
return [ev_time] if ev_time is not None else []
def get_2nd_finger_touch_time(self, direction):
''' Derive the device playback time when the 2nd finger touches '''
ev_time = None
# When the functionality is 'two_fingers_no_delay_2d' in config file,
# check the ABS_MT_POSITION_X or ABS_MT_POSITION_Y event time.
# Use the timestamp which occurs earlier.
for ev_code in self.ev_code_dict[direction]:
motion_code = '%s %s' % (self.EV_ABS, ev_code)
ev_seq_MTB = [self.second_finger_on_MTB, motion_code]
new_ev_time = self._find_event_time(ev_seq_MTB)
ev_time = new_ev_time if ev_time is None else min(ev_time,
new_ev_time)
return ev_time
def get_2nd_finger_lifted_time(self):
''' Derive the device playback time when the 2nd finger is lifted '''
ev_seq_MTB = [self.second_finger_on_MTB, self.finger_off_MTB]
return self._find_event_time(ev_seq_MTB)
def get_two_finger_touch_time_list(self):
''' Derive all device playback times when two fingers touch the pad '''
with open(TrackpadDevice.DEVICE_TIME_FILE) as f:
time_file = f.read().splitlines()
ev = self.dev_event[NOP.FINGER2_LANDED]
touch_time_list = []
for line in time_file:
if ev in line:
touch_time_list.append(self._extract_playback_time(line))
return touch_time_list
def _get_trackpad_driver(self):
''' Query which trackpad driver is used in xorg '''
trackpad_drivers = read_trackpad_test_conf('trackpad_drivers', '.')
xorg_log = read_trackpad_test_conf('xorg_log', '.')
try:
f = open(xorg_log)
except IOError:
raise error.TestError('Xorg log file %s does not exist.' % xorg_log)
log_lines = f.read().splitlines()
f.close()
for line in log_lines:
if 'LoadModule' in line:
for driver in trackpad_drivers:
if driver in line:
logging.info('Trackpad driver "%s" is found.' % driver)
self.trackpad_driver = driver
return self.trackpad_driver
raise error.TestError('Cannot find driver in %s.' %
str(trackpad_drivers))
def _ungrab_device(self):
''' Ungrab the device if the driver is synaptics and is grabbed '''
self.grab_value = -1
display_environ = trackpad_util.Display().get_environ()
synclient_list_cmd = ' '.join([display_environ, 'synclient -l'])
self.grab_device = ' '.join([display_environ,
'synclient GrabEventDevice=%d'])
synclient_settings = utils.system_output(synclient_list_cmd)
for line in synclient_settings.splitlines():
if line.lstrip().startswith('GrabEventDevice'):
self.grab_value = int(line.split('=')[1].strip())
break
logging.info('GrabEventDevice=%d.' % self.grab_value)
if self.grab_value == -1:
err_msg = 'Cannot find GrabEventDevice setting in "%s".'
raise error.TestError(err_msg % synclient_list_cmd)
# Ungrab the device only if it has been grabbed.
elif self.grab_value == 1:
ungrab_cmd = self.grab_device % 0
try:
utils.system(ungrab_cmd)
except:
raise error.TestError('Fail to execute: %s' % ungrab_cmd)
logging.info('The synaptics device file is ungrabbed now.')
def playback(self, packet_data_file):
option_slot0 = '--insert-slot0'
option_output = '--output'
play_cmd = '%s %s %s %s %s < %s' % (self.playback_program,
self.trackpad_device_file,
option_slot0,
option_output,
TrackpadDevice.DEVICE_TIME_FILE,
packet_data_file)
utils.system(play_cmd)
def __del__(self):
# Grab the device again only if it was originally grabbed.
if self.trackpad_driver == 'synaptics' and self.grab_value == 1:
grab_cmd = self.grab_device % 1
try:
utils.system(grab_cmd)
except:
raise error.TestError('Fail to execute: %s' % grab_cmd)
logging.info('The synaptics device file is grabbed successfully.')
# Remove the temporary device time file
if os.path.exists(TrackpadDevice.DEVICE_TIME_FILE):
try:
os.remove(TrackpadDevice.DEVICE_TIME_FILE)
except:
logging.warn('Cannot remove the device time file: %s.' %
TrackpadDevice.DEVICE_TIME_FILE)