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/wm/sticky_keys.h" 6 7 #if defined(USE_X11) 8 #include <X11/extensions/XInput2.h> 9 #include <X11/Xlib.h> 10 #undef RootWindow 11 #endif 12 13 #include "base/basictypes.h" 14 #include "base/debug/stack_trace.h" 15 #include "ui/aura/root_window.h" 16 #include "ui/aura/window.h" 17 #include "ui/aura/window_tracker.h" 18 #include "ui/events/event.h" 19 #include "ui/events/keycodes/keyboard_code_conversion.h" 20 21 namespace ash { 22 23 namespace { 24 25 // Returns true if the type of mouse event should be modified by sticky keys. 26 bool ShouldModifyMouseEvent(ui::MouseEvent* event) { 27 ui::EventType type = event->type(); 28 return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED || 29 type == ui::ET_MOUSEWHEEL; 30 } 31 32 // An implementation of StickyKeysHandler::StickyKeysHandlerDelegate. 33 class StickyKeysHandlerDelegateImpl : 34 public StickyKeysHandler::StickyKeysHandlerDelegate { 35 public: 36 StickyKeysHandlerDelegateImpl(); 37 virtual ~StickyKeysHandlerDelegateImpl(); 38 39 // StickyKeysHandlerDelegate overrides. 40 virtual void DispatchKeyEvent(ui::KeyEvent* event, 41 aura::Window* target) OVERRIDE; 42 43 virtual void DispatchMouseEvent(ui::MouseEvent* event, 44 aura::Window* target) OVERRIDE; 45 46 virtual void DispatchScrollEvent(ui::ScrollEvent* event, 47 aura::Window* target) OVERRIDE; 48 private: 49 DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl); 50 }; 51 52 StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() { 53 } 54 55 StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() { 56 } 57 58 void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent* event, 59 aura::Window* target) { 60 DCHECK(target); 61 target->GetDispatcher()->AsRootWindowHostDelegate()->OnHostKeyEvent(event); 62 } 63 64 void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent* event, 65 aura::Window* target) { 66 DCHECK(target); 67 // We need to send a new, untransformed mouse event to the host. 68 if (event->IsMouseWheelEvent()) { 69 ui::MouseWheelEvent new_event(*static_cast<ui::MouseWheelEvent*>(event)); 70 target->GetDispatcher()->AsRootWindowHostDelegate() 71 ->OnHostMouseEvent(&new_event); 72 } else { 73 ui::MouseEvent new_event(*event, target, target->GetRootWindow()); 74 target->GetDispatcher()->AsRootWindowHostDelegate() 75 ->OnHostMouseEvent(&new_event); 76 } 77 } 78 79 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent( 80 ui::ScrollEvent* event, 81 aura::Window* target) { 82 DCHECK(target); 83 target->GetDispatcher()->AsRootWindowHostDelegate() 84 ->OnHostScrollEvent(event); 85 } 86 87 } // namespace 88 89 /////////////////////////////////////////////////////////////////////////////// 90 // StickyKeys 91 StickyKeys::StickyKeys() 92 : enabled_(false), 93 shift_sticky_key_( 94 new StickyKeysHandler(ui::EF_SHIFT_DOWN, 95 new StickyKeysHandlerDelegateImpl())), 96 alt_sticky_key_( 97 new StickyKeysHandler(ui::EF_ALT_DOWN, 98 new StickyKeysHandlerDelegateImpl())), 99 ctrl_sticky_key_( 100 new StickyKeysHandler(ui::EF_CONTROL_DOWN, 101 new StickyKeysHandlerDelegateImpl())) { 102 } 103 104 StickyKeys::~StickyKeys() { 105 } 106 107 void StickyKeys::Enable(bool enabled) { 108 if (enabled_ != enabled) { 109 enabled_ = enabled; 110 111 // Reset key handlers when activating sticky keys to ensure all 112 // the handlers' states are reset. 113 if (enabled_) { 114 shift_sticky_key_.reset( 115 new StickyKeysHandler(ui::EF_SHIFT_DOWN, 116 new StickyKeysHandlerDelegateImpl())); 117 alt_sticky_key_.reset( 118 new StickyKeysHandler(ui::EF_ALT_DOWN, 119 new StickyKeysHandlerDelegateImpl())); 120 ctrl_sticky_key_.reset( 121 new StickyKeysHandler(ui::EF_CONTROL_DOWN, 122 new StickyKeysHandlerDelegateImpl())); 123 } 124 } 125 } 126 127 bool StickyKeys::HandleKeyEvent(ui::KeyEvent* event) { 128 return shift_sticky_key_->HandleKeyEvent(event) || 129 alt_sticky_key_->HandleKeyEvent(event) || 130 ctrl_sticky_key_->HandleKeyEvent(event); 131 return ctrl_sticky_key_->HandleKeyEvent(event); 132 } 133 134 bool StickyKeys::HandleMouseEvent(ui::MouseEvent* event) { 135 return shift_sticky_key_->HandleMouseEvent(event) || 136 alt_sticky_key_->HandleMouseEvent(event) || 137 ctrl_sticky_key_->HandleMouseEvent(event); 138 } 139 140 bool StickyKeys::HandleScrollEvent(ui::ScrollEvent* event) { 141 return shift_sticky_key_->HandleScrollEvent(event) || 142 alt_sticky_key_->HandleScrollEvent(event) || 143 ctrl_sticky_key_->HandleScrollEvent(event); 144 } 145 146 void StickyKeys::OnKeyEvent(ui::KeyEvent* event) { 147 // Do not consume a translated key event which is generated by an IME. 148 if (event->type() == ui::ET_TRANSLATED_KEY_PRESS || 149 event->type() == ui::ET_TRANSLATED_KEY_RELEASE) { 150 return; 151 } 152 153 if (enabled_ && HandleKeyEvent(event)) 154 event->StopPropagation(); 155 } 156 157 void StickyKeys::OnMouseEvent(ui::MouseEvent* event) { 158 if (enabled_ && HandleMouseEvent(event)) 159 event->StopPropagation(); 160 } 161 162 void StickyKeys::OnScrollEvent(ui::ScrollEvent* event) { 163 if (enabled_ && HandleScrollEvent(event)) 164 event->StopPropagation(); 165 } 166 167 /////////////////////////////////////////////////////////////////////////////// 168 // StickyKeysHandler 169 StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag, 170 StickyKeysHandlerDelegate* delegate) 171 : modifier_flag_(target_modifier_flag), 172 current_state_(DISABLED), 173 event_from_myself_(false), 174 preparing_to_enable_(false), 175 scroll_delta_(0), 176 delegate_(delegate) { 177 } 178 179 StickyKeysHandler::~StickyKeysHandler() { 180 } 181 182 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() { 183 } 184 185 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() { 186 } 187 188 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) { 189 if (event_from_myself_) 190 return false; // Do not handle self-generated key event. 191 switch (current_state_) { 192 case DISABLED: 193 return HandleDisabledState(event); 194 case ENABLED: 195 return HandleEnabledState(event); 196 case LOCKED: 197 return HandleLockedState(event); 198 } 199 NOTREACHED(); 200 return false; 201 } 202 203 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) { 204 preparing_to_enable_ = false; 205 if (event_from_myself_ || current_state_ == DISABLED 206 || !ShouldModifyMouseEvent(event)) { 207 return false; 208 } 209 DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); 210 211 AppendModifier(event); 212 // Only disable on the mouse released event in normal, non-locked mode. 213 if (current_state_ == ENABLED && event->type() != ui::ET_MOUSE_PRESSED) { 214 current_state_ = DISABLED; 215 DispatchEventAndReleaseModifier(event); 216 return true; 217 } 218 219 return false; 220 } 221 222 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { 223 preparing_to_enable_ = false; 224 if (event_from_myself_ || current_state_ == DISABLED) 225 return false; 226 DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); 227 228 // We detect a direction change if the current |scroll_delta_| is assigned 229 // and the offset of the current scroll event has the opposing sign. 230 bool direction_changed = false; 231 if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) { 232 int offset = event->y_offset(); 233 if (scroll_delta_) 234 direction_changed = offset * scroll_delta_ <= 0; 235 scroll_delta_ = offset; 236 } 237 238 if (!direction_changed) 239 AppendModifier(event); 240 241 // We want to modify all the scroll events in the scroll sequence, which ends 242 // with a fling start event. We also stop when the scroll sequence changes 243 // direction. 244 if (current_state_ == ENABLED && 245 (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) { 246 current_state_ = DISABLED; 247 scroll_delta_ = 0; 248 DispatchEventAndReleaseModifier(event); 249 return true; 250 } 251 252 return false; 253 } 254 255 StickyKeysHandler::KeyEventType 256 StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) { 257 bool is_target_key = false; 258 if (event->key_code() == ui::VKEY_SHIFT || 259 event->key_code() == ui::VKEY_LSHIFT || 260 event->key_code() == ui::VKEY_RSHIFT) { 261 is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN); 262 } else if (event->key_code() == ui::VKEY_CONTROL || 263 event->key_code() == ui::VKEY_LCONTROL || 264 event->key_code() == ui::VKEY_RCONTROL) { 265 is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN); 266 } else if (event->key_code() == ui::VKEY_MENU || 267 event->key_code() == ui::VKEY_LMENU || 268 event->key_code() == ui::VKEY_RMENU) { 269 is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN); 270 } else { 271 return event->type() == ui::ET_KEY_PRESSED ? 272 NORMAL_KEY_DOWN : NORMAL_KEY_UP; 273 } 274 275 if (is_target_key) { 276 return event->type() == ui::ET_KEY_PRESSED ? 277 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP; 278 } 279 return event->type() == ui::ET_KEY_PRESSED ? 280 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP; 281 } 282 283 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { 284 switch (TranslateKeyEvent(event)) { 285 case TARGET_MODIFIER_UP: 286 if (preparing_to_enable_) { 287 preparing_to_enable_ = false; 288 scroll_delta_ = 0; 289 current_state_ = ENABLED; 290 modifier_up_event_.reset(new ui::KeyEvent(*event)); 291 return true; 292 } 293 return false; 294 case TARGET_MODIFIER_DOWN: 295 preparing_to_enable_ = true; 296 return false; 297 case NORMAL_KEY_DOWN: 298 preparing_to_enable_ = false; 299 return false; 300 case NORMAL_KEY_UP: 301 case OTHER_MODIFIER_DOWN: 302 case OTHER_MODIFIER_UP: 303 return false; 304 } 305 NOTREACHED(); 306 return false; 307 } 308 309 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) { 310 switch (TranslateKeyEvent(event)) { 311 case NORMAL_KEY_UP: 312 case TARGET_MODIFIER_DOWN: 313 return true; 314 case TARGET_MODIFIER_UP: 315 current_state_ = LOCKED; 316 modifier_up_event_.reset(); 317 return true; 318 case NORMAL_KEY_DOWN: { 319 current_state_ = DISABLED; 320 AppendModifier(event); 321 DispatchEventAndReleaseModifier(event); 322 return true; 323 } 324 case OTHER_MODIFIER_DOWN: 325 case OTHER_MODIFIER_UP: 326 return false; 327 } 328 NOTREACHED(); 329 return false; 330 } 331 332 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { 333 switch (TranslateKeyEvent(event)) { 334 case TARGET_MODIFIER_DOWN: 335 return true; 336 case TARGET_MODIFIER_UP: 337 current_state_ = DISABLED; 338 return false; 339 case NORMAL_KEY_DOWN: 340 case NORMAL_KEY_UP: 341 AppendModifier(event); 342 return false; 343 case OTHER_MODIFIER_DOWN: 344 case OTHER_MODIFIER_UP: 345 return false; 346 } 347 NOTREACHED(); 348 return false; 349 } 350 351 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) { 352 DCHECK(event->IsKeyEvent() || 353 event->IsMouseEvent() || 354 event->IsScrollEvent()); 355 DCHECK(modifier_up_event_.get()); 356 aura::Window* target = static_cast<aura::Window*>(event->target()); 357 DCHECK(target); 358 aura::Window* root_window = target->GetRootWindow(); 359 DCHECK(root_window); 360 361 aura::WindowTracker window_tracker; 362 window_tracker.Add(target); 363 364 event_from_myself_ = true; 365 if (event->IsKeyEvent()) { 366 delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target); 367 } else if (event->IsMouseEvent()) { 368 delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target); 369 } else { 370 delegate_->DispatchScrollEvent( 371 static_cast<ui::ScrollEvent*>(event), target); 372 } 373 374 // The action triggered above may have destroyed the event target, in which 375 // case we will dispatch the modifier up event to the root window instead. 376 aura::Window* modifier_up_target = 377 window_tracker.Contains(target) ? target : root_window; 378 delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target); 379 event_from_myself_ = false; 380 } 381 382 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) { 383 unsigned int& state_ref = *state; 384 switch (modifier_flag_) { 385 case ui::EF_CONTROL_DOWN: 386 state_ref |= ControlMask; 387 break; 388 case ui::EF_ALT_DOWN: 389 state_ref |= Mod1Mask; 390 break; 391 case ui::EF_SHIFT_DOWN: 392 state_ref |= ShiftMask; 393 break; 394 default: 395 NOTREACHED(); 396 } 397 } 398 399 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) { 400 #if defined(USE_X11) 401 XEvent* xev = event->native_event(); 402 if (xev) { 403 XKeyEvent* xkey = &(xev->xkey); 404 AppendNativeEventMask(&xkey->state); 405 } 406 #elif defined(USE_OZONE) 407 NOTIMPLEMENTED() << "Modifier key is not handled"; 408 #endif 409 event->set_flags(event->flags() | modifier_flag_); 410 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(), 411 event->flags())); 412 event->NormalizeFlags(); 413 } 414 415 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) { 416 #if defined(USE_X11) 417 XEvent* xev = event->native_event(); 418 if (xev) { 419 XButtonEvent* xkey = &(xev->xbutton); 420 AppendNativeEventMask(&xkey->state); 421 } 422 #elif defined(USE_OZONE) 423 NOTIMPLEMENTED() << "Modifier key is not handled"; 424 #endif 425 event->set_flags(event->flags() | modifier_flag_); 426 } 427 428 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) { 429 #if defined(USE_X11) 430 XEvent* xev = event->native_event(); 431 if (xev) { 432 XIDeviceEvent* xievent = 433 static_cast<XIDeviceEvent*>(xev->xcookie.data); 434 if (xievent) { 435 AppendNativeEventMask(reinterpret_cast<unsigned int*>( 436 &xievent->mods.effective)); 437 } 438 } 439 #elif defined(USE_OZONE) 440 NOTIMPLEMENTED() << "Modifier key is not handled"; 441 #endif 442 event->set_flags(event->flags() | modifier_flag_); 443 } 444 445 } // namespace ash 446