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