| 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 |