Nicolas Boichat | 8b3c51b | 2017-04-25 09:43:58 +0800 | [diff] [blame] | 1 | # Copyright 2015 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 | import argparse |
| 6 | # Get all evdev/uinput events in our namespace. |
| 7 | # pylint: disable=wildcard-import, unused-wildcard-import |
| 8 | from uinput.ev import * |
| 9 | import subprocess |
| 10 | import time |
| 11 | import uinput |
| 12 | |
Nicolas Boichat | 7ac1acc | 2017-04-25 09:46:07 +0800 | [diff] [blame^] | 13 | |
| 14 | # Time to wait after uinput device creation, before starting to type. This is |
| 15 | # needed as kernel/Chrome takes some time to register the new input device. |
| 16 | # |
| 17 | # TODO(crbug.com/714950): This is a hack, we should figure out a way to check |
| 18 | # when kernel/Chrome is ready (monitor udev events?), instead of waiting for |
| 19 | # an arbitrary amount of time. |
| 20 | STARTUP_DELAY = 0.2 |
| 21 | |
Nicolas Boichat | 8b3c51b | 2017-04-25 09:43:58 +0800 | [diff] [blame] | 22 | # Default delay between key presses in seconds. 12ms is the xdotool default. |
Nicolas Boichat | 7ac1acc | 2017-04-25 09:46:07 +0800 | [diff] [blame^] | 23 | DEFAULT_DELAY = 0.012 |
| 24 | |
Nicolas Boichat | 8b3c51b | 2017-04-25 09:43:58 +0800 | [diff] [blame] | 25 | uinput_device_keyboard = None |
| 26 | |
| 27 | |
| 28 | # This dictionary contains most 7 bit ASCII characters. Add more if needed. |
| 29 | # TODO(ihf): Create this table using xkbcommon to support arbirtrary |
| 30 | # character sets and keyboard layouts. |
| 31 | _CROS_CHAR_MAP = { |
| 32 | "\b": [KEY_BACKSPACE], |
| 33 | "\t": [KEY_TAB], |
| 34 | "\n": [KEY_ENTER], |
| 35 | |
| 36 | " ": [KEY_SPACE], |
| 37 | "!": [KEY_LEFTSHIFT, KEY_1], |
| 38 | '"': [KEY_LEFTSHIFT, KEY_APOSTROPHE], |
| 39 | "#": [KEY_LEFTSHIFT, KEY_3], |
| 40 | "$": [KEY_LEFTSHIFT, KEY_4], |
| 41 | "%": [KEY_LEFTSHIFT, KEY_5], |
| 42 | "&": [KEY_LEFTSHIFT, KEY_7], |
| 43 | "'": [KEY_APOSTROPHE], |
| 44 | "(": [KEY_LEFTSHIFT, KEY_9], |
| 45 | ")": [KEY_LEFTSHIFT, KEY_0], |
| 46 | "*": [KEY_KPASTERISK], |
| 47 | "+": [KEY_LEFTSHIFT, KEY_EQUAL], |
| 48 | ",": [KEY_COMMA], |
| 49 | "-": [KEY_MINUS], |
| 50 | ".": [KEY_DOT], |
| 51 | "/": [KEY_SLASH], |
| 52 | |
| 53 | "0": [KEY_0], |
| 54 | "1": [KEY_1], |
| 55 | "2": [KEY_2], |
| 56 | "3": [KEY_3], |
| 57 | "4": [KEY_4], |
| 58 | "5": [KEY_5], |
| 59 | "6": [KEY_6], |
| 60 | "7": [KEY_7], |
| 61 | "8": [KEY_8], |
| 62 | "9": [KEY_9], |
| 63 | |
| 64 | ":": [KEY_LEFTSHIFT, KEY_SEMICOLON], |
| 65 | ";": [KEY_SEMICOLON], |
| 66 | "<": [KEY_LEFTSHIFT, KEY_COMMA], |
| 67 | "=": [KEY_EQUAL], |
| 68 | ">": [KEY_LEFTSHIFT, KEY_DOT], |
| 69 | "?": [KEY_LEFTSHIFT, KEY_SLASH], |
| 70 | "@": [KEY_LEFTSHIFT, KEY_2], |
| 71 | |
| 72 | "A": [KEY_LEFTSHIFT, KEY_A], |
| 73 | "B": [KEY_LEFTSHIFT, KEY_B], |
| 74 | "C": [KEY_LEFTSHIFT, KEY_C], |
| 75 | "D": [KEY_LEFTSHIFT, KEY_D], |
| 76 | "E": [KEY_LEFTSHIFT, KEY_E], |
| 77 | "F": [KEY_LEFTSHIFT, KEY_F], |
| 78 | "G": [KEY_LEFTSHIFT, KEY_G], |
| 79 | "H": [KEY_LEFTSHIFT, KEY_H], |
| 80 | "I": [KEY_LEFTSHIFT, KEY_I], |
| 81 | "J": [KEY_LEFTSHIFT, KEY_J], |
| 82 | "K": [KEY_LEFTSHIFT, KEY_K], |
| 83 | "L": [KEY_LEFTSHIFT, KEY_L], |
| 84 | "M": [KEY_LEFTSHIFT, KEY_M], |
| 85 | "N": [KEY_LEFTSHIFT, KEY_N], |
| 86 | "O": [KEY_LEFTSHIFT, KEY_O], |
| 87 | "P": [KEY_LEFTSHIFT, KEY_P], |
| 88 | "Q": [KEY_LEFTSHIFT, KEY_Q], |
| 89 | "R": [KEY_LEFTSHIFT, KEY_R], |
| 90 | "S": [KEY_LEFTSHIFT, KEY_S], |
| 91 | "T": [KEY_LEFTSHIFT, KEY_T], |
| 92 | "U": [KEY_LEFTSHIFT, KEY_U], |
| 93 | "V": [KEY_LEFTSHIFT, KEY_V], |
| 94 | "W": [KEY_LEFTSHIFT, KEY_W], |
| 95 | "X": [KEY_LEFTSHIFT, KEY_X], |
| 96 | "Y": [KEY_LEFTSHIFT, KEY_Y], |
| 97 | "Z": [KEY_LEFTSHIFT, KEY_Z], |
| 98 | |
| 99 | "[": [KEY_LEFTBRACE], |
| 100 | "\\": [KEY_BACKSLASH], |
| 101 | "]": [KEY_RIGHTBRACE], |
| 102 | "^": [KEY_LEFTSHIFT, KEY_6], |
| 103 | "_": [KEY_LEFTSHIFT, KEY_MINUS], |
| 104 | "`": [KEY_GRAVE], |
| 105 | |
| 106 | "a": [KEY_A], |
| 107 | "b": [KEY_B], |
| 108 | "c": [KEY_C], |
| 109 | "d": [KEY_D], |
| 110 | "e": [KEY_E], |
| 111 | "f": [KEY_F], |
| 112 | "g": [KEY_G], |
| 113 | "h": [KEY_H], |
| 114 | "i": [KEY_I], |
| 115 | "j": [KEY_J], |
| 116 | "k": [KEY_K], |
| 117 | "l": [KEY_L], |
| 118 | "m": [KEY_M], |
| 119 | "n": [KEY_N], |
| 120 | "o": [KEY_O], |
| 121 | "p": [KEY_P], |
| 122 | "q": [KEY_Q], |
| 123 | "r": [KEY_R], |
| 124 | "s": [KEY_S], |
| 125 | "t": [KEY_T], |
| 126 | "u": [KEY_U], |
| 127 | "v": [KEY_V], |
| 128 | "w": [KEY_W], |
| 129 | "x": [KEY_X], |
| 130 | "y": [KEY_Y], |
| 131 | "z": [KEY_Z], |
| 132 | |
| 133 | "{": [KEY_LEFTSHIFT, KEY_LEFTBRACE], |
| 134 | "|": [KEY_LEFTSHIFT, KEY_BACKSLASH], |
| 135 | "}": [KEY_LEFTSHIFT, KEY_RIGHTBRACE], |
| 136 | "~": [KEY_LEFTSHIFT, KEY_GRAVE], |
| 137 | } |
| 138 | |
| 139 | |
| 140 | # A list of American English ChromeOS keys to define a keyboard device. |
| 141 | _CROS_KEYS_ALL = [ |
| 142 | # Function row. |
| 143 | KEY_ESC, |
| 144 | KEY_F1, |
| 145 | KEY_F2, |
| 146 | KEY_F3, |
| 147 | KEY_F4, |
| 148 | KEY_F5, |
| 149 | KEY_F6, |
| 150 | KEY_F7, |
| 151 | KEY_F8, |
| 152 | KEY_F9, |
| 153 | KEY_F10, |
| 154 | KEY_F11, |
| 155 | KEY_F12, |
| 156 | KEY_HOME, |
| 157 | KEY_END, |
| 158 | KEY_INSERT, |
| 159 | KEY_DELETE, |
| 160 | # First row. |
| 161 | KEY_GRAVE, |
| 162 | KEY_1, |
| 163 | KEY_2, |
| 164 | KEY_3, |
| 165 | KEY_4, |
| 166 | KEY_5, |
| 167 | KEY_6, |
| 168 | KEY_7, |
| 169 | KEY_8, |
| 170 | KEY_9, |
| 171 | KEY_0, |
| 172 | KEY_MINUS, |
| 173 | KEY_EQUAL, |
| 174 | KEY_BACKSPACE, |
| 175 | # Second row. |
| 176 | KEY_TAB, |
| 177 | KEY_Q, |
| 178 | KEY_W, |
| 179 | KEY_E, |
| 180 | KEY_R, |
| 181 | KEY_T, |
| 182 | KEY_Y, |
| 183 | KEY_U, |
| 184 | KEY_I, |
| 185 | KEY_O, |
| 186 | KEY_P, |
| 187 | KEY_LEFTBRACE, |
| 188 | KEY_RIGHTBRACE, |
| 189 | KEY_BACKSLASH, |
| 190 | # Third row |
| 191 | KEY_CAPSLOCK, |
| 192 | KEY_A, |
| 193 | KEY_S, |
| 194 | KEY_D, |
| 195 | KEY_F, |
| 196 | KEY_G, |
| 197 | KEY_H, |
| 198 | KEY_J, |
| 199 | KEY_K, |
| 200 | KEY_L, |
| 201 | KEY_SEMICOLON, |
| 202 | KEY_APOSTROPHE, |
| 203 | KEY_ENTER, |
| 204 | # Forth row. |
| 205 | KEY_LEFTSHIFT, |
| 206 | KEY_Z, |
| 207 | KEY_X, |
| 208 | KEY_C, |
| 209 | KEY_V, |
| 210 | KEY_B, |
| 211 | KEY_N, |
| 212 | KEY_M, |
| 213 | KEY_COMMA, |
| 214 | KEY_DOT, |
| 215 | KEY_SLASH, |
| 216 | KEY_RIGHTSHIFT, |
| 217 | # Fifth row. |
| 218 | KEY_LEFTCTRL, |
| 219 | KEY_FN, |
| 220 | KEY_SEARCH, |
| 221 | KEY_LEFTALT, |
| 222 | KEY_SPACE, |
| 223 | KEY_NUMLOCK, |
| 224 | KEY_SCROLLLOCK, |
| 225 | KEY_RIGHTALT, |
| 226 | KEY_RIGHTCTRL, |
| 227 | # Directional keys. |
| 228 | KEY_UP, |
| 229 | KEY_PAGEUP, |
| 230 | KEY_LEFT, |
| 231 | KEY_RIGHT, |
| 232 | KEY_DOWN, |
| 233 | KEY_PAGEDOWN, |
| 234 | ] |
| 235 | |
| 236 | |
| 237 | def _chars_to_events(chars): |
| 238 | """ |
| 239 | Translates string to key events. |
| 240 | |
| 241 | @param chars: characters to translate to events. |
| 242 | @returns: list of lists of events representing characters. |
| 243 | """ |
| 244 | events = [] |
| 245 | for char in chars: |
| 246 | events.append(_CROS_CHAR_MAP[char]) |
| 247 | return events |
| 248 | |
| 249 | |
| 250 | def _get_uinput_device_keyboard(): |
| 251 | """ |
| 252 | Lazy initialize device and return it. We don't want to create a device |
| 253 | during build_packages or for tests that don't need it, hence init with None. |
| 254 | """ |
| 255 | global uinput_device_keyboard |
| 256 | if uinput_device_keyboard is None: |
| 257 | # For DUTs without keyboard attached force load uinput. |
| 258 | subprocess.Popen(['modprobe', 'uinput']).wait() |
| 259 | uinput_device_keyboard = uinput.Device(_CROS_KEYS_ALL) |
Nicolas Boichat | 7ac1acc | 2017-04-25 09:46:07 +0800 | [diff] [blame^] | 260 | time.sleep(STARTUP_DELAY) |
Nicolas Boichat | 8b3c51b | 2017-04-25 09:43:58 +0800 | [diff] [blame] | 261 | return uinput_device_keyboard |
| 262 | |
| 263 | |
| 264 | def _uinput_translate_name(event_name): |
| 265 | """ |
| 266 | Translates string |event_name| to uinput event. |
| 267 | """ |
| 268 | return getattr(uinput, event_name) |
| 269 | |
| 270 | |
| 271 | def _uinput_emit_keycombo(device, events, syn=True): |
| 272 | """ |
| 273 | Wrapper for uinput.emit_combo. Emits sequence of events. |
| 274 | Example: [KEY_LEFTCTRL, KEY_LEFTALT, KEY_F5] |
| 275 | """ |
Nicolas Boichat | 7ac1acc | 2017-04-25 09:46:07 +0800 | [diff] [blame^] | 276 | time.sleep(DEFAULT_DELAY) |
Nicolas Boichat | 8b3c51b | 2017-04-25 09:43:58 +0800 | [diff] [blame] | 277 | device.emit_combo(events, syn) |
| 278 | |
| 279 | |
| 280 | def press_keys(keys): |
| 281 | """Presses the given keys as one combination. |
| 282 | |
| 283 | Please do not leak uinput dependencies outside of the file. |
| 284 | |
| 285 | @param key: A simple list of key strings, e.g. ['LEFTCTRL', 'F4'] |
| 286 | """ |
| 287 | events = [_uinput_translate_name(en) for en in keys] |
| 288 | _uinput_emit_keycombo(_get_uinput_device_keyboard(), events) |
| 289 | |
| 290 | |
| 291 | def type_chars(text): |
| 292 | """Translates ASCII text to keystrokes and sends them as events. |
| 293 | |
| 294 | @param text: string to send as events to keyboard. |
| 295 | """ |
| 296 | events = _chars_to_events(text) |
| 297 | device = _get_uinput_device_keyboard() |
| 298 | for keys in events: |
| 299 | _uinput_emit_keycombo(device, keys) |