2 # Copyright (c) 2011 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.
6 """SiteCompare module for simulating keyboard input.
8 This module contains functions that can be used to simulate a user
9 pressing keys on a keyboard. Support is provided for formatted strings
10 including special characters to represent modifier keys like CTRL and ALT
13 import time
# for sleep
14 import win32api
# for keybd_event and VkKeyCode
15 import win32con
# Windows constants
17 # TODO(jhaas): Ask the readability guys if this would be acceptable:
19 # from win32con import VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, KEYEVENTF_KEYUP
21 # This is a violation of the style guide but having win32con. everywhere
22 # is just plain ugly, and win32con is a huge import for just a handful of
26 def PressKey(down
, key
):
27 """Presses or unpresses a key.
29 Uses keybd_event to simulate either depressing or releasing
33 down: Whether the key is to be pressed or released
34 key: Virtual key code of key to press or release
37 # keybd_event injects key events at a very low level (it's the
38 # Windows API keyboard device drivers call) so this is a very
39 # reliable way of simulating user input
40 win32api
.keybd_event(key
, 0, (not down
) * win32con
.KEYEVENTF_KEYUP
)
43 def TypeKey(key
, keystroke_time
=0):
44 """Simulate a keypress of a virtual key.
47 key: which key to press
48 keystroke_time: length of time (in seconds) to "hold down" the key
49 Note that zero works just fine
55 # This just wraps a pair of PressKey calls with an intervening delay
57 time
.sleep(keystroke_time
)
61 def TypeString(string_to_type
,
64 time_between_keystrokes
=0):
65 """Simulate typing a string on the keyboard.
68 string_to_type: the string to print
69 use_modifiers: specifies whether the following modifier characters
71 {abc}: type characters with ALT held down
72 [abc]: type characters with CTRL held down
73 \ escapes {}[] and treats these values as literal
74 standard escape sequences are valid even if use_modifiers is false
75 \p is "pause" for one second, useful when driving menus
76 \1-\9 is F-key, \0 is F10
78 TODO(jhaas): support for explicit control of SHIFT, support for
79 nonprintable keys (F-keys, ESC, arrow keys, etc),
80 support for explicit control of left vs. right ALT or SHIFT,
81 support for Windows key
83 keystroke_time: length of time (in secondes) to "hold down" the key
84 time_between_keystrokes: length of time (seconds) to pause between keys
90 shift_held
= win32api
.GetAsyncKeyState(win32con
.VK_SHIFT
) < 0
91 ctrl_held
= win32api
.GetAsyncKeyState(win32con
.VK_CONTROL
) < 0
92 alt_held
= win32api
.GetAsyncKeyState(win32con
.VK_MENU
) < 0
96 'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', 'v': '\v'}
98 for char
in string_to_type
:
102 # Check to see if this is the start or end of a modified block (that is,
103 # {abc} for ALT-modified keys or [abc] for CTRL-modified keys
104 if use_modifiers
and not next_escaped
:
106 if char
== "{" and not alt_held
:
108 PressKey(True, win32con
.VK_MENU
)
109 elif char
== "}" and alt_held
:
111 PressKey(False, win32con
.VK_MENU
)
112 elif char
== "[" and not ctrl_held
:
114 PressKey(True, win32con
.VK_CONTROL
)
115 elif char
== "]" and ctrl_held
:
117 PressKey(False, win32con
.VK_CONTROL
)
121 # If this is an explicitly-escaped character, replace it with the
123 if next_escaped
and char
in escape_chars
: char
= escape_chars
[char
]
125 # If this is \p, pause for one second.
126 if next_escaped
and char
== 'p':
131 # If this is \(d), press F key
132 if next_escaped
and char
.isdigit():
134 if not fkey
: fkey
= 10
136 vk
= win32con
.VK_F1
+ fkey
- 1
138 # If this is the backslash, the next character is escaped
139 if not next_escaped
and char
== "\\":
143 # If we make it here, it's not a special character, or it's an
144 # escaped special character which should be treated as a literal
147 if not vk
: vk
= win32api
.VkKeyScan(char
)
149 # VkKeyScan() returns the scan code in the low byte. The upper
150 # byte specifies modifiers necessary to produce the given character
151 # from the given scan code. The only one we're concerned with at the
152 # moment is Shift. Determine the shift state and compare it to the
153 # current state... if it differs, press or release the shift key.
154 new_shift_held
= bool(vk
& (1<<8))
156 if new_shift_held
!= shift_held
:
157 PressKey(new_shift_held
, win32con
.VK_SHIFT
)
158 shift_held
= new_shift_held
160 # Type the key with the specified length, then wait the specified delay
161 TypeKey(vk
& 0xFF, keystroke_time
)
162 time
.sleep(time_between_keystrokes
)
164 # Release the modifier keys, if held
165 if shift_held
: PressKey(False, win32con
.VK_SHIFT
)
166 if ctrl_held
: PressKey(False, win32con
.VK_CONTROL
)
167 if alt_held
: PressKey(False, win32con
.VK_MENU
)
171 # We're being invoked rather than imported. Let's do some tests
173 # Press command-R to bring up the Run dialog
174 PressKey(True, win32con
.VK_LWIN
)
176 PressKey(False, win32con
.VK_LWIN
)
178 # Wait a sec to make sure it comes up
181 # Invoke Notepad through the Run dialog
182 TypeString("wordpad\n")
184 # Wait another sec, then start typing
186 TypeString("This is a test of SiteCompare's Keyboard.py module.\n\n")
187 TypeString("There should be a blank line above and below this one.\n\n")
188 TypeString("This line has control characters to make "
189 "[b]boldface text[b] and [i]italic text[i] and normal text.\n\n",
191 TypeString(r
"This line should be typed with a visible delay between "
192 "characters. When it ends, there should be a 3-second pause, "
193 "then the menu will select File/Exit, then another 3-second "
194 "pause, then No to exit without saving. Ready?\p\p\p{f}x\p\p\pn",
197 time_between_keystrokes
=0.05)
200 if __name__
== "__main__":