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 262