| # Copyright 2015 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 argparse | 
 | # Get all evdev/uinput events in our namespace. | 
 | # pylint: disable=wildcard-import, unused-wildcard-import | 
 | from uinput.ev import * | 
 | import subprocess | 
 | import time | 
 | import uinput | 
 |  | 
 |  | 
 | # Time to wait after uinput device creation, before starting to type. This is | 
 | # needed as kernel/Chrome takes some time to register the new input device. | 
 | # | 
 | # TODO(crbug.com/714950): This is a hack, we should figure out a way to check | 
 | # when kernel/Chrome is ready (monitor udev events?), instead of waiting for | 
 | # an arbitrary amount of time. | 
 | STARTUP_DELAY = 0.2 | 
 |  | 
 | # Default delay between key presses in seconds. 12ms is the xdotool default. | 
 | DEFAULT_DELAY = 0.012 | 
 |  | 
 | uinput_device_keyboard = None | 
 |  | 
 |  | 
 | # This dictionary contains most 7 bit ASCII characters. Add more if needed. | 
 | # TODO(ihf): Create this table using xkbcommon to support arbirtrary | 
 | # character sets and keyboard layouts. | 
 | _CROS_CHAR_MAP = { | 
 |     "\b": [KEY_BACKSPACE], | 
 |     "\t": [KEY_TAB], | 
 |     "\n": [KEY_ENTER], | 
 |  | 
 |     " ":  [KEY_SPACE], | 
 |     "!":  [KEY_LEFTSHIFT, KEY_1], | 
 |     '"':  [KEY_LEFTSHIFT, KEY_APOSTROPHE], | 
 |     "#":  [KEY_LEFTSHIFT, KEY_3], | 
 |     "$":  [KEY_LEFTSHIFT, KEY_4], | 
 |     "%":  [KEY_LEFTSHIFT, KEY_5], | 
 |     "&":  [KEY_LEFTSHIFT, KEY_7], | 
 |     "'":  [KEY_APOSTROPHE], | 
 |     "(":  [KEY_LEFTSHIFT, KEY_9], | 
 |     ")":  [KEY_LEFTSHIFT, KEY_0], | 
 |     "*":  [KEY_KPASTERISK], | 
 |     "+":  [KEY_LEFTSHIFT, KEY_EQUAL], | 
 |     ",":  [KEY_COMMA], | 
 |     "-":  [KEY_MINUS], | 
 |     ".":  [KEY_DOT], | 
 |     "/":  [KEY_SLASH], | 
 |  | 
 |     "0":  [KEY_0], | 
 |     "1":  [KEY_1], | 
 |     "2":  [KEY_2], | 
 |     "3":  [KEY_3], | 
 |     "4":  [KEY_4], | 
 |     "5":  [KEY_5], | 
 |     "6":  [KEY_6], | 
 |     "7":  [KEY_7], | 
 |     "8":  [KEY_8], | 
 |     "9":  [KEY_9], | 
 |  | 
 |     ":":  [KEY_LEFTSHIFT, KEY_SEMICOLON], | 
 |     ";":  [KEY_SEMICOLON], | 
 |     "<":  [KEY_LEFTSHIFT, KEY_COMMA], | 
 |     "=":  [KEY_EQUAL], | 
 |     ">":  [KEY_LEFTSHIFT, KEY_DOT], | 
 |     "?":  [KEY_LEFTSHIFT, KEY_SLASH], | 
 |     "@":  [KEY_LEFTSHIFT, KEY_2], | 
 |  | 
 |     "A":  [KEY_LEFTSHIFT, KEY_A], | 
 |     "B":  [KEY_LEFTSHIFT, KEY_B], | 
 |     "C":  [KEY_LEFTSHIFT, KEY_C], | 
 |     "D":  [KEY_LEFTSHIFT, KEY_D], | 
 |     "E":  [KEY_LEFTSHIFT, KEY_E], | 
 |     "F":  [KEY_LEFTSHIFT, KEY_F], | 
 |     "G":  [KEY_LEFTSHIFT, KEY_G], | 
 |     "H":  [KEY_LEFTSHIFT, KEY_H], | 
 |     "I":  [KEY_LEFTSHIFT, KEY_I], | 
 |     "J":  [KEY_LEFTSHIFT, KEY_J], | 
 |     "K":  [KEY_LEFTSHIFT, KEY_K], | 
 |     "L":  [KEY_LEFTSHIFT, KEY_L], | 
 |     "M":  [KEY_LEFTSHIFT, KEY_M], | 
 |     "N":  [KEY_LEFTSHIFT, KEY_N], | 
 |     "O":  [KEY_LEFTSHIFT, KEY_O], | 
 |     "P":  [KEY_LEFTSHIFT, KEY_P], | 
 |     "Q":  [KEY_LEFTSHIFT, KEY_Q], | 
 |     "R":  [KEY_LEFTSHIFT, KEY_R], | 
 |     "S":  [KEY_LEFTSHIFT, KEY_S], | 
 |     "T":  [KEY_LEFTSHIFT, KEY_T], | 
 |     "U":  [KEY_LEFTSHIFT, KEY_U], | 
 |     "V":  [KEY_LEFTSHIFT, KEY_V], | 
 |     "W":  [KEY_LEFTSHIFT, KEY_W], | 
 |     "X":  [KEY_LEFTSHIFT, KEY_X], | 
 |     "Y":  [KEY_LEFTSHIFT, KEY_Y], | 
 |     "Z":  [KEY_LEFTSHIFT, KEY_Z], | 
 |  | 
 |     "[":  [KEY_LEFTBRACE], | 
 |     "\\": [KEY_BACKSLASH], | 
 |     "]":  [KEY_RIGHTBRACE], | 
 |     "^":  [KEY_LEFTSHIFT, KEY_6], | 
 |     "_":  [KEY_LEFTSHIFT, KEY_MINUS], | 
 |     "`":  [KEY_GRAVE], | 
 |  | 
 |     "a":  [KEY_A], | 
 |     "b":  [KEY_B], | 
 |     "c":  [KEY_C], | 
 |     "d":  [KEY_D], | 
 |     "e":  [KEY_E], | 
 |     "f":  [KEY_F], | 
 |     "g":  [KEY_G], | 
 |     "h":  [KEY_H], | 
 |     "i":  [KEY_I], | 
 |     "j":  [KEY_J], | 
 |     "k":  [KEY_K], | 
 |     "l":  [KEY_L], | 
 |     "m":  [KEY_M], | 
 |     "n":  [KEY_N], | 
 |     "o":  [KEY_O], | 
 |     "p":  [KEY_P], | 
 |     "q":  [KEY_Q], | 
 |     "r":  [KEY_R], | 
 |     "s":  [KEY_S], | 
 |     "t":  [KEY_T], | 
 |     "u":  [KEY_U], | 
 |     "v":  [KEY_V], | 
 |     "w":  [KEY_W], | 
 |     "x":  [KEY_X], | 
 |     "y":  [KEY_Y], | 
 |     "z":  [KEY_Z], | 
 |  | 
 |     "{": [KEY_LEFTSHIFT, KEY_LEFTBRACE], | 
 |     "|": [KEY_LEFTSHIFT, KEY_BACKSLASH], | 
 |     "}": [KEY_LEFTSHIFT, KEY_RIGHTBRACE], | 
 |     "~": [KEY_LEFTSHIFT, KEY_GRAVE], | 
 | } | 
 |  | 
 |  | 
 | # A list of American English ChromeOS keys to define a keyboard device. | 
 | _CROS_KEYS_ALL = [ | 
 |     # Function row. | 
 |     KEY_ESC, | 
 |     KEY_F1, | 
 |     KEY_F2, | 
 |     KEY_F3, | 
 |     KEY_F4, | 
 |     KEY_F5, | 
 |     KEY_F6, | 
 |     KEY_F7, | 
 |     KEY_F8, | 
 |     KEY_F9, | 
 |     KEY_F10, | 
 |     KEY_F11, | 
 |     KEY_F12, | 
 |     KEY_HOME, | 
 |     KEY_END, | 
 |     KEY_INSERT, | 
 |     KEY_DELETE, | 
 |     # First row. | 
 |     KEY_GRAVE, | 
 |     KEY_1, | 
 |     KEY_2, | 
 |     KEY_3, | 
 |     KEY_4, | 
 |     KEY_5, | 
 |     KEY_6, | 
 |     KEY_7, | 
 |     KEY_8, | 
 |     KEY_9, | 
 |     KEY_0, | 
 |     KEY_MINUS, | 
 |     KEY_EQUAL, | 
 |     KEY_BACKSPACE, | 
 |     # Second row. | 
 |     KEY_TAB, | 
 |     KEY_Q, | 
 |     KEY_W, | 
 |     KEY_E, | 
 |     KEY_R, | 
 |     KEY_T, | 
 |     KEY_Y, | 
 |     KEY_U, | 
 |     KEY_I, | 
 |     KEY_O, | 
 |     KEY_P, | 
 |     KEY_LEFTBRACE, | 
 |     KEY_RIGHTBRACE, | 
 |     KEY_BACKSLASH, | 
 |     # Third row | 
 |     KEY_CAPSLOCK, | 
 |     KEY_A, | 
 |     KEY_S, | 
 |     KEY_D, | 
 |     KEY_F, | 
 |     KEY_G, | 
 |     KEY_H, | 
 |     KEY_J, | 
 |     KEY_K, | 
 |     KEY_L, | 
 |     KEY_SEMICOLON, | 
 |     KEY_APOSTROPHE, | 
 |     KEY_ENTER, | 
 |     # Forth row. | 
 |     KEY_LEFTSHIFT, | 
 |     KEY_Z, | 
 |     KEY_X, | 
 |     KEY_C, | 
 |     KEY_V, | 
 |     KEY_B, | 
 |     KEY_N, | 
 |     KEY_M, | 
 |     KEY_COMMA, | 
 |     KEY_DOT, | 
 |     KEY_SLASH, | 
 |     KEY_RIGHTSHIFT, | 
 |     # Fifth row. | 
 |     KEY_LEFTCTRL, | 
 |     KEY_FN, | 
 |     KEY_SEARCH, | 
 |     KEY_LEFTALT, | 
 |     KEY_SPACE, | 
 |     KEY_NUMLOCK, | 
 |     KEY_SCROLLLOCK, | 
 |     KEY_RIGHTALT, | 
 |     KEY_RIGHTCTRL, | 
 |     # Directional keys. | 
 |     KEY_UP, | 
 |     KEY_PAGEUP, | 
 |     KEY_LEFT, | 
 |     KEY_RIGHT, | 
 |     KEY_DOWN, | 
 |     KEY_PAGEDOWN, | 
 | ] | 
 |  | 
 |  | 
 | def _chars_to_events(chars): | 
 |     """ | 
 |     Translates string to key events. | 
 |  | 
 |     @param chars: characters to translate to events. | 
 |     @returns: list of lists of events representing characters. | 
 |     """ | 
 |     events = [] | 
 |     for char in chars: | 
 |         events.append(_CROS_CHAR_MAP[char]) | 
 |     return events | 
 |  | 
 |  | 
 | def _get_uinput_device_keyboard(): | 
 |     """ | 
 |     Lazy initialize device and return it. We don't want to create a device | 
 |     during build_packages or for tests that don't need it, hence init with None. | 
 |     """ | 
 |     global uinput_device_keyboard | 
 |     if uinput_device_keyboard is None: | 
 |         # For DUTs without keyboard attached force load uinput. | 
 |         subprocess.Popen(['modprobe', 'uinput']).wait() | 
 |         uinput_device_keyboard = uinput.Device(_CROS_KEYS_ALL) | 
 |         time.sleep(STARTUP_DELAY) | 
 |     return uinput_device_keyboard | 
 |  | 
 |  | 
 | def _uinput_translate_name(event_name): | 
 |     """ | 
 |     Translates string |event_name| to uinput event. | 
 |     """ | 
 |     return getattr(uinput, event_name) | 
 |  | 
 |  | 
 | def _uinput_emit_keycombo(device, events, syn=True): | 
 |     """ | 
 |     Wrapper for uinput.emit_combo. Emits sequence of events. | 
 |     Example: [KEY_LEFTCTRL, KEY_LEFTALT, KEY_F5] | 
 |     """ | 
 |     time.sleep(DEFAULT_DELAY) | 
 |     device.emit_combo(events, syn) | 
 |  | 
 |  | 
 | def press_keys(keys): | 
 |     """Presses the given keys as one combination. | 
 |  | 
 |     Please do not leak uinput dependencies outside of the file. | 
 |  | 
 |     @param key: A simple list of key strings, e.g. ['LEFTCTRL', 'F4'] | 
 |     """ | 
 |     events =  [_uinput_translate_name(en) for en in keys] | 
 |     _uinput_emit_keycombo(_get_uinput_device_keyboard(), events) | 
 |  | 
 |  | 
 | def type_chars(text): | 
 |     """Translates ASCII text to keystrokes and sends them as events. | 
 |  | 
 |     @param text: string to send as events to keyboard. | 
 |     """ | 
 |     events = _chars_to_events(text) | 
 |     device = _get_uinput_device_keyboard() | 
 |     for keys in events: | 
 |         _uinput_emit_keycombo(device, keys) |