OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 "chrome_frame/utils.h" |
| 11 |
| 12 namespace simulate_input { |
| 13 |
| 14 class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> { |
| 15 public: |
| 16 BEGIN_MSG_MAP(ForegroundHelperWindow) |
| 17 MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) |
| 18 END_MSG_MAP() |
| 19 |
| 20 ForegroundHelperWindow() : window_(NULL) {} |
| 21 |
| 22 HRESULT SetForeground(HWND window) { |
| 23 DCHECK(::IsWindow(window)); |
| 24 window_ = window; |
| 25 if (NULL == Create(NULL, NULL, NULL, WS_POPUP)) |
| 26 return AtlHresultFromLastError(); |
| 27 |
| 28 static const int kHotKeyId = 0x0000baba; |
| 29 static const int kHotKeyWaitTimeout = 2000; |
| 30 |
| 31 RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22); |
| 32 |
| 33 MSG msg = {0}; |
| 34 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); |
| 35 |
| 36 SendMnemonic(VK_F22, false, false, false, false, false); |
| 37 // There are scenarios where the WM_HOTKEY is not dispatched by the |
| 38 // the corresponding foreground thread. To prevent us from indefinitely |
| 39 // waiting for the hotkey, we set a timer and exit the loop. |
| 40 SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL); |
| 41 |
| 42 while (GetMessage(&msg, NULL, 0, 0)) { |
| 43 TranslateMessage(&msg); |
| 44 DispatchMessage(&msg); |
| 45 if (msg.message == WM_HOTKEY) { |
| 46 break; |
| 47 } |
| 48 if (msg.message == WM_TIMER) { |
| 49 SetForegroundWindow(window); |
| 50 break; |
| 51 } |
| 52 } |
| 53 |
| 54 UnregisterHotKey(m_hWnd, kHotKeyId); |
| 55 KillTimer(kHotKeyId); |
| 56 DestroyWindow(); |
| 57 return S_OK; |
| 58 } |
| 59 |
| 60 LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT |
| 61 SetForegroundWindow(window_); |
| 62 return 1; |
| 63 } |
| 64 private: |
| 65 HWND window_; |
| 66 }; |
| 67 |
| 68 bool ForceSetForegroundWindow(HWND window) { |
| 69 if (GetForegroundWindow() == window) |
| 70 return true; |
| 71 ForegroundHelperWindow foreground_helper_window; |
| 72 HRESULT hr = foreground_helper_window.SetForeground(window); |
| 73 return SUCCEEDED(hr); |
| 74 } |
| 75 |
| 76 struct PidAndWindow { |
| 77 base::ProcessId pid; |
| 78 HWND hwnd; |
| 79 }; |
| 80 |
| 81 BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) { |
| 82 PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param); |
| 83 base::ProcessId pid; |
| 84 GetWindowThreadProcessId(hwnd, &pid); |
| 85 if (pid == paw->pid && IsWindowVisible(hwnd)) { |
| 86 paw->hwnd = hwnd; |
| 87 return FALSE; |
| 88 } |
| 89 |
| 90 return TRUE; |
| 91 } |
| 92 |
| 93 bool EnsureProcessInForeground(base::ProcessId process_id) { |
| 94 HWND hwnd = GetForegroundWindow(); |
| 95 base::ProcessId current_foreground_pid = 0; |
| 96 DWORD active_thread_id = GetWindowThreadProcessId(hwnd, |
| 97 ¤t_foreground_pid); |
| 98 if (current_foreground_pid == process_id) |
| 99 return true; |
| 100 |
| 101 PidAndWindow paw = { process_id }; |
| 102 EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw)); |
| 103 if (!IsWindow(paw.hwnd)) { |
| 104 DLOG(ERROR) << "failed to find process window"; |
| 105 return false; |
| 106 } |
| 107 |
| 108 bool ret = ForceSetForegroundWindow(paw.hwnd); |
| 109 LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret; |
| 110 |
| 111 return ret; |
| 112 } |
| 113 |
| 114 void SendChar(char c, bool control, bool alt) { |
| 115 SendMnemonic(toupper(c), !!isupper(c), control, alt, false, false); |
| 116 } |
| 117 |
| 118 void SendChar(wchar_t c, bool control, bool alt) { |
| 119 SendMnemonic(towupper(c), !!iswupper(c), control, alt, false, true); |
| 120 } |
| 121 |
| 122 // Sends a keystroke to the currently active application with optional |
| 123 // modifiers set. |
| 124 bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed, |
| 125 bool alt_pressed, bool extended, bool unicode) { |
| 126 INPUT keys[4] = {0}; // Keyboard events |
| 127 int key_count = 0; // Number of generated events |
| 128 |
| 129 if (shift_pressed) { |
| 130 keys[key_count].type = INPUT_KEYBOARD; |
| 131 keys[key_count].ki.wVk = VK_SHIFT; |
| 132 key_count++; |
| 133 } |
| 134 |
| 135 if (control_pressed) { |
| 136 keys[key_count].type = INPUT_KEYBOARD; |
| 137 keys[key_count].ki.wVk = VK_CONTROL; |
| 138 key_count++; |
| 139 } |
| 140 |
| 141 if (alt_pressed) { |
| 142 keys[key_count].type = INPUT_KEYBOARD; |
| 143 keys[key_count].ki.wVk = VK_MENU; |
| 144 key_count++; |
| 145 } |
| 146 |
| 147 keys[key_count].type = INPUT_KEYBOARD; |
| 148 keys[key_count].ki.wVk = mnemonic_char; |
| 149 if (extended) |
| 150 keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; |
| 151 if (unicode) |
| 152 keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE; |
| 153 key_count++; |
| 154 |
| 155 // Send key downs |
| 156 for (int i = 0; i < key_count; i++) { |
| 157 SendInput(1, &keys[ i ], sizeof(keys[0])); |
| 158 keys[i].ki.dwFlags |= KEYEVENTF_KEYUP; |
| 159 } |
| 160 |
| 161 // Now send key ups in reverse order |
| 162 for (int i = key_count; i; i--) { |
| 163 SendInput(1, &keys[ i - 1 ], sizeof(keys[0])); |
| 164 } |
| 165 |
| 166 return true; |
| 167 } |
| 168 |
| 169 void SetKeyboardFocusToWindow(HWND window) { |
| 170 SendMouseClick(window, 1, 1, LEFT); |
| 171 } |
| 172 |
| 173 void SendMouseClick(HWND window, int x, int y, MouseButton button) { |
| 174 if (!IsWindow(window)) { |
| 175 NOTREACHED() << "Invalid window handle."; |
| 176 return; |
| 177 } |
| 178 |
| 179 HWND top_level_window = window; |
| 180 if (!IsTopLevelWindow(top_level_window)) { |
| 181 top_level_window = GetAncestor(window, GA_ROOT); |
| 182 } |
| 183 |
| 184 ForceSetForegroundWindow(top_level_window); |
| 185 |
| 186 POINT cursor_position = {x, y}; |
| 187 ClientToScreen(window, &cursor_position); |
| 188 |
| 189 // TODO(joshia): Fix this. GetSystemMetrics(SM_CXSCREEN) will |
| 190 // retrieve screen size of the primarary monitor only. And monitors |
| 191 // arrangement could be pretty arbitrary. |
| 192 double screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; |
| 193 double screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; |
| 194 double location_x = cursor_position.x * (65535.0f / screen_width); |
| 195 double location_y = cursor_position.y * (65535.0f / screen_height); |
| 196 |
| 197 // Take advantage of button flag bitmask layout |
| 198 unsigned int button_flag = MOUSEEVENTF_LEFTDOWN << (button + button); |
| 199 |
| 200 INPUT input_info = {0}; |
| 201 input_info.type = INPUT_MOUSE; |
| 202 input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; |
| 203 input_info.mi.dx = static_cast<long>(location_x); |
| 204 input_info.mi.dy = static_cast<long>(location_y); |
| 205 ::SendInput(1, &input_info, sizeof(INPUT)); |
| 206 |
| 207 Sleep(10); |
| 208 |
| 209 input_info.mi.dwFlags = button_flag | MOUSEEVENTF_ABSOLUTE; |
| 210 ::SendInput(1, &input_info, sizeof(INPUT)); |
| 211 |
| 212 Sleep(10); |
| 213 |
| 214 input_info.mi.dwFlags = (button_flag << 1) | MOUSEEVENTF_ABSOLUTE; |
| 215 ::SendInput(1, &input_info, sizeof(INPUT)); |
| 216 } |
| 217 |
| 218 bool SendExtendedKey(WORD key, bool shift, bool control, bool alt) { |
| 219 return SendMnemonic(key, shift, control, alt, true, false); |
| 220 } |
| 221 |
| 222 } // namespace simulate_input |
| 223 |
OLD | NEW |