Home | History | Annotate | Download | only in autoclick
      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