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_
) {
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_COMMAND_DOWN
, search_sticky_key_
->current_state());
201 overlay_
->SetModifierKeyState(
202 ui::EF_ALTGR_DOWN
, altgr_sticky_key_
->current_state());
203 overlay_
->SetModifierKeyState(
204 ui::EF_MOD3_DOWN
, mod3_sticky_key_
->current_state());
207 shift_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
208 alt_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
209 altgr_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
210 ctrl_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
211 search_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
212 mod3_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
;
214 overlay_
->Show(enabled_
&& key_in_use
);
217 StickyKeysOverlay
* StickyKeysController::GetOverlayForTest() {
218 return overlay_
.get();
221 ///////////////////////////////////////////////////////////////////////////////
223 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag
)
224 : modifier_flag_(modifier_flag
),
225 current_state_(STICKY_KEY_STATE_DISABLED
),
226 preparing_to_enable_(false),
230 StickyKeysHandler::~StickyKeysHandler() {
233 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent
& event
,
234 ui::KeyboardCode key_code
,
237 switch (current_state_
) {
238 case STICKY_KEY_STATE_DISABLED
:
239 return HandleDisabledState(event
, key_code
);
240 case STICKY_KEY_STATE_ENABLED
:
241 return HandleEnabledState(event
, key_code
, mod_down_flags
, released
);
242 case STICKY_KEY_STATE_LOCKED
:
243 return HandleLockedState(event
, key_code
, mod_down_flags
, released
);
249 bool StickyKeysHandler::HandleMouseEvent(
250 const ui::MouseEvent
& event
,
253 if (ShouldModifyMouseEvent(event
))
254 preparing_to_enable_
= false;
256 if (current_state_
== STICKY_KEY_STATE_DISABLED
||
257 !ShouldModifyMouseEvent(event
)) {
260 DCHECK(current_state_
== STICKY_KEY_STATE_ENABLED
||
261 current_state_
== STICKY_KEY_STATE_LOCKED
);
263 *mod_down_flags
|= modifier_flag_
;
264 // Only disable on the mouse released event in normal, non-locked mode.
265 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
266 event
.type() != ui::ET_MOUSE_PRESSED
) {
267 current_state_
= STICKY_KEY_STATE_DISABLED
;
275 bool StickyKeysHandler::HandleScrollEvent(
276 const ui::ScrollEvent
& event
,
279 preparing_to_enable_
= false;
280 if (current_state_
== STICKY_KEY_STATE_DISABLED
)
282 DCHECK(current_state_
== STICKY_KEY_STATE_ENABLED
||
283 current_state_
== STICKY_KEY_STATE_LOCKED
);
285 // We detect a direction change if the current |scroll_delta_| is assigned
286 // and the offset of the current scroll event has the opposing sign.
287 bool direction_changed
= false;
288 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
289 event
.type() == ui::ET_SCROLL
) {
290 int offset
= event
.y_offset();
292 direction_changed
= offset
* scroll_delta_
<= 0;
293 scroll_delta_
= offset
;
296 if (!direction_changed
)
297 *mod_down_flags
|= modifier_flag_
;
299 // We want to modify all the scroll events in the scroll sequence, which ends
300 // with a fling start event. We also stop when the scroll sequence changes
302 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
303 (event
.type() == ui::ET_SCROLL_FLING_START
|| direction_changed
)) {
304 current_state_
= STICKY_KEY_STATE_DISABLED
;
313 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr
<ui::Event
>* new_event
) {
314 if (current_state_
!= STICKY_KEY_STATE_DISABLED
|| !modifier_up_event_
)
319 new_event
->reset(modifier_up_event_
.release());
323 StickyKeysHandler::KeyEventType
StickyKeysHandler::TranslateKeyEvent(
325 ui::KeyboardCode key_code
) {
326 bool is_target_key
= false;
327 if (key_code
== ui::VKEY_SHIFT
||
328 key_code
== ui::VKEY_LSHIFT
||
329 key_code
== ui::VKEY_RSHIFT
) {
330 is_target_key
= (modifier_flag_
== ui::EF_SHIFT_DOWN
);
331 } else if (key_code
== ui::VKEY_CONTROL
||
332 key_code
== ui::VKEY_LCONTROL
||
333 key_code
== ui::VKEY_RCONTROL
) {
334 is_target_key
= (modifier_flag_
== ui::EF_CONTROL_DOWN
);
335 } else if (key_code
== ui::VKEY_MENU
||
336 key_code
== ui::VKEY_LMENU
||
337 key_code
== ui::VKEY_RMENU
) {
338 is_target_key
= (modifier_flag_
== ui::EF_ALT_DOWN
);
339 } else if (key_code
== ui::VKEY_ALTGR
) {
340 is_target_key
= (modifier_flag_
== ui::EF_ALTGR_DOWN
);
341 } else if (key_code
== ui::VKEY_OEM_8
) {
342 is_target_key
= (modifier_flag_
== ui::EF_MOD3_DOWN
);
343 } else if (key_code
== ui::VKEY_LWIN
) {
344 is_target_key
= (modifier_flag_
== ui::EF_COMMAND_DOWN
);
346 return type
== ui::ET_KEY_PRESSED
?
347 NORMAL_KEY_DOWN
: NORMAL_KEY_UP
;
351 return type
== ui::ET_KEY_PRESSED
?
352 TARGET_MODIFIER_DOWN
: TARGET_MODIFIER_UP
;
354 return type
== ui::ET_KEY_PRESSED
?
355 OTHER_MODIFIER_DOWN
: OTHER_MODIFIER_UP
;
358 bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent
& event
,
359 ui::KeyboardCode key_code
) {
360 switch (TranslateKeyEvent(event
.type(), key_code
)) {
361 case TARGET_MODIFIER_UP
:
362 if (preparing_to_enable_
) {
363 preparing_to_enable_
= false;
365 current_state_
= STICKY_KEY_STATE_ENABLED
;
366 modifier_up_event_
.reset(new ui::KeyEvent(event
));
370 case TARGET_MODIFIER_DOWN
:
371 preparing_to_enable_
= true;
373 case NORMAL_KEY_DOWN
:
374 preparing_to_enable_
= false;
377 case OTHER_MODIFIER_DOWN
:
378 case OTHER_MODIFIER_UP
:
385 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent
& event
,
386 ui::KeyboardCode key_code
,
389 switch (TranslateKeyEvent(event
.type(), key_code
)) {
391 case TARGET_MODIFIER_DOWN
:
393 case TARGET_MODIFIER_UP
:
394 current_state_
= STICKY_KEY_STATE_LOCKED
;
395 modifier_up_event_
.reset();
397 case NORMAL_KEY_DOWN
: {
398 current_state_
= STICKY_KEY_STATE_DISABLED
;
399 *mod_down_flags
|= modifier_flag_
;
403 case OTHER_MODIFIER_DOWN
:
404 case OTHER_MODIFIER_UP
:
411 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent
& event
,
412 ui::KeyboardCode key_code
,
415 switch (TranslateKeyEvent(event
.type(), key_code
)) {
416 case TARGET_MODIFIER_DOWN
:
418 case TARGET_MODIFIER_UP
:
419 current_state_
= STICKY_KEY_STATE_DISABLED
;
421 case NORMAL_KEY_DOWN
:
423 *mod_down_flags
|= modifier_flag_
;
425 case OTHER_MODIFIER_DOWN
:
426 case OTHER_MODIFIER_UP
: