Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ash / sticky_keys / sticky_keys_controller.cc
blob6237004f1f417f1a8793a8c0859bf0e486e69500
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"
17 namespace ash {
19 namespace {
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,
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;
44 } // namespace
46 ///////////////////////////////////////////////////////////////////////////////
47 // StickyKeys
48 StickyKeysController::StickyKeysController()
49 : enabled_(false),
50 mod3_enabled_(false),
51 altgr_enabled_(false) {
54 StickyKeysController::~StickyKeysController() {
57 void StickyKeysController::Enable(bool enabled) {
58 if (enabled_ != enabled) {
59 enabled_ = enabled;
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));
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);
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_);
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);
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);
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);
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);
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);
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);
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;
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());
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;
207 overlay_->Show(enabled_ && key_in_use);
210 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
211 return overlay_.get();
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) {
223 StickyKeysHandler::~StickyKeysHandler() {
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);
238 NOTREACHED();
239 return false;
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;
249 if (current_state_ == STICKY_KEY_STATE_DISABLED ||
250 !ShouldModifyMouseEvent(event)) {
251 return false;
253 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
254 current_state_ == STICKY_KEY_STATE_LOCKED);
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;
265 return false;
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);
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;
289 if (!direction_changed)
290 *mod_down_flags |= modifier_flag_;
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;
303 return false;
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;
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;
341 if (is_target_key) {
342 return type == ui::ET_KEY_PRESSED ?
343 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
345 return type == ui::ET_KEY_PRESSED ?
346 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
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;
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;
372 NOTREACHED();
373 return false;
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;
394 case OTHER_MODIFIER_DOWN:
395 case OTHER_MODIFIER_UP:
396 return false;
398 NOTREACHED();
399 return false;
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;
420 NOTREACHED();
421 return false;
424 } // namespace ash