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"
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"
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
> {
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();
34 friend class base::RefCounted
<InputDispatcher
>;
38 // Notifies the task and release this (which should delete it).
41 // The task we notify.
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;
88 next_hook_
= SetWindowsHookEx(WH_KEYBOARD
, &KeyHook
, NULL
,
89 GetCurrentThreadId());
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());
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.
119 void InputDispatcher::DispatchedMessage(WPARAM message
) {
120 if (message
== message_waiting_for_
)
121 MatchingMessageFound();
124 void InputDispatcher::MatchingMessageFound() {
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() {
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
;
153 namespace ui_controls
{
156 bool SendKeyPressImpl(HWND window
,
157 ui::KeyboardCode key
,
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() :
168 if (window
&& ::GetForegroundWindow() != target_window
)
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
);
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();
189 INPUT input
[8] = {}; // 8, assuming all the modifiers are activated.
193 if (!FillKeyboardInput(ui::VKEY_CONTROL
, &input
[i
], false))
199 if (!FillKeyboardInput(ui::VKEY_SHIFT
, &input
[i
], false))
205 if (!FillKeyboardInput(ui::VKEY_LMENU
, &input
[i
], false))
210 if (!FillKeyboardInput(key
, &input
[i
], false))
214 if (!FillKeyboardInput(key
, &input
[i
], true))
219 if (!FillKeyboardInput(ui::VKEY_LMENU
, &input
[i
], true))
225 if (!FillKeyboardInput(ui::VKEY_SHIFT
, &input
[i
], true))
231 if (!FillKeyboardInput(ui::VKEY_CONTROL
, &input
[i
], true))
236 if (::SendInput(i
, input
, sizeof(INPUT
)) != i
)
239 if (dispatcher
.get())
240 dispatcher
->AddRef();
245 bool SendMouseMoveImpl(long screen_x
,
247 const base::Closure
& task
) {
248 // First check if the mouse is already there.
250 ::GetCursorPos(¤t_pos
);
251 if (screen_x
== current_pos
.x
&& screen_y
== current_pos
.y
) {
253 base::MessageLoop::current()->PostTask(FROM_HERE
, task
);
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
)))
275 if (dispatcher
.get())
276 dispatcher
->AddRef();
281 bool SendMouseEventsImpl(MouseButton type
, int state
,
282 const base::Closure
& task
) {
283 DWORD down_flags
= MOUSEEVENTF_ABSOLUTE
;
284 DWORD up_flags
= MOUSEEVENTF_ABSOLUTE
;
289 down_flags
|= MOUSEEVENTF_LEFTDOWN
;
290 up_flags
|= MOUSEEVENTF_LEFTUP
;
291 last_event
= (state
& UP
) ? WM_LBUTTONUP
: WM_LBUTTONDOWN
;
295 down_flags
|= MOUSEEVENTF_MIDDLEDOWN
;
296 up_flags
|= MOUSEEVENTF_MIDDLEUP
;
297 last_event
= (state
& UP
) ? WM_MBUTTONUP
: WM_MBUTTONDOWN
;
301 down_flags
|= MOUSEEVENTF_RIGHTDOWN
;
302 up_flags
|= MOUSEEVENTF_RIGHTUP
;
303 last_event
= (state
& UP
) ? WM_RBUTTONUP
: WM_RBUTTONDOWN
;
311 scoped_refptr
<InputDispatcher
> dispatcher(
312 !task
.is_null() ? new InputDispatcher(task
, last_event
) : NULL
);
315 input
.type
= INPUT_MOUSE
;
316 input
.mi
.dwFlags
= down_flags
;
317 if ((state
& DOWN
) && !::SendInput(1, &input
, sizeof(INPUT
)))
320 input
.mi
.dwFlags
= up_flags
;
321 if ((state
& UP
) && !::SendInput(1, &input
, sizeof(INPUT
)))
324 if (dispatcher
.get())
325 dispatcher
->AddRef();
330 } // namespace internal
331 } // namespace ui_controls