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"
8 #include <X11/extensions/XInput2.h>
13 #include "ash/sticky_keys/sticky_keys_overlay.h"
14 #include "base/basictypes.h"
15 #include "base/debug/stack_trace.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_tracker.h"
18 #include "ui/aura/window_tree_host.h"
19 #include "ui/events/event.h"
20 #include "ui/events/event_processor.h"
21 #include "ui/events/keycodes/keyboard_code_conversion.h"
27 // Returns true if the type of mouse event should be modified by sticky keys.
28 bool ShouldModifyMouseEvent(ui::MouseEvent
* event
) {
29 ui::EventType type
= event
->type();
30 return type
== ui::ET_MOUSE_PRESSED
|| type
== ui::ET_MOUSE_RELEASED
||
31 type
== ui::ET_MOUSEWHEEL
;
34 // An implementation of StickyKeysHandler::StickyKeysHandlerDelegate.
35 class StickyKeysHandlerDelegateImpl
:
36 public StickyKeysHandler::StickyKeysHandlerDelegate
{
38 StickyKeysHandlerDelegateImpl();
39 virtual ~StickyKeysHandlerDelegateImpl();
41 // StickyKeysHandlerDelegate overrides.
42 virtual void DispatchKeyEvent(ui::KeyEvent
* event
,
43 aura::Window
* target
) OVERRIDE
;
45 virtual void DispatchMouseEvent(ui::MouseEvent
* event
,
46 aura::Window
* target
) OVERRIDE
;
48 virtual void DispatchScrollEvent(ui::ScrollEvent
* event
,
49 aura::Window
* target
) OVERRIDE
;
51 void DispatchEvent(ui::Event
* event
, aura::Window
* target
);
53 DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl
);
56 StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() {
59 StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() {
62 void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent
* event
,
63 aura::Window
* target
) {
64 DispatchEvent(event
, target
);
67 void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent
* event
,
68 aura::Window
* target
) {
70 // We need to send a new, untransformed mouse event to the host.
71 if (event
->IsMouseWheelEvent()) {
72 aura::Window
* source
= static_cast<aura::Window
*>(event
->target());
73 ui::MouseWheelEvent
new_event(*static_cast<ui::MouseWheelEvent
*>(event
),
75 source
->GetRootWindow());
76 // Transform the location back to host coordinates before dispatching.
77 new_event
.UpdateForRootTransform(source
->GetHost()->GetRootTransform());
78 DispatchEvent(&new_event
, target
);
80 aura::Window
* source
= static_cast<aura::Window
*>(event
->target());
81 ui::MouseEvent
new_event(*event
, source
, source
->GetRootWindow());
82 // Transform the location back to host coordinates before dispatching.
83 new_event
.UpdateForRootTransform(source
->GetHost()->GetRootTransform());
84 DispatchEvent(&new_event
, target
);
88 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
89 ui::ScrollEvent
* event
,
90 aura::Window
* target
) {
91 DispatchEvent(event
, target
);
94 void StickyKeysHandlerDelegateImpl::DispatchEvent(ui::Event
* event
,
95 aura::Window
* target
) {
97 ui::EventDispatchDetails details
=
98 target
->GetHost()->event_processor()->OnEventFromSource(event
);
99 if (details
.dispatcher_destroyed
)
105 ///////////////////////////////////////////////////////////////////////////////
107 StickyKeysController::StickyKeysController()
109 mod3_enabled_(false),
110 altgr_enabled_(false) {
113 StickyKeysController::~StickyKeysController() {
116 void StickyKeysController::Enable(bool enabled
) {
117 if (enabled_
!= enabled
) {
120 // Reset key handlers when activating sticky keys to ensure all
121 // the handlers' states are reset.
123 shift_sticky_key_
.reset(
124 new StickyKeysHandler(ui::EF_SHIFT_DOWN
,
125 new StickyKeysHandlerDelegateImpl()));
126 alt_sticky_key_
.reset(
127 new StickyKeysHandler(ui::EF_ALT_DOWN
,
128 new StickyKeysHandlerDelegateImpl()));
129 altgr_sticky_key_
.reset(
130 new StickyKeysHandler(ui::EF_ALTGR_DOWN
,
131 new StickyKeysHandlerDelegateImpl()));
132 ctrl_sticky_key_
.reset(
133 new StickyKeysHandler(ui::EF_CONTROL_DOWN
,
134 new StickyKeysHandlerDelegateImpl()));
135 mod3_sticky_key_
.reset(
136 new StickyKeysHandler(ui::EF_MOD3_DOWN
,
137 new StickyKeysHandlerDelegateImpl()));
139 overlay_
.reset(new StickyKeysOverlay());
140 overlay_
->SetModifierVisible(ui::EF_ALTGR_DOWN
, altgr_enabled_
);
141 overlay_
->SetModifierVisible(ui::EF_MOD3_DOWN
, mod3_enabled_
);
142 } else if (overlay_
) {
143 overlay_
->Show(false);
148 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled
,
149 bool altgr_enabled
) {
150 mod3_enabled_
= mod3_enabled
;
151 altgr_enabled_
= altgr_enabled
;
153 overlay_
->SetModifierVisible(ui::EF_ALTGR_DOWN
, altgr_enabled_
);
154 overlay_
->SetModifierVisible(ui::EF_MOD3_DOWN
, mod3_enabled_
);
158 bool StickyKeysController::HandleKeyEvent(ui::KeyEvent
* event
) {
159 return shift_sticky_key_
->HandleKeyEvent(event
) ||
160 alt_sticky_key_
->HandleKeyEvent(event
) ||
161 altgr_sticky_key_
->HandleKeyEvent(event
) ||
162 ctrl_sticky_key_
->HandleKeyEvent(event
) ||
163 mod3_sticky_key_
->HandleKeyEvent(event
);
166 bool StickyKeysController::HandleMouseEvent(ui::MouseEvent
* event
) {
167 return shift_sticky_key_
->HandleMouseEvent(event
) ||
168 alt_sticky_key_
->HandleMouseEvent(event
) ||
169 altgr_sticky_key_
->HandleMouseEvent(event
) ||
170 ctrl_sticky_key_
->HandleMouseEvent(event
) ||
171 mod3_sticky_key_
->HandleMouseEvent(event
);
174 bool StickyKeysController::HandleScrollEvent(ui::ScrollEvent
* event
) {
175 return shift_sticky_key_
->HandleScrollEvent(event
) ||
176 alt_sticky_key_
->HandleScrollEvent(event
) ||
177 altgr_sticky_key_
->HandleScrollEvent(event
) ||
178 ctrl_sticky_key_
->HandleScrollEvent(event
) ||
179 mod3_sticky_key_
->HandleScrollEvent(event
);
182 void StickyKeysController::OnKeyEvent(ui::KeyEvent
* event
) {
183 // Do not consume a translated key event which is generated by an IME.
184 if (event
->type() == ui::ET_TRANSLATED_KEY_PRESS
||
185 event
->type() == ui::ET_TRANSLATED_KEY_RELEASE
) {
190 if (HandleKeyEvent(event
))
191 event
->StopPropagation();
196 void StickyKeysController::OnMouseEvent(ui::MouseEvent
* event
) {
198 if (HandleMouseEvent(event
))
199 event
->StopPropagation();
204 void StickyKeysController::OnScrollEvent(ui::ScrollEvent
* event
) {
206 if (HandleScrollEvent(event
))
207 event
->StopPropagation();
212 void StickyKeysController::UpdateOverlay() {
213 overlay_
->SetModifierKeyState(
214 ui::EF_SHIFT_DOWN
, shift_sticky_key_
->current_state());
215 overlay_
->SetModifierKeyState(
216 ui::EF_CONTROL_DOWN
, ctrl_sticky_key_
->current_state());
217 overlay_
->SetModifierKeyState(
218 ui::EF_ALT_DOWN
, alt_sticky_key_
->current_state());
219 overlay_
->SetModifierKeyState(
220 ui::EF_ALTGR_DOWN
, altgr_sticky_key_
->current_state());
221 overlay_
->SetModifierKeyState(
222 ui::EF_MOD3_DOWN
, mod3_sticky_key_
->current_state());
225 shift_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
226 alt_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
227 altgr_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
228 ctrl_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
229 mod3_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
;
231 overlay_
->Show(enabled_
&& key_in_use
);
234 StickyKeysOverlay
* StickyKeysController::GetOverlayForTest() {
235 return overlay_
.get();
238 ///////////////////////////////////////////////////////////////////////////////
240 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag
,
241 StickyKeysHandlerDelegate
* delegate
)
242 : modifier_flag_(modifier_flag
),
243 current_state_(STICKY_KEY_STATE_DISABLED
),
244 event_from_myself_(false),
245 preparing_to_enable_(false),
247 delegate_(delegate
) {
250 StickyKeysHandler::~StickyKeysHandler() {
253 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
256 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
259 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent
* event
) {
260 if (event_from_myself_
)
261 return false; // Do not handle self-generated key event.
262 switch (current_state_
) {
263 case STICKY_KEY_STATE_DISABLED
:
264 return HandleDisabledState(event
);
265 case STICKY_KEY_STATE_ENABLED
:
266 return HandleEnabledState(event
);
267 case STICKY_KEY_STATE_LOCKED
:
268 return HandleLockedState(event
);
274 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent
* event
) {
275 if (ShouldModifyMouseEvent(event
))
276 preparing_to_enable_
= false;
278 if (event_from_myself_
|| current_state_
== STICKY_KEY_STATE_DISABLED
279 || !ShouldModifyMouseEvent(event
)) {
282 DCHECK(current_state_
== STICKY_KEY_STATE_ENABLED
||
283 current_state_
== STICKY_KEY_STATE_LOCKED
);
285 AppendModifier(event
);
286 // Only disable on the mouse released event in normal, non-locked mode.
287 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
288 event
->type() != ui::ET_MOUSE_PRESSED
) {
289 current_state_
= STICKY_KEY_STATE_DISABLED
;
290 DispatchEventAndReleaseModifier(event
);
297 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent
* event
) {
298 preparing_to_enable_
= false;
299 if (event_from_myself_
|| current_state_
== STICKY_KEY_STATE_DISABLED
)
301 DCHECK(current_state_
== STICKY_KEY_STATE_ENABLED
||
302 current_state_
== STICKY_KEY_STATE_LOCKED
);
304 // We detect a direction change if the current |scroll_delta_| is assigned
305 // and the offset of the current scroll event has the opposing sign.
306 bool direction_changed
= false;
307 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
308 event
->type() == ui::ET_SCROLL
) {
309 int offset
= event
->y_offset();
311 direction_changed
= offset
* scroll_delta_
<= 0;
312 scroll_delta_
= offset
;
315 if (!direction_changed
)
316 AppendModifier(event
);
318 // We want to modify all the scroll events in the scroll sequence, which ends
319 // with a fling start event. We also stop when the scroll sequence changes
321 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
322 (event
->type() == ui::ET_SCROLL_FLING_START
|| direction_changed
)) {
323 current_state_
= STICKY_KEY_STATE_DISABLED
;
325 DispatchEventAndReleaseModifier(event
);
332 StickyKeysHandler::KeyEventType
333 StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent
* event
) {
334 bool is_target_key
= false;
335 if (event
->key_code() == ui::VKEY_SHIFT
||
336 event
->key_code() == ui::VKEY_LSHIFT
||
337 event
->key_code() == ui::VKEY_RSHIFT
) {
338 is_target_key
= (modifier_flag_
== ui::EF_SHIFT_DOWN
);
339 } else if (event
->key_code() == ui::VKEY_CONTROL
||
340 event
->key_code() == ui::VKEY_LCONTROL
||
341 event
->key_code() == ui::VKEY_RCONTROL
) {
342 is_target_key
= (modifier_flag_
== ui::EF_CONTROL_DOWN
);
343 } else if (event
->key_code() == ui::VKEY_MENU
||
344 event
->key_code() == ui::VKEY_LMENU
||
345 event
->key_code() == ui::VKEY_RMENU
) {
346 is_target_key
= (modifier_flag_
== ui::EF_ALT_DOWN
);
347 } else if (event
->key_code() == ui::VKEY_ALTGR
) {
348 is_target_key
= (modifier_flag_
== ui::EF_ALTGR_DOWN
);
349 } else if (event
->key_code() == ui::VKEY_OEM_8
) {
350 is_target_key
= (modifier_flag_
== ui::EF_MOD3_DOWN
);
352 return event
->type() == ui::ET_KEY_PRESSED
?
353 NORMAL_KEY_DOWN
: NORMAL_KEY_UP
;
357 return event
->type() == ui::ET_KEY_PRESSED
?
358 TARGET_MODIFIER_DOWN
: TARGET_MODIFIER_UP
;
360 return event
->type() == ui::ET_KEY_PRESSED
?
361 OTHER_MODIFIER_DOWN
: OTHER_MODIFIER_UP
;
364 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent
* event
) {
365 switch (TranslateKeyEvent(event
)) {
366 case TARGET_MODIFIER_UP
:
367 if (preparing_to_enable_
) {
368 preparing_to_enable_
= false;
370 current_state_
= STICKY_KEY_STATE_ENABLED
;
371 modifier_up_event_
.reset(new ui::KeyEvent(*event
));
375 case TARGET_MODIFIER_DOWN
:
376 preparing_to_enable_
= true;
378 case NORMAL_KEY_DOWN
:
379 preparing_to_enable_
= false;
382 case OTHER_MODIFIER_DOWN
:
383 case OTHER_MODIFIER_UP
:
390 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent
* event
) {
391 switch (TranslateKeyEvent(event
)) {
393 case TARGET_MODIFIER_DOWN
:
395 case TARGET_MODIFIER_UP
:
396 current_state_
= STICKY_KEY_STATE_LOCKED
;
397 modifier_up_event_
.reset();
399 case NORMAL_KEY_DOWN
: {
400 current_state_
= STICKY_KEY_STATE_DISABLED
;
401 AppendModifier(event
);
402 DispatchEventAndReleaseModifier(event
);
405 case OTHER_MODIFIER_DOWN
:
406 case OTHER_MODIFIER_UP
:
413 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent
* event
) {
414 switch (TranslateKeyEvent(event
)) {
415 case TARGET_MODIFIER_DOWN
:
417 case TARGET_MODIFIER_UP
:
418 current_state_
= STICKY_KEY_STATE_DISABLED
;
420 case NORMAL_KEY_DOWN
:
422 AppendModifier(event
);
424 case OTHER_MODIFIER_DOWN
:
425 case OTHER_MODIFIER_UP
:
432 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event
* event
) {
433 DCHECK(event
->IsKeyEvent() ||
434 event
->IsMouseEvent() ||
435 event
->IsScrollEvent());
436 DCHECK(modifier_up_event_
.get());
437 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
439 aura::Window
* root_window
= target
->GetRootWindow();
442 aura::WindowTracker window_tracker
;
443 window_tracker
.Add(target
);
445 event_from_myself_
= true;
446 if (event
->IsKeyEvent()) {
447 delegate_
->DispatchKeyEvent(static_cast<ui::KeyEvent
*>(event
), target
);
448 } else if (event
->IsMouseEvent()) {
449 delegate_
->DispatchMouseEvent(static_cast<ui::MouseEvent
*>(event
), target
);
451 delegate_
->DispatchScrollEvent(
452 static_cast<ui::ScrollEvent
*>(event
), target
);
455 // The action triggered above may have destroyed the event target, in which
456 // case we will dispatch the modifier up event to the root window instead.
457 aura::Window
* modifier_up_target
=
458 window_tracker
.Contains(target
) ? target
: root_window
;
459 delegate_
->DispatchKeyEvent(modifier_up_event_
.get(), modifier_up_target
);
460 event_from_myself_
= false;
463 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state
) {
465 unsigned int& state_ref
= *state
;
466 switch (modifier_flag_
) {
467 case ui::EF_CONTROL_DOWN
:
468 state_ref
|= ControlMask
;
470 case ui::EF_ALT_DOWN
:
471 state_ref
|= Mod1Mask
;
473 case ui::EF_ALTGR_DOWN
:
474 state_ref
|= Mod5Mask
;
476 case ui::EF_SHIFT_DOWN
:
477 state_ref
|= ShiftMask
;
479 case ui::EF_MOD3_DOWN
:
480 state_ref
|= Mod3Mask
;
488 void StickyKeysHandler::AppendModifier(ui::KeyEvent
* event
) {
490 XEvent
* xev
= event
->native_event();
492 XKeyEvent
* xkey
= &(xev
->xkey
);
493 AppendNativeEventMask(&xkey
->state
);
495 #elif defined(USE_OZONE)
496 NOTIMPLEMENTED() << "Modifier key is not handled";
498 event
->set_flags(event
->flags() | modifier_flag_
);
499 event
->set_character(ui::GetCharacterFromKeyCode(event
->key_code(),
501 event
->NormalizeFlags();
504 void StickyKeysHandler::AppendModifier(ui::MouseEvent
* event
) {
506 // The native mouse event can either be a classic X button event or an
507 // XInput2 button event.
508 XEvent
* xev
= event
->native_event();
512 case ButtonRelease
: {
513 XButtonEvent
* xkey
= &(xev
->xbutton
);
514 AppendNativeEventMask(&xkey
->state
);
518 XIDeviceEvent
* xievent
=
519 static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
520 CHECK(xievent
->evtype
== XI_ButtonPress
||
521 xievent
->evtype
== XI_ButtonRelease
);
522 AppendNativeEventMask(
523 reinterpret_cast<unsigned int*>(&xievent
->mods
.effective
));
530 #elif defined(USE_OZONE)
531 NOTIMPLEMENTED() << "Modifier key is not handled";
533 event
->set_flags(event
->flags() | modifier_flag_
);
536 void StickyKeysHandler::AppendModifier(ui::ScrollEvent
* event
) {
538 XEvent
* xev
= event
->native_event();
540 XIDeviceEvent
* xievent
=
541 static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
543 AppendNativeEventMask(reinterpret_cast<unsigned int*>(
544 &xievent
->mods
.effective
));
547 #elif defined(USE_OZONE)
548 NOTIMPLEMENTED() << "Modifier key is not handled";
550 event
->set_flags(event
->flags() | modifier_flag_
);