Home | History | Annotate | Download | only in wm
      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/wm/sticky_keys.h"
      6 
      7 #if defined(USE_X11)
      8 #include <X11/extensions/XInput2.h>
      9 #include <X11/Xlib.h>
     10 #undef RootWindow
     11 #endif
     12 
     13 #include "base/basictypes.h"
     14 #include "base/debug/stack_trace.h"
     15 #include "ui/aura/root_window.h"
     16 #include "ui/aura/window.h"
     17 #include "ui/aura/window_tracker.h"
     18 #include "ui/events/event.h"
     19 #include "ui/events/keycodes/keyboard_code_conversion.h"
     20 
     21 namespace ash {
     22 
     23 namespace {
     24 
     25 // Returns true if the type of mouse event should be modified by sticky keys.
     26 bool ShouldModifyMouseEvent(ui::MouseEvent* event) {
     27   ui::EventType type = event->type();
     28   return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED ||
     29          type == ui::ET_MOUSEWHEEL;
     30 }
     31 
     32 // An implementation of StickyKeysHandler::StickyKeysHandlerDelegate.
     33 class StickyKeysHandlerDelegateImpl :
     34     public StickyKeysHandler::StickyKeysHandlerDelegate {
     35  public:
     36   StickyKeysHandlerDelegateImpl();
     37   virtual ~StickyKeysHandlerDelegateImpl();
     38 
     39   // StickyKeysHandlerDelegate overrides.
     40   virtual void DispatchKeyEvent(ui::KeyEvent* event,
     41                                 aura::Window* target) OVERRIDE;
     42 
     43   virtual void DispatchMouseEvent(ui::MouseEvent* event,
     44                                   aura::Window* target) OVERRIDE;
     45 
     46   virtual void DispatchScrollEvent(ui::ScrollEvent* event,
     47                                    aura::Window* target) OVERRIDE;
     48  private:
     49   DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl);
     50 };
     51 
     52 StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() {
     53 }
     54 
     55 StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() {
     56 }
     57 
     58 void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent* event,
     59                                                      aura::Window* target) {
     60   DCHECK(target);
     61   target->GetDispatcher()->AsRootWindowHostDelegate()->OnHostKeyEvent(event);
     62 }
     63 
     64 void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent* event,
     65                                                        aura::Window* target) {
     66   DCHECK(target);
     67   // We need to send a new, untransformed mouse event to the host.
     68   if (event->IsMouseWheelEvent()) {
     69     ui::MouseWheelEvent new_event(*static_cast<ui::MouseWheelEvent*>(event));
     70     target->GetDispatcher()->AsRootWindowHostDelegate()
     71         ->OnHostMouseEvent(&new_event);
     72   } else {
     73     ui::MouseEvent new_event(*event, target, target->GetRootWindow());
     74     target->GetDispatcher()->AsRootWindowHostDelegate()
     75         ->OnHostMouseEvent(&new_event);
     76   }
     77 }
     78 
     79 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
     80     ui::ScrollEvent* event,
     81     aura::Window* target)  {
     82   DCHECK(target);
     83   target->GetDispatcher()->AsRootWindowHostDelegate()
     84       ->OnHostScrollEvent(event);
     85 }
     86 
     87 }  // namespace
     88 
     89 ///////////////////////////////////////////////////////////////////////////////
     90 //  StickyKeys
     91 StickyKeys::StickyKeys()
     92     : enabled_(false),
     93       shift_sticky_key_(
     94           new StickyKeysHandler(ui::EF_SHIFT_DOWN,
     95                               new StickyKeysHandlerDelegateImpl())),
     96       alt_sticky_key_(
     97           new StickyKeysHandler(ui::EF_ALT_DOWN,
     98                                 new StickyKeysHandlerDelegateImpl())),
     99       ctrl_sticky_key_(
    100           new StickyKeysHandler(ui::EF_CONTROL_DOWN,
    101                                 new StickyKeysHandlerDelegateImpl())) {
    102 }
    103 
    104 StickyKeys::~StickyKeys() {
    105 }
    106 
    107 void StickyKeys::Enable(bool enabled) {
    108   if (enabled_ != enabled) {
    109     enabled_ = enabled;
    110 
    111     // Reset key handlers when activating sticky keys to ensure all
    112     // the handlers' states are reset.
    113     if (enabled_) {
    114       shift_sticky_key_.reset(
    115           new StickyKeysHandler(ui::EF_SHIFT_DOWN,
    116                               new StickyKeysHandlerDelegateImpl()));
    117       alt_sticky_key_.reset(
    118           new StickyKeysHandler(ui::EF_ALT_DOWN,
    119                                 new StickyKeysHandlerDelegateImpl()));
    120       ctrl_sticky_key_.reset(
    121           new StickyKeysHandler(ui::EF_CONTROL_DOWN,
    122                                 new StickyKeysHandlerDelegateImpl()));
    123     }
    124   }
    125 }
    126 
    127 bool StickyKeys::HandleKeyEvent(ui::KeyEvent* event) {
    128   return shift_sticky_key_->HandleKeyEvent(event) ||
    129       alt_sticky_key_->HandleKeyEvent(event) ||
    130       ctrl_sticky_key_->HandleKeyEvent(event);
    131   return ctrl_sticky_key_->HandleKeyEvent(event);
    132 }
    133 
    134 bool StickyKeys::HandleMouseEvent(ui::MouseEvent* event) {
    135   return shift_sticky_key_->HandleMouseEvent(event) ||
    136       alt_sticky_key_->HandleMouseEvent(event) ||
    137       ctrl_sticky_key_->HandleMouseEvent(event);
    138 }
    139 
    140 bool StickyKeys::HandleScrollEvent(ui::ScrollEvent* event) {
    141   return shift_sticky_key_->HandleScrollEvent(event) ||
    142       alt_sticky_key_->HandleScrollEvent(event) ||
    143       ctrl_sticky_key_->HandleScrollEvent(event);
    144 }
    145 
    146 void StickyKeys::OnKeyEvent(ui::KeyEvent* event) {
    147   // Do not consume a translated key event which is generated by an IME.
    148   if (event->type() == ui::ET_TRANSLATED_KEY_PRESS ||
    149       event->type() == ui::ET_TRANSLATED_KEY_RELEASE) {
    150     return;
    151   }
    152 
    153   if (enabled_ && HandleKeyEvent(event))
    154     event->StopPropagation();
    155 }
    156 
    157 void StickyKeys::OnMouseEvent(ui::MouseEvent* event) {
    158   if (enabled_ && HandleMouseEvent(event))
    159     event->StopPropagation();
    160 }
    161 
    162 void StickyKeys::OnScrollEvent(ui::ScrollEvent* event) {
    163   if (enabled_ && HandleScrollEvent(event))
    164     event->StopPropagation();
    165 }
    166 
    167 ///////////////////////////////////////////////////////////////////////////////
    168 //  StickyKeysHandler
    169 StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag,
    170                                      StickyKeysHandlerDelegate* delegate)
    171     : modifier_flag_(target_modifier_flag),
    172       current_state_(DISABLED),
    173       event_from_myself_(false),
    174       preparing_to_enable_(false),
    175       scroll_delta_(0),
    176       delegate_(delegate) {
    177 }
    178 
    179 StickyKeysHandler::~StickyKeysHandler() {
    180 }
    181 
    182 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
    183 }
    184 
    185 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
    186 }
    187 
    188 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) {
    189   if (event_from_myself_)
    190     return false;  // Do not handle self-generated key event.
    191   switch (current_state_) {
    192     case DISABLED:
    193       return HandleDisabledState(event);
    194     case ENABLED:
    195       return HandleEnabledState(event);
    196     case LOCKED:
    197       return HandleLockedState(event);
    198   }
    199   NOTREACHED();
    200   return false;
    201 }
    202 
    203 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
    204   preparing_to_enable_ = false;
    205   if (event_from_myself_ || current_state_ == DISABLED
    206       || !ShouldModifyMouseEvent(event)) {
    207     return false;
    208   }
    209   DCHECK(current_state_ == ENABLED || current_state_ == LOCKED);
    210 
    211   AppendModifier(event);
    212   // Only disable on the mouse released event in normal, non-locked mode.
    213   if (current_state_ == ENABLED && event->type() != ui::ET_MOUSE_PRESSED) {
    214     current_state_ = DISABLED;
    215     DispatchEventAndReleaseModifier(event);
    216     return true;
    217   }
    218 
    219   return false;
    220 }
    221 
    222 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
    223   preparing_to_enable_ = false;
    224   if (event_from_myself_ || current_state_ == DISABLED)
    225     return false;
    226   DCHECK(current_state_ == ENABLED || current_state_ == LOCKED);
    227 
    228   // We detect a direction change if the current |scroll_delta_| is assigned
    229   // and the offset of the current scroll event has the opposing sign.
    230   bool direction_changed = false;
    231   if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) {
    232     int offset = event->y_offset();
    233     if (scroll_delta_)
    234       direction_changed = offset * scroll_delta_ <= 0;
    235     scroll_delta_ = offset;
    236   }
    237 
    238   if (!direction_changed)
    239     AppendModifier(event);
    240 
    241   // We want to modify all the scroll events in the scroll sequence, which ends
    242   // with a fling start event. We also stop when the scroll sequence changes
    243   // direction.
    244   if (current_state_ == ENABLED &&
    245       (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
    246     current_state_ = DISABLED;
    247     scroll_delta_ = 0;
    248     DispatchEventAndReleaseModifier(event);
    249     return true;
    250   }
    251 
    252   return false;
    253 }
    254 
    255 StickyKeysHandler::KeyEventType
    256     StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) {
    257   bool is_target_key = false;
    258   if (event->key_code() == ui::VKEY_SHIFT ||
    259       event->key_code() == ui::VKEY_LSHIFT ||
    260       event->key_code() == ui::VKEY_RSHIFT) {
    261     is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
    262   } else if (event->key_code() == ui::VKEY_CONTROL ||
    263       event->key_code() == ui::VKEY_LCONTROL ||
    264       event->key_code() == ui::VKEY_RCONTROL) {
    265     is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
    266   } else if (event->key_code() == ui::VKEY_MENU ||
    267       event->key_code() == ui::VKEY_LMENU ||
    268       event->key_code() == ui::VKEY_RMENU) {
    269     is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
    270   } else {
    271     return event->type() == ui::ET_KEY_PRESSED ?
    272         NORMAL_KEY_DOWN : NORMAL_KEY_UP;
    273   }
    274 
    275   if (is_target_key) {
    276     return event->type() == ui::ET_KEY_PRESSED ?
    277         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
    278   }
    279   return event->type() == ui::ET_KEY_PRESSED ?
    280       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
    281 }
    282 
    283 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) {
    284   switch (TranslateKeyEvent(event)) {
    285     case TARGET_MODIFIER_UP:
    286       if (preparing_to_enable_) {
    287         preparing_to_enable_ = false;
    288         scroll_delta_ = 0;
    289         current_state_ = ENABLED;
    290         modifier_up_event_.reset(new ui::KeyEvent(*event));
    291         return true;
    292       }
    293       return false;
    294     case TARGET_MODIFIER_DOWN:
    295       preparing_to_enable_ = true;
    296       return false;
    297     case NORMAL_KEY_DOWN:
    298       preparing_to_enable_ = false;
    299       return false;
    300     case NORMAL_KEY_UP:
    301     case OTHER_MODIFIER_DOWN:
    302     case OTHER_MODIFIER_UP:
    303       return false;
    304   }
    305   NOTREACHED();
    306   return false;
    307 }
    308 
    309 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
    310   switch (TranslateKeyEvent(event)) {
    311     case NORMAL_KEY_UP:
    312     case TARGET_MODIFIER_DOWN:
    313       return true;
    314     case TARGET_MODIFIER_UP:
    315       current_state_ = LOCKED;
    316       modifier_up_event_.reset();
    317       return true;
    318     case NORMAL_KEY_DOWN: {
    319       current_state_ = DISABLED;
    320       AppendModifier(event);
    321       DispatchEventAndReleaseModifier(event);
    322       return true;
    323     }
    324     case OTHER_MODIFIER_DOWN:
    325     case OTHER_MODIFIER_UP:
    326       return false;
    327   }
    328   NOTREACHED();
    329   return false;
    330 }
    331 
    332 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
    333   switch (TranslateKeyEvent(event)) {
    334     case TARGET_MODIFIER_DOWN:
    335       return true;
    336     case TARGET_MODIFIER_UP:
    337       current_state_ = DISABLED;
    338       return false;
    339     case NORMAL_KEY_DOWN:
    340     case NORMAL_KEY_UP:
    341       AppendModifier(event);
    342       return false;
    343     case OTHER_MODIFIER_DOWN:
    344     case OTHER_MODIFIER_UP:
    345       return false;
    346   }
    347   NOTREACHED();
    348   return false;
    349 }
    350 
    351 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) {
    352   DCHECK(event->IsKeyEvent() ||
    353          event->IsMouseEvent() ||
    354          event->IsScrollEvent());
    355   DCHECK(modifier_up_event_.get());
    356   aura::Window* target = static_cast<aura::Window*>(event->target());
    357   DCHECK(target);
    358   aura::Window* root_window = target->GetRootWindow();
    359   DCHECK(root_window);
    360 
    361   aura::WindowTracker window_tracker;
    362   window_tracker.Add(target);
    363 
    364   event_from_myself_ = true;
    365   if (event->IsKeyEvent()) {
    366     delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target);
    367   } else if (event->IsMouseEvent()) {
    368     delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target);
    369   } else {
    370     delegate_->DispatchScrollEvent(
    371         static_cast<ui::ScrollEvent*>(event), target);
    372   }
    373 
    374   // The action triggered above may have destroyed the event target, in which
    375   // case we will dispatch the modifier up event to the root window instead.
    376   aura::Window* modifier_up_target =
    377       window_tracker.Contains(target) ? target : root_window;
    378   delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target);
    379   event_from_myself_ = false;
    380 }
    381 
    382 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) {
    383   unsigned int& state_ref = *state;
    384   switch (modifier_flag_) {
    385     case ui::EF_CONTROL_DOWN:
    386       state_ref |= ControlMask;
    387       break;
    388     case ui::EF_ALT_DOWN:
    389       state_ref |= Mod1Mask;
    390       break;
    391     case ui::EF_SHIFT_DOWN:
    392       state_ref |= ShiftMask;
    393       break;
    394     default:
    395       NOTREACHED();
    396   }
    397 }
    398 
    399 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
    400 #if defined(USE_X11)
    401   XEvent* xev = event->native_event();
    402   if (xev) {
    403     XKeyEvent* xkey = &(xev->xkey);
    404     AppendNativeEventMask(&xkey->state);
    405   }
    406 #elif defined(USE_OZONE)
    407   NOTIMPLEMENTED() << "Modifier key is not handled";
    408 #endif
    409   event->set_flags(event->flags() | modifier_flag_);
    410   event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
    411                                                    event->flags()));
    412   event->NormalizeFlags();
    413 }
    414 
    415 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
    416 #if defined(USE_X11)
    417   XEvent* xev = event->native_event();
    418   if (xev) {
    419     XButtonEvent* xkey = &(xev->xbutton);
    420     AppendNativeEventMask(&xkey->state);
    421   }
    422 #elif defined(USE_OZONE)
    423   NOTIMPLEMENTED() << "Modifier key is not handled";
    424 #endif
    425   event->set_flags(event->flags() | modifier_flag_);
    426 }
    427 
    428 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
    429 #if defined(USE_X11)
    430   XEvent* xev = event->native_event();
    431   if (xev) {
    432     XIDeviceEvent* xievent =
    433         static_cast<XIDeviceEvent*>(xev->xcookie.data);
    434     if (xievent) {
    435       AppendNativeEventMask(reinterpret_cast<unsigned int*>(
    436           &xievent->mods.effective));
    437     }
    438   }
    439 #elif defined(USE_OZONE)
    440   NOTIMPLEMENTED() << "Modifier key is not handled";
    441 #endif
    442   event->set_flags(event->flags() | modifier_flag_);
    443 }
    444 
    445 }  // namespace ash
    446