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. 4 5 #include "ash/autoclick/autoclick_controller.h" 6 7 #include "ash/shell.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 19 namespace ash { 20 21 namespace { 22 23 // The threshold of mouse movement measured in DIP that will 24 // initiate a new autoclick. 25 const int kMovementThreshold = 20; 26 27 bool IsModifierKey(ui::KeyboardCode key_code) { 28 return key_code == ui::VKEY_SHIFT || 29 key_code == ui::VKEY_LSHIFT || 30 key_code == ui::VKEY_CONTROL || 31 key_code == ui::VKEY_LCONTROL || 32 key_code == ui::VKEY_RCONTROL || 33 key_code == ui::VKEY_MENU || 34 key_code == ui::VKEY_LMENU || 35 key_code == ui::VKEY_RMENU; 36 } 37 38 } // namespace 39 40 // static. 41 const int AutoclickController::kDefaultAutoclickDelayMs = 400; 42 43 class AutoclickControllerImpl : public AutoclickController, 44 public ui::EventHandler { 45 public: 46 AutoclickControllerImpl(); 47 virtual ~AutoclickControllerImpl(); 48 49 private: 50 // AutoclickController overrides: 51 virtual void SetEnabled(bool enabled) OVERRIDE; 52 virtual bool IsEnabled() const OVERRIDE; 53 virtual void SetAutoclickDelay(int delay_ms) OVERRIDE; 54 virtual int GetAutoclickDelay() const OVERRIDE; 55 56 // ui::EventHandler overrides: 57 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; 58 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; 59 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; 60 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; 61 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; 62 63 void InitClickTimer(); 64 65 void DoAutoclick(); 66 67 bool enabled_; 68 int delay_ms_; 69 int mouse_event_flags_; 70 scoped_ptr<base::Timer> autoclick_timer_; 71 // The position in screen coordinates used to determine 72 // the distance the mouse has moved. 73 gfx::Point anchor_location_; 74 75 DISALLOW_COPY_AND_ASSIGN(AutoclickControllerImpl); 76 }; 77 78 79 AutoclickControllerImpl::AutoclickControllerImpl() 80 : enabled_(false), 81 delay_ms_(kDefaultAutoclickDelayMs), 82 mouse_event_flags_(ui::EF_NONE), 83 anchor_location_(-kMovementThreshold, -kMovementThreshold) { 84 InitClickTimer(); 85 } 86 87 AutoclickControllerImpl::~AutoclickControllerImpl() { 88 } 89 90 void AutoclickControllerImpl::SetEnabled(bool enabled) { 91 if (enabled_ == enabled) 92 return; 93 enabled_ = enabled; 94 95 if (enabled_) { 96 Shell::GetInstance()->AddPreTargetHandler(this); 97 autoclick_timer_->Stop(); 98 } else { 99 Shell::GetInstance()->RemovePreTargetHandler(this); 100 } 101 } 102 103 bool AutoclickControllerImpl::IsEnabled() const { 104 return enabled_; 105 } 106 107 void AutoclickControllerImpl::SetAutoclickDelay(int delay_ms) { 108 delay_ms_ = delay_ms; 109 InitClickTimer(); 110 } 111 112 int AutoclickControllerImpl::GetAutoclickDelay() const { 113 return delay_ms_; 114 } 115 116 void AutoclickControllerImpl::InitClickTimer() { 117 autoclick_timer_.reset(new base::Timer( 118 FROM_HERE, 119 base::TimeDelta::FromMilliseconds(delay_ms_), 120 base::Bind(&AutoclickControllerImpl::DoAutoclick, 121 base::Unretained(this)), 122 false)); 123 } 124 125 void AutoclickControllerImpl::OnMouseEvent(ui::MouseEvent* event) { 126 if (event->type() == ui::ET_MOUSE_MOVED && 127 !(event->flags() & ui::EF_IS_SYNTHESIZED)) { 128 mouse_event_flags_ = event->flags(); 129 130 gfx::Point mouse_location = event->root_location(); 131 ash::wm::ConvertPointToScreen( 132 wm::GetRootWindowAt(mouse_location), 133 &mouse_location); 134 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(); 145 } 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(); 151 } 152 } 153 154 void AutoclickControllerImpl::OnKeyEvent(ui::KeyEvent* event) { 155 int modifier_mask = 156 ui::EF_SHIFT_DOWN | 157 ui::EF_CONTROL_DOWN | 158 ui::EF_ALT_DOWN | 159 ui::EF_COMMAND_DOWN | 160 ui::EF_EXTENDED; 161 int new_modifiers = event->flags() & modifier_mask; 162 mouse_event_flags_ = (mouse_event_flags_ & ~modifier_mask) | new_modifiers; 163 164 if (!IsModifierKey(event->key_code())) 165 autoclick_timer_->Stop(); 166 } 167 168 void AutoclickControllerImpl::OnTouchEvent(ui::TouchEvent* event) { 169 autoclick_timer_->Stop(); 170 } 171 172 void AutoclickControllerImpl::OnGestureEvent(ui::GestureEvent* event) { 173 autoclick_timer_->Stop(); 174 } 175 176 void AutoclickControllerImpl::OnScrollEvent(ui::ScrollEvent* event) { 177 autoclick_timer_->Stop(); 178 } 179 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."; 185 186 gfx::Point click_location(screen_location); 187 anchor_location_ = click_location; 188 wm::ConvertPointFromScreen(root_window, &click_location); 189 190 aura::WindowTreeHost* host = root_window->GetHost(); 191 host->ConvertPointToHost(&click_location); 192 193 ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, 194 click_location, 195 click_location, 196 mouse_event_flags_ | ui::EF_LEFT_MOUSE_BUTTON, 197 ui::EF_LEFT_MOUSE_BUTTON); 198 ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, 199 click_location, 200 click_location, 201 mouse_event_flags_ | ui::EF_LEFT_MOUSE_BUTTON, 202 ui::EF_LEFT_MOUSE_BUTTON); 203 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) 209 return; 210 } 211 212 // static. 213 AutoclickController* AutoclickController::CreateInstance() { 214 return new AutoclickControllerImpl(); 215 } 216 217 } // namespace ash 218