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_event_dispatcher.h"
12 #include "ui/events/event.h"
13 #include "ui/events/event_constants.h"
14 #include "ui/events/event_handler.h"
15 #include "ui/gfx/point.h"
16 #include "ui/gfx/vector2d.h"
22 // The threshold of mouse movement measured in DIP that will
23 // initiate a new autoclick.
24 const int kMovementThreshold
= 20;
26 bool IsModifierKey(ui::KeyboardCode key_code
) {
27 return key_code
== ui::VKEY_SHIFT
||
28 key_code
== ui::VKEY_LSHIFT
||
29 key_code
== ui::VKEY_CONTROL
||
30 key_code
== ui::VKEY_LCONTROL
||
31 key_code
== ui::VKEY_RCONTROL
||
32 key_code
== ui::VKEY_MENU
||
33 key_code
== ui::VKEY_LMENU
||
34 key_code
== ui::VKEY_RMENU
;
40 const int AutoclickController::kDefaultAutoclickDelayMs
= 400;
42 class AutoclickControllerImpl
: public AutoclickController
,
43 public ui::EventHandler
{
45 AutoclickControllerImpl();
46 virtual ~AutoclickControllerImpl();
49 // AutoclickController overrides:
50 virtual void SetEnabled(bool enabled
) OVERRIDE
;
51 virtual bool IsEnabled() const OVERRIDE
;
52 virtual void SetAutoclickDelay(int delay_ms
) OVERRIDE
;
53 virtual int GetAutoclickDelay() const OVERRIDE
;
55 // ui::EventHandler overrides:
56 virtual void OnMouseEvent(ui::MouseEvent
* event
) OVERRIDE
;
57 virtual void OnKeyEvent(ui::KeyEvent
* event
) OVERRIDE
;
58 virtual void OnTouchEvent(ui::TouchEvent
* event
) OVERRIDE
;
59 virtual void OnGestureEvent(ui::GestureEvent
* event
) OVERRIDE
;
60 virtual void OnScrollEvent(ui::ScrollEvent
* event
) OVERRIDE
;
62 void InitClickTimer();
68 int mouse_event_flags_
;
69 scoped_ptr
<base::Timer
> autoclick_timer_
;
70 // The position in screen coordinates used to determine
71 // the distance the mouse has moved.
72 gfx::Point anchor_location_
;
74 DISALLOW_COPY_AND_ASSIGN(AutoclickControllerImpl
);
78 AutoclickControllerImpl::AutoclickControllerImpl()
80 delay_ms_(kDefaultAutoclickDelayMs
),
81 mouse_event_flags_(ui::EF_NONE
),
82 anchor_location_(-kMovementThreshold
, -kMovementThreshold
) {
86 AutoclickControllerImpl::~AutoclickControllerImpl() {
89 void AutoclickControllerImpl::SetEnabled(bool enabled
) {
90 if (enabled_
== enabled
)
95 Shell::GetInstance()->AddPreTargetHandler(this);
96 autoclick_timer_
->Stop();
98 Shell::GetInstance()->RemovePreTargetHandler(this);
102 bool AutoclickControllerImpl::IsEnabled() const {
106 void AutoclickControllerImpl::SetAutoclickDelay(int delay_ms
) {
107 delay_ms_
= delay_ms
;
111 int AutoclickControllerImpl::GetAutoclickDelay() const {
115 void AutoclickControllerImpl::InitClickTimer() {
116 autoclick_timer_
.reset(new base::Timer(
118 base::TimeDelta::FromMilliseconds(delay_ms_
),
119 base::Bind(&AutoclickControllerImpl::DoAutoclick
,
120 base::Unretained(this)),
124 void AutoclickControllerImpl::OnMouseEvent(ui::MouseEvent
* event
) {
125 if (event
->type() == ui::ET_MOUSE_MOVED
&&
126 !(event
->flags() & ui::EF_IS_SYNTHESIZED
)) {
127 mouse_event_flags_
= event
->flags();
129 gfx::Point mouse_location
= event
->root_location();
130 ash::wm::ConvertPointToScreen(
131 wm::GetRootWindowAt(mouse_location
),
134 // The distance between the mouse location and the anchor location
135 // must exceed a certain threshold to initiate a new autoclick countdown.
136 // This ensures that mouse jitter caused by poor motor control does not
137 // 1. initiate an unwanted autoclick from rest
138 // 2. prevent the autoclick from ever occuring when the mouse
139 // arrives at the target.
140 gfx::Vector2d delta
= mouse_location
- anchor_location_
;
141 if (delta
.LengthSquared() >= kMovementThreshold
* kMovementThreshold
) {
142 anchor_location_
= event
->root_location();
143 autoclick_timer_
->Reset();
145 } else if (event
->type() == ui::ET_MOUSE_PRESSED
) {
146 autoclick_timer_
->Stop();
147 } else if (event
->type() == ui::ET_MOUSEWHEEL
&&
148 autoclick_timer_
->IsRunning()) {
149 autoclick_timer_
->Reset();
153 void AutoclickControllerImpl::OnKeyEvent(ui::KeyEvent
* event
) {
156 ui::EF_CONTROL_DOWN
|
158 ui::EF_COMMAND_DOWN
|
160 int new_modifiers
= event
->flags() & modifier_mask
;
161 mouse_event_flags_
= (mouse_event_flags_
& ~modifier_mask
) | new_modifiers
;
163 if (!IsModifierKey(event
->key_code()))
164 autoclick_timer_
->Stop();
167 void AutoclickControllerImpl::OnTouchEvent(ui::TouchEvent
* event
) {
168 autoclick_timer_
->Stop();
171 void AutoclickControllerImpl::OnGestureEvent(ui::GestureEvent
* event
) {
172 autoclick_timer_
->Stop();
175 void AutoclickControllerImpl::OnScrollEvent(ui::ScrollEvent
* event
) {
176 autoclick_timer_
->Stop();
179 void AutoclickControllerImpl::DoAutoclick() {
180 gfx::Point screen_location
=
181 aura::Env::GetInstance()->last_mouse_location();
182 aura::Window
* root_window
= wm::GetRootWindowAt(screen_location
);
183 DCHECK(root_window
) << "Root window not found while attempting autoclick.";
185 gfx::Point
click_location(screen_location
);
186 anchor_location_
= click_location
;
187 wm::ConvertPointFromScreen(root_window
, &click_location
);
189 aura::WindowEventDispatcher
* dispatcher
= root_window
->GetDispatcher();
190 dispatcher
->host()->ConvertPointToHost(&click_location
);
192 ui::MouseEvent
press_event(ui::ET_MOUSE_PRESSED
,
195 mouse_event_flags_
| ui::EF_LEFT_MOUSE_BUTTON
,
196 ui::EF_LEFT_MOUSE_BUTTON
);
197 ui::MouseEvent
release_event(ui::ET_MOUSE_RELEASED
,
200 mouse_event_flags_
| ui::EF_LEFT_MOUSE_BUTTON
,
201 ui::EF_LEFT_MOUSE_BUTTON
);
203 ui::EventDispatchDetails details
=
204 dispatcher
->OnEventFromSource(&press_event
);
205 if (!details
.dispatcher_destroyed
)
206 details
= dispatcher
->OnEventFromSource(&release_event
);
207 if (details
.dispatcher_destroyed
)
212 AutoclickController
* AutoclickController::CreateInstance() {
213 return new AutoclickControllerImpl();