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 #include "ui/wm/core/coordinate_conversion.h" 19 20 namespace ash { 21 22 namespace { 23 24 // The threshold of mouse movement measured in DIP that will 25 // initiate a new autoclick. 26 const int kMovementThreshold = 20; 27 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; 37 } 38 39 } // namespace 40 41 // static. 42 const int AutoclickController::kDefaultAutoclickDelayMs = 400; 43 44 class AutoclickControllerImpl : public AutoclickController, 45 public ui::EventHandler { 46 public: 47 AutoclickControllerImpl(); 48 virtual ~AutoclickControllerImpl(); 49 50 private: 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; 56 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; 63 64 void InitClickTimer(); 65 66 void DoAutoclick(); 67 68 bool enabled_; 69 int delay_ms_; 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_; 75 76 DISALLOW_COPY_AND_ASSIGN(AutoclickControllerImpl); 77 }; 78 79 80 AutoclickControllerImpl::AutoclickControllerImpl() 81 : enabled_(false), 82 delay_ms_(kDefaultAutoclickDelayMs), 83 mouse_event_flags_(ui::EF_NONE), 84 anchor_location_(-kMovementThreshold, -kMovementThreshold) { 85 InitClickTimer(); 86 } 87 88 AutoclickControllerImpl::~AutoclickControllerImpl() { 89 } 90 91 void AutoclickControllerImpl::SetEnabled(bool enabled) { 92 if (enabled_ == enabled) 93 return; 94 enabled_ = enabled; 95 96 if (enabled_) { 97 Shell::GetInstance()->AddPreTargetHandler(this); 98 autoclick_timer_->Stop(); 99 } else { 100 Shell::GetInstance()->RemovePreTargetHandler(this); 101 } 102 } 103 104 bool AutoclickControllerImpl::IsEnabled() const { 105 return enabled_; 106 } 107 108 void AutoclickControllerImpl::SetAutoclickDelay(int delay_ms) { 109 delay_ms_ = delay_ms; 110 InitClickTimer(); 111 } 112 113 int AutoclickControllerImpl::GetAutoclickDelay() const { 114 return delay_ms_; 115 } 116 117 void AutoclickControllerImpl::InitClickTimer() { 118 autoclick_timer_.reset(new base::Timer( 119 FROM_HERE, 120 base::TimeDelta::FromMilliseconds(delay_ms_), 121 base::Bind(&AutoclickControllerImpl::DoAutoclick, 122 base::Unretained(this)), 123 false)); 124 } 125 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(); 130 131 gfx::Point mouse_location = event->root_location(); 132 ::wm::ConvertPointToScreen(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