| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |