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
));
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
,
81 mod3_enabled_
= mod3_enabled
;
82 altgr_enabled_
= altgr_enabled
;
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
,
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
,
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
,
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
,
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
);
145 return RewriteUpdate(consumed
, released
, mod_down_flags
, flags
);
148 ui::EventRewriteStatus
StickyKeysController::RewriteMouseEvent(
149 const ui::MouseEvent
& event
,
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
);
157 return RewriteUpdate(consumed
, released
, mod_down_flags
, flags
);
160 ui::EventRewriteStatus
StickyKeysController::RewriteScrollEvent(
161 const ui::ScrollEvent
& event
,
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
);
169 return RewriteUpdate(consumed
, released
, mod_down_flags
, flags
);
172 ui::EventRewriteStatus
StickyKeysController::NextDispatchEvent(
173 scoped_ptr
<ui::Event
>* new_event
) {
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
);
182 return ui::EVENT_REWRITE_CONTINUE
;
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());
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 ///////////////////////////////////////////////////////////////////////////////
216 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag
)
217 : modifier_flag_(modifier_flag
),
218 current_state_(STICKY_KEY_STATE_DISABLED
),
219 preparing_to_enable_(false),
223 StickyKeysHandler::~StickyKeysHandler() {
226 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent
& event
,
227 ui::KeyboardCode key_code
,
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
);
242 bool StickyKeysHandler::HandleMouseEvent(
243 const ui::MouseEvent
& event
,
246 if (ShouldModifyMouseEvent(event
))
247 preparing_to_enable_
= false;
249 if (current_state_
== STICKY_KEY_STATE_DISABLED
||
250 !ShouldModifyMouseEvent(event
)) {
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
;
268 bool StickyKeysHandler::HandleScrollEvent(
269 const ui::ScrollEvent
& event
,
272 preparing_to_enable_
= false;
273 if (current_state_
== STICKY_KEY_STATE_DISABLED
)
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();
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
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
;
306 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr
<ui::Event
>* new_event
) {
307 if (current_state_
!= STICKY_KEY_STATE_DISABLED
|| !modifier_up_event_
)
312 new_event
->reset(modifier_up_event_
.release());
316 StickyKeysHandler::KeyEventType
StickyKeysHandler::TranslateKeyEvent(
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
);
337 return type
== ui::ET_KEY_PRESSED
?
338 NORMAL_KEY_DOWN
: NORMAL_KEY_UP
;
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;
356 current_state_
= STICKY_KEY_STATE_ENABLED
;
357 modifier_up_event_
.reset(new ui::KeyEvent(event
));
361 case TARGET_MODIFIER_DOWN
:
362 preparing_to_enable_
= true;
364 case NORMAL_KEY_DOWN
:
365 preparing_to_enable_
= false;
368 case OTHER_MODIFIER_DOWN
:
369 case OTHER_MODIFIER_UP
:
376 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent
& event
,
377 ui::KeyboardCode key_code
,
380 switch (TranslateKeyEvent(event
.type(), key_code
)) {
382 case TARGET_MODIFIER_DOWN
:
384 case TARGET_MODIFIER_UP
:
385 current_state_
= STICKY_KEY_STATE_LOCKED
;
386 modifier_up_event_
.reset();
388 case NORMAL_KEY_DOWN
: {
389 current_state_
= STICKY_KEY_STATE_DISABLED
;
390 *mod_down_flags
|= modifier_flag_
;
394 case OTHER_MODIFIER_DOWN
:
395 case OTHER_MODIFIER_UP
:
402 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent
& event
,
403 ui::KeyboardCode key_code
,
406 switch (TranslateKeyEvent(event
.type(), key_code
)) {
407 case TARGET_MODIFIER_DOWN
:
409 case TARGET_MODIFIER_UP
:
410 current_state_
= STICKY_KEY_STATE_DISABLED
;
412 case NORMAL_KEY_DOWN
:
414 *mod_down_flags
|= modifier_flag_
;
416 case OTHER_MODIFIER_DOWN
:
417 case OTHER_MODIFIER_UP
: