e5bb0afe2483c32c1dbd4a20dfff9f6583cf4c8b
[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     ui::MouseWheelEvent new_event(event->native_event());
73     DispatchEvent(&new_event, target);
74   } else {
75     ui::MouseEvent new_event(event->native_event());
76     DispatchEvent(&new_event, target);
77   }
78 }
79
80 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
81     ui::ScrollEvent* event,
82     aura::Window* target)  {
83   DispatchEvent(event, target);
84 }
85
86 void StickyKeysHandlerDelegateImpl::DispatchEvent(ui::Event* event,
87                                                   aura::Window* target) {
88   DCHECK(target);
89   ui::EventDispatchDetails details =
90       target->GetHost()->event_processor()->OnEventFromSource(event);
91   if (details.dispatcher_destroyed)
92     return;
93 }
94
95 }  // namespace
96
97 ///////////////////////////////////////////////////////////////////////////////
98 //  StickyKeys
99 StickyKeysController::StickyKeysController()
100     : enabled_(false),
101       mod3_enabled_(false),
102       altgr_enabled_(false) {
103 }
104
105 StickyKeysController::~StickyKeysController() {
106 }
107
108 void StickyKeysController::Enable(bool enabled) {
109   if (enabled_ != enabled) {
110     enabled_ = enabled;
111
112     // Reset key handlers when activating sticky keys to ensure all
113     // the handlers' states are reset.
114     if (enabled_) {
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()));
127
128       overlay_.reset(new StickyKeysOverlay());
129       overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
130     } else if (overlay_) {
131       overlay_->Show(false);
132     }
133   }
134 }
135
136 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
137                                                bool altgr_enabled) {
138   mod3_enabled_ = mod3_enabled;
139   altgr_enabled_ = altgr_enabled;
140   if (overlay_)
141     overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
142 }
143
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);
149 }
150
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);
156 }
157
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);
163 }
164
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) {
169     return;
170   }
171
172   if (enabled_) {
173     if (HandleKeyEvent(event))
174       event->StopPropagation();
175     UpdateOverlay();
176   }
177 }
178
179 void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
180   if (enabled_) {
181     if (HandleMouseEvent(event))
182       event->StopPropagation();
183     UpdateOverlay();
184   }
185 }
186
187 void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
188   if (enabled_) {
189     if (HandleScrollEvent(event))
190       event->StopPropagation();
191     UpdateOverlay();
192   }
193 }
194
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());
204
205   bool key_in_use =
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;
210
211   overlay_->Show(enabled_ && key_in_use);
212 }
213
214 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
215   return overlay_.get();
216 }
217
218 ///////////////////////////////////////////////////////////////////////////////
219 //  StickyKeysHandler
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),
226       scroll_delta_(0),
227       delegate_(delegate) {
228 }
229
230 StickyKeysHandler::~StickyKeysHandler() {
231 }
232
233 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() {
234 }
235
236 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() {
237 }
238
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);
249   }
250   NOTREACHED();
251   return false;
252 }
253
254 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
255   if (ShouldModifyMouseEvent(event))
256     preparing_to_enable_ = false;
257
258   if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED
259       || !ShouldModifyMouseEvent(event)) {
260     return false;
261   }
262   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
263          current_state_ == STICKY_KEY_STATE_LOCKED);
264
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);
271     return true;
272   }
273
274   return false;
275 }
276
277 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
278   preparing_to_enable_ = false;
279   if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED)
280     return false;
281   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
282          current_state_ == STICKY_KEY_STATE_LOCKED);
283
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();
290     if (scroll_delta_)
291       direction_changed = offset * scroll_delta_ <= 0;
292     scroll_delta_ = offset;
293   }
294
295   if (!direction_changed)
296     AppendModifier(event);
297
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
300   // direction.
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;
304     scroll_delta_ = 0;
305     DispatchEventAndReleaseModifier(event);
306     return true;
307   }
308
309   return false;
310 }
311
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);
329   } else {
330     return event->type() == ui::ET_KEY_PRESSED ?
331         NORMAL_KEY_DOWN : NORMAL_KEY_UP;
332   }
333
334   if (is_target_key) {
335     return event->type() == ui::ET_KEY_PRESSED ?
336         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
337   }
338   return event->type() == ui::ET_KEY_PRESSED ?
339       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
340 }
341
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;
347         scroll_delta_ = 0;
348         current_state_ = STICKY_KEY_STATE_ENABLED;
349         modifier_up_event_.reset(new ui::KeyEvent(*event));
350         return true;
351       }
352       return false;
353     case TARGET_MODIFIER_DOWN:
354       preparing_to_enable_ = true;
355       return false;
356     case NORMAL_KEY_DOWN:
357       preparing_to_enable_ = false;
358       return false;
359     case NORMAL_KEY_UP:
360     case OTHER_MODIFIER_DOWN:
361     case OTHER_MODIFIER_UP:
362       return false;
363   }
364   NOTREACHED();
365   return false;
366 }
367
368 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
369   switch (TranslateKeyEvent(event)) {
370     case NORMAL_KEY_UP:
371     case TARGET_MODIFIER_DOWN:
372       return true;
373     case TARGET_MODIFIER_UP:
374       current_state_ = STICKY_KEY_STATE_LOCKED;
375       modifier_up_event_.reset();
376       return true;
377     case NORMAL_KEY_DOWN: {
378       current_state_ = STICKY_KEY_STATE_DISABLED;
379       AppendModifier(event);
380       DispatchEventAndReleaseModifier(event);
381       return true;
382     }
383     case OTHER_MODIFIER_DOWN:
384     case OTHER_MODIFIER_UP:
385       return false;
386   }
387   NOTREACHED();
388   return false;
389 }
390
391 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
392   switch (TranslateKeyEvent(event)) {
393     case TARGET_MODIFIER_DOWN:
394       return true;
395     case TARGET_MODIFIER_UP:
396       current_state_ = STICKY_KEY_STATE_DISABLED;
397       return false;
398     case NORMAL_KEY_DOWN:
399     case NORMAL_KEY_UP:
400       AppendModifier(event);
401       return false;
402     case OTHER_MODIFIER_DOWN:
403     case OTHER_MODIFIER_UP:
404       return false;
405   }
406   NOTREACHED();
407   return false;
408 }
409
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());
416   DCHECK(target);
417   aura::Window* root_window = target->GetRootWindow();
418   DCHECK(root_window);
419
420   aura::WindowTracker window_tracker;
421   window_tracker.Add(target);
422
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);
428   } else {
429     delegate_->DispatchScrollEvent(
430         static_cast<ui::ScrollEvent*>(event), target);
431   }
432
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;
439 }
440
441 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) {
442 #if defined(USE_X11)
443   unsigned int& state_ref = *state;
444   switch (modifier_flag_) {
445     case ui::EF_CONTROL_DOWN:
446       state_ref |= ControlMask;
447       break;
448     case ui::EF_ALT_DOWN:
449       state_ref |= Mod1Mask;
450       break;
451     case ui::EF_ALTGR_DOWN:
452       state_ref |= Mod5Mask;
453       break;
454     case ui::EF_SHIFT_DOWN:
455       state_ref |= ShiftMask;
456       break;
457     default:
458       NOTREACHED();
459   }
460 #endif
461 }
462
463 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) {
464 #if defined(USE_X11)
465   XEvent* xev = event->native_event();
466   if (xev) {
467     XKeyEvent* xkey = &(xev->xkey);
468     AppendNativeEventMask(&xkey->state);
469   }
470 #elif defined(USE_OZONE)
471   NOTIMPLEMENTED() << "Modifier key is not handled";
472 #endif
473   event->set_flags(event->flags() | modifier_flag_);
474   event->set_character(ui::GetCharacterFromKeyCode(event->key_code(),
475                                                    event->flags()));
476   event->NormalizeFlags();
477 }
478
479 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) {
480 #if defined(USE_X11)
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();
484   if (xev) {
485     switch (xev->type) {
486       case ButtonPress:
487       case ButtonRelease: {
488         XButtonEvent* xkey = &(xev->xbutton);
489         AppendNativeEventMask(&xkey->state);
490         break;
491       }
492       case GenericEvent: {
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));
499         break;
500       }
501       default:
502         NOTREACHED();
503     }
504   }
505 #elif defined(USE_OZONE)
506   NOTIMPLEMENTED() << "Modifier key is not handled";
507 #endif
508   event->set_flags(event->flags() | modifier_flag_);
509 }
510
511 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) {
512 #if defined(USE_X11)
513   XEvent* xev = event->native_event();
514   if (xev) {
515     XIDeviceEvent* xievent =
516         static_cast<XIDeviceEvent*>(xev->xcookie.data);
517     if (xievent) {
518       AppendNativeEventMask(reinterpret_cast<unsigned int*>(
519           &xievent->mods.effective));
520     }
521   }
522 #elif defined(USE_OZONE)
523   NOTIMPLEMENTED() << "Modifier key is not handled";
524 #endif
525   event->set_flags(event->flags() | modifier_flag_);
526 }
527
528 }  // namespace ash