- add sources.
[platform/framework/web/crosswalk.git] / src / ui / base / test / ui_controls_internal_win.cc
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.
4
5 #include "ui/base/test/ui_controls_internal_win.h"
6
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"
14
15 namespace {
16
17 // InputDispatcher ------------------------------------------------------------
18
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);
24
25   // Invoked from the hook. If mouse_message matches message_waiting_for_
26   // MatchingMessageFound is invoked.
27   void DispatchedMessage(WPARAM mouse_message);
28
29   // Invoked when a matching event is found. Uninstalls the hook and schedules
30   // an event that notifies the task.
31   void MatchingMessageFound();
32
33  private:
34   friend class base::RefCounted<InputDispatcher>;
35
36   ~InputDispatcher();
37
38   // Notifies the task and release this (which should delete it).
39   void NotifyTask();
40
41   // The task we notify.
42   base::Closure task_;
43
44   // Message we're waiting for. Not used for keyboard events.
45   const WPARAM message_waiting_for_;
46
47   DISALLOW_COPY_AND_ASSIGN(InputDispatcher);
48 };
49
50 // Have we installed the hook?
51 bool installed_hook_ = false;
52
53 // Return value from SetWindowsHookEx.
54 HHOOK next_hook_ = NULL;
55
56 // If a hook is installed, this is the dispatcher.
57 InputDispatcher* current_dispatcher_ = NULL;
58
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);
65   }
66   return CallNextHookEx(next_hook, n_code, w_param, l_param);
67 }
68
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();
77     }
78   }
79   return CallNextHookEx(next_hook, n_code, w_param, l_param);
80 }
81
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());
95   }
96   DCHECK(next_hook_);
97 }
98
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_);
105   }
106 }
107
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);
112 }
113
114 InputDispatcher::~InputDispatcher() {
115   // Make sure the hook isn't installed.
116   UninstallHook(this);
117 }
118
119 void InputDispatcher::DispatchedMessage(WPARAM message) {
120   if (message == message_waiting_for_)
121     MatchingMessageFound();
122 }
123
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));
130 }
131
132 void InputDispatcher::NotifyTask() {
133   task_.Run();
134   Release();
135 }
136
137 // Private functions ----------------------------------------------------------
138
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;
147
148   return true;
149 }
150
151 // Send a key event (up/down)
152 bool SendKeyEvent(ui::KeyboardCode key, bool up) {
153   INPUT input = { 0 };
154
155   if (!FillKeyboardInput(key, &input, up))
156     return false;
157
158   if (!::SendInput(1, &input, sizeof(INPUT)))
159     return false;
160
161   return true;
162 }
163
164 }  // namespace
165
166 namespace ui_controls {
167 namespace internal {
168
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;
183
184   scoped_refptr<InputDispatcher> dispatcher(
185       !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL);
186
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);
196
197     if (dispatcher.get())
198       dispatcher->AddRef();
199     return true;
200   }
201
202   INPUT input[8] = { 0 };  // 8, assuming all the modifiers are activated.
203
204   UINT i = 0;
205   if (control) {
206     if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false))
207       return false;
208     i++;
209   }
210
211   if (shift) {
212     if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false))
213       return false;
214     i++;
215   }
216
217   if (alt) {
218     if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], false))
219       return false;
220     i++;
221   }
222
223   if (!FillKeyboardInput(key, &input[i], false))
224     return false;
225   i++;
226
227   if (!FillKeyboardInput(key, &input[i], true))
228     return false;
229   i++;
230
231   if (alt) {
232     if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], true))
233       return false;
234     i++;
235   }
236
237   if (shift) {
238     if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true))
239       return false;
240     i++;
241   }
242
243   if (control) {
244     if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true))
245       return false;
246     i++;
247   }
248
249   if (::SendInput(i, input, sizeof(INPUT)) != i)
250     return false;
251
252   if (dispatcher.get())
253     dispatcher->AddRef();
254
255   return true;
256 }
257
258 bool SendMouseMoveImpl(long x, long y, const base::Closure& task) {
259   // First check if the mouse is already there.
260   POINT current_pos;
261   ::GetCursorPos(&current_pos);
262   if (x == current_pos.x && y == current_pos.y) {
263     if (!task.is_null())
264       base::MessageLoop::current()->PostTask(FROM_HERE, task);
265     return true;
266   }
267
268   INPUT input = { 0 };
269
270   int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
271   int screen_height  = ::GetSystemMetrics(SM_CYSCREEN) - 1;
272   LONG pixel_x  = static_cast<LONG>(x * (65535.0f / screen_width));
273   LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height));
274
275   input.type = INPUT_MOUSE;
276   input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
277   input.mi.dx = pixel_x;
278   input.mi.dy = pixel_y;
279
280   scoped_refptr<InputDispatcher> dispatcher(
281       !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL);
282
283   if (!::SendInput(1, &input, sizeof(INPUT)))
284     return false;
285
286   if (dispatcher.get())
287     dispatcher->AddRef();
288
289   return true;
290 }
291
292 bool SendMouseEventsImpl(MouseButton type, int state,
293                          const base::Closure& task) {
294   DWORD down_flags = MOUSEEVENTF_ABSOLUTE;
295   DWORD up_flags = MOUSEEVENTF_ABSOLUTE;
296   UINT last_event;
297
298   switch (type) {
299     case LEFT:
300       down_flags |= MOUSEEVENTF_LEFTDOWN;
301       up_flags |= MOUSEEVENTF_LEFTUP;
302       last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
303       break;
304
305     case MIDDLE:
306       down_flags |= MOUSEEVENTF_MIDDLEDOWN;
307       up_flags |= MOUSEEVENTF_MIDDLEUP;
308       last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
309       break;
310
311     case RIGHT:
312       down_flags |= MOUSEEVENTF_RIGHTDOWN;
313       up_flags |= MOUSEEVENTF_RIGHTUP;
314       last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
315       break;
316
317     default:
318       NOTREACHED();
319       return false;
320   }
321
322   scoped_refptr<InputDispatcher> dispatcher(
323       !task.is_null() ? new InputDispatcher(task, last_event) : NULL);
324
325   INPUT input = { 0 };
326   input.type = INPUT_MOUSE;
327   input.mi.dwFlags = down_flags;
328   if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT)))
329     return false;
330
331   input.mi.dwFlags = up_flags;
332   if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT)))
333     return false;
334
335   if (dispatcher.get())
336     dispatcher->AddRef();
337
338   return true;
339 }
340
341 }  // namespace internal
342 }  // namespace ui_controls