Upstream version 9.38.198.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 #include "ash/sticky_keys/sticky_keys_overlay.h"
8 #include "base/basictypes.h"
9 #include "base/debug/stack_trace.h"
10 #include "ui/aura/window.h"
11 #include "ui/aura/window_tracker.h"
12 #include "ui/aura/window_tree_host.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_processor.h"
15 #include "ui/events/keycodes/keyboard_code_conversion.h"
16
17 namespace ash {
18
19 namespace {
20
21 // Returns true if the type of mouse event should be modified by sticky keys.
22 bool ShouldModifyMouseEvent(const ui::MouseEvent& event) {
23   ui::EventType type = event.type();
24   return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED ||
25          type == ui::ET_MOUSEWHEEL;
26 }
27
28 // Handle the common tail of event rewriting.
29 ui::EventRewriteStatus RewriteUpdate(bool consumed,
30                                      bool released,
31                                      int mod_down_flags,
32                                      int* flags) {
33   int changed_down_flags = mod_down_flags & ~*flags;
34   *flags |= mod_down_flags;
35   if (consumed)
36     return ui::EVENT_REWRITE_DISCARD;
37   if (released)
38     return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
39   if (changed_down_flags)
40     return ui::EVENT_REWRITE_REWRITTEN;
41   return ui::EVENT_REWRITE_CONTINUE;
42 }
43
44 }  // namespace
45
46 ///////////////////////////////////////////////////////////////////////////////
47 //  StickyKeys
48 StickyKeysController::StickyKeysController()
49     : enabled_(false),
50       mod3_enabled_(false),
51       altgr_enabled_(false) {
52 }
53
54 StickyKeysController::~StickyKeysController() {
55 }
56
57 void StickyKeysController::Enable(bool enabled) {
58   if (enabled_ != enabled) {
59     enabled_ = enabled;
60
61     // Reset key handlers when activating sticky keys to ensure all
62     // the handlers' states are reset.
63     if (enabled_) {
64       shift_sticky_key_.reset(new StickyKeysHandler(ui::EF_SHIFT_DOWN));
65       alt_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALT_DOWN));
66       altgr_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALTGR_DOWN));
67       ctrl_sticky_key_.reset(new StickyKeysHandler(ui::EF_CONTROL_DOWN));
68       mod3_sticky_key_.reset(new StickyKeysHandler(ui::EF_MOD3_DOWN));
69
70       overlay_.reset(new StickyKeysOverlay());
71       overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
72       overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
73     } else if (overlay_) {
74       overlay_->Show(false);
75     }
76   }
77 }
78
79 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
80                                                bool altgr_enabled) {
81   mod3_enabled_ = mod3_enabled;
82   altgr_enabled_ = altgr_enabled;
83   if (overlay_) {
84     overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
85     overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
86   }
87 }
88
89 bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
90                                           ui::KeyboardCode key_code,
91                                           int* mod_down_flags,
92                                           bool* released) {
93   return shift_sticky_key_->HandleKeyEvent(
94              event, key_code, mod_down_flags, released) ||
95          alt_sticky_key_->HandleKeyEvent(
96              event, key_code, mod_down_flags, released) ||
97          altgr_sticky_key_->HandleKeyEvent(
98              event, key_code, mod_down_flags, released) ||
99          ctrl_sticky_key_->HandleKeyEvent(
100              event, key_code, mod_down_flags, released) ||
101          mod3_sticky_key_->HandleKeyEvent(
102              event, key_code, mod_down_flags, released);
103 }
104
105 bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
106                                             int* mod_down_flags,
107                                             bool* released) {
108   return shift_sticky_key_->HandleMouseEvent(
109              event, mod_down_flags, released) ||
110          alt_sticky_key_->HandleMouseEvent(
111              event, mod_down_flags, released) ||
112          altgr_sticky_key_->HandleMouseEvent(
113              event, mod_down_flags, released) ||
114          ctrl_sticky_key_->HandleMouseEvent(
115              event, mod_down_flags, released) ||
116          mod3_sticky_key_->HandleMouseEvent(
117              event, mod_down_flags, released);
118 }
119
120 bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
121                                              int* mod_down_flags,
122                                              bool* released) {
123   return shift_sticky_key_->HandleScrollEvent(
124              event, mod_down_flags, released) ||
125          alt_sticky_key_->HandleScrollEvent(
126              event, mod_down_flags, released) ||
127          altgr_sticky_key_->HandleScrollEvent(
128              event, mod_down_flags, released) ||
129          ctrl_sticky_key_->HandleScrollEvent(
130              event, mod_down_flags, released) ||
131          mod3_sticky_key_->HandleScrollEvent(
132              event, mod_down_flags, released);
133 }
134
135 ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
136     const ui::KeyEvent& event,
137     ui::KeyboardCode key_code,
138     int* flags) {
139   if (!enabled_)
140     return ui::EVENT_REWRITE_CONTINUE;
141   int mod_down_flags = 0;
142   bool released = false;
143   bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
144   UpdateOverlay();
145   return RewriteUpdate(consumed, released, mod_down_flags, flags);
146 }
147
148 ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
149     const ui::MouseEvent& event,
150     int* flags) {
151   if (!enabled_)
152     return ui::EVENT_REWRITE_CONTINUE;
153   int mod_down_flags = 0;
154   bool released = false;
155   bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
156   UpdateOverlay();
157   return RewriteUpdate(consumed, released, mod_down_flags, flags);
158 }
159
160 ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
161     const ui::ScrollEvent& event,
162     int* flags) {
163   if (!enabled_)
164     return ui::EVENT_REWRITE_CONTINUE;
165   int mod_down_flags = 0;
166   bool released = false;
167   bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
168   UpdateOverlay();
169   return RewriteUpdate(consumed, released, mod_down_flags, flags);
170 }
171
172 ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
173     scoped_ptr<ui::Event>* new_event) {
174   DCHECK(new_event);
175   new_event->reset();
176   int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) +
177                   alt_sticky_key_->GetModifierUpEvent(new_event) +
178                   altgr_sticky_key_->GetModifierUpEvent(new_event) +
179                   ctrl_sticky_key_->GetModifierUpEvent(new_event) +
180                   mod3_sticky_key_->GetModifierUpEvent(new_event);
181   if (!new_event)
182     return ui::EVENT_REWRITE_CONTINUE;
183   if (remaining)
184     return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
185   return ui::EVENT_REWRITE_REWRITTEN;
186 }
187
188 void StickyKeysController::UpdateOverlay() {
189   overlay_->SetModifierKeyState(
190       ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
191   overlay_->SetModifierKeyState(
192       ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
193   overlay_->SetModifierKeyState(
194       ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
195   overlay_->SetModifierKeyState(
196       ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
197   overlay_->SetModifierKeyState(
198       ui::EF_MOD3_DOWN, mod3_sticky_key_->current_state());
199
200   bool key_in_use =
201       shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
202       alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
203       altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
204       ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
205       mod3_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
206
207   overlay_->Show(enabled_ && key_in_use);
208 }
209
210 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
211   return overlay_.get();
212 }
213
214 ///////////////////////////////////////////////////////////////////////////////
215 //  StickyKeysHandler
216 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag)
217     : modifier_flag_(modifier_flag),
218       current_state_(STICKY_KEY_STATE_DISABLED),
219       preparing_to_enable_(false),
220       scroll_delta_(0) {
221 }
222
223 StickyKeysHandler::~StickyKeysHandler() {
224 }
225
226 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
227                                        ui::KeyboardCode key_code,
228                                        int* mod_down_flags,
229                                        bool* released) {
230   switch (current_state_) {
231     case STICKY_KEY_STATE_DISABLED:
232       return HandleDisabledState(event, key_code);
233     case STICKY_KEY_STATE_ENABLED:
234       return HandleEnabledState(event, key_code, mod_down_flags, released);
235     case STICKY_KEY_STATE_LOCKED:
236       return HandleLockedState(event, key_code, mod_down_flags, released);
237   }
238   NOTREACHED();
239   return false;
240 }
241
242 bool StickyKeysHandler::HandleMouseEvent(
243     const ui::MouseEvent& event,
244     int* mod_down_flags,
245     bool* released) {
246   if (ShouldModifyMouseEvent(event))
247     preparing_to_enable_ = false;
248
249   if (current_state_ == STICKY_KEY_STATE_DISABLED ||
250       !ShouldModifyMouseEvent(event)) {
251     return false;
252   }
253   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
254          current_state_ == STICKY_KEY_STATE_LOCKED);
255
256   *mod_down_flags |= modifier_flag_;
257   // Only disable on the mouse released event in normal, non-locked mode.
258   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
259       event.type() != ui::ET_MOUSE_PRESSED) {
260     current_state_ = STICKY_KEY_STATE_DISABLED;
261     *released = true;
262     return false;
263   }
264
265   return false;
266 }
267
268 bool StickyKeysHandler::HandleScrollEvent(
269     const ui::ScrollEvent& event,
270     int* mod_down_flags,
271     bool* released) {
272   preparing_to_enable_ = false;
273   if (current_state_ == STICKY_KEY_STATE_DISABLED)
274     return false;
275   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
276          current_state_ == STICKY_KEY_STATE_LOCKED);
277
278   // We detect a direction change if the current |scroll_delta_| is assigned
279   // and the offset of the current scroll event has the opposing sign.
280   bool direction_changed = false;
281   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
282       event.type() == ui::ET_SCROLL) {
283     int offset = event.y_offset();
284     if (scroll_delta_)
285       direction_changed = offset * scroll_delta_ <= 0;
286     scroll_delta_ = offset;
287   }
288
289   if (!direction_changed)
290     *mod_down_flags |= modifier_flag_;
291
292   // We want to modify all the scroll events in the scroll sequence, which ends
293   // with a fling start event. We also stop when the scroll sequence changes
294   // direction.
295   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
296       (event.type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
297     current_state_ = STICKY_KEY_STATE_DISABLED;
298     scroll_delta_ = 0;
299     *released = true;
300     return false;
301   }
302
303   return false;
304 }
305
306 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr<ui::Event>* new_event) {
307   if (current_state_ != STICKY_KEY_STATE_DISABLED || !modifier_up_event_)
308     return 0;
309   DCHECK(new_event);
310   if (*new_event)
311     return 1;
312   new_event->reset(modifier_up_event_.release());
313   return 0;
314 }
315
316 StickyKeysHandler::KeyEventType StickyKeysHandler::TranslateKeyEvent(
317     ui::EventType type,
318     ui::KeyboardCode key_code) {
319   bool is_target_key = false;
320   if (key_code == ui::VKEY_SHIFT ||
321       key_code == ui::VKEY_LSHIFT ||
322       key_code == ui::VKEY_RSHIFT) {
323     is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
324   } else if (key_code == ui::VKEY_CONTROL ||
325       key_code == ui::VKEY_LCONTROL ||
326       key_code == ui::VKEY_RCONTROL) {
327     is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
328   } else if (key_code == ui::VKEY_MENU ||
329       key_code == ui::VKEY_LMENU ||
330       key_code == ui::VKEY_RMENU) {
331     is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
332   } else if (key_code == ui::VKEY_ALTGR) {
333     is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
334   } else if (key_code == ui::VKEY_OEM_8) {
335     is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN);
336   } else {
337     return type == ui::ET_KEY_PRESSED ?
338         NORMAL_KEY_DOWN : NORMAL_KEY_UP;
339   }
340
341   if (is_target_key) {
342     return type == ui::ET_KEY_PRESSED ?
343         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
344   }
345   return type == ui::ET_KEY_PRESSED ?
346       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
347 }
348
349 bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event,
350                                             ui::KeyboardCode key_code) {
351   switch (TranslateKeyEvent(event.type(), key_code)) {
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(const ui::KeyEvent& event,
377                                            ui::KeyboardCode key_code,
378                                            int* mod_down_flags,
379                                            bool* released) {
380   switch (TranslateKeyEvent(event.type(), key_code)) {
381     case NORMAL_KEY_UP:
382     case TARGET_MODIFIER_DOWN:
383       return false;
384     case TARGET_MODIFIER_UP:
385       current_state_ = STICKY_KEY_STATE_LOCKED;
386       modifier_up_event_.reset();
387       return true;
388     case NORMAL_KEY_DOWN: {
389       current_state_ = STICKY_KEY_STATE_DISABLED;
390       *mod_down_flags |= modifier_flag_;
391       *released = true;
392       return false;
393     }
394     case OTHER_MODIFIER_DOWN:
395     case OTHER_MODIFIER_UP:
396       return false;
397   }
398   NOTREACHED();
399   return false;
400 }
401
402 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event,
403                                           ui::KeyboardCode key_code,
404                                           int* mod_down_flags,
405                                           bool* released) {
406   switch (TranslateKeyEvent(event.type(), key_code)) {
407     case TARGET_MODIFIER_DOWN:
408       return true;
409     case TARGET_MODIFIER_UP:
410       current_state_ = STICKY_KEY_STATE_DISABLED;
411       return false;
412     case NORMAL_KEY_DOWN:
413     case NORMAL_KEY_UP:
414       *mod_down_flags |= modifier_flag_;
415       return false;
416     case OTHER_MODIFIER_DOWN:
417     case OTHER_MODIFIER_UP:
418       return false;
419   }
420   NOTREACHED();
421   return false;
422 }
423
424 }  // namespace ash