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 8 import argparse 9 import copy 10 import os 11 import sys 12 import termios 13 import tty 14 15 if __name__ == '__main__': 16 sys.path.append( 17 os.path.abspath(os.path.join(os.path.dirname(__file__), 18 '..', '..', '..'))) 19 from devil import base_error 20 from devil.android.sdk import keyevent 21 from devil.android.tools import script_common 22 from devil.utils import logging_common 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 70 def 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 83 class MultipleDevicesError(base_error.BaseError): 84 def __init__(self, devices): 85 super(MultipleDevicesError, self).__init__( 86 'More than one device found: %s' % ', '.join(str(d) for d in devices)) 87 88 89 def main(raw_args): 90 parser = argparse.ArgumentParser( 91 description="Use your keyboard as your phone's keyboard.") 92 logging_common.AddLoggingArguments(parser) 93 script_common.AddDeviceArguments(parser) 94 args = parser.parse_args(raw_args) 95 96 logging_common.InitializeLogging(args) 97 98 devices = script_common.GetDevices(args.devices, None) 99 if len(devices) > 1: 100 raise MultipleDevicesError(devices) 101 102 def next_char(): 103 while True: 104 yield sys.stdin.read(1) 105 106 try: 107 fd = sys.stdin.fileno() 108 109 # See man 3 termios for more info on what this is doing. 110 old_attrs = termios.tcgetattr(fd) 111 new_attrs = copy.deepcopy(old_attrs) 112 new_attrs[tty.LFLAG] = new_attrs[tty.LFLAG] & ~(termios.ICANON) 113 new_attrs[tty.CC][tty.VMIN] = 1 114 new_attrs[tty.CC][tty.VTIME] = 0 115 termios.tcsetattr(fd, termios.TCSAFLUSH, new_attrs) 116 117 Keyboard(devices[0], next_char()) 118 finally: 119 termios.tcsetattr(fd, termios.TCSAFLUSH, old_attrs) 120 return 0 121 122 123 if __name__ == '__main__': 124 sys.exit(main(sys.argv[1:])) 125