Upstream version 10.39.225.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       search_sticky_key_.reset(new StickyKeysHandler(ui::EF_COMMAND_DOWN));
70
71       overlay_.reset(new StickyKeysOverlay());
72       overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
73       overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
74     } else if (overlay_) {
75       overlay_->Show(false);
76     }
77   }
78 }
79
80 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
81                                                bool altgr_enabled) {
82   mod3_enabled_ = mod3_enabled;
83   altgr_enabled_ = altgr_enabled;
84   if (overlay_) {
85     overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
86     overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
87   }
88 }
89
90 bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
91                                           ui::KeyboardCode key_code,
92                                           int* mod_down_flags,
93                                           bool* released) {
94   return shift_sticky_key_->HandleKeyEvent(
95              event, key_code, mod_down_flags, released) ||
96          alt_sticky_key_->HandleKeyEvent(
97              event, key_code, mod_down_flags, released) ||
98          altgr_sticky_key_->HandleKeyEvent(
99              event, key_code, mod_down_flags, released) ||
100          ctrl_sticky_key_->HandleKeyEvent(
101              event, key_code, mod_down_flags, released) ||
102          mod3_sticky_key_->HandleKeyEvent(
103              event, key_code, mod_down_flags, released) ||
104          search_sticky_key_->HandleKeyEvent(
105              event, key_code, mod_down_flags, released);
106 }
107
108 bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
109                                             int* mod_down_flags,
110                                             bool* released) {
111   return shift_sticky_key_->HandleMouseEvent(
112              event, mod_down_flags, released) ||
113          alt_sticky_key_->HandleMouseEvent(
114              event, mod_down_flags, released) ||
115          altgr_sticky_key_->HandleMouseEvent(
116              event, mod_down_flags, released) ||
117          ctrl_sticky_key_->HandleMouseEvent(
118              event, mod_down_flags, released) ||
119          mod3_sticky_key_->HandleMouseEvent(
120              event, mod_down_flags, released);
121 }
122
123 bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
124                                              int* mod_down_flags,
125                                              bool* released) {
126   return shift_sticky_key_->HandleScrollEvent(
127              event, mod_down_flags, released) ||
128          alt_sticky_key_->HandleScrollEvent(
129              event, mod_down_flags, released) ||
130          altgr_sticky_key_->HandleScrollEvent(
131              event, mod_down_flags, released) ||
132          ctrl_sticky_key_->HandleScrollEvent(
133              event, mod_down_flags, released) ||
134          mod3_sticky_key_->HandleScrollEvent(
135              event, mod_down_flags, released);
136 }
137
138 ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
139     const ui::KeyEvent& event,
140     ui::KeyboardCode key_code,
141     int* flags) {
142   if (!enabled_)
143     return ui::EVENT_REWRITE_CONTINUE;
144   int mod_down_flags = 0;
145   bool released = false;
146   bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
147   UpdateOverlay();
148   return RewriteUpdate(consumed, released, mod_down_flags, flags);
149 }
150
151 ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
152     const ui::MouseEvent& event,
153     int* flags) {
154   if (!enabled_)
155     return ui::EVENT_REWRITE_CONTINUE;
156   int mod_down_flags = 0;
157   bool released = false;
158   bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
159   UpdateOverlay();
160   return RewriteUpdate(consumed, released, mod_down_flags, flags);
161 }
162
163 ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
164     const ui::ScrollEvent& event,
165     int* flags) {
166   if (!enabled_)
167     return ui::EVENT_REWRITE_CONTINUE;
168   int mod_down_flags = 0;
169   bool released = false;
170   bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
171   UpdateOverlay();
172   return RewriteUpdate(consumed, released, mod_down_flags, flags);
173 }
174
175 ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
176     scoped_ptr<ui::Event>* new_event) {
177   DCHECK(new_event);
178   new_event->reset();
179   int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) +
180                   alt_sticky_key_->GetModifierUpEvent(new_event) +
181                   altgr_sticky_key_->GetModifierUpEvent(new_event) +
182                   ctrl_sticky_key_->GetModifierUpEvent(new_event) +
183                   mod3_sticky_key_->GetModifierUpEvent(new_event) +
184                   search_sticky_key_->GetModifierUpEvent(new_event);
185   if (!new_event)
186     return ui::EVENT_REWRITE_CONTINUE;
187   if (remaining)
188     return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
189   return ui::EVENT_REWRITE_REWRITTEN;
190 }
191
192 void StickyKeysController::UpdateOverlay() {
193   overlay_->SetModifierKeyState(
194       ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
195   overlay_->SetModifierKeyState(
196       ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
197   overlay_->SetModifierKeyState(
198       ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
199   overlay_->SetModifierKeyState(
200       ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
201   overlay_->SetModifierKeyState(
202       ui::EF_MOD3_DOWN, mod3_sticky_key_->current_state());
203
204   bool key_in_use =
205       shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
206       alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
207       altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
208       ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
209       mod3_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     : modifier_flag_(modifier_flag),
222       current_state_(STICKY_KEY_STATE_DISABLED),
223       preparing_to_enable_(false),
224       scroll_delta_(0) {
225 }
226
227 StickyKeysHandler::~StickyKeysHandler() {
228 }
229
230 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
231                                        ui::KeyboardCode key_code,
232                                        int* mod_down_flags,
233                                        bool* released) {
234   switch (current_state_) {
235     case STICKY_KEY_STATE_DISABLED:
236       return HandleDisabledState(event, key_code);
237     case STICKY_KEY_STATE_ENABLED:
238       return HandleEnabledState(event, key_code, mod_down_flags, released);
239     case STICKY_KEY_STATE_LOCKED:
240       return HandleLockedState(event, key_code, mod_down_flags, released);
241   }
242   NOTREACHED();
243   return false;
244 }
245
246 bool StickyKeysHandler::HandleMouseEvent(
247     const ui::MouseEvent& event,
248     int* mod_down_flags,
249     bool* released) {
250   if (ShouldModifyMouseEvent(event))
251     preparing_to_enable_ = false;
252
253   if (current_state_ == STICKY_KEY_STATE_DISABLED ||
254       !ShouldModifyMouseEvent(event)) {
255     return false;
256   }
257   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
258          current_state_ == STICKY_KEY_STATE_LOCKED);
259
260   *mod_down_flags |= modifier_flag_;
261   // Only disable on the mouse released event in normal, non-locked mode.
262   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
263       event.type() != ui::ET_MOUSE_PRESSED) {
264     current_state_ = STICKY_KEY_STATE_DISABLED;
265     *released = true;
266     return false;
267   }
268
269   return false;
270 }
271
272 bool StickyKeysHandler::HandleScrollEvent(
273     const ui::ScrollEvent& event,
274     int* mod_down_flags,
275     bool* released) {
276   preparing_to_enable_ = false;
277   if (current_state_ == STICKY_KEY_STATE_DISABLED)
278     return false;
279   DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
280          current_state_ == STICKY_KEY_STATE_LOCKED);
281
282   // We detect a direction change if the current |scroll_delta_| is assigned
283   // and the offset of the current scroll event has the opposing sign.
284   bool direction_changed = false;
285   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
286       event.type() == ui::ET_SCROLL) {
287     int offset = event.y_offset();
288     if (scroll_delta_)
289       direction_changed = offset * scroll_delta_ <= 0;
290     scroll_delta_ = offset;
291   }
292
293   if (!direction_changed)
294     *mod_down_flags |= modifier_flag_;
295
296   // We want to modify all the scroll events in the scroll sequence, which ends
297   // with a fling start event. We also stop when the scroll sequence changes
298   // direction.
299   if (current_state_ == STICKY_KEY_STATE_ENABLED &&
300       (event.type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
301     current_state_ = STICKY_KEY_STATE_DISABLED;
302     scroll_delta_ = 0;
303     *released = true;
304     return false;
305   }
306
307   return false;
308 }
309
310 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr<ui::Event>* new_event) {
311   if (current_state_ != STICKY_KEY_STATE_DISABLED || !modifier_up_event_)
312     return 0;
313   DCHECK(new_event);
314   if (*new_event)
315     return 1;
316   new_event->reset(modifier_up_event_.release());
317   return 0;
318 }
319
320 StickyKeysHandler::KeyEventType StickyKeysHandler::TranslateKeyEvent(
321     ui::EventType type,
322     ui::KeyboardCode key_code) {
323   bool is_target_key = false;
324   if (key_code == ui::VKEY_SHIFT ||
325       key_code == ui::VKEY_LSHIFT ||
326       key_code == ui::VKEY_RSHIFT) {
327     is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
328   } else if (key_code == ui::VKEY_CONTROL ||
329       key_code == ui::VKEY_LCONTROL ||
330       key_code == ui::VKEY_RCONTROL) {
331     is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
332   } else if (key_code == ui::VKEY_MENU ||
333       key_code == ui::VKEY_LMENU ||
334       key_code == ui::VKEY_RMENU) {
335     is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
336   } else if (key_code == ui::VKEY_ALTGR) {
337     is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
338   } else if (key_code == ui::VKEY_OEM_8) {
339     is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN);
340   } else if (key_code == ui::VKEY_LWIN) {
341     is_target_key = (modifier_flag_ == ui::EF_COMMAND_DOWN);
342   } else {
343     return type == ui::ET_KEY_PRESSED ?
344         NORMAL_KEY_DOWN : NORMAL_KEY_UP;
345   }
346
347   if (is_target_key) {
348     return type == ui::ET_KEY_PRESSED ?
349         TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
350   }
351   return type == ui::ET_KEY_PRESSED ?
352       OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
353 }
354
355 bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event,
356                                             ui::KeyboardCode key_code) {
357   switch (TranslateKeyEvent(event.type(), key_code)) {
358     case TARGET_MODIFIER_UP:
359       if (preparing_to_enable_) {
360         preparing_to_enable_ = false;
361         scroll_delta_ = 0;
362         current_state_ = STICKY_KEY_STATE_ENABLED;
363         modifier_up_event_.reset(new ui::KeyEvent(event));
364         return true;
365       }
366       return false;
367     case TARGET_MODIFIER_DOWN:
368       preparing_to_enable_ = true;
369       return false;
370     case NORMAL_KEY_DOWN:
371       preparing_to_enable_ = false;
372       return false;
373     case NORMAL_KEY_UP:
374     case OTHER_MODIFIER_DOWN:
375     case OTHER_MODIFIER_UP:
376       return false;
377   }
378   NOTREACHED();
379   return false;
380 }
381
382 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event,
383                                            ui::KeyboardCode key_code,
384                                            int* mod_down_flags,
385                                            bool* released) {
386   switch (TranslateKeyEvent(event.type(), key_code)) {
387     case NORMAL_KEY_UP:
388     case TARGET_MODIFIER_DOWN:
389       return false;
390     case TARGET_MODIFIER_UP:
391       current_state_ = STICKY_KEY_STATE_LOCKED;
392       modifier_up_event_.reset();
393       return true;
394     case NORMAL_KEY_DOWN: {
395       current_state_ = STICKY_KEY_STATE_DISABLED;
396       *mod_down_flags |= modifier_flag_;
397       *released = true;
398       return false;
399     }
400     case OTHER_MODIFIER_DOWN:
401     case OTHER_MODIFIER_UP:
402       return false;
403   }
404   NOTREACHED();
405   return false;
406 }
407
408 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event,
409                                           ui::KeyboardCode key_code,
410                                           int* mod_down_flags,
411                                           bool* released) {
412   switch (TranslateKeyEvent(event.type(), key_code)) {
413     case TARGET_MODIFIER_DOWN:
414       return true;
415     case TARGET_MODIFIER_UP:
416       current_state_ = STICKY_KEY_STATE_DISABLED;
417       return false;
418     case NORMAL_KEY_DOWN:
419     case NORMAL_KEY_UP:
420       *mod_down_flags |= modifier_flag_;
421       return false;
422     case OTHER_MODIFIER_DOWN:
423     case OTHER_MODIFIER_UP:
424       return false;
425   }
426   NOTREACHED();
427   return false;
428 }
429
430 }  // namespace ash