OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright 2014 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 """Windows API functions required by tests of Chrome with NVDA.""" | |
7 | |
8 | |
9 from ctypes import wintypes | |
10 | |
11 import ctypes | |
12 | |
13 _user = ctypes.windll.user32 | |
14 # Argument types for Windows API functions. | |
15 _user.VkKeyScanW.argtypes = [wintypes.WCHAR] | |
16 _user.VkKeyScanW.restype = wintypes.SHORT | |
17 _ENUM_WINDOWS_CALLBACK = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintyp es.LPARAM) | |
dmazzoni
2014/11/03 16:26:49
nit: wrap to 80 chars
| |
18 _user.EnumWindows.argtypes = [_ENUM_WINDOWS_CALLBACK, wintypes.LPARAM] | |
19 _user.GetWindowThreadProcessId.argtypes = [wintypes.HWND, ctypes.POINTER(wintype s.DWORD)] | |
20 # Flags required by some API calls. | |
21 _MAP_VKCODE_TO_SCAN_CODE = 0 | |
22 _PRESS_KEY = 0 | |
23 _RELEASE_KEY = 2 | |
24 | |
25 _MODIFIER_KEYS = { | |
26 'shift': 0x10, | |
27 'control': 0x11, | |
28 'alt': 0x12, | |
29 'windows': 0x5B, | |
30 | |
31 # Screen reader modifiers. | |
32 'insert': 0x2D, | |
33 'capslock': 0x14, | |
34 } | |
35 | |
36 _SPECIAL_KEYS = { | |
37 'enter': 0x0D, | |
38 'space': 0x20, | |
39 'tab': 0x09, | |
40 'applications': 0x5D, # Context menu key. | |
41 'escape': 0x1B, | |
42 'backspace': 0x08, | |
43 'delete': 0x2E, | |
44 | |
45 # Navigation keys. | |
46 'pageup': 0x21, | |
47 'pagedown': 0x22, | |
48 'end': 0x23, | |
49 'home': 0x24, | |
50 'left': 0x25, | |
51 'up': 0x26, | |
52 'right': 0x27, | |
53 'down': 0x28, | |
54 | |
55 # Browser keys. | |
56 'back': 0xA6, | |
57 'forward': 0xA7, | |
58 'refresh': 0xA8, | |
59 'home': 0xAC, | |
60 } | |
61 | |
62 def _getKeyAndScanCode(key): | |
63 if key[0] == '{' and key[-1] == '}': | |
64 # This is a modifier or a special key. | |
65 key = key[1:-1] | |
66 keyCode = dict(_MODIFIER_KEYS.items() + _SPECIAL_KEYS.items()).get(key) | |
67 if keyCode is not None: | |
68 return keyCode, _user.MapVirtualKeyW(keyCode, _MAP_VKCODE_TO_SCAN_CODE) | |
69 else: | |
70 raise ValueError, key | |
71 else: | |
72 # This is a character to be typed. | |
73 keyCodes = [] | |
74 retVal = _user.VkKeyScanW(key) | |
75 modifiers = retVal >> 8 # Upper byte only. | |
76 keyCode = retVal & 0xFF # Lower byte only. | |
77 if modifiers == -1 or keyCode == -1: | |
78 raise ValueError, key | |
79 if modifiers & 0x1: # Shift key. | |
80 keyCodes.append(_getKeyAndScanCode('{shift}')) | |
81 if modifiers & 0x2: # Control key. | |
82 keyCodes.append(_getKeyAndScanCode('{control}')) | |
83 if modifiers & 0x4: # Alt key. | |
84 keyCodes.append(_getKeyAndScanCode('{alt}')) | |
85 keyCodes.append((keyCode, _user.MapVirtualKeyW(keyCode, _MAP_VKCODE_TO_SCAN_ CODE))) | |
86 return keyCodes | |
87 | |
88 def typeKeys(keys): | |
89 """Enters the given keys in the active application's input queue. | |
90 | |
91 The argument to this function should be a string containing a sequence of | |
92 keys to be pressed. Keys are separated using '+'. Special keys like control | |
93 should be placed in braces. | |
94 Examples: | |
95 Open the Task Manager: winapi.typeKeys('{control}+{shift}{escape}') | |
96 Read the current line using NVDA: winapi.typeKeys('{insert}{up}') | |
97 Type a combination of capital and small letters: winapi.typeKeys('TeStIng') | |
98 """ | |
99 try: | |
100 pressedModifiers = [] | |
101 for key in keys.split('+'): | |
102 if key[0] == '{' and key[-1] == '}': | |
103 # This is a modifier or a special key. | |
104 keyCode, scanCode = _getKeyAndScanCode(key) | |
105 _user.keybd_event(keyCode, scanCode, _PRESS_KEY) | |
106 if key[1:-1] in _MODIFIER_KEYS: | |
107 pressedModifiers.append((keyCode, scanCode)) | |
108 else: | |
109 # Key is a sequence of one or more characters. | |
110 textCodes = [_getKeyAndScanCode(c) for c in key] | |
111 for charCodes in textCodes: | |
112 for keyCode, scanCode in charCodes: | |
113 _user.keybd_event(keyCode, scanCode, _PRESS_KEY) | |
114 for keyCode, scanCode in reversed(charCodes): | |
115 _user.keybd_event(keyCode, scanCode, _RELEASE_KEY) | |
116 except ValueError: | |
117 raise | |
118 finally: | |
119 # Release any modifiers in reverse order. | |
120 for keyCode, scanCode in reversed(pressedModifiers): | |
121 _user.keybd_event(keyCode, scanCode, _RELEASE_KEY) | |
122 | |
123 def _getWindowHandleFromProcessId(processId): | |
124 hWnds = [] | |
125 def enumWindowsCallback(hWnd, lParam): | |
126 if _isWindowHandleOwnedByProcess(processId, hWnd): | |
127 hWnds.append(hWnd) | |
128 return True # Continue enumerating. | |
129 _user.EnumWindows(_ENUM_WINDOWS_CALLBACK(enumWindowsCallback), 0) | |
130 return hWnds[0] if len(hWnds) > 0 else None | |
dmazzoni
2014/11/03 16:26:50
If there's more than one, could you return the one
| |
131 | |
132 def _isWindowHandleOwnedByProcess(processId, hWnd): | |
133 windowProcessId = wintypes.DWORD() | |
134 if _user.GetWindowThreadProcessId(hWnd, ctypes.byref(windowProcessId)): | |
135 if windowProcessId.value == processId: | |
136 return True | |
137 return False | |
138 | |
139 def focusWindow(processId): | |
140 hWnd = _getWindowHandleFromProcessId(processId) | |
141 if hWnd is None: | |
142 raise ValueError, processId | |
143 _user.SwitchToThisWindow(hWnd) | |
144 | |
OLD | NEW |