15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/sticky_keys/sticky_keys_controller.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/sticky_keys/sticky_keys_overlay.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/stack_trace.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window_tracker.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window_tree_host.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/events/event.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/events/event_processor.h"
151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "ui/events/keycodes/keyboard_code_conversion.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ash {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns true if the type of mouse event should be modified by sticky keys.
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ShouldModifyMouseEvent(const ui::MouseEvent& event) {
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ui::EventType type = event.type();
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED ||
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         type == ui::ET_MOUSEWHEEL;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Handle the common tail of event rewriting.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::EventRewriteStatus RewriteUpdate(bool consumed,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     bool released,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     int mod_down_flags,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     int* flags) {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int changed_down_flags = mod_down_flags & ~*flags;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *flags |= mod_down_flags;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (consumed)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::EVENT_REWRITE_DISCARD;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (released)
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (changed_down_flags)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::EVENT_REWRITE_REWRITTEN;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::EVENT_REWRITE_CONTINUE;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  StickyKeys
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StickyKeysController::StickyKeysController()
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : enabled_(false),
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod3_enabled_(false),
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      altgr_enabled_(false) {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StickyKeysController::~StickyKeysController() {
551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StickyKeysController::Enable(bool enabled) {
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (enabled_ != enabled) {
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    enabled_ = enabled;
605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Reset key handlers when activating sticky keys to ensure all
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // the handlers' states are reset.
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (enabled_) {
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      shift_sticky_key_.reset(new StickyKeysHandler(ui::EF_SHIFT_DOWN));
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      alt_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALT_DOWN));
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      altgr_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALTGR_DOWN));
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ctrl_sticky_key_.reset(new StickyKeysHandler(ui::EF_CONTROL_DOWN));
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod3_sticky_key_.reset(new StickyKeysHandler(ui::EF_MOD3_DOWN));
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      overlay_.reset(new StickyKeysOverlay());
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (overlay_) {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      overlay_->Show(false);
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               bool altgr_enabled) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mod3_enabled_ = mod3_enabled;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  altgr_enabled_ = altgr_enabled;
831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (overlay_) {
841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                          ui::KeyboardCode key_code,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          int* mod_down_flags,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          bool* released) {
93a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return shift_sticky_key_->HandleKeyEvent(
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, key_code, mod_down_flags, released) ||
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         alt_sticky_key_->HandleKeyEvent(
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, key_code, mod_down_flags, released) ||
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         altgr_sticky_key_->HandleKeyEvent(
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, key_code, mod_down_flags, released) ||
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ctrl_sticky_key_->HandleKeyEvent(
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, key_code, mod_down_flags, released) ||
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         mod3_sticky_key_->HandleKeyEvent(
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, key_code, mod_down_flags, released);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            int* mod_down_flags,
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            bool* released) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return shift_sticky_key_->HandleMouseEvent(
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         alt_sticky_key_->HandleMouseEvent(
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         altgr_sticky_key_->HandleMouseEvent(
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ctrl_sticky_key_->HandleMouseEvent(
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         mod3_sticky_key_->HandleMouseEvent(
117a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)             event, mod_down_flags, released);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             int* mod_down_flags,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             bool* released) {
123a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return shift_sticky_key_->HandleScrollEvent(
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
125a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)         alt_sticky_key_->HandleScrollEvent(
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             event, mod_down_flags, released) ||
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         altgr_sticky_key_->HandleScrollEvent(
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
129a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)         ctrl_sticky_key_->HandleScrollEvent(
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released) ||
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         mod3_sticky_key_->HandleScrollEvent(
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             event, mod_down_flags, released);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
134a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
136a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    const ui::KeyEvent& event,
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ui::KeyboardCode key_code,
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* flags) {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!enabled_)
140a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return ui::EVENT_REWRITE_CONTINUE;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mod_down_flags = 0;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool released = false;
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateOverlay();
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RewriteUpdate(consumed, released, mod_down_flags, flags);
146a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
148a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::MouseEvent& event,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* flags) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!enabled_)
152a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return ui::EVENT_REWRITE_CONTINUE;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mod_down_flags = 0;
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool released = false;
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateOverlay();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RewriteUpdate(consumed, released, mod_down_flags, flags);
158a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
160a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::ScrollEvent& event,
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* flags) {
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!enabled_)
164a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return ui::EVENT_REWRITE_CONTINUE;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mod_down_flags = 0;
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool released = false;
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateOverlay();
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RewriteUpdate(consumed, released, mod_down_flags, flags);
170a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
172a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<ui::Event>* new_event) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(new_event);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_event->reset();
176a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) +
177a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                  alt_sticky_key_->GetModifierUpEvent(new_event) +
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  altgr_sticky_key_->GetModifierUpEvent(new_event) +
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  ctrl_sticky_key_->GetModifierUpEvent(new_event) +
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  mod3_sticky_key_->GetModifierUpEvent(new_event);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!new_event)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::EVENT_REWRITE_CONTINUE;
183a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (remaining)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
185a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return ui::EVENT_REWRITE_REWRITTEN;
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StickyKeysController::UpdateOverlay() {
189a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  overlay_->SetModifierKeyState(
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  overlay_->SetModifierKeyState(
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  overlay_->SetModifierKeyState(
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
195a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  overlay_->SetModifierKeyState(
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
197a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  overlay_->SetModifierKeyState(
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::EF_MOD3_DOWN, mod3_sticky_key_->current_state());
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool key_in_use =
201a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mod3_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
207a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  overlay_->Show(enabled_ && key_in_use);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
209a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return overlay_.get();
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
213a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//  StickyKeysHandler
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : modifier_flag_(modifier_flag),
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_state_(STICKY_KEY_STATE_DISABLED),
219a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      preparing_to_enable_(false),
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scroll_delta_(0) {
221a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StickyKeysHandler::~StickyKeysHandler() {
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
225a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
226a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       ui::KeyboardCode key_code,
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       int* mod_down_flags,
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       bool* released) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (current_state_) {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case STICKY_KEY_STATE_DISABLED:
232a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      return HandleDisabledState(event, key_code);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case STICKY_KEY_STATE_ENABLED:
234a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      return HandleEnabledState(event, key_code, mod_down_flags, released);
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case STICKY_KEY_STATE_LOCKED:
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return HandleLockedState(event, key_code, mod_down_flags, released);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
238a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  NOTREACHED();
239a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return false;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool StickyKeysHandler::HandleMouseEvent(
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ui::MouseEvent& event,
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* mod_down_flags,
245a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    bool* released) {
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldModifyMouseEvent(event))
247a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    preparing_to_enable_ = false;
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_state_ == STICKY_KEY_STATE_DISABLED ||
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !ShouldModifyMouseEvent(event)) {
251a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return false;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         current_state_ == STICKY_KEY_STATE_LOCKED);
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *mod_down_flags |= modifier_flag_;
257a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Only disable on the mouse released event in normal, non-locked mode.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_state_ == STICKY_KEY_STATE_ENABLED &&
259a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      event.type() != ui::ET_MOUSE_PRESSED) {
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    current_state_ = STICKY_KEY_STATE_DISABLED;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *released = true;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return false;
2666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool StickyKeysHandler::HandleScrollEvent(
2696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const ui::ScrollEvent& event,
2706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    int* mod_down_flags,
2716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    bool* released) {
2726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  preparing_to_enable_ = false;
2736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (current_state_ == STICKY_KEY_STATE_DISABLED)
2746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return false;
2756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
276a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)         current_state_ == STICKY_KEY_STATE_LOCKED);
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We detect a direction change if the current |scroll_delta_| is assigned
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // and the offset of the current scroll event has the opposing sign.
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool direction_changed = false;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_state_ == STICKY_KEY_STATE_ENABLED &&
282a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      event.type() == ui::ET_SCROLL) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int offset = event.y_offset();
284a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (scroll_delta_)
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      direction_changed = offset * scroll_delta_ <= 0;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scroll_delta_ = offset;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
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
306int 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
316StickyKeysHandler::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
349bool 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
376bool 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
402bool 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