OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome_frame/test/simulate_input.h" | |
6 | |
7 #include <atlbase.h> | |
8 #include <atlwin.h> | |
9 | |
10 #include "base/test/test_timeouts.h" | |
11 #include "base/threading/platform_thread.h" | |
12 #include "chrome_frame/utils.h" | |
13 | |
14 namespace simulate_input { | |
15 | |
16 class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> { | |
17 public: | |
18 BEGIN_MSG_MAP(ForegroundHelperWindow) | |
19 MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) | |
20 END_MSG_MAP() | |
21 | |
22 ForegroundHelperWindow() : window_(NULL) {} | |
23 | |
24 HRESULT SetForeground(HWND window) { | |
25 DCHECK(::IsWindow(window)); | |
26 window_ = window; | |
27 if (NULL == Create(NULL, NULL, NULL, WS_POPUP)) | |
28 return AtlHresultFromLastError(); | |
29 | |
30 static const int kHotKeyId = 0x0000baba; | |
31 static const int kHotKeyWaitTimeout = 2000; | |
32 | |
33 RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22); | |
34 | |
35 MSG msg = {0}; | |
36 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); | |
37 | |
38 SendMnemonic(VK_F22, NONE, false, false, KEY_DOWN); | |
39 // There are scenarios where the WM_HOTKEY is not dispatched by the | |
40 // the corresponding foreground thread. To prevent us from indefinitely | |
41 // waiting for the hotkey, we set a timer and exit the loop. | |
42 SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL); | |
43 | |
44 while (GetMessage(&msg, NULL, 0, 0)) { | |
45 TranslateMessage(&msg); | |
46 DispatchMessage(&msg); | |
47 if (msg.message == WM_HOTKEY) { | |
48 break; | |
49 } | |
50 if (msg.message == WM_TIMER) { | |
51 SetForegroundWindow(window); | |
52 break; | |
53 } | |
54 } | |
55 | |
56 UnregisterHotKey(m_hWnd, kHotKeyId); | |
57 KillTimer(kHotKeyId); | |
58 DestroyWindow(); | |
59 return S_OK; | |
60 } | |
61 | |
62 LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT | |
63 SetForegroundWindow(window_); | |
64 return 1; | |
65 } | |
66 private: | |
67 HWND window_; | |
68 }; | |
69 | |
70 bool ForceSetForegroundWindow(HWND window) { | |
71 if (GetForegroundWindow() == window) | |
72 return true; | |
73 ForegroundHelperWindow foreground_helper_window; | |
74 HRESULT hr = foreground_helper_window.SetForeground(window); | |
75 return SUCCEEDED(hr); | |
76 } | |
77 | |
78 struct PidAndWindow { | |
79 base::ProcessId pid; | |
80 HWND hwnd; | |
81 }; | |
82 | |
83 BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) { | |
84 PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param); | |
85 base::ProcessId pid; | |
86 GetWindowThreadProcessId(hwnd, &pid); | |
87 if (pid == paw->pid && IsWindowVisible(hwnd)) { | |
88 paw->hwnd = hwnd; | |
89 return FALSE; | |
90 } | |
91 | |
92 return TRUE; | |
93 } | |
94 | |
95 bool EnsureProcessInForeground(base::ProcessId process_id) { | |
96 HWND hwnd = GetForegroundWindow(); | |
97 base::ProcessId current_foreground_pid = 0; | |
98 DWORD active_thread_id = GetWindowThreadProcessId(hwnd, | |
99 ¤t_foreground_pid); | |
100 if (current_foreground_pid == process_id) | |
101 return true; | |
102 | |
103 PidAndWindow paw = { process_id }; | |
104 EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw)); | |
105 if (!IsWindow(paw.hwnd)) { | |
106 LOG(ERROR) << "failed to find process window"; | |
107 return false; | |
108 } | |
109 | |
110 bool ret = ForceSetForegroundWindow(paw.hwnd); | |
111 LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret; | |
112 | |
113 return ret; | |
114 } | |
115 | |
116 void SendScanCode(short scan_code, uint32 modifiers) { | |
117 DCHECK(-1 != scan_code); | |
118 | |
119 // High order byte in |scan_code| is SHIFT/CTRL/ALT key state. | |
120 modifiers = static_cast<Modifier>(modifiers | HIBYTE(scan_code)); | |
121 DCHECK(modifiers <= ALT); | |
122 | |
123 // Low order byte in |scan_code| is the actual scan code. | |
124 SendMnemonic(LOBYTE(scan_code), modifiers, false, true, KEY_DOWN); | |
125 } | |
126 | |
127 void SendCharA(char c, uint32 modifiers) { | |
128 SendScanCode(VkKeyScanA(c), modifiers); | |
129 } | |
130 | |
131 void SendCharW(wchar_t c, uint32 modifiers) { | |
132 SendScanCode(VkKeyScanW(c), modifiers); | |
133 } | |
134 | |
135 // Sends a keystroke to the currently active application with optional | |
136 // modifiers set. | |
137 void SendMnemonic(WORD mnemonic_char, | |
138 uint32 modifiers, | |
139 bool extended, | |
140 bool unicode, | |
141 KeyMode key_mode) { | |
142 const int kMaxInputs = 4; | |
143 INPUT keys[kMaxInputs] = {0}; // Keyboard events | |
144 int key_count = 0; // Number of generated events | |
145 | |
146 if (modifiers & SHIFT) { | |
147 keys[key_count].type = INPUT_KEYBOARD; | |
148 keys[key_count].ki.wVk = VK_SHIFT; | |
149 keys[key_count].ki.wScan = MapVirtualKey(VK_SHIFT, 0); | |
150 key_count++; | |
151 } | |
152 | |
153 if (modifiers & CONTROL) { | |
154 keys[key_count].type = INPUT_KEYBOARD; | |
155 keys[key_count].ki.wVk = VK_CONTROL; | |
156 keys[key_count].ki.wScan = MapVirtualKey(VK_CONTROL, 0); | |
157 key_count++; | |
158 } | |
159 | |
160 if (modifiers & ALT) { | |
161 keys[key_count].type = INPUT_KEYBOARD; | |
162 keys[key_count].ki.wVk = VK_MENU; | |
163 keys[key_count].ki.wScan = MapVirtualKey(VK_MENU, 0); | |
164 key_count++; | |
165 } | |
166 | |
167 if (mnemonic_char) { | |
168 keys[key_count].type = INPUT_KEYBOARD; | |
169 keys[key_count].ki.wVk = mnemonic_char; | |
170 keys[key_count].ki.wScan = MapVirtualKey(mnemonic_char, 0); | |
171 | |
172 if (extended) | |
173 keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; | |
174 if (unicode) | |
175 keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE; | |
176 key_count++; | |
177 } | |
178 | |
179 DCHECK_LE(key_count, kMaxInputs); | |
180 | |
181 // Add the key up bit if needed. | |
182 if (key_mode == KEY_UP) { | |
183 for (int i = 0; i < key_count; i++) { | |
184 keys[i].ki.dwFlags |= KEYEVENTF_KEYUP; | |
185 } | |
186 } | |
187 | |
188 SendInput(key_count, &keys[0], sizeof(keys[0])); | |
189 } | |
190 | |
191 void SetKeyboardFocusToWindow(HWND window) { | |
192 SendMouseClick(window, 1, 1, LEFT); | |
193 } | |
194 | |
195 void SendMouseClick(int x, int y, MouseButton button) { | |
196 const base::TimeDelta kMessageTimeout = TestTimeouts::tiny_timeout(); | |
197 // TODO(joshia): Fix this. GetSystemMetrics(SM_CXSCREEN) will | |
198 // retrieve screen size of the primarary monitor only. And monitors | |
199 // arrangement could be pretty arbitrary. | |
200 double screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; | |
201 double screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; | |
202 double location_x = x * (65535.0f / screen_width); | |
203 double location_y = y * (65535.0f / screen_height); | |
204 | |
205 // Take advantage of button flag bitmask layout | |
206 unsigned int button_flag = MOUSEEVENTF_LEFTDOWN << (button + button); | |
207 | |
208 INPUT input_info = {0}; | |
209 input_info.type = INPUT_MOUSE; | |
210 input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; | |
211 input_info.mi.dx = static_cast<LONG>(location_x); | |
212 input_info.mi.dy = static_cast<LONG>(location_y); | |
213 ::SendInput(1, &input_info, sizeof(INPUT)); | |
214 base::PlatformThread::Sleep(kMessageTimeout); | |
215 | |
216 input_info.mi.dwFlags = button_flag | MOUSEEVENTF_ABSOLUTE; | |
217 ::SendInput(1, &input_info, sizeof(INPUT)); | |
218 base::PlatformThread::Sleep(kMessageTimeout); | |
219 | |
220 input_info.mi.dwFlags = (button_flag << 1) | MOUSEEVENTF_ABSOLUTE; | |
221 ::SendInput(1, &input_info, sizeof(INPUT)); | |
222 base::PlatformThread::Sleep(kMessageTimeout); | |
223 } | |
224 | |
225 void SendMouseClick(HWND window, int x, int y, MouseButton button) { | |
226 if (!IsWindow(window)) { | |
227 NOTREACHED() << "Invalid window handle."; | |
228 return; | |
229 } | |
230 | |
231 HWND top_level_window = window; | |
232 if (!IsTopLevelWindow(top_level_window)) { | |
233 top_level_window = GetAncestor(window, GA_ROOT); | |
234 } | |
235 | |
236 ForceSetForegroundWindow(top_level_window); | |
237 | |
238 POINT cursor_position = {x, y}; | |
239 ClientToScreen(window, &cursor_position); | |
240 SendMouseClick(cursor_position.x, cursor_position.y, button); | |
241 } | |
242 | |
243 void SendExtendedKey(WORD key, uint32 modifiers) { | |
244 SendMnemonic(key, modifiers, true, false, KEY_UP); | |
245 } | |
246 | |
247 void SendStringW(const std::wstring& s) { | |
248 for (size_t i = 0; i < s.length(); i++) { | |
249 SendCharW(s[i], NONE); | |
250 Sleep(10); | |
251 } | |
252 } | |
253 | |
254 void SendStringA(const std::string& s) { | |
255 for (size_t i = 0; i < s.length(); i++) { | |
256 SendCharA(s[i], NONE); | |
257 Sleep(10); | |
258 } | |
259 } | |
260 | |
261 } // namespace simulate_input | |
OLD | NEW |