Revert of media-internals: Simplify the code path for creating a media log entry...
[chromium-blink-merge.git] / ui / base / test / ui_controls_internal_win.cc
blob059c820c707c3e3aa7d6f2399ee57604a023214c
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 // Send a key event (up/down)
152 bool SendKeyEvent(ui::KeyboardCode key, bool up) {
153 INPUT input = { 0 };
155 if (!FillKeyboardInput(key, &input, up))
156 return false;
158 if (!::SendInput(1, &input, sizeof(INPUT)))
159 return false;
161 return true;
164 } // namespace
166 namespace ui_controls {
167 namespace internal {
169 bool SendKeyPressImpl(HWND window,
170 ui::KeyboardCode key,
171 bool control,
172 bool shift,
173 bool alt,
174 const base::Closure& task) {
175 // SendInput only works as we expect it if one of our windows is the
176 // foreground window already.
177 HWND target_window = (::GetActiveWindow() &&
178 ::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ?
179 ::GetActiveWindow() :
180 window;
181 if (window && ::GetForegroundWindow() != target_window)
182 return false;
184 scoped_refptr<InputDispatcher> dispatcher(
185 !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL);
187 // If a pop-up menu is open, it won't receive events sent using SendInput.
188 // Check for a pop-up menu using its window class (#32768) and if one
189 // exists, send the key event directly there.
190 HWND popup_menu = ::FindWindow(L"#32768", 0);
191 if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) {
192 WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key);
193 LPARAM l_param = 0;
194 ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param);
195 ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param);
197 if (dispatcher.get())
198 dispatcher->AddRef();
199 return true;
202 INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated.
204 UINT i = 0;
205 if (control) {
206 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false))
207 return false;
208 i++;
211 if (shift) {
212 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false))
213 return false;
214 i++;
217 if (alt) {
218 if (!FillKeyboardInput(ui::VKEY_LMENU, &input[i], false))
219 return false;
220 i++;
223 if (!FillKeyboardInput(key, &input[i], false))
224 return false;
225 i++;
227 if (!FillKeyboardInput(key, &input[i], true))
228 return false;
229 i++;
231 if (alt) {
232 if (!FillKeyboardInput(ui::VKEY_LMENU, &input[i], true))
233 return false;
234 i++;
237 if (shift) {
238 if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true))
239 return false;
240 i++;
243 if (control) {
244 if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true))
245 return false;
246 i++;
249 if (::SendInput(i, input, sizeof(INPUT)) != i)
250 return false;
252 if (dispatcher.get())
253 dispatcher->AddRef();
255 return true;
258 bool SendMouseMoveImpl(long screen_x,
259 long screen_y,
260 const base::Closure& task) {
261 // First check if the mouse is already there.
262 POINT current_pos;
263 ::GetCursorPos(&current_pos);
264 if (screen_x == current_pos.x && screen_y == current_pos.y) {
265 if (!task.is_null())
266 base::MessageLoop::current()->PostTask(FROM_HERE, task);
267 return true;
270 INPUT input = { 0 };
272 int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
273 int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1;
274 LONG pixel_x = static_cast<LONG>(screen_x * (65535.0f / screen_width));
275 LONG pixel_y = static_cast<LONG>(screen_y * (65535.0f / screen_height));
277 input.type = INPUT_MOUSE;
278 input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
279 input.mi.dx = pixel_x;
280 input.mi.dy = pixel_y;
282 scoped_refptr<InputDispatcher> dispatcher(
283 !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL);
285 if (!::SendInput(1, &input, sizeof(INPUT)))
286 return false;
288 if (dispatcher.get())
289 dispatcher->AddRef();
291 return true;
294 bool SendMouseEventsImpl(MouseButton type, int state,
295 const base::Closure& task) {
296 DWORD down_flags = MOUSEEVENTF_ABSOLUTE;
297 DWORD up_flags = MOUSEEVENTF_ABSOLUTE;
298 UINT last_event;
300 switch (type) {
301 case LEFT:
302 down_flags |= MOUSEEVENTF_LEFTDOWN;
303 up_flags |= MOUSEEVENTF_LEFTUP;
304 last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
305 break;
307 case MIDDLE:
308 down_flags |= MOUSEEVENTF_MIDDLEDOWN;
309 up_flags |= MOUSEEVENTF_MIDDLEUP;
310 last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
311 break;
313 case RIGHT:
314 down_flags |= MOUSEEVENTF_RIGHTDOWN;
315 up_flags |= MOUSEEVENTF_RIGHTUP;
316 last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
317 break;
319 default:
320 NOTREACHED();
321 return false;
324 scoped_refptr<InputDispatcher> dispatcher(
325 !task.is_null() ? new InputDispatcher(task, last_event) : NULL);
327 INPUT input = { 0 };
328 input.type = INPUT_MOUSE;
329 input.mi.dwFlags = down_flags;
330 if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT)))
331 return false;
333 input.mi.dwFlags = up_flags;
334 if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT)))
335 return false;
337 if (dispatcher.get())
338 dispatcher->AddRef();
340 return true;
343 } // namespace internal
344 } // namespace ui_controls