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