Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / ash / sticky_keys / sticky_keys_controller.cc
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.
4
5 #include "ash/sticky_keys/sticky_keys_controller.h"
6
7 #if defined(USE_X11)
8 #include <X11/extensions/XInput2.h>
9 #include <X11/Xlib.h>
10 #undef RootWindow
11 #endif
12
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"
22
23 namespace ash {
24
25 namespace {
26
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;
32 }
33
34 // An implementation of StickyKeysHandler::StickyKeysHandlerDelegate.
35 class StickyKeysHandlerDelegateImpl :
36     public StickyKeysHandler::StickyKeysHandlerDelegate {
37  public:
38   StickyKeysHandlerDelegateImpl();
39   virtual ~StickyKeysHandlerDelegateImpl();
40
41   // StickyKeysHandlerDelegate overrides.
42   virtual void DispatchKeyEvent(ui::KeyEvent* event,
43                                 aura::Window* target) OVERRIDE;
44
45   virtual void DispatchMouseEvent(ui::MouseEvent* event,
46                                   aura::Window* target) OVERRIDE;
47
48   virtual void DispatchScrollEvent(ui::ScrollEvent* event,
49                                    aura::Window* target) OVERRIDE;
50  private:
51   void DispatchEvent(ui::Event* event, aura::Window* target);
52
53   DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl);
54 };
55
56 StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() {
57 }
58
59 StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() {
60 }
61
62 void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent* event,
63                                                      aura::Window* target) {
64   DispatchEvent(event, target);
65 }
66
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);
85   }
86 }
87
88 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
89     ui::ScrollEvent* event,
90     aura::Window* target)  {
91   DispatchEvent(event, target);
92 }
93
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;
101 }
102
103 }  // namespace
104
105 ///////////////////////////////////////////////////////////////////////////////
106 //  StickyKeys
107 StickyKeysController::StickyKeysController()
108     : enabled_(false),
109       mod3_enabled_(false),
110       altgr_enabled_(false) {
111 }
112
113 StickyKeysController::~StickyKeysController() {
114 }
115
116 void StickyKeysController::Enable(bool enabled) {
117   if (enabled_ != enabled) {
118     enabled_ = enabled;
119
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
136       overlay_.reset(new StickyKeysOverlay());
137       overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
138     } else if (overlay_) {
139       overlay_->Show(false);
140     }
141   }
142 }
143
144 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
145                                                bool altgr_enabled) {
146   mod3_enabled_ = mod3_enabled;
147   altgr_enabled_ = altgr_enabled;
148   if (overlay_)
149     overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
150 }
151
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);
157 }
158
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);
164 }
165
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);
171 }
172
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) {
177     return;
178   }
179
180   if (enabled_) {
181     if (HandleKeyEvent(event))
182       event->StopPropagation();
183     UpdateOverlay();
184   }
185 }
186
187 void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
188   if (enabled_) {
189     if (HandleMouseEvent(event))
190       event->StopPropagation();
191     UpdateOverlay();
192   }
193 }
194
195 void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
196   if (enabled_) {
197     if (HandleScrollEvent(event))
198       event->StopPropagation();
199     UpdateOverlay();
200   }
201 }
202
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());
212
213   bool key_in_use =
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;
218
219   overlay_->Show(enabled_ && key_in_use);
220 }
221
222 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
223   return overlay_.get();
224 }
225
226 ///////////////////////////////////////////////////////////////////////////////
227 //  StickyKeysHandler
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),
234       scroll_delta_(0),
235       delegate_(delegate) {
236 }
237
238 StickyKeysHandler::~StickyKeysHandler() {
239 }
240
241 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
242 }
243
244 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
245 }
246
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);
257   }
258   NOTREACHED();
259   return false;
260 }
261
262 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
263   if (ShouldModifyMouseEvent(event))
264     preparing_to_enable_ = false;
265
266   if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED
267       || !ShouldModifyMouseEvent(event)) {
268     return false;
269   }
270   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
271          current_state_ == STICKY_KEY_STATE_LOCKED);
272
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);
279     return true;
280   }
281
282   return false;
283 }
284
285 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
286   preparing_to_enable_ = false;
287   if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED)
288     return false;
289   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
290          current_state_ == STICKY_KEY_STATE_LOCKED);
291
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();
298     if (scroll_delta_)
299       direction_changed = offset * scroll_delta_ <= 0;
300     scroll_delta_ = offset;
301   }
302
303   if (!direction_changed)
304     AppendModifier(event);
305
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
308   // direction.
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;
312     scroll_delta_ = 0;
313     DispatchEventAndReleaseModifier(event);
314     return true;
315   }
316
317   return false;
318 }
319
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);
337   } else {
338     return event->type() == ui::ET_KEY_PRESSED ?
339         NORMAL_KEY_DOWN : NORMAL_KEY_UP;
340   }
341
342   if (is_target_key) {
343     return event->type() == ui::ET_KEY_PRESSED ?
344         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
345   }
346   return event->type() == ui::ET_KEY_PRESSED ?
347       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
348 }
349
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;
355         scroll_delta_ = 0;
356         current_state_ = STICKY_KEY_STATE_ENABLED;
357         modifier_up_event_.reset(new ui::KeyEvent(*event));
358         return true;
359       }
360       return false;
361     case TARGET_MODIFIER_DOWN:
362       preparing_to_enable_ = true;
363       return false;
364     case NORMAL_KEY_DOWN:
365       preparing_to_enable_ = false;
366       return false;
367     case NORMAL_KEY_UP:
368     case OTHER_MODIFIER_DOWN:
369     case OTHER_MODIFIER_UP:
370       return false;
371   }
372   NOTREACHED();
373   return false;
374 }
375
376 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
377   switch (TranslateKeyEvent(event)) {
378     case NORMAL_KEY_UP:
379     case TARGET_MODIFIER_DOWN:
380       return true;
381     case TARGET_MODIFIER_UP:
382       current_state_ = STICKY_KEY_STATE_LOCKED;
383       modifier_up_event_.reset();
384       return true;
385     case NORMAL_KEY_DOWN: {
386       current_state_ = STICKY_KEY_STATE_DISABLED;
387       AppendModifier(event);
388       DispatchEventAndReleaseModifier(event);
389       return true;
390     }
391     case OTHER_MODIFIER_DOWN:
392     case OTHER_MODIFIER_UP:
393       return false;
394   }
395   NOTREACHED();
396   return false;
397 }
398
399 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
400   switch (TranslateKeyEvent(event)) {
401     case TARGET_MODIFIER_DOWN:
402       return true;
403     case TARGET_MODIFIER_UP:
404       current_state_ = STICKY_KEY_STATE_DISABLED;
405       return false;
406     case NORMAL_KEY_DOWN:
407     case NORMAL_KEY_UP:
408       AppendModifier(event);
409       return false;
410     case OTHER_MODIFIER_DOWN:
411     case OTHER_MODIFIER_UP:
412       return false;
413   }
414   NOTREACHED();
415   return false;
416 }
417
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());
424   DCHECK(target);
425   aura::Window* root_window = target->GetRootWindow();
426   DCHECK(root_window);
427
428   aura::WindowTracker window_tracker;
429   window_tracker.Add(target);
430
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);
436   } else {
437     delegate_->DispatchScrollEvent(
438         static_cast<ui::ScrollEvent*>(event), target);
439   }
440
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;
447 }
448
449 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) {
450 #if defined(USE_X11)
451   unsigned int& state_ref = *state;
452   switch (modifier_flag_) {
453     case ui::EF_CONTROL_DOWN:
454       state_ref |= ControlMask;
455       break;
456     case ui::EF_ALT_DOWN:
457       state_ref |= Mod1Mask;
458       break;
459     case ui::EF_ALTGR_DOWN:
460       state_ref |= Mod5Mask;
461       break;
462     case ui::EF_SHIFT_DOWN:
463       state_ref |= ShiftMask;
464       break;
465     default:
466       NOTREACHED();
467   }
468 #endif
469 }
470
471 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
472 #if defined(USE_X11)
473   XEvent* xev = event->native_event();
474   if (xev) {
475     XKeyEvent* xkey = &(xev->xkey);
476     AppendNativeEventMask(&xkey->state);
477   }
478 #elif defined(USE_OZONE)
479   NOTIMPLEMENTED() << "Modifier key is not handled";
480 #endif
481   event->set_flags(event->flags() | modifier_flag_);
482   event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
483                                                    event->flags()));
484   event->NormalizeFlags();
485 }
486
487 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
488 #if defined(USE_X11)
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();
492   if (xev) {
493     switch (xev->type) {
494       case ButtonPress:
495       case ButtonRelease: {
496         XButtonEvent* xkey = &(xev->xbutton);
497         AppendNativeEventMask(&xkey->state);
498         break;
499       }
500       case GenericEvent: {
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));
507         break;
508       }
509       default:
510         NOTREACHED();
511     }
512   }
513 #elif defined(USE_OZONE)
514   NOTIMPLEMENTED() << "Modifier key is not handled";
515 #endif
516   event->set_flags(event->flags() | modifier_flag_);
517 }
518
519 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
520 #if defined(USE_X11)
521   XEvent* xev = event->native_event();
522   if (xev) {
523     XIDeviceEvent* xievent =
524         static_cast<XIDeviceEvent*>(xev->xcookie.data);
525     if (xievent) {
526       AppendNativeEventMask(reinterpret_cast<unsigned int*>(
527           &xievent->mods.effective));
528     }
529   }
530 #elif defined(USE_OZONE)
531   NOTIMPLEMENTED() << "Modifier key is not handled";
532 #endif
533   event->set_flags(event->flags() | modifier_flag_);
534 }
535
536 }  // namespace ash