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.
5 #include "ash/sticky_keys/sticky_keys_controller.h"
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"
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;
28 // Handle the common tail of event rewriting.
29 ui::EventRewriteStatus RewriteUpdate(bool consumed,
33 int changed_down_flags = mod_down_flags & ~*flags;
34 *flags |= mod_down_flags;
36 return ui::EVENT_REWRITE_DISCARD;
38 return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
39 if (changed_down_flags)
40 return ui::EVENT_REWRITE_REWRITTEN;
41 return ui::EVENT_REWRITE_CONTINUE;
46 ///////////////////////////////////////////////////////////////////////////////
48 StickyKeysController::StickyKeysController()
51 altgr_enabled_(false) {
54 StickyKeysController::~StickyKeysController() {
57 void StickyKeysController::Enable(bool enabled) {
58 if (enabled_ != enabled) {
61 // Reset key handlers when activating sticky keys to ensure all
62 // the handlers' states are reset.
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 search_sticky_key_.reset(new StickyKeysHandler(ui::EF_COMMAND_DOWN));
71 overlay_.reset(new StickyKeysOverlay());
72 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
73 overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
74 } else if (overlay_) {
75 overlay_->Show(false);
80 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
82 mod3_enabled_ = mod3_enabled;
83 altgr_enabled_ = altgr_enabled;
85 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
86 overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
90 bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
91 ui::KeyboardCode key_code,
94 return shift_sticky_key_->HandleKeyEvent(
95 event, key_code, mod_down_flags, released) ||
96 alt_sticky_key_->HandleKeyEvent(
97 event, key_code, mod_down_flags, released) ||
98 altgr_sticky_key_->HandleKeyEvent(
99 event, key_code, mod_down_flags, released) ||
100 ctrl_sticky_key_->HandleKeyEvent(
101 event, key_code, mod_down_flags, released) ||
102 mod3_sticky_key_->HandleKeyEvent(
103 event, key_code, mod_down_flags, released) ||
104 search_sticky_key_->HandleKeyEvent(
105 event, key_code, mod_down_flags, released);
108 bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
111 return shift_sticky_key_->HandleMouseEvent(
112 event, mod_down_flags, released) ||
113 alt_sticky_key_->HandleMouseEvent(
114 event, mod_down_flags, released) ||
115 altgr_sticky_key_->HandleMouseEvent(
116 event, mod_down_flags, released) ||
117 ctrl_sticky_key_->HandleMouseEvent(
118 event, mod_down_flags, released) ||
119 mod3_sticky_key_->HandleMouseEvent(
120 event, mod_down_flags, released);
123 bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
126 return shift_sticky_key_->HandleScrollEvent(
127 event, mod_down_flags, released) ||
128 alt_sticky_key_->HandleScrollEvent(
129 event, mod_down_flags, released) ||
130 altgr_sticky_key_->HandleScrollEvent(
131 event, mod_down_flags, released) ||
132 ctrl_sticky_key_->HandleScrollEvent(
133 event, mod_down_flags, released) ||
134 mod3_sticky_key_->HandleScrollEvent(
135 event, mod_down_flags, released);
138 ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
139 const ui::KeyEvent& event,
140 ui::KeyboardCode key_code,
143 return ui::EVENT_REWRITE_CONTINUE;
144 int mod_down_flags = 0;
145 bool released = false;
146 bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
148 return RewriteUpdate(consumed, released, mod_down_flags, flags);
151 ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
152 const ui::MouseEvent& event,
155 return ui::EVENT_REWRITE_CONTINUE;
156 int mod_down_flags = 0;
157 bool released = false;
158 bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
160 return RewriteUpdate(consumed, released, mod_down_flags, flags);
163 ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
164 const ui::ScrollEvent& event,
167 return ui::EVENT_REWRITE_CONTINUE;
168 int mod_down_flags = 0;
169 bool released = false;
170 bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
172 return RewriteUpdate(consumed, released, mod_down_flags, flags);
175 ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
176 scoped_ptr<ui::Event>* new_event) {
179 int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) +
180 alt_sticky_key_->GetModifierUpEvent(new_event) +
181 altgr_sticky_key_->GetModifierUpEvent(new_event) +
182 ctrl_sticky_key_->GetModifierUpEvent(new_event) +
183 mod3_sticky_key_->GetModifierUpEvent(new_event) +
184 search_sticky_key_->GetModifierUpEvent(new_event);
186 return ui::EVENT_REWRITE_CONTINUE;
188 return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
189 return ui::EVENT_REWRITE_REWRITTEN;
192 void StickyKeysController::UpdateOverlay() {
193 overlay_->SetModifierKeyState(
194 ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
195 overlay_->SetModifierKeyState(
196 ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
197 overlay_->SetModifierKeyState(
198 ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
199 overlay_->SetModifierKeyState(
200 ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
201 overlay_->SetModifierKeyState(
202 ui::EF_MOD3_DOWN, mod3_sticky_key_->current_state());
205 shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
206 alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
207 altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
208 ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
209 mod3_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
211 overlay_->Show(enabled_ && key_in_use);
214 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
215 return overlay_.get();
218 ///////////////////////////////////////////////////////////////////////////////
220 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag)
221 : modifier_flag_(modifier_flag),
222 current_state_(STICKY_KEY_STATE_DISABLED),
223 preparing_to_enable_(false),
227 StickyKeysHandler::~StickyKeysHandler() {
230 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
231 ui::KeyboardCode key_code,
234 switch (current_state_) {
235 case STICKY_KEY_STATE_DISABLED:
236 return HandleDisabledState(event, key_code);
237 case STICKY_KEY_STATE_ENABLED:
238 return HandleEnabledState(event, key_code, mod_down_flags, released);
239 case STICKY_KEY_STATE_LOCKED:
240 return HandleLockedState(event, key_code, mod_down_flags, released);
246 bool StickyKeysHandler::HandleMouseEvent(
247 const ui::MouseEvent& event,
250 if (ShouldModifyMouseEvent(event))
251 preparing_to_enable_ = false;
253 if (current_state_ == STICKY_KEY_STATE_DISABLED ||
254 !ShouldModifyMouseEvent(event)) {
257 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
258 current_state_ == STICKY_KEY_STATE_LOCKED);
260 *mod_down_flags |= modifier_flag_;
261 // Only disable on the mouse released event in normal, non-locked mode.
262 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
263 event.type() != ui::ET_MOUSE_PRESSED) {
264 current_state_ = STICKY_KEY_STATE_DISABLED;
272 bool StickyKeysHandler::HandleScrollEvent(
273 const ui::ScrollEvent& event,
276 preparing_to_enable_ = false;
277 if (current_state_ == STICKY_KEY_STATE_DISABLED)
279 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
280 current_state_ == STICKY_KEY_STATE_LOCKED);
282 // We detect a direction change if the current |scroll_delta_| is assigned
283 // and the offset of the current scroll event has the opposing sign.
284 bool direction_changed = false;
285 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
286 event.type() == ui::ET_SCROLL) {
287 int offset = event.y_offset();
289 direction_changed = offset * scroll_delta_ <= 0;
290 scroll_delta_ = offset;
293 if (!direction_changed)
294 *mod_down_flags |= modifier_flag_;
296 // We want to modify all the scroll events in the scroll sequence, which ends
297 // with a fling start event. We also stop when the scroll sequence changes
299 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
300 (event.type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
301 current_state_ = STICKY_KEY_STATE_DISABLED;
310 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr<ui::Event>* new_event) {
311 if (current_state_ != STICKY_KEY_STATE_DISABLED || !modifier_up_event_)
316 new_event->reset(modifier_up_event_.release());
320 StickyKeysHandler::KeyEventType StickyKeysHandler::TranslateKeyEvent(
322 ui::KeyboardCode key_code) {
323 bool is_target_key = false;
324 if (key_code == ui::VKEY_SHIFT ||
325 key_code == ui::VKEY_LSHIFT ||
326 key_code == ui::VKEY_RSHIFT) {
327 is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
328 } else if (key_code == ui::VKEY_CONTROL ||
329 key_code == ui::VKEY_LCONTROL ||
330 key_code == ui::VKEY_RCONTROL) {
331 is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
332 } else if (key_code == ui::VKEY_MENU ||
333 key_code == ui::VKEY_LMENU ||
334 key_code == ui::VKEY_RMENU) {
335 is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
336 } else if (key_code == ui::VKEY_ALTGR) {
337 is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
338 } else if (key_code == ui::VKEY_OEM_8) {
339 is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN);
340 } else if (key_code == ui::VKEY_LWIN) {
341 is_target_key = (modifier_flag_ == ui::EF_COMMAND_DOWN);
343 return type == ui::ET_KEY_PRESSED ?
344 NORMAL_KEY_DOWN : NORMAL_KEY_UP;
348 return type == ui::ET_KEY_PRESSED ?
349 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
351 return type == ui::ET_KEY_PRESSED ?
352 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
355 bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event,
356 ui::KeyboardCode key_code) {
357 switch (TranslateKeyEvent(event.type(), key_code)) {
358 case TARGET_MODIFIER_UP:
359 if (preparing_to_enable_) {
360 preparing_to_enable_ = false;
362 current_state_ = STICKY_KEY_STATE_ENABLED;
363 modifier_up_event_.reset(new ui::KeyEvent(event));
367 case TARGET_MODIFIER_DOWN:
368 preparing_to_enable_ = true;
370 case NORMAL_KEY_DOWN:
371 preparing_to_enable_ = false;
374 case OTHER_MODIFIER_DOWN:
375 case OTHER_MODIFIER_UP:
382 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event,
383 ui::KeyboardCode key_code,
386 switch (TranslateKeyEvent(event.type(), key_code)) {
388 case TARGET_MODIFIER_DOWN:
390 case TARGET_MODIFIER_UP:
391 current_state_ = STICKY_KEY_STATE_LOCKED;
392 modifier_up_event_.reset();
394 case NORMAL_KEY_DOWN: {
395 current_state_ = STICKY_KEY_STATE_DISABLED;
396 *mod_down_flags |= modifier_flag_;
400 case OTHER_MODIFIER_DOWN:
401 case OTHER_MODIFIER_UP:
408 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event,
409 ui::KeyboardCode key_code,
412 switch (TranslateKeyEvent(event.type(), key_code)) {
413 case TARGET_MODIFIER_DOWN:
415 case TARGET_MODIFIER_UP:
416 current_state_ = STICKY_KEY_STATE_DISABLED;
418 case NORMAL_KEY_DOWN:
420 *mod_down_flags |= modifier_flag_;
422 case OTHER_MODIFIER_DOWN:
423 case OTHER_MODIFIER_UP: