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_);