1 // Copyright (c) 2012 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/aura/test/event_generator.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "ui/aura/client/screen_position_client.h"
11 #include "ui/aura/root_window.h"
12 #include "ui/events/event.h"
13 #include "ui/events/event_utils.h"
14 #include "ui/gfx/vector2d_conversions.h"
18 #include "ui/base/x/x11_util.h"
22 #include "ui/events/keycodes/keyboard_code_conversion.h"
29 void DummyCallback(ui::EventType, const gfx::Vector2dF&) {
32 class DefaultEventGeneratorDelegate : public EventGeneratorDelegate {
34 explicit DefaultEventGeneratorDelegate(Window* root_window)
35 : root_window_(root_window) {}
36 virtual ~DefaultEventGeneratorDelegate() {}
38 // EventGeneratorDelegate overrides:
39 virtual RootWindow* GetRootWindowAt(const gfx::Point& point) const OVERRIDE {
40 return root_window_->GetDispatcher();
43 virtual client::ScreenPositionClient* GetScreenPositionClient(
44 const aura::Window* window) const OVERRIDE {
51 DISALLOW_COPY_AND_ASSIGN(DefaultEventGeneratorDelegate);
54 class TestKeyEvent : public ui::KeyEvent {
56 TestKeyEvent(const base::NativeEvent& native_event, int flags, bool is_char)
57 : KeyEvent(native_event, is_char) {
62 class TestTouchEvent : public ui::TouchEvent {
64 TestTouchEvent(ui::EventType type,
65 const gfx::Point& root_location,
68 : TouchEvent(type, root_location, flags, touch_id, ui::EventTimeForNow(),
69 1.0f, 1.0f, 1.0f, 1.0f) {
73 DISALLOW_COPY_AND_ASSIGN(TestTouchEvent);
76 const int kAllButtonMask = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON;
80 EventGenerator::EventGenerator(Window* root_window)
81 : delegate_(new DefaultEventGeneratorDelegate(root_window)),
82 current_root_window_(delegate_->GetRootWindowAt(current_location_)),
88 EventGenerator::EventGenerator(Window* root_window, const gfx::Point& point)
89 : delegate_(new DefaultEventGeneratorDelegate(root_window)),
90 current_location_(point),
91 current_root_window_(delegate_->GetRootWindowAt(current_location_)),
97 EventGenerator::EventGenerator(Window* root_window, Window* window)
98 : delegate_(new DefaultEventGeneratorDelegate(root_window)),
99 current_location_(CenterOfWindow(window)),
100 current_root_window_(delegate_->GetRootWindowAt(current_location_)),
106 EventGenerator::EventGenerator(EventGeneratorDelegate* delegate)
107 : delegate_(delegate),
108 current_root_window_(delegate_->GetRootWindowAt(current_location_)),
114 EventGenerator::~EventGenerator() {
115 for (std::list<ui::Event*>::iterator i = pending_events_.begin();
116 i != pending_events_.end(); ++i)
118 pending_events_.clear();
121 void EventGenerator::PressLeftButton() {
122 PressButton(ui::EF_LEFT_MOUSE_BUTTON);
125 void EventGenerator::ReleaseLeftButton() {
126 ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON);
129 void EventGenerator::ClickLeftButton() {
134 void EventGenerator::DoubleClickLeftButton() {
135 flags_ |= ui::EF_IS_DOUBLE_CLICK;
137 flags_ ^= ui::EF_IS_DOUBLE_CLICK;
141 void EventGenerator::PressRightButton() {
142 PressButton(ui::EF_RIGHT_MOUSE_BUTTON);
145 void EventGenerator::ReleaseRightButton() {
146 ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON);
149 void EventGenerator::SendMouseExit() {
150 gfx::Point exit_location(current_location_);
151 ConvertPointToTarget(current_root_window_, &exit_location);
152 ui::MouseEvent mouseev(ui::ET_MOUSE_EXITED, exit_location, exit_location,
157 void EventGenerator::MoveMouseToInHost(const gfx::Point& point_in_host) {
158 const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
159 ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
160 ui::MouseEvent mouseev(event_type, point_in_host, point_in_host, flags_);
163 current_location_ = point_in_host;
164 current_root_window_->ConvertPointFromHost(¤t_location_);
167 void EventGenerator::MoveMouseTo(const gfx::Point& point_in_screen,
170 const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
171 ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
173 gfx::Vector2dF diff(point_in_screen - current_location_);
174 for (float i = 1; i <= count; i++) {
175 gfx::Vector2dF step(diff);
176 step.Scale(i / count);
177 gfx::Point move_point = current_location_ + gfx::ToRoundedVector2d(step);
179 UpdateCurrentRootWindow(move_point);
180 ConvertPointToTarget(current_root_window_, &move_point);
181 ui::MouseEvent mouseev(event_type, move_point, move_point, flags_);
184 current_location_ = point_in_screen;
187 void EventGenerator::MoveMouseRelativeTo(const Window* window,
188 const gfx::Point& point_in_parent) {
189 gfx::Point point(point_in_parent);
190 ConvertPointFromTarget(window, &point);
194 void EventGenerator::DragMouseTo(const gfx::Point& point) {
200 void EventGenerator::MoveMouseToCenterOf(Window* window) {
201 MoveMouseTo(CenterOfWindow(window));
204 void EventGenerator::PressTouch() {
208 void EventGenerator::PressTouchId(int touch_id) {
209 TestTouchEvent touchev(
210 ui::ET_TOUCH_PRESSED, GetLocationInCurrentRoot(), touch_id, flags_);
214 void EventGenerator::MoveTouch(const gfx::Point& point) {
215 MoveTouchId(point, 0);
218 void EventGenerator::MoveTouchId(const gfx::Point& point, int touch_id) {
219 current_location_ = point;
220 TestTouchEvent touchev(
221 ui::ET_TOUCH_MOVED, GetLocationInCurrentRoot(), touch_id, flags_);
225 UpdateCurrentRootWindow(point);
228 void EventGenerator::ReleaseTouch() {
232 void EventGenerator::ReleaseTouchId(int touch_id) {
233 TestTouchEvent touchev(
234 ui::ET_TOUCH_RELEASED, GetLocationInCurrentRoot(), touch_id, flags_);
238 void EventGenerator::PressMoveAndReleaseTouchTo(const gfx::Point& point) {
244 void EventGenerator::PressMoveAndReleaseTouchToCenterOf(Window* window) {
245 PressMoveAndReleaseTouchTo(CenterOfWindow(window));
248 void EventGenerator::GestureTapAt(const gfx::Point& location) {
249 const int kTouchId = 2;
250 ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
253 ui::EventTimeForNow());
256 ui::TouchEvent release(
257 ui::ET_TOUCH_RELEASED, location, kTouchId,
258 press.time_stamp() + base::TimeDelta::FromMilliseconds(50));
262 void EventGenerator::GestureTapDownAndUp(const gfx::Point& location) {
263 const int kTouchId = 3;
264 ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
267 ui::EventTimeForNow());
270 ui::TouchEvent release(
271 ui::ET_TOUCH_RELEASED, location, kTouchId,
272 press.time_stamp() + base::TimeDelta::FromMilliseconds(1000));
276 void EventGenerator::GestureScrollSequence(const gfx::Point& start,
277 const gfx::Point& end,
278 const base::TimeDelta& step_delay,
280 GestureScrollSequenceWithCallback(start, end, step_delay, steps,
281 base::Bind(&DummyCallback));
284 void EventGenerator::GestureScrollSequenceWithCallback(
285 const gfx::Point& start,
286 const gfx::Point& end,
287 const base::TimeDelta& step_delay,
289 const ScrollStepCallback& callback) {
290 const int kTouchId = 5;
291 base::TimeDelta timestamp = ui::EventTimeForNow();
292 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, start, kTouchId, timestamp);
295 callback.Run(ui::ET_GESTURE_SCROLL_BEGIN, gfx::Vector2dF());
297 int dx = (end.x() - start.x()) / steps;
298 int dy = (end.y() - start.y()) / steps;
299 gfx::Point location = start;
300 for (int i = 0; i < steps; ++i) {
301 location.Offset(dx, dy);
302 timestamp += step_delay;
303 ui::TouchEvent move(ui::ET_TOUCH_MOVED, location, kTouchId, timestamp);
305 callback.Run(ui::ET_GESTURE_SCROLL_UPDATE, gfx::Vector2dF(dx, dy));
308 ui::TouchEvent release(ui::ET_TOUCH_RELEASED, end, kTouchId, timestamp);
311 callback.Run(ui::ET_GESTURE_SCROLL_END, gfx::Vector2dF());
314 void EventGenerator::GestureMultiFingerScroll(int count,
315 const gfx::Point start[],
316 int event_separation_time_ms,
320 const int kMaxTouchPoints = 10;
321 int delays[kMaxTouchPoints] = { 0 };
322 GestureMultiFingerScrollWithDelays(
323 count, start, delays, event_separation_time_ms, steps, move_x, move_y);
326 void EventGenerator::GestureMultiFingerScrollWithDelays(
328 const gfx::Point start[],
329 const int delay_adding_finger_ms[],
330 int event_separation_time_ms,
334 const int kMaxTouchPoints = 10;
335 gfx::Point points[kMaxTouchPoints];
336 CHECK_LE(count, kMaxTouchPoints);
339 int delta_x = move_x / steps;
340 int delta_y = move_y / steps;
342 for (int i = 0; i < count; ++i) {
343 points[i] = start[i];
346 base::TimeDelta press_time_first = ui::EventTimeForNow();
347 base::TimeDelta press_time[kMaxTouchPoints];
348 bool pressed[kMaxTouchPoints];
349 for (int i = 0; i < count; ++i) {
351 press_time[i] = press_time_first +
352 base::TimeDelta::FromMilliseconds(delay_adding_finger_ms[i]);
356 for (int step = 0; step < steps; ++step) {
357 base::TimeDelta move_time = press_time_first +
358 base::TimeDelta::FromMilliseconds(event_separation_time_ms * step);
360 while (last_id < count &&
362 move_time >= press_time[last_id]) {
363 ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
366 press_time[last_id]);
368 pressed[last_id] = true;
372 for (int i = 0; i < count; ++i) {
373 points[i].Offset(delta_x, delta_y);
376 ui::TouchEvent move(ui::ET_TOUCH_MOVED, points[i], i, move_time);
381 base::TimeDelta release_time = press_time_first +
382 base::TimeDelta::FromMilliseconds(event_separation_time_ms * steps);
383 for (int i = 0; i < last_id; ++i) {
384 ui::TouchEvent release(
385 ui::ET_TOUCH_RELEASED, points[i], i, release_time);
390 void EventGenerator::ScrollSequence(const gfx::Point& start,
391 const base::TimeDelta& step_delay,
396 base::TimeDelta timestamp = base::TimeDelta::FromInternalValue(
397 base::TimeTicks::Now().ToInternalValue());
398 ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL,
405 Dispatch(&fling_cancel);
407 float dx = x_offset / steps;
408 float dy = y_offset / steps;
409 for (int i = 0; i < steps; ++i) {
410 timestamp += step_delay;
411 ui::ScrollEvent move(ui::ET_SCROLL,
421 ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START,
428 Dispatch(&fling_start);
431 void EventGenerator::ScrollSequence(const gfx::Point& start,
432 const base::TimeDelta& step_delay,
433 const std::vector<gfx::Point>& offsets,
435 int steps = offsets.size();
436 base::TimeDelta timestamp = ui::EventTimeForNow();
437 ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL,
444 Dispatch(&fling_cancel);
446 for (int i = 0; i < steps; ++i) {
447 timestamp += step_delay;
448 ui::ScrollEvent scroll(ui::ET_SCROLL,
452 offsets[i].x(), offsets[i].y(),
453 offsets[i].x(), offsets[i].y(),
458 ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START,
462 offsets[steps - 1].x(), offsets[steps - 1].y(),
463 offsets[steps - 1].x(), offsets[steps - 1].y(),
465 Dispatch(&fling_start);
468 void EventGenerator::PressKey(ui::KeyboardCode key_code, int flags) {
469 DispatchKeyEvent(true, key_code, flags);
472 void EventGenerator::ReleaseKey(ui::KeyboardCode key_code, int flags) {
473 DispatchKeyEvent(false, key_code, flags);
476 void EventGenerator::Dispatch(ui::Event* event) {
477 DoDispatchEvent(event, async_);
480 void EventGenerator::DispatchKeyEvent(bool is_press,
481 ui::KeyboardCode key_code,
484 UINT key_press = WM_KEYDOWN;
485 uint16 character = ui::GetCharacterFromKeyCode(key_code, flags);
486 if (is_press && character) {
487 MSG native_event = { NULL, WM_KEYDOWN, key_code, 0 };
488 TestKeyEvent keyev(native_event, flags, false);
490 // On Windows, WM_KEYDOWN event is followed by WM_CHAR with a character
491 // if the key event cooresponds to a real character.
493 key_code = static_cast<ui::KeyboardCode>(character);
496 { NULL, (is_press ? key_press : WM_KEYUP), key_code, 0 };
497 TestKeyEvent keyev(native_event, flags, key_press == WM_CHAR);
499 ui::EventType type = is_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED;
500 ui::KeyEvent keyev(type, key_code, flags, false);
505 void EventGenerator::UpdateCurrentRootWindow(const gfx::Point& point) {
506 current_root_window_ = delegate_->GetRootWindowAt(point);
509 void EventGenerator::PressButton(int flag) {
510 if (!(flags_ & flag)) {
512 grab_ = flags_ & kAllButtonMask;
513 gfx::Point location = GetLocationInCurrentRoot();
514 ui::MouseEvent mouseev(ui::ET_MOUSE_PRESSED, location, location, flags_);
519 void EventGenerator::ReleaseButton(int flag) {
521 gfx::Point location = GetLocationInCurrentRoot();
522 ui::MouseEvent mouseev(ui::ET_MOUSE_RELEASED, location,
527 grab_ = flags_ & kAllButtonMask;
530 void EventGenerator::ConvertPointFromTarget(const aura::Window* target,
531 gfx::Point* point) const {
533 aura::client::ScreenPositionClient* client =
534 delegate_->GetScreenPositionClient(target);
536 client->ConvertPointToScreen(target, point);
538 aura::Window::ConvertPointToTarget(target, target->GetRootWindow(), point);
541 void EventGenerator::ConvertPointToTarget(const aura::Window* target,
542 gfx::Point* point) const {
544 aura::client::ScreenPositionClient* client =
545 delegate_->GetScreenPositionClient(target);
547 client->ConvertPointFromScreen(target, point);
549 aura::Window::ConvertPointToTarget(target->GetRootWindow(), target, point);
552 gfx::Point EventGenerator::GetLocationInCurrentRoot() const {
553 gfx::Point p(current_location_);
554 ConvertPointToTarget(current_root_window_, &p);
558 gfx::Point EventGenerator::CenterOfWindow(const Window* window) const {
559 gfx::Point center = gfx::Rect(window->bounds().size()).CenterPoint();
560 ConvertPointFromTarget(window, ¢er);
564 void EventGenerator::DoDispatchEvent(ui::Event* event, bool async) {
566 ui::Event* pending_event;
567 if (event->IsKeyEvent()) {
568 pending_event = new ui::KeyEvent(*static_cast<ui::KeyEvent*>(event));
569 } else if (event->IsMouseEvent()) {
570 pending_event = new ui::MouseEvent(*static_cast<ui::MouseEvent*>(event));
571 } else if (event->IsTouchEvent()) {
572 pending_event = new ui::TouchEvent(*static_cast<ui::TouchEvent*>(event));
573 } else if (event->IsScrollEvent()) {
575 new ui::ScrollEvent(*static_cast<ui::ScrollEvent*>(event));
577 NOTREACHED() << "Invalid event type";
580 if (pending_events_.empty()) {
581 base::MessageLoopProxy::current()->PostTask(
583 base::Bind(&EventGenerator::DispatchNextPendingEvent,
584 base::Unretained(this)));
586 pending_events_.push_back(pending_event);
588 RootWindowHostDelegate* root_window_host_delegate =
589 current_root_window_->AsRootWindowHostDelegate();
590 if (event->IsKeyEvent()) {
591 root_window_host_delegate->OnHostKeyEvent(
592 static_cast<ui::KeyEvent*>(event));
593 } else if (event->IsMouseEvent()) {
594 root_window_host_delegate->OnHostMouseEvent(
595 static_cast<ui::MouseEvent*>(event));
596 } else if (event->IsTouchEvent()) {
597 root_window_host_delegate->OnHostTouchEvent(
598 static_cast<ui::TouchEvent*>(event));
599 } else if (event->IsScrollEvent()) {
600 root_window_host_delegate->OnHostScrollEvent(
601 static_cast<ui::ScrollEvent*>(event));
603 NOTREACHED() << "Invalid event type";
608 void EventGenerator::DispatchNextPendingEvent() {
609 DCHECK(!pending_events_.empty());
610 ui::Event* event = pending_events_.front();
611 DoDispatchEvent(event, false);
612 pending_events_.pop_front();
614 if (!pending_events_.empty()) {
615 base::MessageLoopProxy::current()->PostTask(
617 base::Bind(&EventGenerator::DispatchNextPendingEvent,
618 base::Unretained(this)));