Home | History | Annotate | Download | only in sticky_keys
      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/sticky_keys/sticky_keys_controller.h"
      6 
      7 #include "ash/sticky_keys/sticky_keys_overlay.h"
      8 #include "base/basictypes.h"
      9 #include "base/debug/stack_trace.h"
     10 #include "ui/aura/window.h"
     11 #include "ui/aura/window_tracker.h"
     12 #include "ui/aura/window_tree_host.h"
     13 #include "ui/events/event.h"
     14 #include "ui/events/event_processor.h"
     15 #include "ui/events/keycodes/keyboard_code_conversion.h"
     16 
     17 namespace ash {
     18 
     19 namespace {
     20 
     21 // Returns true if the type of mouse event should be modified by sticky keys.
     22 bool ShouldModifyMouseEvent(const ui::MouseEvent& event) {
     23   ui::EventType type = event.type();
     24   return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED ||
     25          type == ui::ET_MOUSEWHEEL;
     26 }
     27 
     28 // Handle the common tail of event rewriting.
     29 ui::EventRewriteStatus RewriteUpdate(bool consumed,
     30                                      bool released,
     31                                      int mod_down_flags,
     32                                      int* flags) {
     33   int changed_down_flags = mod_down_flags & ~*flags;
     34   *flags |= mod_down_flags;
     35   if (consumed)
     36     return ui::EVENT_REWRITE_DISCARD;
     37   if (released)
     38     return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
     39   if (changed_down_flags)
     40     return ui::EVENT_REWRITE_REWRITTEN;
     41   return ui::EVENT_REWRITE_CONTINUE;
     42 }
     43 
     44 }  // namespace
     45 
     46 ///////////////////////////////////////////////////////////////////////////////
     47 //  StickyKeys
     48 StickyKeysController::StickyKeysController()
     49     : enabled_(false),
     50       mod3_enabled_(false),
     51       altgr_enabled_(false) {
     52 }
     53 
     54 StickyKeysController::~StickyKeysController() {
     55 }
     56 
     57 void StickyKeysController::Enable(bool enabled) {
     58   if (enabled_ != enabled) {
     59     enabled_ = enabled;
     60 
     61     // Reset key handlers when activating sticky keys to ensure all
     62     // the handlers' states are reset.
     63     if (enabled_) {
     64       shift_sticky_key_.reset(new StickyKeysHandler(ui::EF_SHIFT_DOWN));
     65       alt_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALT_DOWN));
     66       altgr_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALTGR_DOWN));
     67       ctrl_sticky_key_.reset(new StickyKeysHandler(ui::EF_CONTROL_DOWN));
     68       mod3_sticky_key_.reset(new StickyKeysHandler(ui::EF_MOD3_DOWN));
     69 
     70       overlay_.reset(new StickyKeysOverlay());
     71       overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
     72       overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
     73     } else if (overlay_) {
     74       overlay_->Show(false);
     75     }
     76   }
     77 }
     78 
     79 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
     80                                                bool altgr_enabled) {
     81   mod3_enabled_ = mod3_enabled;
     82   altgr_enabled_ = altgr_enabled;
     83   if (overlay_) {
     84     overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
     85     overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
     86   }
     87 }
     88 
     89 bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
     90                                           ui::KeyboardCode key_code,
     91                                           int* mod_down_flags,
     92                                           bool* released) {
     93   return shift_sticky_key_->HandleKeyEvent(
     94              event, key_code, mod_down_flags, released) ||
     95          alt_sticky_key_->HandleKeyEvent(
     96              event, key_code, mod_down_flags, released) ||
     97          altgr_sticky_key_->HandleKeyEvent(
     98              event, key_code, mod_down_flags, released) ||
     99          ctrl_sticky_key_->HandleKeyEvent(
    100              event, key_code, mod_down_flags, released) ||
    101          mod3_sticky_key_->HandleKeyEvent(
    102              event, key_code, mod_down_flags, released);
    103 }
    104 
    105 bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
    106                                             int* mod_down_flags,
    107                                             bool* released) {
    108   return shift_sticky_key_->HandleMouseEvent(
    109              event, mod_down_flags, released) ||
    110          alt_sticky_key_->HandleMouseEvent(
    111              event, mod_down_flags, released) ||
    112          altgr_sticky_key_->HandleMouseEvent(
    113              event, mod_down_flags, released) ||
    114          ctrl_sticky_key_->HandleMouseEvent(
    115              event, mod_down_flags, released) ||
    116          mod3_sticky_key_->HandleMouseEvent(
    117              event, mod_down_flags, released);
    118 }
    119 
    120 bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
    121                                              int* mod_down_flags,
    122                                              bool* released) {
    123   return shift_sticky_key_->HandleScrollEvent(
    124              event, mod_down_flags, released) ||
    125          alt_sticky_key_->HandleScrollEvent(
    126              event, mod_down_flags, released) ||
    127          altgr_sticky_key_->HandleScrollEvent(
    128              event, mod_down_flags, released) ||
    129          ctrl_sticky_key_->HandleScrollEvent(
    130              event, mod_down_flags, released) ||
    131          mod3_sticky_key_->HandleScrollEvent(
    132              event, mod_down_flags, released);
    133 }
    134 
    135 ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
    136     const ui::KeyEvent& event,
    137     ui::KeyboardCode key_code,
    138     int* flags) {
    139   if (!enabled_)
    140     return ui::EVENT_REWRITE_CONTINUE;
    141   int mod_down_flags = 0;
    142   bool released = false;
    143   bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
    144   UpdateOverlay();
    145   return RewriteUpdate(consumed, released, mod_down_flags, flags);
    146 }
    147 
    148 ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
    149     const ui::MouseEvent& event,
    150     int* flags) {
    151   if (!enabled_)
    152     return ui::EVENT_REWRITE_CONTINUE;
    153   int mod_down_flags = 0;
    154   bool released = false;
    155   bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
    156   UpdateOverlay();
    157   return RewriteUpdate(consumed, released, mod_down_flags, flags);
    158 }
    159 
    160 ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
    161     const ui::ScrollEvent& event,
    162     int* flags) {
    163   if (!enabled_)
    164     return ui::EVENT_REWRITE_CONTINUE;
    165   int mod_down_flags = 0;
    166   bool released = false;
    167   bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
    168   UpdateOverlay();
    169   return RewriteUpdate(consumed, released, mod_down_flags, flags);
    170 }
    171 
    172 ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
    173     scoped_ptr<ui::Event>* new_event) {
    174   DCHECK(new_event);
    175   new_event->reset();
    176   int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) +
    177                   alt_sticky_key_->GetModifierUpEvent(new_event) +
    178                   altgr_sticky_key_->GetModifierUpEvent(new_event) +
    179                   ctrl_sticky_key_->GetModifierUpEvent(new_event) +
    180                   mod3_sticky_key_->GetModifierUpEvent(new_event);
    181   if (!new_event)
    182     return ui::EVENT_REWRITE_CONTINUE;
    183   if (remaining)
    184     return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
    185   return ui::EVENT_REWRITE_REWRITTEN;
    186 }
    187 
    188 void StickyKeysController::UpdateOverlay() {
    189   overlay_->SetModifierKeyState(
    190       ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
    191   overlay_->SetModifierKeyState(
    192       ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
    193   overlay_->SetModifierKeyState(
    194       ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
    195   overlay_->SetModifierKeyState(
    196       ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
    197   overlay_->SetModifierKeyState(
    198       ui::EF_MOD3_DOWN, mod3_sticky_key_->current_state());
    199 
    200   bool key_in_use =
    201       shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
    202       alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
    203       altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
    204       ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
    205       mod3_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
    206 
    207   overlay_->Show(enabled_ && key_in_use);
    208 }
    209 
    210 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
    211   return overlay_.get();
    212 }
    213 
    214 ///////////////////////////////////////////////////////////////////////////////
    215 //  StickyKeysHandler
    216 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag)
    217     : modifier_flag_(modifier_flag),
    218       current_state_(STICKY_KEY_STATE_DISABLED),
    219       preparing_to_enable_(false),
    220       scroll_delta_(0) {
    221 }
    222 
    223 StickyKeysHandler::~StickyKeysHandler() {
    224 }
    225 
    226 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
    227                                        ui::KeyboardCode key_code,
    228                                        int* mod_down_flags,
    229                                        bool* released) {
    230   switch (current_state_) {
    231     case STICKY_KEY_STATE_DISABLED:
    232       return HandleDisabledState(event, key_code);
    233     case STICKY_KEY_STATE_ENABLED:
    234       return HandleEnabledState(event, key_code, mod_down_flags, released);
    235     case STICKY_KEY_STATE_LOCKED:
    236       return HandleLockedState(event, key_code, mod_down_flags, released);
    237   }
    238   NOTREACHED();
    239   return false;
    240 }
    241 
    242 bool StickyKeysHandler::HandleMouseEvent(
    243     const ui::MouseEvent& event,
    244     int* mod_down_flags,
    245     bool* released) {
    246   if (ShouldModifyMouseEvent(event))
    247     preparing_to_enable_ = false;
    248 
    249   if (current_state_ == STICKY_KEY_STATE_DISABLED ||
    250       !ShouldModifyMouseEvent(event)) {
    251     return false;
    252   }
    253   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
    254          current_state_ == STICKY_KEY_STATE_LOCKED);
    255 
    256   *mod_down_flags |= modifier_flag_;
    257   // Only disable on the mouse released event in normal, non-locked mode.
    258   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
    259       event.type() != ui::ET_MOUSE_PRESSED) {
    260     current_state_ = STICKY_KEY_STATE_DISABLED;
    261     *released = true;
    262     return false;
    263   }
    264 
    265   return false;
    266 }
    267 
    268 bool StickyKeysHandler::HandleScrollEvent(
    269     const ui::ScrollEvent& event,
    270     int* mod_down_flags,
    271     bool* released) {
    272   preparing_to_enable_ = false;
    273   if (current_state_ == STICKY_KEY_STATE_DISABLED)
    274     return false;
    275   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
    276          current_state_ == STICKY_KEY_STATE_LOCKED);
    277 
    278   // We detect a direction change if the current |scroll_delta_| is assigned
    279   // and the offset of the current scroll event has the opposing sign.
    280   bool direction_changed = false;
    281   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
    282       event.type() == ui::ET_SCROLL) {
    283     int offset = event.y_offset();
    284     if (scroll_delta_)
    285       direction_changed = offset * scroll_delta_ <= 0;
    286     scroll_delta_ = offset;
    287   }
    288 
    289   if (!direction_changed)
    290     *mod_down_flags |= modifier_flag_;
    291 
    292   // We want to modify all the scroll events in the scroll sequence, which ends
    293   // with a fling start event. We also stop when the scroll sequence changes
    294   // direction.
    295   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
    296       (event.type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
    297     current_state_ = STICKY_KEY_STATE_DISABLED;
    298     scroll_delta_ = 0;
    299     *released = true;
    300     return false;
    301   }
    302 
    303   return false;
    304 }
    305 
    306 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr<ui::Event>* new_event) {
    307   if (current_state_ != STICKY_KEY_STATE_DISABLED || !modifier_up_event_)
    308     return 0;
    309   DCHECK(new_event);
    310   if (*new_event)
    311     return 1;
    312   new_event->reset(modifier_up_event_.release());
    313   return 0;
    314 }
    315 
    316 StickyKeysHandler::KeyEventType StickyKeysHandler::TranslateKeyEvent(
    317     ui::EventType type,
    318     ui::KeyboardCode key_code) {
    319   bool is_target_key = false;
    320   if (key_code == ui::VKEY_SHIFT ||
    321       key_code == ui::VKEY_LSHIFT ||
    322       key_code == ui::VKEY_RSHIFT) {
    323     is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
    324   } else if (key_code == ui::VKEY_CONTROL ||
    325       key_code == ui::VKEY_LCONTROL ||
    326       key_code == ui::VKEY_RCONTROL) {
    327     is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
    328   } else if (key_code == ui::VKEY_MENU ||
    329       key_code == ui::VKEY_LMENU ||
    330       key_code == ui::VKEY_RMENU) {
    331     is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
    332   } else if (key_code == ui::VKEY_ALTGR) {
    333     is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
    334   } else if (key_code == ui::VKEY_OEM_8) {
    335     is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN);
    336   } else {
    337     return type == ui::ET_KEY_PRESSED ?
    338         NORMAL_KEY_DOWN : NORMAL_KEY_UP;
    339   }
    340 
    341   if (is_target_key) {
    342     return type == ui::ET_KEY_PRESSED ?
    343         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
    344   }
    345   return type == ui::ET_KEY_PRESSED ?
    346       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
    347 }
    348 
    349 bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event,
    350                                             ui::KeyboardCode key_code) {
    351   switch (TranslateKeyEvent(event.type(), key_code)) {
    352     case TARGET_MODIFIER_UP:
    353       if (preparing_to_enable_) {
    354         preparing_to_enable_ = false;
    355         scroll_delta_ = 0;
    356         current_state_ = STICKY_KEY_STATE_ENABLED;
    357         modifier_up_event_.reset(new ui::KeyEvent(event));
    358         return true;
    359       }
    360       return false;
    361     case TARGET_MODIFIER_DOWN:
    362       preparing_to_enable_ = true;
    363       return false;
    364     case NORMAL_KEY_DOWN:
    365       preparing_to_enable_ = false;
    366       return false;
    367     case NORMAL_KEY_UP:
    368     case OTHER_MODIFIER_DOWN:
    369     case OTHER_MODIFIER_UP:
    370       return false;
    371   }
    372   NOTREACHED();
    373   return false;
    374 }
    375 
    376 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event,
    377                                            ui::KeyboardCode key_code,
    378                                            int* mod_down_flags,
    379                                            bool* released) {
    380   switch (TranslateKeyEvent(event.type(), key_code)) {
    381     case NORMAL_KEY_UP:
    382     case TARGET_MODIFIER_DOWN:
    383       return false;
    384     case TARGET_MODIFIER_UP:
    385       current_state_ = STICKY_KEY_STATE_LOCKED;
    386       modifier_up_event_.reset();
    387       return true;
    388     case NORMAL_KEY_DOWN: {
    389       current_state_ = STICKY_KEY_STATE_DISABLED;
    390       *mod_down_flags |= modifier_flag_;
    391       *released = true;
    392       return false;
    393     }
    394     case OTHER_MODIFIER_DOWN:
    395     case OTHER_MODIFIER_UP:
    396       return false;
    397   }
    398   NOTREACHED();
    399   return false;
    400 }
    401 
    402 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event,
    403                                           ui::KeyboardCode key_code,
    404                                           int* mod_down_flags,
    405                                           bool* released) {
    406   switch (TranslateKeyEvent(event.type(), key_code)) {
    407     case TARGET_MODIFIER_DOWN:
    408       return true;
    409     case TARGET_MODIFIER_UP:
    410       current_state_ = STICKY_KEY_STATE_DISABLED;
    411       return false;
    412     case NORMAL_KEY_DOWN:
    413     case NORMAL_KEY_UP:
    414       *mod_down_flags |= modifier_flag_;
    415       return false;
    416     case OTHER_MODIFIER_DOWN:
    417     case OTHER_MODIFIER_UP:
    418       return false;
    419   }
    420   NOTREACHED();
    421   return false;
    422 }
    423 
    424 }  // namespace ash
    425