Home | History | Annotate | Download | only in sticky_keys
      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 
     16 namespace ui {
     17 class Event;
     18 class KeyEvent;
     19 class MouseEvent;
     20 }  // namespace ui
     21 
     22 namespace aura {
     23 class Window;
     24 }  // namespace aura
     25 
     26 namespace ash {
     27 
     28 class StickyKeysOverlay;
     29 class 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.
     64 class 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.
    186 class 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