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_event_dispatcher.h"
18 #include "ui/aura/window_tracker.h"
19 #include "ui/events/event.h"
20 #include "ui/events/keycodes/keyboard_code_conversion.h"
26 // Returns true if the type of mouse event should be modified by sticky keys.
27 bool ShouldModifyMouseEvent(ui::MouseEvent
* event
) {
28 ui::EventType type
= event
->type();
29 return type
== ui::ET_MOUSE_PRESSED
|| type
== ui::ET_MOUSE_RELEASED
||
30 type
== ui::ET_MOUSEWHEEL
;
33 // An implementation of StickyKeysHandler::StickyKeysHandlerDelegate.
34 class StickyKeysHandlerDelegateImpl
:
35 public StickyKeysHandler::StickyKeysHandlerDelegate
{
37 StickyKeysHandlerDelegateImpl();
38 virtual ~StickyKeysHandlerDelegateImpl();
40 // StickyKeysHandlerDelegate overrides.
41 virtual void DispatchKeyEvent(ui::KeyEvent
* event
,
42 aura::Window
* target
) OVERRIDE
;
44 virtual void DispatchMouseEvent(ui::MouseEvent
* event
,
45 aura::Window
* target
) OVERRIDE
;
47 virtual void DispatchScrollEvent(ui::ScrollEvent
* event
,
48 aura::Window
* target
) OVERRIDE
;
50 void DispatchEvent(ui::Event
* event
, aura::Window
* target
);
52 DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl
);
55 StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() {
58 StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() {
61 void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent
* event
,
62 aura::Window
* target
) {
63 DispatchEvent(event
, target
);
66 void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent
* event
,
67 aura::Window
* target
) {
69 // We need to send a new, untransformed mouse event to the host.
70 if (event
->IsMouseWheelEvent()) {
71 ui::MouseWheelEvent
new_event(*static_cast<ui::MouseWheelEvent
*>(event
));
72 DispatchEvent(&new_event
, target
);
74 ui::MouseEvent
new_event(*event
, target
, target
->GetRootWindow());
75 DispatchEvent(&new_event
, target
);
79 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
80 ui::ScrollEvent
* event
,
81 aura::Window
* target
) {
82 DispatchEvent(event
, target
);
85 void StickyKeysHandlerDelegateImpl::DispatchEvent(ui::Event
* event
,
86 aura::Window
* target
) {
88 ui::EventDispatchDetails details
=
89 target
->GetDispatcher()->OnEventFromSource(event
);
90 if (details
.dispatcher_destroyed
)
96 ///////////////////////////////////////////////////////////////////////////////
98 StickyKeysController::StickyKeysController()
102 StickyKeysController::~StickyKeysController() {
105 void StickyKeysController::Enable(bool enabled
) {
106 if (enabled_
!= enabled
) {
109 // Reset key handlers when activating sticky keys to ensure all
110 // the handlers' states are reset.
112 shift_sticky_key_
.reset(
113 new StickyKeysHandler(ui::EF_SHIFT_DOWN
,
114 new StickyKeysHandlerDelegateImpl()));
115 alt_sticky_key_
.reset(
116 new StickyKeysHandler(ui::EF_ALT_DOWN
,
117 new StickyKeysHandlerDelegateImpl()));
118 ctrl_sticky_key_
.reset(
119 new StickyKeysHandler(ui::EF_CONTROL_DOWN
,
120 new StickyKeysHandlerDelegateImpl()));
122 overlay_
.reset(new StickyKeysOverlay());
123 } else if (overlay_
.get()) {
124 overlay_
->Show(false);
129 bool StickyKeysController::HandleKeyEvent(ui::KeyEvent
* event
) {
130 return shift_sticky_key_
->HandleKeyEvent(event
) ||
131 alt_sticky_key_
->HandleKeyEvent(event
) ||
132 ctrl_sticky_key_
->HandleKeyEvent(event
);
135 bool StickyKeysController::HandleMouseEvent(ui::MouseEvent
* event
) {
136 return shift_sticky_key_
->HandleMouseEvent(event
) ||
137 alt_sticky_key_
->HandleMouseEvent(event
) ||
138 ctrl_sticky_key_
->HandleMouseEvent(event
);
141 bool StickyKeysController::HandleScrollEvent(ui::ScrollEvent
* event
) {
142 return shift_sticky_key_
->HandleScrollEvent(event
) ||
143 alt_sticky_key_
->HandleScrollEvent(event
) ||
144 ctrl_sticky_key_
->HandleScrollEvent(event
);
147 void StickyKeysController::OnKeyEvent(ui::KeyEvent
* event
) {
148 // Do not consume a translated key event which is generated by an IME.
149 if (event
->type() == ui::ET_TRANSLATED_KEY_PRESS
||
150 event
->type() == ui::ET_TRANSLATED_KEY_RELEASE
) {
155 if (HandleKeyEvent(event
))
156 event
->StopPropagation();
161 void StickyKeysController::OnMouseEvent(ui::MouseEvent
* event
) {
163 if (HandleMouseEvent(event
))
164 event
->StopPropagation();
169 void StickyKeysController::OnScrollEvent(ui::ScrollEvent
* event
) {
171 if (HandleScrollEvent(event
))
172 event
->StopPropagation();
177 void StickyKeysController::UpdateOverlay() {
178 overlay_
->SetModifierKeyState(
179 ui::EF_SHIFT_DOWN
, shift_sticky_key_
->current_state());
180 overlay_
->SetModifierKeyState(
181 ui::EF_CONTROL_DOWN
, ctrl_sticky_key_
->current_state());
182 overlay_
->SetModifierKeyState(
183 ui::EF_ALT_DOWN
, alt_sticky_key_
->current_state());
186 shift_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
187 alt_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
||
188 ctrl_sticky_key_
->current_state() != STICKY_KEY_STATE_DISABLED
;
190 overlay_
->Show(enabled_
&& key_in_use
);
193 StickyKeysOverlay
* StickyKeysController::GetOverlayForTest() {
194 return overlay_
.get();
197 ///////////////////////////////////////////////////////////////////////////////
199 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag
,
200 StickyKeysHandlerDelegate
* delegate
)
201 : modifier_flag_(modifier_flag
),
202 current_state_(STICKY_KEY_STATE_DISABLED
),
203 event_from_myself_(false),
204 preparing_to_enable_(false),
206 delegate_(delegate
) {
209 StickyKeysHandler::~StickyKeysHandler() {
212 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
215 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
218 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent
* event
) {
219 if (event_from_myself_
)
220 return false; // Do not handle self-generated key event.
221 switch (current_state_
) {
222 case STICKY_KEY_STATE_DISABLED
:
223 return HandleDisabledState(event
);
224 case STICKY_KEY_STATE_ENABLED
:
225 return HandleEnabledState(event
);
226 case STICKY_KEY_STATE_LOCKED
:
227 return HandleLockedState(event
);
233 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent
* event
) {
234 if (ShouldModifyMouseEvent(event
))
235 preparing_to_enable_
= false;
237 if (event_from_myself_
|| current_state_
== STICKY_KEY_STATE_DISABLED
238 || !ShouldModifyMouseEvent(event
)) {
241 DCHECK(current_state_
== STICKY_KEY_STATE_ENABLED
||
242 current_state_
== STICKY_KEY_STATE_LOCKED
);
244 AppendModifier(event
);
245 // Only disable on the mouse released event in normal, non-locked mode.
246 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
247 event
->type() != ui::ET_MOUSE_PRESSED
) {
248 current_state_
= STICKY_KEY_STATE_DISABLED
;
249 DispatchEventAndReleaseModifier(event
);
256 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent
* event
) {
257 preparing_to_enable_
= false;
258 if (event_from_myself_
|| current_state_
== STICKY_KEY_STATE_DISABLED
)
260 DCHECK(current_state_
== STICKY_KEY_STATE_ENABLED
||
261 current_state_
== STICKY_KEY_STATE_LOCKED
);
263 // We detect a direction change if the current |scroll_delta_| is assigned
264 // and the offset of the current scroll event has the opposing sign.
265 bool direction_changed
= false;
266 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
267 event
->type() == ui::ET_SCROLL
) {
268 int offset
= event
->y_offset();
270 direction_changed
= offset
* scroll_delta_
<= 0;
271 scroll_delta_
= offset
;
274 if (!direction_changed
)
275 AppendModifier(event
);
277 // We want to modify all the scroll events in the scroll sequence, which ends
278 // with a fling start event. We also stop when the scroll sequence changes
280 if (current_state_
== STICKY_KEY_STATE_ENABLED
&&
281 (event
->type() == ui::ET_SCROLL_FLING_START
|| direction_changed
)) {
282 current_state_
= STICKY_KEY_STATE_DISABLED
;
284 DispatchEventAndReleaseModifier(event
);
291 StickyKeysHandler::KeyEventType
292 StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent
* event
) {
293 bool is_target_key
= false;
294 if (event
->key_code() == ui::VKEY_SHIFT
||
295 event
->key_code() == ui::VKEY_LSHIFT
||
296 event
->key_code() == ui::VKEY_RSHIFT
) {
297 is_target_key
= (modifier_flag_
== ui::EF_SHIFT_DOWN
);
298 } else if (event
->key_code() == ui::VKEY_CONTROL
||
299 event
->key_code() == ui::VKEY_LCONTROL
||
300 event
->key_code() == ui::VKEY_RCONTROL
) {
301 is_target_key
= (modifier_flag_
== ui::EF_CONTROL_DOWN
);
302 } else if (event
->key_code() == ui::VKEY_MENU
||
303 event
->key_code() == ui::VKEY_LMENU
||
304 event
->key_code() == ui::VKEY_RMENU
) {
305 is_target_key
= (modifier_flag_
== ui::EF_ALT_DOWN
);
307 return event
->type() == ui::ET_KEY_PRESSED
?
308 NORMAL_KEY_DOWN
: NORMAL_KEY_UP
;
312 return event
->type() == ui::ET_KEY_PRESSED
?
313 TARGET_MODIFIER_DOWN
: TARGET_MODIFIER_UP
;
315 return event
->type() == ui::ET_KEY_PRESSED
?
316 OTHER_MODIFIER_DOWN
: OTHER_MODIFIER_UP
;
319 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent
* event
) {
320 switch (TranslateKeyEvent(event
)) {
321 case TARGET_MODIFIER_UP
:
322 if (preparing_to_enable_
) {
323 preparing_to_enable_
= false;
325 current_state_
= STICKY_KEY_STATE_ENABLED
;
326 modifier_up_event_
.reset(new ui::KeyEvent(*event
));
330 case TARGET_MODIFIER_DOWN
:
331 preparing_to_enable_
= true;
333 case NORMAL_KEY_DOWN
:
334 preparing_to_enable_
= false;
337 case OTHER_MODIFIER_DOWN
:
338 case OTHER_MODIFIER_UP
:
345 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent
* event
) {
346 switch (TranslateKeyEvent(event
)) {
348 case TARGET_MODIFIER_DOWN
:
350 case TARGET_MODIFIER_UP
:
351 current_state_
= STICKY_KEY_STATE_LOCKED
;
352 modifier_up_event_
.reset();
354 case NORMAL_KEY_DOWN
: {
355 current_state_
= STICKY_KEY_STATE_DISABLED
;
356 AppendModifier(event
);
357 DispatchEventAndReleaseModifier(event
);
360 case OTHER_MODIFIER_DOWN
:
361 case OTHER_MODIFIER_UP
:
368 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent
* event
) {
369 switch (TranslateKeyEvent(event
)) {
370 case TARGET_MODIFIER_DOWN
:
372 case TARGET_MODIFIER_UP
:
373 current_state_
= STICKY_KEY_STATE_DISABLED
;
375 case NORMAL_KEY_DOWN
:
377 AppendModifier(event
);
379 case OTHER_MODIFIER_DOWN
:
380 case OTHER_MODIFIER_UP
:
387 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event
* event
) {
388 DCHECK(event
->IsKeyEvent() ||
389 event
->IsMouseEvent() ||
390 event
->IsScrollEvent());
391 DCHECK(modifier_up_event_
.get());
392 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
394 aura::Window
* root_window
= target
->GetRootWindow();
397 aura::WindowTracker window_tracker
;
398 window_tracker
.Add(target
);
400 event_from_myself_
= true;
401 if (event
->IsKeyEvent()) {
402 delegate_
->DispatchKeyEvent(static_cast<ui::KeyEvent
*>(event
), target
);
403 } else if (event
->IsMouseEvent()) {
404 delegate_
->DispatchMouseEvent(static_cast<ui::MouseEvent
*>(event
), target
);
406 delegate_
->DispatchScrollEvent(
407 static_cast<ui::ScrollEvent
*>(event
), target
);
410 // The action triggered above may have destroyed the event target, in which
411 // case we will dispatch the modifier up event to the root window instead.
412 aura::Window
* modifier_up_target
=
413 window_tracker
.Contains(target
) ? target
: root_window
;
414 delegate_
->DispatchKeyEvent(modifier_up_event_
.get(), modifier_up_target
);
415 event_from_myself_
= false;
418 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state
) {
420 unsigned int& state_ref
= *state
;
421 switch (modifier_flag_
) {
422 case ui::EF_CONTROL_DOWN
:
423 state_ref
|= ControlMask
;
425 case ui::EF_ALT_DOWN
:
426 state_ref
|= Mod1Mask
;
428 case ui::EF_SHIFT_DOWN
:
429 state_ref
|= ShiftMask
;
437 void StickyKeysHandler::AppendModifier(ui::KeyEvent
* event
) {
439 XEvent
* xev
= event
->native_event();
441 XKeyEvent
* xkey
= &(xev
->xkey
);
442 AppendNativeEventMask(&xkey
->state
);
444 #elif defined(USE_OZONE)
445 NOTIMPLEMENTED() << "Modifier key is not handled";
447 event
->set_flags(event
->flags() | modifier_flag_
);
448 event
->set_character(ui::GetCharacterFromKeyCode(event
->key_code(),
450 event
->NormalizeFlags();
453 void StickyKeysHandler::AppendModifier(ui::MouseEvent
* event
) {
455 XEvent
* xev
= event
->native_event();
457 XButtonEvent
* xkey
= &(xev
->xbutton
);
458 AppendNativeEventMask(&xkey
->state
);
460 #elif defined(USE_OZONE)
461 NOTIMPLEMENTED() << "Modifier key is not handled";
463 event
->set_flags(event
->flags() | modifier_flag_
);
466 void StickyKeysHandler::AppendModifier(ui::ScrollEvent
* event
) {
468 XEvent
* xev
= event
->native_event();
470 XIDeviceEvent
* xievent
=
471 static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
473 AppendNativeEventMask(reinterpret_cast<unsigned int*>(
474 &xievent
->mods
.effective
));
477 #elif defined(USE_OZONE)
478 NOTIMPLEMENTED() << "Modifier key is not handled";
480 event
->set_flags(event
->flags() | modifier_flag_
);