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#ifndef ASH_STICKY_KEYS_STICKY_KEYS_CONTROLLER_H_
6#define ASH_STICKY_KEYS_STICKY_KEYS_CONTROLLER_H_
7
8#include "ash/ash_export.h"
9#include "ash/sticky_keys/sticky_keys_state.h"
10#include "base/memory/scoped_ptr.h"
11#include "ui/events/event_constants.h"
12#include "ui/events/event_handler.h"
13#include "ui/events/event_rewriter.h"
14#include "ui/events/keycodes/keyboard_codes.h"
15
16namespace ui {
17class Event;
18class KeyEvent;
19class MouseEvent;
20}  // namespace ui
21
22namespace aura {
23class Window;
24}  // namespace aura
25
26namespace ash {
27
28class StickyKeysOverlay;
29class StickyKeysHandler;
30
31// StickyKeysController is an accessibility feature for users to be able to
32// compose key and mouse event with modifier keys without simultaneous key
33// press event. Instead they can compose events separately pressing each of the
34// modifier keys involved.
35// e.g. Composing Ctrl + T
36//       User Action   : The KeyEvent widget will receives
37// ----------------------------------------------------------
38// 1. Press Ctrl key   : Ctrl Keydown.
39// 2. Release Ctrl key : No event
40// 3. Press T key      : T keydown event with ctrl modifier.
41// 4.                  : Ctrl Keyup
42// 5. Release T key    : T keyup without ctrl modifier (Windows behavior)
43//
44// By typing same modifier keys twice, users can generate bunch of modified key
45// events.
46// e.g. To focus tabs consistently by Ctrl + 1, Ctrl + 2 ...
47//       User Action   : The KeyEvent widget will receives
48// ----------------------------------------------------------
49// 1. Press Ctrl key   : Ctrl Keydown
50// 2. Release Ctrl key : No event
51// 3. Press Ctrl key   : No event
52// 4. Release Ctrl key : No event
53// 5. Press 1 key      : 1 Keydown event with Ctrl modifier.
54// 6. Release 1 key    : 1 Keyup event with Ctrl modifier.
55// 7. Press 2 key      : 2 Keydown event with Ctrl modifier.
56// 8. Release 2 key    : 2 Keyup event with Ctrl modifier.
57// 9. Press Ctrl key   : No event
58// 10. Release Ctrl key: Ctrl Keyup
59//
60// In the case of Chrome OS, StickyKeysController supports Shift,Alt,Ctrl
61// modifiers. Each handling or state is performed independently.
62//
63// StickyKeysController is disabled by default.
64class ASH_EXPORT StickyKeysController {
65 public:
66  StickyKeysController();
67  virtual ~StickyKeysController();
68
69  // Activate sticky keys to intercept and modify incoming events.
70  void Enable(bool enabled);
71
72  void SetModifiersEnabled(bool mod3_enabled, bool altgr_enabled);
73
74  // Returns the StickyKeyOverlay used by the controller. Ownership is not
75  // passed.
76  StickyKeysOverlay* GetOverlayForTest();
77
78  // Handles keyboard event. Returns an |EventRewriteStatus|, and may
79  // modify |flags|:
80  // - Returns ui::EVENT_REWRITE_DISCARD, and leaves |flags| untouched,
81  //   if the event is consumed (i.e. a sticky modifier press or release);
82  // - Returns ui::EVENT_REWRITE_REWRITTEN if the event needs to be modified
83  //   according to the returned |flags| (i.e. a sticky-modified key);
84  // - Returns ui::EVENT_REWRITE_DISPATCH_ANOTHER if the event needs to be
85  //   modified according to the returned |flags|, and there are delayed
86  //   modifier-up   events now to be retrieved using |NextDispatchEvent()|
87  //   (i.e. a sticky-modified key that ends a sticky state);
88  // - Otherwise returns ui::EVENT_REWRITE_CONTINUE and leaves |flags|
89  //   unchanged.
90  ui::EventRewriteStatus RewriteKeyEvent(const ui::KeyEvent& event,
91                                         ui::KeyboardCode key_code,
92                                         int* flags);
93
94  // Handles mouse event.
95  ui::EventRewriteStatus RewriteMouseEvent(const ui::MouseEvent& event,
96                                           int* flags);
97
98  // Handles scroll event.
99  ui::EventRewriteStatus RewriteScrollEvent(const ui::ScrollEvent& event,
100                                            int* flags);
101
102  // Obtains a pending modifier-up event. If the immediately previous
103  // call to |Rewrite...Event()| or |NextDispatchEvent()| returned
104  // ui::EVENT_REWRITE_DISPATCH_ANOTHER, this sets |new_event| and returns:
105  // - ui::EVENT_REWRITE_DISPATCH_ANOTHER if there is at least one more
106  //   pending modifier-up event;
107  // - ui::EVENT_REWRITE_REWRITE if this is the last or only modifier-up event;
108  // Otherwise, there is no pending modifier-up event, and this function
109  // returns ui::EVENT_REWRITE_CONTINUE and sets |new_event| to NULL.
110  ui::EventRewriteStatus NextDispatchEvent(scoped_ptr<ui::Event>* new_event);
111
112 private:
113  // Handles keyboard event. Returns true if Sticky key consumes keyboard event.
114  // Adds to |mod_down_flags| any flag to be added to the key event.
115  // Sets |released| if any modifier is to be released after the key event.
116  bool HandleKeyEvent(const ui::KeyEvent& event,
117                      ui::KeyboardCode key_code,
118                      int* mod_down_flags,
119                      bool* released);
120
121  // Handles mouse event. Returns true if Sticky key consumes keyboard event.
122  // Sets |released| if any modifier is to be released after the key event.
123  bool HandleMouseEvent(const ui::MouseEvent& event,
124                        int* mod_down_flags,
125                        bool* released);
126
127  // Handles scroll event. Returns true if Sticky key consumes keyboard event.
128  // Sets |released| if any modifier is to be released after the key event.
129  bool HandleScrollEvent(const ui::ScrollEvent& event,
130                         int* mod_down_flags,
131                         bool* released);
132
133  // Updates the overlay UI with the current state of the sticky keys.
134  void UpdateOverlay();
135
136  // Whether sticky keys is activated and modifying events.
137  bool enabled_;
138
139  // Whether the current layout has a mod3 key.
140  bool mod3_enabled_;
141
142  // Whether the current layout has an altgr key.
143  bool altgr_enabled_;
144
145  // Sticky key handlers.
146  scoped_ptr<StickyKeysHandler> shift_sticky_key_;
147  scoped_ptr<StickyKeysHandler> alt_sticky_key_;
148  scoped_ptr<StickyKeysHandler> altgr_sticky_key_;
149  scoped_ptr<StickyKeysHandler> ctrl_sticky_key_;
150  scoped_ptr<StickyKeysHandler> mod3_sticky_key_;
151
152  scoped_ptr<StickyKeysOverlay> overlay_;
153
154  DISALLOW_COPY_AND_ASSIGN(StickyKeysController);
155};
156
157// StickyKeysHandler handles key event and controls sticky keysfor specific
158// modifier keys. If monitored keyboard events are recieved, StickyKeysHandler
159// changes internal state. If non modifier keyboard events or mouse events are
160// received, StickyKeysHandler will append modifier based on internal state.
161// For other events, StickyKeysHandler does nothing.
162//
163// The DISABLED state is default state and any incoming non modifier keyboard
164// events will not be modified. The ENABLED state is one shot modification
165// state. Only next keyboard event will be modified. After that, internal state
166// will be back to DISABLED state with sending modifier keyup event. In the case
167// of LOCKED state, all incomming keyboard events will be modified. The LOCKED
168// state will be back to DISABLED state by next monitoring modifier key.
169//
170// The detailed state flow as follows:
171//                                     Current state
172//                  |   DISABLED    |    ENABLED     |    LOCKED   |
173// ----------------------------------------------------------------|
174// Modifier KeyDown |   noop        |    noop(*)     |    noop(*)  |
175// Modifier KeyUp   | To ENABLED(*) | To LOCKED(*)   | To DISABLED |
176// Normal KeyDown   |   noop        | To DISABLED(#) |    noop(#)  |
177// Normal KeyUp     |   noop        |    noop        |    noop(#)  |
178// Other KeyUp/Down |   noop        |    noop        |    noop     |
179// Mouse Press      |   noop        |    noop(#)     |    noop(#)  |
180// Mouse Release    |   noop        | To DISABLED(#) |    noop(#)  |
181// Mouse Wheel      |   noop        | To DISABLED(#) |    noop(#)  |
182// Other Mouse Event|   noop        |    noop        |    noop     |
183//
184// Here, (*) means key event will be consumed by StickyKeys, and (#) means event
185// is modified.
186class ASH_EXPORT StickyKeysHandler {
187 public:
188  explicit StickyKeysHandler(ui::EventFlags modifier_flag);
189  ~StickyKeysHandler();
190
191  // Handles keyboard event. Returns true if Sticky key consumes keyboard event.
192  // Sets its own modifier flag in |mod_down_flags| if it is active and needs
193  // to be added to the event, and sets |released| if releasing it.
194  bool HandleKeyEvent(const ui::KeyEvent& event,
195                      ui::KeyboardCode key_code,
196                      int* mod_down_flags,
197                      bool* released);
198
199  // Handles mouse event. Returns true if sticky key consumes mouse event.
200  // Sets its own modifier flag in |mod_down_flags| if it is active and needs
201  // to be added to the event, and sets |released| if releasing it.
202  bool HandleMouseEvent(const ui::MouseEvent& event,
203                        int* mod_down_flags,
204                        bool* released);
205
206  // Handles scroll event. Returns true if sticky key consumes scroll event.
207  // Sets its own modifier flag in |mod_down_flags| if it is active and needs
208  // to be added to the event, and sets |released| if releasing it.
209  bool HandleScrollEvent(const ui::ScrollEvent& event,
210                         int* mod_down_flags,
211                         bool* released);
212
213  // Fetches a pending modifier-up event if one exists and the return
214  // parameter |new_event| is available (i.e. not set). Returns the number
215  // of pending events still remaining to be returned.
216  int GetModifierUpEvent(scoped_ptr<ui::Event>* new_event);
217
218  // Returns current internal state.
219  StickyKeyState current_state() const { return current_state_; }
220
221 private:
222  // Represents event type in Sticky Key context.
223  enum KeyEventType {
224    TARGET_MODIFIER_DOWN,  // The monitoring modifier key is down.
225    TARGET_MODIFIER_UP,  // The monitoring modifier key is up.
226    NORMAL_KEY_DOWN,  // The non modifier key is down.
227    NORMAL_KEY_UP,  // The non modifier key is up.
228    OTHER_MODIFIER_DOWN,  // The modifier key but not monitored key is down.
229    OTHER_MODIFIER_UP,  // The modifier key but not monitored key is up.
230  };
231
232  // Translates event type and key code to sticky keys event type.
233  KeyEventType TranslateKeyEvent(ui::EventType type, ui::KeyboardCode key_code);
234
235  // Handles key event in DISABLED state. Returns true if sticky keys
236  // consumes the keyboard event.
237  bool HandleDisabledState(const ui::KeyEvent& event,
238                           ui::KeyboardCode key_code);
239
240  // Handles key event in ENABLED state. Returns true if sticky keys
241  // consumes the keyboard event.
242  bool HandleEnabledState(const ui::KeyEvent& event,
243                          ui::KeyboardCode key_code,
244                          int* mod_down_flags,
245                          bool* released);
246
247  // Handles key event in LOCKED state. Returns true if sticky keys
248  // consumes the keyboard event.
249  bool HandleLockedState(const ui::KeyEvent& event,
250                         ui::KeyboardCode key_code,
251                         int* mod_down_flags,
252                         bool* released);
253
254  // The modifier flag to be monitored and appended to events.
255  const ui::EventFlags modifier_flag_;
256
257  // The current sticky key status.
258  StickyKeyState current_state_;
259
260  // True if we received the TARGET_MODIFIER_DOWN event while in the DISABLED
261  // state but before we receive the TARGET_MODIFIER_UP event. Normal
262  // shortcuts (eg. ctrl + t) during this time will prevent a transition to
263  // the ENABLED state.
264  bool preparing_to_enable_;
265
266  // Tracks the scroll direction of the current scroll sequence. Sticky keys
267  // stops modifying the scroll events of the sequence when the direction
268  // changes. If no sequence is tracked, the value is 0.
269  int scroll_delta_;
270
271  // The modifier up key event to be sent on non modifier key on ENABLED state.
272  scoped_ptr<ui::KeyEvent> modifier_up_event_;
273
274  DISALLOW_COPY_AND_ASSIGN(StickyKeysHandler);
275};
276
277}  // namespace ash
278
279#endif  // ASH_STICKY_KEYS_STICKY_KEYS_CONTROLLER_H_
280