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 #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