Add ICU message format support
[chromium-blink-merge.git] / ui / base / test / ui_controls_internal_win.cc
blob0dadb6d592e569c26c3511c9da41a9ea961172c4
1 // Copyright 2013 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 "ui/base/test/ui_controls_internal_win.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/message_loop/message_loop.h"
12 #include "ui/events/keycodes/keyboard_code_conversion_win.h"
13 #include "ui/events/keycodes/keyboard_codes.h"
15 namespace {
17 // InputDispatcher ------------------------------------------------------------
19 // InputDispatcher is used to listen for a mouse/keyboard event. When the
20 // appropriate event is received the task is notified.
21 class InputDispatcher : public base::RefCounted<InputDispatcher> {
22 public:
23 InputDispatcher(const base::Closure& task, WPARAM message_waiting_for);
25 // Invoked from the hook. If mouse_message matches message_waiting_for_
26 // MatchingMessageFound is invoked.
27 void DispatchedMessage(WPARAM mouse_message);
29 // Invoked when a matching event is found. Uninstalls the hook and schedules
30 // an event that notifies the task.
31 void MatchingMessageFound();
33 private:
34 friend class base::RefCounted<InputDispatcher>;
36 ~InputDispatcher();
38 // Notifies the task and release this (which should delete it).
39 void NotifyTask();
41 // The task we notify.
42 base::Closure task_;
44 // Message we're waiting for. Not used for keyboard events.
45 const WPARAM message_waiting_for_;
47 DISALLOW_COPY_AND_ASSIGN(InputDispatcher);
50 // Have we installed the hook?
51 bool installed_hook_ = false;
53 // Return value from SetWindowsHookEx.
54 HHOOK next_hook_ = NULL;
56 // If a hook is installed, this is the dispatcher.
57 InputDispatcher* current_dispatcher_ = NULL;
59 // Callback from hook when a mouse message is received.
60 LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) {
61 HHOOK next_hook = next_hook_;
62 if (n_code == HC_ACTION) {
63 DCHECK(current_dispatcher_);
64 current_dispatcher_->DispatchedMessage(w_param);
66 return CallNextHookEx(next_hook, n_code, w_param, l_param);
69 // Callback from hook when a key message is received.
70 LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) {
71 HHOOK next_hook = next_hook_;
72 if (n_code == HC_ACTION) {
73 DCHECK(current_dispatcher_);
74 if (l_param & (1 << 30)) {
75 // Only send on key up.
76 current_dispatcher_->MatchingMessageFound();
79 return CallNextHookEx(next_hook, n_code, w_param, l_param);
82 // Installs dispatcher as the current hook.
83 void InstallHook(InputDispatcher* dispatcher, bool key_hook) {
84 DCHECK(!installed_hook_);
85 current_dispatcher_ = dispatcher;
86 installed_hook_ = true;
87 if (key_hook) {
88 next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL,
89 GetCurrentThreadId());
90 } else {
91 // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I
92 // didn't get a mouse message like I do with MouseHook.
93 next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL,
94 GetCurrentThreadId());
96 DCHECK(next_hook_);
99 // Uninstalls the hook set in InstallHook.
100 void UninstallHook(InputDispatcher* dispatcher) {
101 if (current_dispatcher_ == dispatcher) {
102 installed_hook_ = false;
103 current_dispatcher_ = NULL;
104 UnhookWindowsHookEx(next_hook_);
108 InputDispatcher::InputDispatcher(const base::Closure& task,
109 WPARAM message_waiting_for)
110 : task_(task), message_waiting_for_(message_waiting_for) {
111 InstallHook(this, message_waiting_for == WM_KEYUP);
114 InputDispatcher::~InputDispatcher() {
115 // Make sure the hook isn't installed.
116 UninstallHook(this);
119 void InputDispatcher::DispatchedMessage(WPARAM message) {
120 if (message == message_waiting_for_)
121 MatchingMessageFound();
124 void InputDispatcher::MatchingMessageFound() {
125 UninstallHook(this);
126 // At the time we're invoked the event has not actually been processed.
127 // Use PostTask to make sure the event has been processed before notifying.
128 base::MessageLoop::current()->PostTask(
129 FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this));
132 void InputDispatcher::NotifyTask() {
133 task_.Run();
134 Release();
137 // Private functions ----------------------------------------------------------
139 // Populate the INPUT structure with the appropriate keyboard event
140 // parameters required by SendInput
141 bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) {
142 memset(input, 0, sizeof(INPUT));
143 input->type = INPUT_KEYBOARD;
144 input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key);
145 input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP :
146 KEYEVENTF_EXTENDEDKEY;
148 return true;
151 } // namespace
153 namespace ui_controls {
154 namespace internal {
156 bool SendKeyPressImpl(HWND window,
157 ui::KeyboardCode key,
158 bool control,
159 bool shift,
160 bool alt,
161 const base::Closure& task) {
162 // SendInput only works as we expect it if one of our windows is the
163 // foreground window already.
164 HWND target_window = (::GetActiveWindow() &&
165 ::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ?
166 ::GetActiveWindow() :
167 window;
168 if (window && ::GetForegroundWindow() != target_window)
169 return false;
171 scoped_refptr<InputDispatcher> dispatcher(
172 !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL);
174 // If a pop-up menu is open, it won't receive events sent using SendInput.
175 // Check for a pop-up menu using its window class (#32768) and if one
176 // exists, send the key event directly there.
177 HWND popup_menu = ::FindWindow(L"#32768", 0);
178 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) {
179 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key);
180 LPARAM l_param = 0;
181 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param);
182 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param);
184 if (dispatcher.get())
185 dispatcher->AddRef();
186 return true;
189 INPUT input[8] = {}; // 8, assuming all the modifiers are activated.
191 UINT i = 0;
192 if (control) {
193 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false))
194 return false;
195 i++;
198 if (shift) {
199 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false))
200 return false;
201 i++;
204 if (alt) {
205 if (!FillKeyboardInput(ui::VKEY_LMENU, &input[i], false))
206 return false;
207 i++;
210 if (!FillKeyboardInput(key, &input[i], false))
211 return false;
212 i++;
214 if (!FillKeyboardInput(key, &input[i], true))
215 return false;
216 i++;
218 if (alt) {
219 if (!FillKeyboardInput(ui::VKEY_LMENU, &input[i], true))
220 return false;
221 i++;
224 if (shift) {
225 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true))
226 return false;
227 i++;
230 if (control) {
231 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true))
232 return false;
233 i++;
236 if (::SendInput(i, input, sizeof(INPUT)) != i)
237 return false;
239 if (dispatcher.get())
240 dispatcher->AddRef();
242 return true;
245 bool SendMouseMoveImpl(long screen_x,
246 long screen_y,
247 const base::Closure& task) {
248 // First check if the mouse is already there.
249 POINT current_pos;
250 ::GetCursorPos(&current_pos);
251 if (screen_x == current_pos.x && screen_y == current_pos.y) {
252 if (!task.is_null())
253 base::MessageLoop::current()->PostTask(FROM_HERE, task);
254 return true;
257 INPUT input = { 0 };
259 int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
260 int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1;
261 LONG pixel_x = static_cast<LONG>(screen_x * (65535.0f / screen_width));
262 LONG pixel_y = static_cast<LONG>(screen_y * (65535.0f / screen_height));
264 input.type = INPUT_MOUSE;
265 input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
266 input.mi.dx = pixel_x;
267 input.mi.dy = pixel_y;
269 scoped_refptr<InputDispatcher> dispatcher(
270 !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL);
272 if (!::SendInput(1, &input, sizeof(INPUT)))
273 return false;
275 if (dispatcher.get())
276 dispatcher->AddRef();
278 return true;
281 bool SendMouseEventsImpl(MouseButton type, int state,
282 const base::Closure& task) {
283 DWORD down_flags = MOUSEEVENTF_ABSOLUTE;
284 DWORD up_flags = MOUSEEVENTF_ABSOLUTE;
285 UINT last_event;
287 switch (type) {
288 case LEFT:
289 down_flags |= MOUSEEVENTF_LEFTDOWN;
290 up_flags |= MOUSEEVENTF_LEFTUP;
291 last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
292 break;
294 case MIDDLE:
295 down_flags |= MOUSEEVENTF_MIDDLEDOWN;
296 up_flags |= MOUSEEVENTF_MIDDLEUP;
297 last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
298 break;
300 case RIGHT:
301 down_flags |= MOUSEEVENTF_RIGHTDOWN;
302 up_flags |= MOUSEEVENTF_RIGHTUP;
303 last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
304 break;
306 default:
307 NOTREACHED();
308 return false;
311 scoped_refptr<InputDispatcher> dispatcher(
312 !task.is_null() ? new InputDispatcher(task, last_event) : NULL);
314 INPUT input = { 0 };
315 input.type = INPUT_MOUSE;
316 input.mi.dwFlags = down_flags;
317 if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT)))
318 return false;
320 input.mi.dwFlags = up_flags;
321 if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT)))
322 return false;
324 if (dispatcher.get())
325 dispatcher->AddRef();
327 return true;
330 } // namespace internal
331 } // namespace ui_controls