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