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 "ash/autoclick/autoclick_controller.h"
8 #include "ash/wm/coordinate_conversion.h"
9 #include "base/timer/timer.h"
10 #include "ui/aura/env.h"
11 #include "ui/aura/window_tree_host.h"
12 #include "ui/events/event.h"
13 #include "ui/events/event_constants.h"
14 #include "ui/events/event_handler.h"
15 #include "ui/events/event_processor.h"
16 #include "ui/gfx/point.h"
17 #include "ui/gfx/vector2d.h"
18 #include "ui/wm/core/coordinate_conversion.h"
24 // The threshold of mouse movement measured in DIP that will
25 // initiate a new autoclick.
26 const int kMovementThreshold
= 20;
28 bool IsModifierKey(ui::KeyboardCode key_code
) {
29 return key_code
== ui::VKEY_SHIFT
||
30 key_code
== ui::VKEY_LSHIFT
||
31 key_code
== ui::VKEY_CONTROL
||
32 key_code
== ui::VKEY_LCONTROL
||
33 key_code
== ui::VKEY_RCONTROL
||
34 key_code
== ui::VKEY_MENU
||
35 key_code
== ui::VKEY_LMENU
||
36 key_code
== ui::VKEY_RMENU
;
42 const int AutoclickController::kDefaultAutoclickDelayMs
= 400;
44 class AutoclickControllerImpl
: public AutoclickController
,
45 public ui::EventHandler
{
47 AutoclickControllerImpl();
48 virtual ~AutoclickControllerImpl();
51 // AutoclickController overrides:
52 virtual void SetEnabled(bool enabled
) OVERRIDE
;
53 virtual bool IsEnabled() const OVERRIDE
;
54 virtual void SetAutoclickDelay(int delay_ms
) OVERRIDE
;
55 virtual int GetAutoclickDelay() const OVERRIDE
;
57 // ui::EventHandler overrides:
58 virtual void OnMouseEvent(ui::MouseEvent
* event
) OVERRIDE
;
59 virtual void OnKeyEvent(ui::KeyEvent
* event
) OVERRIDE
;
60 virtual void OnTouchEvent(ui::TouchEvent
* event
) OVERRIDE
;
61 virtual void OnGestureEvent(ui::GestureEvent
* event
) OVERRIDE
;
62 virtual void OnScrollEvent(ui::ScrollEvent
* event
) OVERRIDE
;
64 void InitClickTimer();
70 int mouse_event_flags_
;
71 scoped_ptr
<base::Timer
> autoclick_timer_
;
72 // The position in screen coordinates used to determine
73 // the distance the mouse has moved.
74 gfx::Point anchor_location_
;
76 DISALLOW_COPY_AND_ASSIGN(AutoclickControllerImpl
);
80 AutoclickControllerImpl::AutoclickControllerImpl()
82 delay_ms_(kDefaultAutoclickDelayMs
),
83 mouse_event_flags_(ui::EF_NONE
),
84 anchor_location_(-kMovementThreshold
, -kMovementThreshold
) {
88 AutoclickControllerImpl::~AutoclickControllerImpl() {
91 void AutoclickControllerImpl::SetEnabled(bool enabled
) {
92 if (enabled_
== enabled
)
97 Shell::GetInstance()->AddPreTargetHandler(this);
98 autoclick_timer_
->Stop();
100 Shell::GetInstance()->RemovePreTargetHandler(this);
104 bool AutoclickControllerImpl::IsEnabled() const {
108 void AutoclickControllerImpl::SetAutoclickDelay(int delay_ms
) {
109 delay_ms_
= delay_ms
;
113 int AutoclickControllerImpl::GetAutoclickDelay() const {
117 void AutoclickControllerImpl::InitClickTimer() {
118 autoclick_timer_
.reset(new base::Timer(
120 base::TimeDelta::FromMilliseconds(delay_ms_
),
121 base::Bind(&AutoclickControllerImpl::DoAutoclick
,
122 base::Unretained(this)),
126 void AutoclickControllerImpl::OnMouseEvent(ui::MouseEvent
* event
) {
127 if (event
->type() == ui::ET_MOUSE_MOVED
&&
128 !(event
->flags() & ui::EF_IS_SYNTHESIZED
)) {
129 mouse_event_flags_
= event
->flags();
131 gfx::Point mouse_location
= event
->root_location();
132 ::wm::ConvertPointToScreen(wm::GetRootWindowAt(mouse_location
),
135 // The distance between the mouse location and the anchor location
136 // must exceed a certain threshold to initiate a new autoclick countdown.
137 // This ensures that mouse jitter caused by poor motor control does not
138 // 1. initiate an unwanted autoclick from rest
139 // 2. prevent the autoclick from ever occuring when the mouse
140 // arrives at the target.
141 gfx::Vector2d delta
= mouse_location
- anchor_location_
;
142 if (delta
.LengthSquared() >= kMovementThreshold
* kMovementThreshold
) {
143 anchor_location_
= event
->root_location();
144 autoclick_timer_
->Reset();
146 } else if (event
->type() == ui::ET_MOUSE_PRESSED
) {
147 autoclick_timer_
->Stop();
148 } else if (event
->type() == ui::ET_MOUSEWHEEL
&&
149 autoclick_timer_
->IsRunning()) {
150 autoclick_timer_
->Reset();
154 void AutoclickControllerImpl::OnKeyEvent(ui::KeyEvent
* event
) {
157 ui::EF_CONTROL_DOWN
|
159 ui::EF_COMMAND_DOWN
|
161 int new_modifiers
= event
->flags() & modifier_mask
;
162 mouse_event_flags_
= (mouse_event_flags_
& ~modifier_mask
) | new_modifiers
;
164 if (!IsModifierKey(event
->key_code()))
165 autoclick_timer_
->Stop();
168 void AutoclickControllerImpl::OnTouchEvent(ui::TouchEvent
* event
) {
169 autoclick_timer_
->Stop();
172 void AutoclickControllerImpl::OnGestureEvent(ui::GestureEvent
* event
) {
173 autoclick_timer_
->Stop();
176 void AutoclickControllerImpl::OnScrollEvent(ui::ScrollEvent
* event
) {
177 autoclick_timer_
->Stop();
180 void AutoclickControllerImpl::DoAutoclick() {
181 gfx::Point screen_location
=
182 aura::Env::GetInstance()->last_mouse_location();
183 aura::Window
* root_window
= wm::GetRootWindowAt(screen_location
);
184 DCHECK(root_window
) << "Root window not found while attempting autoclick.";
186 gfx::Point
click_location(screen_location
);
187 anchor_location_
= click_location
;
188 ::wm::ConvertPointFromScreen(root_window
, &click_location
);
190 aura::WindowTreeHost
* host
= root_window
->GetHost();
191 host
->ConvertPointToHost(&click_location
);
193 ui::MouseEvent
press_event(ui::ET_MOUSE_PRESSED
,
196 mouse_event_flags_
| ui::EF_LEFT_MOUSE_BUTTON
,
197 ui::EF_LEFT_MOUSE_BUTTON
);
198 ui::MouseEvent
release_event(ui::ET_MOUSE_RELEASED
,
201 mouse_event_flags_
| ui::EF_LEFT_MOUSE_BUTTON
,
202 ui::EF_LEFT_MOUSE_BUTTON
);
204 ui::EventDispatchDetails details
=
205 host
->event_processor()->OnEventFromSource(&press_event
);
206 if (!details
.dispatcher_destroyed
)
207 details
= host
->event_processor()->OnEventFromSource(&release_event
);
208 if (details
.dispatcher_destroyed
)
213 AutoclickController
* AutoclickController::CreateInstance() {
214 return new AutoclickControllerImpl();