Blink roll 174125:174137
[chromium-blink-merge.git] / ash / sticky_keys / sticky_keys_controller.cc
bloba10bb615a7f0ed0d763187790ea8dd0bdabd7ad5
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"
7 #if defined(USE_X11)
8 #include <X11/extensions/XInput2.h>
9 #include <X11/Xlib.h>
10 #undef RootWindow
11 #endif
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"
23 namespace ash {
25 namespace {
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 {
37 public:
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;
50 private:
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) {
69 DCHECK(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),
74 source,
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);
79 } else {
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) {
96 DCHECK(target);
97 ui::EventDispatchDetails details =
98 target->GetHost()->event_processor()->OnEventFromSource(event);
99 if (details.dispatcher_destroyed)
100 return;
103 } // namespace
105 ///////////////////////////////////////////////////////////////////////////////
106 // StickyKeys
107 StickyKeysController::StickyKeysController()
108 : enabled_(false),
109 mod3_enabled_(false),
110 altgr_enabled_(false) {
113 StickyKeysController::~StickyKeysController() {
116 void StickyKeysController::Enable(bool enabled) {
117 if (enabled_ != enabled) {
118 enabled_ = enabled;
120 // Reset key handlers when activating sticky keys to ensure all
121 // the handlers' states are reset.
122 if (enabled_) {
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;
152 if (overlay_) {
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) {
186 return;
189 if (enabled_) {
190 if (HandleKeyEvent(event))
191 event->StopPropagation();
192 UpdateOverlay();
196 void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
197 if (enabled_) {
198 if (HandleMouseEvent(event))
199 event->StopPropagation();
200 UpdateOverlay();
204 void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
205 if (enabled_) {
206 if (HandleScrollEvent(event))
207 event->StopPropagation();
208 UpdateOverlay();
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());
224 bool key_in_use =
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 ///////////////////////////////////////////////////////////////////////////////
239 // StickyKeysHandler
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),
246 scroll_delta_(0),
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);
270 NOTREACHED();
271 return false;
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)) {
280 return false;
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);
291 return true;
294 return false;
297 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
298 preparing_to_enable_ = false;
299 if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED)
300 return false;
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();
310 if (scroll_delta_)
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
320 // direction.
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;
324 scroll_delta_ = 0;
325 DispatchEventAndReleaseModifier(event);
326 return true;
329 return false;
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);
351 } else {
352 return event->type() == ui::ET_KEY_PRESSED ?
353 NORMAL_KEY_DOWN : NORMAL_KEY_UP;
356 if (is_target_key) {
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;
369 scroll_delta_ = 0;
370 current_state_ = STICKY_KEY_STATE_ENABLED;
371 modifier_up_event_.reset(new ui::KeyEvent(*event));
372 return true;
374 return false;
375 case TARGET_MODIFIER_DOWN:
376 preparing_to_enable_ = true;
377 return false;
378 case NORMAL_KEY_DOWN:
379 preparing_to_enable_ = false;
380 return false;
381 case NORMAL_KEY_UP:
382 case OTHER_MODIFIER_DOWN:
383 case OTHER_MODIFIER_UP:
384 return false;
386 NOTREACHED();
387 return false;
390 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
391 switch (TranslateKeyEvent(event)) {
392 case NORMAL_KEY_UP:
393 case TARGET_MODIFIER_DOWN:
394 return true;
395 case TARGET_MODIFIER_UP:
396 current_state_ = STICKY_KEY_STATE_LOCKED;
397 modifier_up_event_.reset();
398 return true;
399 case NORMAL_KEY_DOWN: {
400 current_state_ = STICKY_KEY_STATE_DISABLED;
401 AppendModifier(event);
402 DispatchEventAndReleaseModifier(event);
403 return true;
405 case OTHER_MODIFIER_DOWN:
406 case OTHER_MODIFIER_UP:
407 return false;
409 NOTREACHED();
410 return false;
413 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
414 switch (TranslateKeyEvent(event)) {
415 case TARGET_MODIFIER_DOWN:
416 return true;
417 case TARGET_MODIFIER_UP:
418 current_state_ = STICKY_KEY_STATE_DISABLED;
419 return false;
420 case NORMAL_KEY_DOWN:
421 case NORMAL_KEY_UP:
422 AppendModifier(event);
423 return false;
424 case OTHER_MODIFIER_DOWN:
425 case OTHER_MODIFIER_UP:
426 return false;
428 NOTREACHED();
429 return false;
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());
438 DCHECK(target);
439 aura::Window* root_window = target->GetRootWindow();
440 DCHECK(root_window);
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);
450 } else {
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) {
464 #if defined(USE_X11)
465 unsigned int& state_ref = *state;
466 switch (modifier_flag_) {
467 case ui::EF_CONTROL_DOWN:
468 state_ref |= ControlMask;
469 break;
470 case ui::EF_ALT_DOWN:
471 state_ref |= Mod1Mask;
472 break;
473 case ui::EF_ALTGR_DOWN:
474 state_ref |= Mod5Mask;
475 break;
476 case ui::EF_SHIFT_DOWN:
477 state_ref |= ShiftMask;
478 break;
479 case ui::EF_MOD3_DOWN:
480 state_ref |= Mod3Mask;
481 break;
482 default:
483 NOTREACHED();
485 #endif
488 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
489 #if defined(USE_X11)
490 XEvent* xev = event->native_event();
491 if (xev) {
492 XKeyEvent* xkey = &(xev->xkey);
493 AppendNativeEventMask(&xkey->state);
495 #elif defined(USE_OZONE)
496 NOTIMPLEMENTED() << "Modifier key is not handled";
497 #endif
498 event->set_flags(event->flags() | modifier_flag_);
499 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
500 event->flags()));
501 event->NormalizeFlags();
504 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
505 #if defined(USE_X11)
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();
509 if (xev) {
510 switch (xev->type) {
511 case ButtonPress:
512 case ButtonRelease: {
513 XButtonEvent* xkey = &(xev->xbutton);
514 AppendNativeEventMask(&xkey->state);
515 break;
517 case GenericEvent: {
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));
524 break;
526 default:
527 NOTREACHED();
530 #elif defined(USE_OZONE)
531 NOTIMPLEMENTED() << "Modifier key is not handled";
532 #endif
533 event->set_flags(event->flags() | modifier_flag_);
536 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
537 #if defined(USE_X11)
538 XEvent* xev = event->native_event();
539 if (xev) {
540 XIDeviceEvent* xievent =
541 static_cast<XIDeviceEvent*>(xev->xcookie.data);
542 if (xievent) {
543 AppendNativeEventMask(reinterpret_cast<unsigned int*>(
544 &xievent->mods.effective));
547 #elif defined(USE_OZONE)
548 NOTIMPLEMENTED() << "Modifier key is not handled";
549 #endif
550 event->set_flags(event->flags() | modifier_flag_);
553 } // namespace ash