Home | History | Annotate | Download | only in wm
      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_WM_STICKY_KEYS_H_
      6 #define ASH_WM_STICKY_KEYS_H_
      7 
      8 #include "ash/ash_export.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "ui/events/event_constants.h"
     11 #include "ui/events/event_handler.h"
     12 
     13 namespace ui {
     14 class Event;
     15 class KeyEvent;
     16 class MouseEvent;
     17 }  // namespace ui
     18 
     19 namespace aura {
     20 class Window;
     21 }  // namespace aura
     22 
     23 namespace ash {
     24 
     25 class StickyKeysHandler;
     26 
     27 // StickyKeys is an accessibility feature for users to be able to compose key
     28 // and mouse event with modifier keys without simultaneous key press event.
     29 // Instead they can compose events separately pressing each of the modifier
     30 // keys involved.
     31 // e.g. Composing Ctrl + T
     32 //       User Action   : The KeyEvent widget will receives
     33 // ----------------------------------------------------------
     34 // 1. Press Ctrl key   : Ctrl Keydown.
     35 // 2. Release Ctrl key : No event
     36 // 3. Press T key      : T keydown event with ctrl modifier.
     37 // 4.                  : Ctrl Keyup
     38 // 5. Release T key    : T keyup without ctrl modifier (Windows behavior)
     39 //
     40 // By typing same modifier keys twice, users can generate bunch of modified key
     41 // events.
     42 // e.g. To focus tabs consistently by Ctrl + 1, Ctrl + 2 ...
     43 //       User Action   : The KeyEvent widget will receives
     44 // ----------------------------------------------------------
     45 // 1. Press Ctrl key   : Ctrl Keydown
     46 // 2. Release Ctrl key : No event
     47 // 3. Press Ctrl key   : No event
     48 // 4. Release Ctrl key : No event
     49 // 5. Press 1 key      : 1 Keydown event with Ctrl modifier.
     50 // 6. Release 1 key    : 1 Keyup event with Ctrl modifier.
     51 // 7. Press 2 key      : 2 Keydown event with Ctrl modifier.
     52 // 8. Release 2 key    : 2 Keyup event with Ctrl modifier.
     53 // 9. Press Ctrl key   : No event
     54 // 10. Release Ctrl key: Ctrl Keyup
     55 //
     56 // In the case of Chrome OS, StickyKeys supports Shift,Alt,Ctrl modifiers. Each
     57 // handling or state is performed independently.
     58 //
     59 // StickyKeys is disabled by default.
     60 class ASH_EXPORT StickyKeys : public ui::EventHandler {
     61  public:
     62   StickyKeys();
     63   virtual ~StickyKeys();
     64 
     65   // Activate sticky keys to intercept and modify incoming events.
     66   void Enable(bool enabled);
     67 
     68  private:
     69   // Handles keyboard event. Returns true if Sticky key consumes keyboard event.
     70   bool HandleKeyEvent(ui::KeyEvent* event);
     71 
     72   // Handles mouse event. Returns true if sticky key consumes mouse event.
     73   bool HandleMouseEvent(ui::MouseEvent* event);
     74 
     75   // Handles scroll event. Returns true if sticky key consumes scroll event.
     76   bool HandleScrollEvent(ui::ScrollEvent* event);
     77 
     78   // Overridden from ui::EventHandler:
     79   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
     80   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
     81   virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
     82 
     83   // Whether sticky keys is activated and modifying events.
     84   bool enabled_;
     85 
     86   // Sticky key handlers.
     87   scoped_ptr<StickyKeysHandler> shift_sticky_key_;
     88   scoped_ptr<StickyKeysHandler> alt_sticky_key_;
     89   scoped_ptr<StickyKeysHandler> ctrl_sticky_key_;
     90 
     91   DISALLOW_COPY_AND_ASSIGN(StickyKeys);
     92 };
     93 
     94 // StickyKeysHandler handles key event and performs StickyKeys for specific
     95 // modifier keys. If monitored keyboard events are recieved, StickyKeysHandler
     96 // changes internal state. If non modifier keyboard events or mouse events are
     97 // received, StickyKeysHandler will append modifier based on internal state.
     98 // For other events, StickyKeysHandler does nothing.
     99 //
    100 // The DISABLED state is default state and any incoming non modifier keyboard
    101 // events will not be modified. The ENABLED state is one shot modification
    102 // state. Only next keyboard event will be modified. After that, internal state
    103 // will be back to DISABLED state with sending modifier keyup event. In the case
    104 // of LOCKED state, all incomming keyboard events will be modified. The LOCKED
    105 // state will be back to DISABLED state by next monitoring modifier key.
    106 //
    107 // The detailed state flow as follows:
    108 //                                     Current state
    109 //                  |   DISABLED    |    ENABLED     |    LOCKED   |
    110 // ----------------------------------------------------------------|
    111 // Modifier KeyDown |   noop        |    noop(*)     |    noop(*)  |
    112 // Modifier KeyUp   | To ENABLED(*) | To LOCKED(*)   | To DISABLED |
    113 // Normal KeyDown   |   noop        | To DISABLED(#) |    noop(#)  |
    114 // Normal KeyUp     |   noop        |    noop        |    noop(#)  |
    115 // Other KeyUp/Down |   noop        |    noop        |    noop     |
    116 // Mouse Press      |   noop        |    noop(#)     |    noop(#)  |
    117 // Mouse Release    |   noop        | To DISABLED(#) |    noop(#)  |
    118 // Mouse Wheel      |   noop        | To DISABLED(#) |    noop(#)  |
    119 // Other Mouse Event|   noop        |    noop        |    noop     |
    120 //
    121 // Here, (*) means key event will be consumed by StickyKeys, and (#) means event
    122 // is modified.
    123 class ASH_EXPORT StickyKeysHandler {
    124  public:
    125   class StickyKeysHandlerDelegate {
    126    public:
    127     StickyKeysHandlerDelegate();
    128     virtual ~StickyKeysHandlerDelegate();
    129 
    130     // Dispatches keyboard event synchronously.
    131     virtual void DispatchKeyEvent(ui::KeyEvent* event,
    132                                   aura::Window* target) = 0;
    133 
    134     // Dispatches mouse event synchronously.
    135     virtual void DispatchMouseEvent(ui::MouseEvent* event,
    136                                     aura::Window* target) = 0;
    137 
    138     // Dispatches scroll event synchronously.
    139     virtual void DispatchScrollEvent(ui::ScrollEvent* event,
    140                                      aura::Window* target) = 0;
    141   };
    142   // Represents Sticky Key state.
    143   enum StickyKeyState {
    144     // The sticky key is disabled. Incomming non modifier key events are not
    145     // affected.
    146     DISABLED,
    147     // The sticky key is enabled. Incomming non modifier key down events are
    148     // modified with |modifier_flag_|. After that, sticky key state become
    149     // DISABLED.
    150     ENABLED,
    151     // The sticky key is locked. Incomming non modifier key down events are
    152     // modified with |modifier_flag_|.
    153     LOCKED,
    154   };
    155 
    156   // This class takes an ownership of |delegate|.
    157   StickyKeysHandler(ui::EventFlags modifier_flag,
    158                     StickyKeysHandlerDelegate* delegate);
    159   ~StickyKeysHandler();
    160 
    161   // Handles key event. Returns true if key is consumed.
    162   bool HandleKeyEvent(ui::KeyEvent* event);
    163 
    164   // Handles a mouse event. Returns true if mouse event is consumed.
    165   bool HandleMouseEvent(ui::MouseEvent* event);
    166 
    167   // Handles a scroll event. Returns true if scroll event is consumed.
    168   bool HandleScrollEvent(ui::ScrollEvent* event);
    169 
    170   // Returns current internal state.
    171   StickyKeyState current_state() const { return current_state_; }
    172 
    173  private:
    174   // Represents event type in Sticky Key context.
    175   enum KeyEventType {
    176     TARGET_MODIFIER_DOWN,  // The monitoring modifier key is down.
    177     TARGET_MODIFIER_UP,  // The monitoring modifier key is up.
    178     NORMAL_KEY_DOWN,  // The non modifier key is down.
    179     NORMAL_KEY_UP,  // The non modifier key is up.
    180     OTHER_MODIFIER_DOWN,  // The modifier key but not monitored key is down.
    181     OTHER_MODIFIER_UP,  // The modifier key but not monitored key is up.
    182   };
    183 
    184   // Translates |event| to sticky keys event type.
    185   KeyEventType TranslateKeyEvent(ui::KeyEvent* event);
    186 
    187   // Handles key event in DISABLED state.
    188   bool HandleDisabledState(ui::KeyEvent* event);
    189 
    190   // Handles key event in ENABLED state.
    191   bool HandleEnabledState(ui::KeyEvent* event);
    192 
    193   // Handles key event in LOCKED state.
    194   bool HandleLockedState(ui::KeyEvent* event);
    195 
    196   // Dispatches |event| to its target and then dispatch a key released event
    197   // for the modifier key. This function is required to ensure that the events
    198   // are sent in the correct order when disabling sticky key after a key/mouse
    199   // button press.
    200   void DispatchEventAndReleaseModifier(ui::Event* event);
    201 
    202   // Adds |modifier_flags_| to a native X11 event state mask.
    203   void AppendNativeEventMask(unsigned int* state);
    204 
    205   // Adds |modifier_flags_| into |event|.
    206   void AppendModifier(ui::KeyEvent* event);
    207   void AppendModifier(ui::MouseEvent* event);
    208   void AppendModifier(ui::ScrollEvent* event);
    209 
    210   // The modifier flag to be monitored and appended.
    211   const ui::EventFlags modifier_flag_;
    212 
    213   // The current sticky key status.
    214   StickyKeyState current_state_;
    215 
    216   // True if the received key event is sent by StickyKeyHandler.
    217   bool event_from_myself_;
    218 
    219   // True if we received the TARGET_MODIFIER_DOWN event while in the DISABLED
    220   // state but before we receive the TARGET_MODIFIER_UP event. Normal
    221   // shortcuts (eg. ctrl + t) during this time will prevent a transition to
    222   // the ENABLED state.
    223   bool preparing_to_enable_;
    224 
    225   // Tracks the scroll direction of the current scroll sequence. Sticky keys
    226   // stops modifying the scroll events of the sequence when the direction
    227   // changes. If no sequence is tracked, the value is 0.
    228   int scroll_delta_;
    229 
    230   // The modifier up key event to be sent on non modifier key on ENABLED state.
    231   scoped_ptr<ui::KeyEvent> modifier_up_event_;
    232 
    233   scoped_ptr<StickyKeysHandlerDelegate> delegate_;
    234 
    235   DISALLOW_COPY_AND_ASSIGN(StickyKeysHandler);
    236 };
    237 
    238 }  // namespace ash
    239 
    240 #endif  // ASH_WM_STICKY_KEYS_H_
    241