summaryrefslogtreecommitdiffstats
path: root/ash/sticky_keys/sticky_keys_controller.cc
diff options
context:
space:
mode:
authortengs@chromium.org <tengs@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-20 07:52:28 +0000
committertengs@chromium.org <tengs@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-20 07:52:28 +0000
commit08bdabed6431d795ff0a3bccc838b10b9e463efb (patch)
tree9f9ff38568d617d824117f0d522c8448f551958f /ash/sticky_keys/sticky_keys_controller.cc
parent2bf93f2315882ea991d67c6de3824566cbb93cbf (diff)
downloadchromium_src-08bdabed6431d795ff0a3bccc838b10b9e463efb.zip
chromium_src-08bdabed6431d795ff0a3bccc838b10b9e463efb.tar.gz
chromium_src-08bdabed6431d795ff0a3bccc838b10b9e463efb.tar.bz2
Move sticky keys files to their own directory and rename to StickyKeysController.
There will be new files added for the sticky keys UI in the future, and this refactor improves organization for sticky keys code and is more inline with how the other accessibility features are organized. TEST=StickyKeysTests pass Review URL: https://codereview.chromium.org/118543002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242036 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/sticky_keys/sticky_keys_controller.cc')
-rw-r--r--ash/sticky_keys/sticky_keys_controller.cc445
1 files changed, 445 insertions, 0 deletions
diff --git a/ash/sticky_keys/sticky_keys_controller.cc b/ash/sticky_keys/sticky_keys_controller.cc
new file mode 100644
index 0000000..a34cded
--- /dev/null
+++ b/ash/sticky_keys/sticky_keys_controller.cc
@@ -0,0 +1,445 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/sticky_keys/sticky_keys_controller.h"
+
+#if defined(USE_X11)
+#include <X11/extensions/XInput2.h>
+#include <X11/Xlib.h>
+#undef RootWindow
+#endif
+
+#include "base/basictypes.h"
+#include "base/debug/stack_trace.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+
+namespace ash {
+
+namespace {
+
+// Returns true if the type of mouse event should be modified by sticky keys.
+bool ShouldModifyMouseEvent(ui::MouseEvent* event) {
+ ui::EventType type = event->type();
+ return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED ||
+ type == ui::ET_MOUSEWHEEL;
+}
+
+// An implementation of StickyKeysHandler::StickyKeysHandlerDelegate.
+class StickyKeysHandlerDelegateImpl :
+ public StickyKeysHandler::StickyKeysHandlerDelegate {
+ public:
+ StickyKeysHandlerDelegateImpl();
+ virtual ~StickyKeysHandlerDelegateImpl();
+
+ // StickyKeysHandlerDelegate overrides.
+ virtual void DispatchKeyEvent(ui::KeyEvent* event,
+ aura::Window* target) OVERRIDE;
+
+ virtual void DispatchMouseEvent(ui::MouseEvent* event,
+ aura::Window* target) OVERRIDE;
+
+ virtual void DispatchScrollEvent(ui::ScrollEvent* event,
+ aura::Window* target) OVERRIDE;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl);
+};
+
+StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() {
+}
+
+StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() {
+}
+
+void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent* event,
+ aura::Window* target) {
+ DCHECK(target);
+ target->GetDispatcher()->AsRootWindowHostDelegate()->OnHostKeyEvent(event);
+}
+
+void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent* event,
+ aura::Window* target) {
+ DCHECK(target);
+ // We need to send a new, untransformed mouse event to the host.
+ if (event->IsMouseWheelEvent()) {
+ ui::MouseWheelEvent new_event(*static_cast<ui::MouseWheelEvent*>(event));
+ target->GetDispatcher()->AsRootWindowHostDelegate()
+ ->OnHostMouseEvent(&new_event);
+ } else {
+ ui::MouseEvent new_event(*event, target, target->GetRootWindow());
+ target->GetDispatcher()->AsRootWindowHostDelegate()
+ ->OnHostMouseEvent(&new_event);
+ }
+}
+
+void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
+ ui::ScrollEvent* event,
+ aura::Window* target) {
+ DCHECK(target);
+ target->GetDispatcher()->AsRootWindowHostDelegate()
+ ->OnHostScrollEvent(event);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// StickyKeys
+StickyKeysController::StickyKeysController()
+ : enabled_(false),
+ shift_sticky_key_(
+ new StickyKeysHandler(ui::EF_SHIFT_DOWN,
+ new StickyKeysHandlerDelegateImpl())),
+ alt_sticky_key_(
+ new StickyKeysHandler(ui::EF_ALT_DOWN,
+ new StickyKeysHandlerDelegateImpl())),
+ ctrl_sticky_key_(
+ new StickyKeysHandler(ui::EF_CONTROL_DOWN,
+ new StickyKeysHandlerDelegateImpl())) {
+}
+
+StickyKeysController::~StickyKeysController() {
+}
+
+void StickyKeysController::Enable(bool enabled) {
+ if (enabled_ != enabled) {
+ enabled_ = enabled;
+
+ // Reset key handlers when activating sticky keys to ensure all
+ // the handlers' states are reset.
+ if (enabled_) {
+ shift_sticky_key_.reset(
+ new StickyKeysHandler(ui::EF_SHIFT_DOWN,
+ new StickyKeysHandlerDelegateImpl()));
+ alt_sticky_key_.reset(
+ new StickyKeysHandler(ui::EF_ALT_DOWN,
+ new StickyKeysHandlerDelegateImpl()));
+ ctrl_sticky_key_.reset(
+ new StickyKeysHandler(ui::EF_CONTROL_DOWN,
+ new StickyKeysHandlerDelegateImpl()));
+ }
+ }
+}
+
+bool StickyKeysController::HandleKeyEvent(ui::KeyEvent* event) {
+ return shift_sticky_key_->HandleKeyEvent(event) ||
+ alt_sticky_key_->HandleKeyEvent(event) ||
+ ctrl_sticky_key_->HandleKeyEvent(event);
+ return ctrl_sticky_key_->HandleKeyEvent(event);
+}
+
+bool StickyKeysController::HandleMouseEvent(ui::MouseEvent* event) {
+ return shift_sticky_key_->HandleMouseEvent(event) ||
+ alt_sticky_key_->HandleMouseEvent(event) ||
+ ctrl_sticky_key_->HandleMouseEvent(event);
+}
+
+bool StickyKeysController::HandleScrollEvent(ui::ScrollEvent* event) {
+ return shift_sticky_key_->HandleScrollEvent(event) ||
+ alt_sticky_key_->HandleScrollEvent(event) ||
+ ctrl_sticky_key_->HandleScrollEvent(event);
+}
+
+void StickyKeysController::OnKeyEvent(ui::KeyEvent* event) {
+ // Do not consume a translated key event which is generated by an IME.
+ if (event->type() == ui::ET_TRANSLATED_KEY_PRESS ||
+ event->type() == ui::ET_TRANSLATED_KEY_RELEASE) {
+ return;
+ }
+
+ if (enabled_ && HandleKeyEvent(event))
+ event->StopPropagation();
+}
+
+void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
+ if (enabled_ && HandleMouseEvent(event))
+ event->StopPropagation();
+}
+
+void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
+ if (enabled_ && HandleScrollEvent(event))
+ event->StopPropagation();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StickyKeysHandler
+StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag,
+ StickyKeysHandlerDelegate* delegate)
+ : modifier_flag_(target_modifier_flag),
+ current_state_(DISABLED),
+ event_from_myself_(false),
+ preparing_to_enable_(false),
+ scroll_delta_(0),
+ delegate_(delegate) {
+}
+
+StickyKeysHandler::~StickyKeysHandler() {
+}
+
+StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
+}
+
+StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
+}
+
+bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) {
+ if (event_from_myself_)
+ return false; // Do not handle self-generated key event.
+ switch (current_state_) {
+ case DISABLED:
+ return HandleDisabledState(event);
+ case ENABLED:
+ return HandleEnabledState(event);
+ case LOCKED:
+ return HandleLockedState(event);
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
+ preparing_to_enable_ = false;
+ if (event_from_myself_ || current_state_ == DISABLED
+ || !ShouldModifyMouseEvent(event)) {
+ return false;
+ }
+ DCHECK(current_state_ == ENABLED || current_state_ == LOCKED);
+
+ AppendModifier(event);
+ // Only disable on the mouse released event in normal, non-locked mode.
+ if (current_state_ == ENABLED && event->type() != ui::ET_MOUSE_PRESSED) {
+ current_state_ = DISABLED;
+ DispatchEventAndReleaseModifier(event);
+ return true;
+ }
+
+ return false;
+}
+
+bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
+ preparing_to_enable_ = false;
+ if (event_from_myself_ || current_state_ == DISABLED)
+ return false;
+ DCHECK(current_state_ == ENABLED || current_state_ == LOCKED);
+
+ // We detect a direction change if the current |scroll_delta_| is assigned
+ // and the offset of the current scroll event has the opposing sign.
+ bool direction_changed = false;
+ if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) {
+ int offset = event->y_offset();
+ if (scroll_delta_)
+ direction_changed = offset * scroll_delta_ <= 0;
+ scroll_delta_ = offset;
+ }
+
+ if (!direction_changed)
+ AppendModifier(event);
+
+ // We want to modify all the scroll events in the scroll sequence, which ends
+ // with a fling start event. We also stop when the scroll sequence changes
+ // direction.
+ if (current_state_ == ENABLED &&
+ (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
+ current_state_ = DISABLED;
+ scroll_delta_ = 0;
+ DispatchEventAndReleaseModifier(event);
+ return true;
+ }
+
+ return false;
+}
+
+StickyKeysHandler::KeyEventType
+ StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) {
+ bool is_target_key = false;
+ if (event->key_code() == ui::VKEY_SHIFT ||
+ event->key_code() == ui::VKEY_LSHIFT ||
+ event->key_code() == ui::VKEY_RSHIFT) {
+ is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
+ } else if (event->key_code() == ui::VKEY_CONTROL ||
+ event->key_code() == ui::VKEY_LCONTROL ||
+ event->key_code() == ui::VKEY_RCONTROL) {
+ is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
+ } else if (event->key_code() == ui::VKEY_MENU ||
+ event->key_code() == ui::VKEY_LMENU ||
+ event->key_code() == ui::VKEY_RMENU) {
+ is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
+ } else {
+ return event->type() == ui::ET_KEY_PRESSED ?
+ NORMAL_KEY_DOWN : NORMAL_KEY_UP;
+ }
+
+ if (is_target_key) {
+ return event->type() == ui::ET_KEY_PRESSED ?
+ TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
+ }
+ return event->type() == ui::ET_KEY_PRESSED ?
+ OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
+}
+
+bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) {
+ switch (TranslateKeyEvent(event)) {
+ case TARGET_MODIFIER_UP:
+ if (preparing_to_enable_) {
+ preparing_to_enable_ = false;
+ scroll_delta_ = 0;
+ current_state_ = ENABLED;
+ modifier_up_event_.reset(new ui::KeyEvent(*event));
+ return true;
+ }
+ return false;
+ case TARGET_MODIFIER_DOWN:
+ preparing_to_enable_ = true;
+ return false;
+ case NORMAL_KEY_DOWN:
+ preparing_to_enable_ = false;
+ return false;
+ case NORMAL_KEY_UP:
+ case OTHER_MODIFIER_DOWN:
+ case OTHER_MODIFIER_UP:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
+ switch (TranslateKeyEvent(event)) {
+ case NORMAL_KEY_UP:
+ case TARGET_MODIFIER_DOWN:
+ return true;
+ case TARGET_MODIFIER_UP:
+ current_state_ = LOCKED;
+ modifier_up_event_.reset();
+ return true;
+ case NORMAL_KEY_DOWN: {
+ current_state_ = DISABLED;
+ AppendModifier(event);
+ DispatchEventAndReleaseModifier(event);
+ return true;
+ }
+ case OTHER_MODIFIER_DOWN:
+ case OTHER_MODIFIER_UP:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
+ switch (TranslateKeyEvent(event)) {
+ case TARGET_MODIFIER_DOWN:
+ return true;
+ case TARGET_MODIFIER_UP:
+ current_state_ = DISABLED;
+ return false;
+ case NORMAL_KEY_DOWN:
+ case NORMAL_KEY_UP:
+ AppendModifier(event);
+ return false;
+ case OTHER_MODIFIER_DOWN:
+ case OTHER_MODIFIER_UP:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) {
+ DCHECK(event->IsKeyEvent() ||
+ event->IsMouseEvent() ||
+ event->IsScrollEvent());
+ DCHECK(modifier_up_event_.get());
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ DCHECK(target);
+ aura::Window* root_window = target->GetRootWindow();
+ DCHECK(root_window);
+
+ aura::WindowTracker window_tracker;
+ window_tracker.Add(target);
+
+ event_from_myself_ = true;
+ if (event->IsKeyEvent()) {
+ delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target);
+ } else if (event->IsMouseEvent()) {
+ delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target);
+ } else {
+ delegate_->DispatchScrollEvent(
+ static_cast<ui::ScrollEvent*>(event), target);
+ }
+
+ // The action triggered above may have destroyed the event target, in which
+ // case we will dispatch the modifier up event to the root window instead.
+ aura::Window* modifier_up_target =
+ window_tracker.Contains(target) ? target : root_window;
+ delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target);
+ event_from_myself_ = false;
+}
+
+void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) {
+ unsigned int& state_ref = *state;
+ switch (modifier_flag_) {
+ case ui::EF_CONTROL_DOWN:
+ state_ref |= ControlMask;
+ break;
+ case ui::EF_ALT_DOWN:
+ state_ref |= Mod1Mask;
+ break;
+ case ui::EF_SHIFT_DOWN:
+ state_ref |= ShiftMask;
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
+#if defined(USE_X11)
+ XEvent* xev = event->native_event();
+ if (xev) {
+ XKeyEvent* xkey = &(xev->xkey);
+ AppendNativeEventMask(&xkey->state);
+ }
+#elif defined(USE_OZONE)
+ NOTIMPLEMENTED() << "Modifier key is not handled";
+#endif
+ event->set_flags(event->flags() | modifier_flag_);
+ event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
+ event->flags()));
+ event->NormalizeFlags();
+}
+
+void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
+#if defined(USE_X11)
+ XEvent* xev = event->native_event();
+ if (xev) {
+ XButtonEvent* xkey = &(xev->xbutton);
+ AppendNativeEventMask(&xkey->state);
+ }
+#elif defined(USE_OZONE)
+ NOTIMPLEMENTED() << "Modifier key is not handled";
+#endif
+ event->set_flags(event->flags() | modifier_flag_);
+}
+
+void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
+#if defined(USE_X11)
+ XEvent* xev = event->native_event();
+ if (xev) {
+ XIDeviceEvent* xievent =
+ static_cast<XIDeviceEvent*>(xev->xcookie.data);
+ if (xievent) {
+ AppendNativeEventMask(reinterpret_cast<unsigned int*>(
+ &xievent->mods.effective));
+ }
+ }
+#elif defined(USE_OZONE)
+ NOTIMPLEMENTED() << "Modifier key is not handled";
+#endif
+ event->set_flags(event->flags() | modifier_flag_);
+}
+
+} // namespace ash