Upstream version 7.36.149.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       mod3_sticky_key_.reset(
136           new StickyKeysHandler(ui::EF_MOD3_DOWN,
137                                 new StickyKeysHandlerDelegateImpl()));
138
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);
144     }
145   }
146 }
147
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_);
155   }
156 }
157
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);
164 }
165
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);
172 }
173
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);
180 }
181
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;
187   }
188
189   if (enabled_) {
190     if (HandleKeyEvent(event))
191       event->StopPropagation();
192     UpdateOverlay();
193   }
194 }
195
196 void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
197   if (enabled_) {
198     if (HandleMouseEvent(event))
199       event->StopPropagation();
200     UpdateOverlay();
201   }
202 }
203
204 void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
205   if (enabled_) {
206     if (HandleScrollEvent(event))
207       event->StopPropagation();
208     UpdateOverlay();
209   }
210 }
211
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());
223
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;
230
231   overlay_->Show(enabled_ && key_in_use);
232 }
233
234 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
235   return overlay_.get();
236 }
237
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) {
248 }
249
250 StickyKeysHandler::~StickyKeysHandler() {
251 }
252
253 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
254 }
255
256 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
257 }
258
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);
269   }
270   NOTREACHED();
271   return false;
272 }
273
274 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
275   if (ShouldModifyMouseEvent(event))
276     preparing_to_enable_ = false;
277
278   if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED
279       || !ShouldModifyMouseEvent(event)) {
280     return false;
281   }
282   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
283          current_state_ == STICKY_KEY_STATE_LOCKED);
284
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;
292   }
293
294   return false;
295 }
296
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);
303
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;
313   }
314
315   if (!direction_changed)
316     AppendModifier(event);
317
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;
327   }
328
329   return false;
330 }
331
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;
354   }
355
356   if (is_target_key) {
357     return event->type() == ui::ET_KEY_PRESSED ?
358         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
359   }
360   return event->type() == ui::ET_KEY_PRESSED ?
361       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
362 }
363
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;
373       }
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;
385   }
386   NOTREACHED();
387   return false;
388 }
389
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;
404     }
405     case OTHER_MODIFIER_DOWN:
406     case OTHER_MODIFIER_UP:
407       return false;
408   }
409   NOTREACHED();
410   return false;
411 }
412
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;
427   }
428   NOTREACHED();
429   return false;
430 }
431
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);
441
442   aura::WindowTracker window_tracker;
443   window_tracker.Add(target);
444
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);
453   }
454
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;
461 }
462
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();
484   }
485 #endif
486 }
487
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);
494   }
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();
502 }
503
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;
516       }
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;
525       }
526       default:
527         NOTREACHED();
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 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));
545     }
546   }
547 #elif defined(USE_OZONE)
548   NOTIMPLEMENTED() << "Modifier key is not handled";
549 #endif
550   event->set_flags(event->flags() | modifier_flag_);
551 }
552
553 }  // namespace ash