This source file includes following definitions.
- KeyHook
- InstallHook
- UninstallHook
- message_waiting_for_
- DispatchedMessage
- MatchingMessageFound
- NotifyTask
- FillKeyboardInput
- SendKeyEvent
- SendKeyPressImpl
- SendMouseMoveImpl
- SendMouseEventsImpl
#include "ui/base/test/ui_controls_internal_win.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "ui/events/keycodes/keyboard_code_conversion_win.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace {
class InputDispatcher : public base::RefCounted<InputDispatcher> {
public:
InputDispatcher(const base::Closure& task, WPARAM message_waiting_for);
void DispatchedMessage(WPARAM mouse_message);
void MatchingMessageFound();
private:
friend class base::RefCounted<InputDispatcher>;
~InputDispatcher();
void NotifyTask();
base::Closure task_;
const WPARAM message_waiting_for_;
DISALLOW_COPY_AND_ASSIGN(InputDispatcher);
};
bool installed_hook_ = false;
HHOOK next_hook_ = NULL;
InputDispatcher* current_dispatcher_ = NULL;
LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) {
HHOOK next_hook = next_hook_;
if (n_code == HC_ACTION) {
DCHECK(current_dispatcher_);
current_dispatcher_->DispatchedMessage(w_param);
}
return CallNextHookEx(next_hook, n_code, w_param, l_param);
}
LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) {
HHOOK next_hook = next_hook_;
if (n_code == HC_ACTION) {
DCHECK(current_dispatcher_);
if (l_param & (1 << 30)) {
current_dispatcher_->MatchingMessageFound();
}
}
return CallNextHookEx(next_hook, n_code, w_param, l_param);
}
void InstallHook(InputDispatcher* dispatcher, bool key_hook) {
DCHECK(!installed_hook_);
current_dispatcher_ = dispatcher;
installed_hook_ = true;
if (key_hook) {
next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL,
GetCurrentThreadId());
} else {
next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL,
GetCurrentThreadId());
}
DCHECK(next_hook_);
}
void UninstallHook(InputDispatcher* dispatcher) {
if (current_dispatcher_ == dispatcher) {
installed_hook_ = false;
current_dispatcher_ = NULL;
UnhookWindowsHookEx(next_hook_);
}
}
InputDispatcher::InputDispatcher(const base::Closure& task,
WPARAM message_waiting_for)
: task_(task), message_waiting_for_(message_waiting_for) {
InstallHook(this, message_waiting_for == WM_KEYUP);
}
InputDispatcher::~InputDispatcher() {
UninstallHook(this);
}
void InputDispatcher::DispatchedMessage(WPARAM message) {
if (message == message_waiting_for_)
MatchingMessageFound();
}
void InputDispatcher::MatchingMessageFound() {
UninstallHook(this);
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this));
}
void InputDispatcher::NotifyTask() {
task_.Run();
Release();
}
bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) {
memset(input, 0, sizeof(INPUT));
input->type = INPUT_KEYBOARD;
input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key);
input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP :
KEYEVENTF_EXTENDEDKEY;
return true;
}
bool SendKeyEvent(ui::KeyboardCode key, bool up) {
INPUT input = { 0 };
if (!FillKeyboardInput(key, &input, up))
return false;
if (!::SendInput(1, &input, sizeof(INPUT)))
return false;
return true;
}
}
namespace ui_controls {
namespace internal {
bool SendKeyPressImpl(HWND window,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
const base::Closure& task) {
HWND target_window = (::GetActiveWindow() &&
::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ?
::GetActiveWindow() :
window;
if (window && ::GetForegroundWindow() != target_window)
return false;
scoped_refptr<InputDispatcher> dispatcher(
!task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL);
HWND popup_menu = ::FindWindow(L"#32768", 0);
if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) {
WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key);
LPARAM l_param = 0;
::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param);
::SendMessage(popup_menu, WM_KEYUP, w_param, l_param);
if (dispatcher.get())
dispatcher->AddRef();
return true;
}
INPUT input[8] = { 0 };
UINT i = 0;
if (control) {
if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false))
return false;
i++;
}
if (shift) {
if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false))
return false;
i++;
}
if (alt) {
if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], false))
return false;
i++;
}
if (!FillKeyboardInput(key, &input[i], false))
return false;
i++;
if (!FillKeyboardInput(key, &input[i], true))
return false;
i++;
if (alt) {
if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], true))
return false;
i++;
}
if (shift) {
if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true))
return false;
i++;
}
if (control) {
if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true))
return false;
i++;
}
if (::SendInput(i, input, sizeof(INPUT)) != i)
return false;
if (dispatcher.get())
dispatcher->AddRef();
return true;
}
bool SendMouseMoveImpl(long screen_x,
long screen_y,
const base::Closure& task) {
POINT current_pos;
::GetCursorPos(¤t_pos);
if (screen_x == current_pos.x && screen_y == current_pos.y) {
if (!task.is_null())
base::MessageLoop::current()->PostTask(FROM_HERE, task);
return true;
}
INPUT input = { 0 };
int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1;
LONG pixel_x = static_cast<LONG>(screen_x * (65535.0f / screen_width));
LONG pixel_y = static_cast<LONG>(screen_y * (65535.0f / screen_height));
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
input.mi.dx = pixel_x;
input.mi.dy = pixel_y;
scoped_refptr<InputDispatcher> dispatcher(
!task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL);
if (!::SendInput(1, &input, sizeof(INPUT)))
return false;
if (dispatcher.get())
dispatcher->AddRef();
return true;
}
bool SendMouseEventsImpl(MouseButton type, int state,
const base::Closure& task) {
DWORD down_flags = MOUSEEVENTF_ABSOLUTE;
DWORD up_flags = MOUSEEVENTF_ABSOLUTE;
UINT last_event;
switch (type) {
case LEFT:
down_flags |= MOUSEEVENTF_LEFTDOWN;
up_flags |= MOUSEEVENTF_LEFTUP;
last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
break;
case MIDDLE:
down_flags |= MOUSEEVENTF_MIDDLEDOWN;
up_flags |= MOUSEEVENTF_MIDDLEUP;
last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
break;
case RIGHT:
down_flags |= MOUSEEVENTF_RIGHTDOWN;
up_flags |= MOUSEEVENTF_RIGHTUP;
last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
break;
default:
NOTREACHED();
return false;
}
scoped_refptr<InputDispatcher> dispatcher(
!task.is_null() ? new InputDispatcher(task, last_event) : NULL);
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dwFlags = down_flags;
if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT)))
return false;
input.mi.dwFlags = up_flags;
if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT)))
return false;
if (dispatcher.get())
dispatcher->AddRef();
return true;
}
}
}