• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
MESSAGE_HANDLER(WM_HOTKEY,OnHotKey)19   MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
20 END_MSG_MAP()
21 
22   ForegroundHelperWindow() : window_(NULL) {}
23 
SetForeground(HWND window)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 
OnHotKey(UINT msg,WPARAM wp,LPARAM lp,BOOL & handled)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 
ForceSetForegroundWindow(HWND window)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 
FindWindowInProcessCallback(HWND hwnd,LPARAM param)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 
EnsureProcessInForeground(base::ProcessId process_id)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       &current_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 
SendScanCode(short scan_code,uint32 modifiers)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 
SendCharA(char c,uint32 modifiers)127 void SendCharA(char c, uint32 modifiers) {
128   SendScanCode(VkKeyScanA(c), modifiers);
129 }
130 
SendCharW(wchar_t c,uint32 modifiers)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.
SendMnemonic(WORD mnemonic_char,uint32 modifiers,bool extended,bool unicode,KeyMode key_mode)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 
SetKeyboardFocusToWindow(HWND window)191 void SetKeyboardFocusToWindow(HWND window) {
192   SendMouseClick(window, 1, 1, LEFT);
193 }
194 
SendMouseClick(int x,int y,MouseButton button)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 
SendMouseClick(HWND window,int x,int y,MouseButton button)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 
SendExtendedKey(WORD key,uint32 modifiers)243 void SendExtendedKey(WORD key, uint32 modifiers) {
244   SendMnemonic(key, modifiers, true, false, KEY_UP);
245 }
246 
SendStringW(const std::wstring & s)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 
SendStringA(const std::string & s)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