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()));
136 overlay_.reset(new StickyKeysOverlay());
137 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
138 } else if (overlay_) {
139 overlay_->Show(false);
144 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
145 bool altgr_enabled) {
146 mod3_enabled_ = mod3_enabled;
147 altgr_enabled_ = altgr_enabled;
149 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
152 bool StickyKeysController::HandleKeyEvent(ui::KeyEvent* event) {
153 return shift_sticky_key_->HandleKeyEvent(event) ||
154 alt_sticky_key_->HandleKeyEvent(event) ||
155 altgr_sticky_key_->HandleKeyEvent(event) ||
156 ctrl_sticky_key_->HandleKeyEvent(event);
159 bool StickyKeysController::HandleMouseEvent(ui::MouseEvent* event) {
160 return shift_sticky_key_->HandleMouseEvent(event) ||
161 alt_sticky_key_->HandleMouseEvent(event) ||
162 altgr_sticky_key_->HandleMouseEvent(event) ||
163 ctrl_sticky_key_->HandleMouseEvent(event);
166 bool StickyKeysController::HandleScrollEvent(ui::ScrollEvent* event) {
167 return shift_sticky_key_->HandleScrollEvent(event) ||
168 alt_sticky_key_->HandleScrollEvent(event) ||
169 altgr_sticky_key_->HandleScrollEvent(event) ||
170 ctrl_sticky_key_->HandleScrollEvent(event);
173 void StickyKeysController::OnKeyEvent(ui::KeyEvent* event) {
174 // Do not consume a translated key event which is generated by an IME.
175 if (event->type() == ui::ET_TRANSLATED_KEY_PRESS ||
176 event->type() == ui::ET_TRANSLATED_KEY_RELEASE) {
181 if (HandleKeyEvent(event))
182 event->StopPropagation();
187 void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
189 if (HandleMouseEvent(event))
190 event->StopPropagation();
195 void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
197 if (HandleScrollEvent(event))
198 event->StopPropagation();
203 void StickyKeysController::UpdateOverlay() {
204 overlay_->SetModifierKeyState(
205 ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
206 overlay_->SetModifierKeyState(
207 ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
208 overlay_->SetModifierKeyState(
209 ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
210 overlay_->SetModifierKeyState(
211 ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
214 shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
215 alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
216 altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
217 ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
219 overlay_->Show(enabled_ && key_in_use);
222 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
223 return overlay_.get();
226 ///////////////////////////////////////////////////////////////////////////////
228 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag,
229 StickyKeysHandlerDelegate* delegate)
230 : modifier_flag_(modifier_flag),
231 current_state_(STICKY_KEY_STATE_DISABLED),
232 event_from_myself_(false),
233 preparing_to_enable_(false),
235 delegate_(delegate) {
238 StickyKeysHandler::~StickyKeysHandler() {
241 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
244 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
247 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) {
248 if (event_from_myself_)
249 return false; // Do not handle self-generated key event.
250 switch (current_state_) {
251 case STICKY_KEY_STATE_DISABLED:
252 return HandleDisabledState(event);
253 case STICKY_KEY_STATE_ENABLED:
254 return HandleEnabledState(event);
255 case STICKY_KEY_STATE_LOCKED:
256 return HandleLockedState(event);
262 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
263 if (ShouldModifyMouseEvent(event))
264 preparing_to_enable_ = false;
266 if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED
267 || !ShouldModifyMouseEvent(event)) {
270 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
271 current_state_ == STICKY_KEY_STATE_LOCKED);
273 AppendModifier(event);
274 // Only disable on the mouse released event in normal, non-locked mode.
275 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
276 event->type() != ui::ET_MOUSE_PRESSED) {
277 current_state_ = STICKY_KEY_STATE_DISABLED;
278 DispatchEventAndReleaseModifier(event);
285 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
286 preparing_to_enable_ = false;
287 if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED)
289 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
290 current_state_ == STICKY_KEY_STATE_LOCKED);
292 // We detect a direction change if the current |scroll_delta_| is assigned
293 // and the offset of the current scroll event has the opposing sign.
294 bool direction_changed = false;
295 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
296 event->type() == ui::ET_SCROLL) {
297 int offset = event->y_offset();
299 direction_changed = offset * scroll_delta_ <= 0;
300 scroll_delta_ = offset;
303 if (!direction_changed)
304 AppendModifier(event);
306 // We want to modify all the scroll events in the scroll sequence, which ends
307 // with a fling start event. We also stop when the scroll sequence changes
309 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
310 (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
311 current_state_ = STICKY_KEY_STATE_DISABLED;
313 DispatchEventAndReleaseModifier(event);
320 StickyKeysHandler::KeyEventType
321 StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) {
322 bool is_target_key = false;
323 if (event->key_code() == ui::VKEY_SHIFT ||
324 event->key_code() == ui::VKEY_LSHIFT ||
325 event->key_code() == ui::VKEY_RSHIFT) {
326 is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
327 } else if (event->key_code() == ui::VKEY_CONTROL ||
328 event->key_code() == ui::VKEY_LCONTROL ||
329 event->key_code() == ui::VKEY_RCONTROL) {
330 is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
331 } else if (event->key_code() == ui::VKEY_MENU ||
332 event->key_code() == ui::VKEY_LMENU ||
333 event->key_code() == ui::VKEY_RMENU) {
334 is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
335 } else if (event->key_code() == ui::VKEY_ALTGR) {
336 is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
338 return event->type() == ui::ET_KEY_PRESSED ?
339 NORMAL_KEY_DOWN : NORMAL_KEY_UP;
343 return event->type() == ui::ET_KEY_PRESSED ?
344 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
346 return event->type() == ui::ET_KEY_PRESSED ?
347 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
350 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) {
351 switch (TranslateKeyEvent(event)) {
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(ui::KeyEvent* event) {
377 switch (TranslateKeyEvent(event)) {
379 case TARGET_MODIFIER_DOWN:
381 case TARGET_MODIFIER_UP:
382 current_state_ = STICKY_KEY_STATE_LOCKED;
383 modifier_up_event_.reset();
385 case NORMAL_KEY_DOWN: {
386 current_state_ = STICKY_KEY_STATE_DISABLED;
387 AppendModifier(event);
388 DispatchEventAndReleaseModifier(event);
391 case OTHER_MODIFIER_DOWN:
392 case OTHER_MODIFIER_UP:
399 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
400 switch (TranslateKeyEvent(event)) {
401 case TARGET_MODIFIER_DOWN:
403 case TARGET_MODIFIER_UP:
404 current_state_ = STICKY_KEY_STATE_DISABLED;
406 case NORMAL_KEY_DOWN:
408 AppendModifier(event);
410 case OTHER_MODIFIER_DOWN:
411 case OTHER_MODIFIER_UP:
418 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) {
419 DCHECK(event->IsKeyEvent() ||
420 event->IsMouseEvent() ||
421 event->IsScrollEvent());
422 DCHECK(modifier_up_event_.get());
423 aura::Window* target = static_cast<aura::Window*>(event->target());
425 aura::Window* root_window = target->GetRootWindow();
428 aura::WindowTracker window_tracker;
429 window_tracker.Add(target);
431 event_from_myself_ = true;
432 if (event->IsKeyEvent()) {
433 delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target);
434 } else if (event->IsMouseEvent()) {
435 delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target);
437 delegate_->DispatchScrollEvent(
438 static_cast<ui::ScrollEvent*>(event), target);
441 // The action triggered above may have destroyed the event target, in which
442 // case we will dispatch the modifier up event to the root window instead.
443 aura::Window* modifier_up_target =
444 window_tracker.Contains(target) ? target : root_window;
445 delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target);
446 event_from_myself_ = false;
449 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) {
451 unsigned int& state_ref = *state;
452 switch (modifier_flag_) {
453 case ui::EF_CONTROL_DOWN:
454 state_ref |= ControlMask;
456 case ui::EF_ALT_DOWN:
457 state_ref |= Mod1Mask;
459 case ui::EF_ALTGR_DOWN:
460 state_ref |= Mod5Mask;
462 case ui::EF_SHIFT_DOWN:
463 state_ref |= ShiftMask;
471 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
473 XEvent* xev = event->native_event();
475 XKeyEvent* xkey = &(xev->xkey);
476 AppendNativeEventMask(&xkey->state);
478 #elif defined(USE_OZONE)
479 NOTIMPLEMENTED() << "Modifier key is not handled";
481 event->set_flags(event->flags() | modifier_flag_);
482 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
484 event->NormalizeFlags();
487 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
489 // The native mouse event can either be a classic X button event or an
490 // XInput2 button event.
491 XEvent* xev = event->native_event();
495 case ButtonRelease: {
496 XButtonEvent* xkey = &(xev->xbutton);
497 AppendNativeEventMask(&xkey->state);
501 XIDeviceEvent* xievent =
502 static_cast<XIDeviceEvent*>(xev->xcookie.data);
503 CHECK(xievent->evtype == XI_ButtonPress ||
504 xievent->evtype == XI_ButtonRelease);
505 AppendNativeEventMask(
506 reinterpret_cast<unsigned int*>(&xievent->mods.effective));
513 #elif defined(USE_OZONE)
514 NOTIMPLEMENTED() << "Modifier key is not handled";
516 event->set_flags(event->flags() | modifier_flag_);
519 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
521 XEvent* xev = event->native_event();
523 XIDeviceEvent* xievent =
524 static_cast<XIDeviceEvent*>(xev->xcookie.data);
526 AppendNativeEventMask(reinterpret_cast<unsigned int*>(
527 &xievent->mods.effective));
530 #elif defined(USE_OZONE)
531 NOTIMPLEMENTED() << "Modifier key is not handled";
533 event->set_flags(event->flags() | modifier_flag_);