1#!/usr/bin/env python
2# Copyright 2016 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Use your keyboard as your phone's keyboard. Experimental."""
7
8import argparse
9import copy
10import os
11import sys
12import termios
13import tty
14
15if __name__ == '__main__':
16  sys.path.append(
17      os.path.abspath(os.path.join(os.path.dirname(__file__),
18                                   '..', '..', '..')))
19from devil import base_error
20from devil.android.sdk import keyevent
21from devil.android.tools import script_common
22from devil.utils import run_tests_helper
23
24
25_KEY_MAPPING = {
26  '\x08': keyevent.KEYCODE_DEL,
27  '\x0a': keyevent.KEYCODE_ENTER,
28  ' ': keyevent.KEYCODE_SPACE,
29  '.': keyevent.KEYCODE_PERIOD,
30  '0': keyevent.KEYCODE_0,
31  '1': keyevent.KEYCODE_1,
32  '2': keyevent.KEYCODE_2,
33  '3': keyevent.KEYCODE_3,
34  '4': keyevent.KEYCODE_4,
35  '5': keyevent.KEYCODE_5,
36  '6': keyevent.KEYCODE_6,
37  '7': keyevent.KEYCODE_7,
38  '8': keyevent.KEYCODE_8,
39  '9': keyevent.KEYCODE_9,
40  'a': keyevent.KEYCODE_A,
41  'b': keyevent.KEYCODE_B,
42  'c': keyevent.KEYCODE_C,
43  'd': keyevent.KEYCODE_D,
44  'e': keyevent.KEYCODE_E,
45  'f': keyevent.KEYCODE_F,
46  'g': keyevent.KEYCODE_G,
47  'h': keyevent.KEYCODE_H,
48  'i': keyevent.KEYCODE_I,
49  'j': keyevent.KEYCODE_J,
50  'k': keyevent.KEYCODE_K,
51  'l': keyevent.KEYCODE_L,
52  'm': keyevent.KEYCODE_M,
53  'n': keyevent.KEYCODE_N,
54  'o': keyevent.KEYCODE_O,
55  'p': keyevent.KEYCODE_P,
56  'q': keyevent.KEYCODE_Q,
57  'r': keyevent.KEYCODE_R,
58  's': keyevent.KEYCODE_S,
59  't': keyevent.KEYCODE_T,
60  'u': keyevent.KEYCODE_U,
61  'v': keyevent.KEYCODE_V,
62  'w': keyevent.KEYCODE_W,
63  'x': keyevent.KEYCODE_X,
64  'y': keyevent.KEYCODE_Y,
65  'z': keyevent.KEYCODE_Z,
66  '\x7f': keyevent.KEYCODE_DEL,
67}
68
69
70def Keyboard(device, stream_itr):
71  try:
72    for c in stream_itr:
73      k = _KEY_MAPPING.get(c)
74      if k:
75        device.SendKeyEvent(k)
76      else:
77        print
78        print '(No mapping for character 0x%x)' % ord(c)
79  except KeyboardInterrupt:
80    pass
81
82
83def AddArguments(parser):
84  parser.add_argument('-d', '--device', action='append', dest='devices',
85                      metavar='DEVICE', help='device serial')
86  parser.add_argument('-v', '--verbose', action='count', help='print more')
87
88
89class MultipleDevicesError(base_error.BaseError):
90  def __init__(self, devices):
91    super(MultipleDevicesError, self).__init__(
92        'More than one device found: %s' % ', '.join(str(d) for d in devices))
93
94
95def main(raw_args):
96  parser = argparse.ArgumentParser(
97      description="Use your keyboard as your phone's keyboard.")
98  AddArguments(parser)
99  args = parser.parse_args(raw_args)
100
101  run_tests_helper.SetLogLevel(args.verbose)
102
103  devices = script_common.GetDevices(args.devices, None)
104  if len(devices) > 1:
105    raise MultipleDevicesError(devices)
106
107  def next_char():
108    while True:
109      yield sys.stdin.read(1)
110
111  try:
112    fd = sys.stdin.fileno()
113
114    # See man 3 termios for more info on what this is doing.
115    old_attrs = termios.tcgetattr(fd)
116    new_attrs = copy.deepcopy(old_attrs)
117    new_attrs[tty.LFLAG] = new_attrs[tty.LFLAG] & ~(termios.ICANON)
118    new_attrs[tty.CC][tty.VMIN] = 1
119    new_attrs[tty.CC][tty.VTIME] = 0
120    termios.tcsetattr(fd, termios.TCSAFLUSH, new_attrs)
121
122    Keyboard(devices[0], next_char())
123  finally:
124    termios.tcsetattr(fd, termios.TCSAFLUSH, old_attrs)
125  return 0
126
127
128if __name__ == '__main__':
129  sys.exit(main(sys.argv[1:]))
130