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 ui::MouseWheelEvent new_event(event->native_event());
73 DispatchEvent(&new_event, target);
75 ui::MouseEvent new_event(event->native_event());
76 DispatchEvent(&new_event, target);
80 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
81 ui::ScrollEvent* event,
82 aura::Window* target) {
83 DispatchEvent(event, target);
86 void StickyKeysHandlerDelegateImpl::DispatchEvent(ui::Event* event,
87 aura::Window* target) {
89 ui::EventDispatchDetails details =
90 target->GetHost()->event_processor()->OnEventFromSource(event);
91 if (details.dispatcher_destroyed)
97 ///////////////////////////////////////////////////////////////////////////////
99 StickyKeysController::StickyKeysController()
101 mod3_enabled_(false),
102 altgr_enabled_(false) {
105 StickyKeysController::~StickyKeysController() {
108 void StickyKeysController::Enable(bool enabled) {
109 if (enabled_ != enabled) {
112 // Reset key handlers when activating sticky keys to ensure all
113 // the handlers' states are reset.
115 shift_sticky_key_.reset(
116 new StickyKeysHandler(ui::EF_SHIFT_DOWN,
117 new StickyKeysHandlerDelegateImpl()));
118 alt_sticky_key_.reset(
119 new StickyKeysHandler(ui::EF_ALT_DOWN,
120 new StickyKeysHandlerDelegateImpl()));
121 altgr_sticky_key_.reset(
122 new StickyKeysHandler(ui::EF_ALTGR_DOWN,
123 new StickyKeysHandlerDelegateImpl()));
124 ctrl_sticky_key_.reset(
125 new StickyKeysHandler(ui::EF_CONTROL_DOWN,
126 new StickyKeysHandlerDelegateImpl()));
128 overlay_.reset(new StickyKeysOverlay());
129 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
130 } else if (overlay_) {
131 overlay_->Show(false);
136 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
137 bool altgr_enabled) {
138 mod3_enabled_ = mod3_enabled;
139 altgr_enabled_ = altgr_enabled;
141 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
144 bool StickyKeysController::HandleKeyEvent(ui::KeyEvent* event) {
145 return shift_sticky_key_->HandleKeyEvent(event) ||
146 alt_sticky_key_->HandleKeyEvent(event) ||
147 altgr_sticky_key_->HandleKeyEvent(event) ||
148 ctrl_sticky_key_->HandleKeyEvent(event);
151 bool StickyKeysController::HandleMouseEvent(ui::MouseEvent* event) {
152 return shift_sticky_key_->HandleMouseEvent(event) ||
153 alt_sticky_key_->HandleMouseEvent(event) ||
154 altgr_sticky_key_->HandleMouseEvent(event) ||
155 ctrl_sticky_key_->HandleMouseEvent(event);
158 bool StickyKeysController::HandleScrollEvent(ui::ScrollEvent* event) {
159 return shift_sticky_key_->HandleScrollEvent(event) ||
160 alt_sticky_key_->HandleScrollEvent(event) ||
161 altgr_sticky_key_->HandleScrollEvent(event) ||
162 ctrl_sticky_key_->HandleScrollEvent(event);
165 void StickyKeysController::OnKeyEvent(ui::KeyEvent* event) {
166 // Do not consume a translated key event which is generated by an IME.
167 if (event->type() == ui::ET_TRANSLATED_KEY_PRESS ||
168 event->type() == ui::ET_TRANSLATED_KEY_RELEASE) {
173 if (HandleKeyEvent(event))
174 event->StopPropagation();
179 void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
181 if (HandleMouseEvent(event))
182 event->StopPropagation();
187 void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
189 if (HandleScrollEvent(event))
190 event->StopPropagation();
195 void StickyKeysController::UpdateOverlay() {
196 overlay_->SetModifierKeyState(
197 ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
198 overlay_->SetModifierKeyState(
199 ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
200 overlay_->SetModifierKeyState(
201 ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
202 overlay_->SetModifierKeyState(
203 ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
206 shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
207 alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
208 altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
209 ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
211 overlay_->Show(enabled_ && key_in_use);
214 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
215 return overlay_.get();
218 ///////////////////////////////////////////////////////////////////////////////
220 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag,
221 StickyKeysHandlerDelegate* delegate)
222 : modifier_flag_(modifier_flag),
223 current_state_(STICKY_KEY_STATE_DISABLED),
224 event_from_myself_(false),
225 preparing_to_enable_(false),
227 delegate_(delegate) {
230 StickyKeysHandler::~StickyKeysHandler() {
233 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
236 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
239 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) {
240 if (event_from_myself_)
241 return false; // Do not handle self-generated key event.
242 switch (current_state_) {
243 case STICKY_KEY_STATE_DISABLED:
244 return HandleDisabledState(event);
245 case STICKY_KEY_STATE_ENABLED:
246 return HandleEnabledState(event);
247 case STICKY_KEY_STATE_LOCKED:
248 return HandleLockedState(event);
254 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
255 if (ShouldModifyMouseEvent(event))
256 preparing_to_enable_ = false;
258 if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED
259 || !ShouldModifyMouseEvent(event)) {
262 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
263 current_state_ == STICKY_KEY_STATE_LOCKED);
265 AppendModifier(event);
266 // Only disable on the mouse released event in normal, non-locked mode.
267 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
268 event->type() != ui::ET_MOUSE_PRESSED) {
269 current_state_ = STICKY_KEY_STATE_DISABLED;
270 DispatchEventAndReleaseModifier(event);
277 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
278 preparing_to_enable_ = false;
279 if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED)
281 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
282 current_state_ == STICKY_KEY_STATE_LOCKED);
284 // We detect a direction change if the current |scroll_delta_| is assigned
285 // and the offset of the current scroll event has the opposing sign.
286 bool direction_changed = false;
287 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
288 event->type() == ui::ET_SCROLL) {
289 int offset = event->y_offset();
291 direction_changed = offset * scroll_delta_ <= 0;
292 scroll_delta_ = offset;
295 if (!direction_changed)
296 AppendModifier(event);
298 // We want to modify all the scroll events in the scroll sequence, which ends
299 // with a fling start event. We also stop when the scroll sequence changes
301 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
302 (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
303 current_state_ = STICKY_KEY_STATE_DISABLED;
305 DispatchEventAndReleaseModifier(event);
312 StickyKeysHandler::KeyEventType
313 StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) {
314 bool is_target_key = false;
315 if (event->key_code() == ui::VKEY_SHIFT ||
316 event->key_code() == ui::VKEY_LSHIFT ||
317 event->key_code() == ui::VKEY_RSHIFT) {
318 is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
319 } else if (event->key_code() == ui::VKEY_CONTROL ||
320 event->key_code() == ui::VKEY_LCONTROL ||
321 event->key_code() == ui::VKEY_RCONTROL) {
322 is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
323 } else if (event->key_code() == ui::VKEY_MENU ||
324 event->key_code() == ui::VKEY_LMENU ||
325 event->key_code() == ui::VKEY_RMENU) {
326 is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
327 } else if (event->key_code() == ui::VKEY_ALTGR) {
328 is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
330 return event->type() == ui::ET_KEY_PRESSED ?
331 NORMAL_KEY_DOWN : NORMAL_KEY_UP;
335 return event->type() == ui::ET_KEY_PRESSED ?
336 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
338 return event->type() == ui::ET_KEY_PRESSED ?
339 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
342 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) {
343 switch (TranslateKeyEvent(event)) {
344 case TARGET_MODIFIER_UP:
345 if (preparing_to_enable_) {
346 preparing_to_enable_ = false;
348 current_state_ = STICKY_KEY_STATE_ENABLED;
349 modifier_up_event_.reset(new ui::KeyEvent(*event));
353 case TARGET_MODIFIER_DOWN:
354 preparing_to_enable_ = true;
356 case NORMAL_KEY_DOWN:
357 preparing_to_enable_ = false;
360 case OTHER_MODIFIER_DOWN:
361 case OTHER_MODIFIER_UP:
368 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
369 switch (TranslateKeyEvent(event)) {
371 case TARGET_MODIFIER_DOWN:
373 case TARGET_MODIFIER_UP:
374 current_state_ = STICKY_KEY_STATE_LOCKED;
375 modifier_up_event_.reset();
377 case NORMAL_KEY_DOWN: {
378 current_state_ = STICKY_KEY_STATE_DISABLED;
379 AppendModifier(event);
380 DispatchEventAndReleaseModifier(event);
383 case OTHER_MODIFIER_DOWN:
384 case OTHER_MODIFIER_UP:
391 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
392 switch (TranslateKeyEvent(event)) {
393 case TARGET_MODIFIER_DOWN:
395 case TARGET_MODIFIER_UP:
396 current_state_ = STICKY_KEY_STATE_DISABLED;
398 case NORMAL_KEY_DOWN:
400 AppendModifier(event);
402 case OTHER_MODIFIER_DOWN:
403 case OTHER_MODIFIER_UP:
410 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) {
411 DCHECK(event->IsKeyEvent() ||
412 event->IsMouseEvent() ||
413 event->IsScrollEvent());
414 DCHECK(modifier_up_event_.get());
415 aura::Window* target = static_cast<aura::Window*>(event->target());
417 aura::Window* root_window = target->GetRootWindow();
420 aura::WindowTracker window_tracker;
421 window_tracker.Add(target);
423 event_from_myself_ = true;
424 if (event->IsKeyEvent()) {
425 delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target);
426 } else if (event->IsMouseEvent()) {
427 delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target);
429 delegate_->DispatchScrollEvent(
430 static_cast<ui::ScrollEvent*>(event), target);
433 // The action triggered above may have destroyed the event target, in which
434 // case we will dispatch the modifier up event to the root window instead.
435 aura::Window* modifier_up_target =
436 window_tracker.Contains(target) ? target : root_window;
437 delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target);
438 event_from_myself_ = false;
441 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) {
443 unsigned int& state_ref = *state;
444 switch (modifier_flag_) {
445 case ui::EF_CONTROL_DOWN:
446 state_ref |= ControlMask;
448 case ui::EF_ALT_DOWN:
449 state_ref |= Mod1Mask;
451 case ui::EF_ALTGR_DOWN:
452 state_ref |= Mod5Mask;
454 case ui::EF_SHIFT_DOWN:
455 state_ref |= ShiftMask;
463 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
465 XEvent* xev = event->native_event();
467 XKeyEvent* xkey = &(xev->xkey);
468 AppendNativeEventMask(&xkey->state);
470 #elif defined(USE_OZONE)
471 NOTIMPLEMENTED() << "Modifier key is not handled";
473 event->set_flags(event->flags() | modifier_flag_);
474 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
476 event->NormalizeFlags();
479 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
481 // The native mouse event can either be a classic X button event or an
482 // XInput2 button event.
483 XEvent* xev = event->native_event();
487 case ButtonRelease: {
488 XButtonEvent* xkey = &(xev->xbutton);
489 AppendNativeEventMask(&xkey->state);
493 XIDeviceEvent* xievent =
494 static_cast<XIDeviceEvent*>(xev->xcookie.data);
495 CHECK(xievent->evtype == XI_ButtonPress ||
496 xievent->evtype == XI_ButtonRelease);
497 AppendNativeEventMask(
498 reinterpret_cast<unsigned int*>(&xievent->mods.effective));
505 #elif defined(USE_OZONE)
506 NOTIMPLEMENTED() << "Modifier key is not handled";
508 event->set_flags(event->flags() | modifier_flag_);
511 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
513 XEvent* xev = event->native_event();
515 XIDeviceEvent* xievent =
516 static_cast<XIDeviceEvent*>(xev->xcookie.data);
518 AppendNativeEventMask(reinterpret_cast<unsigned int*>(
519 &xievent->mods.effective));
522 #elif defined(USE_OZONE)
523 NOTIMPLEMENTED() << "Modifier key is not handled";
525 event->set_flags(event->flags() | modifier_flag_);