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.
5 #include "chrome_frame/test/simulate_input.h"
10 #include "chrome_frame/utils.h"
12 namespace simulate_input
{
14 class ForegroundHelperWindow
: public CWindowImpl
<ForegroundHelperWindow
> {
16 BEGIN_MSG_MAP(ForegroundHelperWindow
)
17 MESSAGE_HANDLER(WM_HOTKEY
, OnHotKey
)
20 ForegroundHelperWindow() : window_(NULL
) {}
22 HRESULT
SetForeground(HWND window
) {
23 DCHECK(::IsWindow(window
));
25 if (NULL
== Create(NULL
, NULL
, NULL
, WS_POPUP
))
26 return AtlHresultFromLastError();
28 static const int kHotKeyId
= 0x0000baba;
29 static const int kHotKeyWaitTimeout
= 2000;
31 RegisterHotKey(m_hWnd
, kHotKeyId
, 0, VK_F22
);
34 PeekMessage(&msg
, NULL
, 0, 0, PM_NOREMOVE
);
36 SendMnemonic(VK_F22
, NONE
, 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
);
42 while (GetMessage(&msg
, NULL
, 0, 0)) {
43 TranslateMessage(&msg
);
44 DispatchMessage(&msg
);
45 if (msg
.message
== WM_HOTKEY
) {
48 if (msg
.message
== WM_TIMER
) {
49 SetForegroundWindow(window
);
54 UnregisterHotKey(m_hWnd
, kHotKeyId
);
60 LRESULT
OnHotKey(UINT msg
, WPARAM wp
, LPARAM lp
, BOOL
& handled
) { // NOLINT
61 SetForegroundWindow(window_
);
68 bool ForceSetForegroundWindow(HWND window
) {
69 if (GetForegroundWindow() == window
)
71 ForegroundHelperWindow foreground_helper_window
;
72 HRESULT hr
= foreground_helper_window
.SetForeground(window
);
81 BOOL CALLBACK
FindWindowInProcessCallback(HWND hwnd
, LPARAM param
) {
82 PidAndWindow
* paw
= reinterpret_cast<PidAndWindow
*>(param
);
84 GetWindowThreadProcessId(hwnd
, &pid
);
85 if (pid
== paw
->pid
&& IsWindowVisible(hwnd
)) {
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
)
101 PidAndWindow paw
= { process_id
};
102 EnumWindows(FindWindowInProcessCallback
, reinterpret_cast<LPARAM
>(&paw
));
103 if (!IsWindow(paw
.hwnd
)) {
104 LOG(ERROR
) << "failed to find process window";
108 bool ret
= ForceSetForegroundWindow(paw
.hwnd
);
109 LOG_IF(ERROR
, !ret
) << "ForceSetForegroundWindow: " << ret
;
114 void SendScanCode(short scan_code
, Modifier modifiers
) {
115 DCHECK(-1 != scan_code
);
117 // High order byte in |scan_code| is SHIFT/CTRL/ALT key state.
118 modifiers
= static_cast<Modifier
>(modifiers
| HIBYTE(scan_code
));
119 DCHECK(modifiers
<= ALT
);
121 // Low order byte in |scan_code| is the actual scan code.
122 SendMnemonic(LOBYTE(scan_code
), modifiers
, false, true);
125 void SendCharA(char c
, Modifier modifiers
) {
126 SendScanCode(VkKeyScanA(c
), modifiers
);
129 void SendCharW(wchar_t c
, Modifier modifiers
) {
130 SendScanCode(VkKeyScanW(c
), modifiers
);
133 // Sends a keystroke to the currently active application with optional
135 void SendMnemonic(WORD mnemonic_char
, Modifier modifiers
, bool extended
,
137 INPUT keys
[4] = {0}; // Keyboard events
138 int key_count
= 0; // Number of generated events
140 if (modifiers
& SHIFT
) {
141 keys
[key_count
].type
= INPUT_KEYBOARD
;
142 keys
[key_count
].ki
.wVk
= VK_SHIFT
;
143 keys
[key_count
].ki
.wScan
= MapVirtualKey(VK_SHIFT
, 0);
147 if (modifiers
& CONTROL
) {
148 keys
[key_count
].type
= INPUT_KEYBOARD
;
149 keys
[key_count
].ki
.wVk
= VK_CONTROL
;
150 keys
[key_count
].ki
.wScan
= MapVirtualKey(VK_CONTROL
, 0);
154 if (modifiers
& ALT
) {
155 keys
[key_count
].type
= INPUT_KEYBOARD
;
156 keys
[key_count
].ki
.wVk
= VK_MENU
;
157 keys
[key_count
].ki
.wScan
= MapVirtualKey(VK_MENU
, 0);
161 keys
[key_count
].type
= INPUT_KEYBOARD
;
162 keys
[key_count
].ki
.wVk
= mnemonic_char
;
163 keys
[key_count
].ki
.wScan
= MapVirtualKey(mnemonic_char
, 0);
166 keys
[key_count
].ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
168 keys
[key_count
].ki
.dwFlags
|= KEYEVENTF_UNICODE
;
171 bool should_sleep
= key_count
> 1;
174 for (int i
= 0; i
< key_count
; i
++) {
175 SendInput(1, &keys
[ i
], sizeof(keys
[0]));
176 keys
[i
].ki
.dwFlags
|= KEYEVENTF_KEYUP
;
182 // Now send key ups in reverse order
183 for (int i
= key_count
; i
; i
--) {
184 SendInput(1, &keys
[ i
- 1 ], sizeof(keys
[0]));
191 void SetKeyboardFocusToWindow(HWND window
) {
192 SendMouseClick(window
, 1, 1, LEFT
);
195 void SendMouseClick(int x
, int y
, MouseButton button
) {
196 // TODO(joshia): Fix this. GetSystemMetrics(SM_CXSCREEN) will
197 // retrieve screen size of the primarary monitor only. And monitors
198 // arrangement could be pretty arbitrary.
199 double screen_width
= ::GetSystemMetrics(SM_CXSCREEN
) - 1;
200 double screen_height
= ::GetSystemMetrics(SM_CYSCREEN
) - 1;
201 double location_x
= x
* (65535.0f
/ screen_width
);
202 double location_y
= y
* (65535.0f
/ screen_height
);
204 // Take advantage of button flag bitmask layout
205 unsigned int button_flag
= MOUSEEVENTF_LEFTDOWN
<< (button
+ button
);
207 INPUT input_info
= {0};
208 input_info
.type
= INPUT_MOUSE
;
209 input_info
.mi
.dwFlags
= MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
;
210 input_info
.mi
.dx
= static_cast<LONG
>(location_x
);
211 input_info
.mi
.dy
= static_cast<LONG
>(location_y
);
212 ::SendInput(1, &input_info
, sizeof(INPUT
));
216 input_info
.mi
.dwFlags
= button_flag
| MOUSEEVENTF_ABSOLUTE
;
217 ::SendInput(1, &input_info
, sizeof(INPUT
));
221 input_info
.mi
.dwFlags
= (button_flag
<< 1) | MOUSEEVENTF_ABSOLUTE
;
222 ::SendInput(1, &input_info
, sizeof(INPUT
));
225 void SendMouseClick(HWND window
, int x
, int y
, MouseButton button
) {
226 if (!IsWindow(window
)) {
227 NOTREACHED() << "Invalid window handle.";
231 HWND top_level_window
= window
;
232 if (!IsTopLevelWindow(top_level_window
)) {
233 top_level_window
= GetAncestor(window
, GA_ROOT
);
236 ForceSetForegroundWindow(top_level_window
);
238 POINT cursor_position
= {x
, y
};
239 ClientToScreen(window
, &cursor_position
);
240 SendMouseClick(cursor_position
.x
, cursor_position
.y
, button
);
243 void SendExtendedKey(WORD key
, Modifier modifiers
) {
244 SendMnemonic(key
, modifiers
, true, false);
247 void SendStringW(const std::wstring
& s
) {
248 for (size_t i
= 0; i
< s
.length(); i
++) {
249 SendCharW(s
[i
], NONE
);
254 void SendStringA(const std::string
& s
) {
255 for (size_t i
= 0; i
< s
.length(); i
++) {
256 SendCharA(s
[i
], NONE
);
261 } // namespace simulate_input