1 // Copyright (c) 2012 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/accelerators/accelerator_controller.h" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <iostream> 10 #include <string> 11 12 #include "ash/accelerators/accelerator_table.h" 13 #include "ash/ash_switches.h" 14 #include "ash/caps_lock_delegate.h" 15 #include "ash/debug.h" 16 #include "ash/desktop_background/desktop_background_controller.h" 17 #include "ash/desktop_background/user_wallpaper_delegate.h" 18 #include "ash/display/display_controller.h" 19 #include "ash/display/display_manager.h" 20 #include "ash/focus_cycler.h" 21 #include "ash/ime_control_delegate.h" 22 #include "ash/launcher/launcher.h" 23 #include "ash/launcher/launcher_delegate.h" 24 #include "ash/launcher/launcher_model.h" 25 #include "ash/magnifier/magnification_controller.h" 26 #include "ash/magnifier/partial_magnification_controller.h" 27 #include "ash/root_window_controller.h" 28 #include "ash/rotator/screen_rotation.h" 29 #include "ash/screenshot_delegate.h" 30 #include "ash/session_state_delegate.h" 31 #include "ash/shelf/shelf_widget.h" 32 #include "ash/shell.h" 33 #include "ash/shell_delegate.h" 34 #include "ash/shell_window_ids.h" 35 #include "ash/system/brightness/brightness_control_delegate.h" 36 #include "ash/system/keyboard_brightness/keyboard_brightness_control_delegate.h" 37 #include "ash/system/status_area_widget.h" 38 #include "ash/system/tray/system_tray.h" 39 #include "ash/system/tray/system_tray_delegate.h" 40 #include "ash/system/tray/system_tray_notifier.h" 41 #include "ash/system/web_notification/web_notification_tray.h" 42 #include "ash/touch/touch_hud_debug.h" 43 #include "ash/volume_control_delegate.h" 44 #include "ash/wm/partial_screenshot_view.h" 45 #include "ash/wm/power_button_controller.h" 46 #include "ash/wm/property_util.h" 47 #include "ash/wm/window_cycle_controller.h" 48 #include "ash/wm/window_selector_controller.h" 49 #include "ash/wm/window_util.h" 50 #include "ash/wm/workspace/snap_sizer.h" 51 #include "base/bind.h" 52 #include "base/command_line.h" 53 #include "content/public/browser/gpu_data_manager.h" 54 #include "ui/aura/env.h" 55 #include "ui/aura/root_window.h" 56 #include "ui/base/accelerators/accelerator.h" 57 #include "ui/base/accelerators/accelerator_manager.h" 58 #include "ui/base/events/event.h" 59 #include "ui/base/keycodes/keyboard_codes.h" 60 #include "ui/compositor/debug_utils.h" 61 #include "ui/compositor/layer.h" 62 #include "ui/compositor/layer_animation_sequence.h" 63 #include "ui/compositor/layer_animator.h" 64 #include "ui/gfx/screen.h" 65 #include "ui/oak/oak.h" 66 #include "ui/views/controls/webview/webview.h" 67 #include "ui/views/debug_utils.h" 68 #include "ui/views/widget/widget.h" 69 70 #if defined(OS_CHROMEOS) 71 #include "ash/system/chromeos/keyboard_brightness_controller.h" 72 #include "base/chromeos/chromeos_version.h" 73 #endif // defined(OS_CHROMEOS) 74 75 namespace ash { 76 namespace { 77 78 using internal::DisplayInfo; 79 80 bool DebugShortcutsEnabled() { 81 #if defined(NDEBUG) 82 return CommandLine::ForCurrentProcess()->HasSwitch( 83 switches::kAshDebugShortcuts); 84 #else 85 return true; 86 #endif 87 } 88 89 bool HandleCycleWindowMRU(WindowCycleController::Direction direction, 90 bool is_alt_down) { 91 Shell::GetInstance()-> 92 window_cycle_controller()->HandleCycleWindow(direction, is_alt_down); 93 // Always report we handled the key, even if the window didn't change. 94 return true; 95 } 96 97 bool HandleCycleWindowOverviewMRU(WindowSelector::Direction direction) { 98 Shell::GetInstance()-> 99 window_selector_controller()->HandleCycleWindow(direction); 100 return true; 101 } 102 103 void HandleCycleWindowLinear(CycleDirection direction) { 104 Shell::GetInstance()-> 105 window_cycle_controller()->HandleLinearCycleWindow(); 106 } 107 108 void ToggleOverviewMode() { 109 Shell::GetInstance()->window_selector_controller()->ToggleOverview(); 110 } 111 112 bool HandleAccessibleFocusCycle(bool reverse) { 113 if (!Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled()) 114 return false; 115 aura::Window* active_window = ash::wm::GetActiveWindow(); 116 if (!active_window) 117 return false; 118 views::Widget* widget = 119 views::Widget::GetWidgetForNativeWindow(active_window); 120 if (!widget) 121 return false; 122 views::FocusManager* focus_manager = widget->GetFocusManager(); 123 if (!focus_manager) 124 return false; 125 views::View* view = focus_manager->GetFocusedView(); 126 if (!view) 127 return false; 128 if (!strcmp(view->GetClassName(), views::WebView::kViewClassName)) 129 return false; 130 131 focus_manager->AdvanceFocus(reverse); 132 return true; 133 } 134 135 void HandleSilenceSpokenFeedback() { 136 if (!Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled()) 137 return; 138 139 Shell::GetInstance()->delegate()->SilenceSpokenFeedback(); 140 } 141 142 #if defined(OS_CHROMEOS) 143 bool HandleLock() { 144 Shell::GetInstance()->session_state_delegate()->LockScreen(); 145 return true; 146 } 147 148 bool HandleFileManager(bool as_dialog) { 149 Shell::GetInstance()->delegate()->OpenFileManager(as_dialog); 150 return true; 151 } 152 153 bool HandleCrosh() { 154 Shell::GetInstance()->delegate()->OpenCrosh(); 155 return true; 156 } 157 158 bool HandleToggleSpokenFeedback() { 159 Shell::GetInstance()->delegate()-> 160 ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW); 161 return true; 162 } 163 164 #endif // defined(OS_CHROMEOS) 165 166 bool HandleRotatePaneFocus(Shell::Direction direction) { 167 Shell* shell = Shell::GetInstance(); 168 switch (direction) { 169 case Shell::FORWARD: 170 shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD); 171 break; 172 case Shell::BACKWARD: 173 shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD); 174 break; 175 } 176 return true; 177 } 178 179 // Rotate the active window. 180 bool HandleRotateActiveWindow() { 181 aura::Window* active_window = wm::GetActiveWindow(); 182 if (active_window) { 183 // The rotation animation bases its target transform on the current 184 // rotation and position. Since there could be an animation in progress 185 // right now, queue this animation so when it starts it picks up a neutral 186 // rotation and position. Use replace so we only enqueue one at a time. 187 active_window->layer()->GetAnimator()-> 188 set_preemption_strategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); 189 active_window->layer()->GetAnimator()->StartAnimation( 190 new ui::LayerAnimationSequence( 191 new ash::ScreenRotation(360, active_window->layer()))); 192 } 193 return true; 194 } 195 196 gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) { 197 switch (current) { 198 case gfx::Display::ROTATE_0: 199 return gfx::Display::ROTATE_90; 200 case gfx::Display::ROTATE_90: 201 return gfx::Display::ROTATE_180; 202 case gfx::Display::ROTATE_180: 203 return gfx::Display::ROTATE_270; 204 case gfx::Display::ROTATE_270: 205 return gfx::Display::ROTATE_0; 206 } 207 NOTREACHED() << "Unknown rotation:" << current; 208 return gfx::Display::ROTATE_0; 209 } 210 211 bool HandleScaleUI(bool up) { 212 internal::DisplayManager* display_manager = 213 Shell::GetInstance()->display_manager(); 214 int64 display_id = display_manager->GetDisplayIdForUIScaling(); 215 if (display_id == gfx::Display::kInvalidDisplayID) 216 return false; 217 const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id); 218 float next_scale = 219 internal::DisplayManager::GetNextUIScale(display_info, up); 220 display_manager->SetDisplayUIScale(display_id, next_scale); 221 return true; 222 } 223 224 bool HandleScaleReset() { 225 internal::DisplayManager* display_manager = 226 Shell::GetInstance()->display_manager(); 227 int64 display_id = display_manager->GetDisplayIdForUIScaling(); 228 if (display_id == gfx::Display::kInvalidDisplayID) 229 return false; 230 display_manager->SetDisplayUIScale(display_id, 1.0f); 231 return true; 232 } 233 234 // Rotates the screen. 235 bool HandleRotateScreen() { 236 gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint(); 237 gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point); 238 const DisplayInfo& display_info = 239 Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); 240 Shell::GetInstance()->display_manager()->SetDisplayRotation( 241 display.id(), GetNextRotation(display_info.rotation())); 242 return true; 243 } 244 245 bool HandleToggleDesktopBackgroundMode() { 246 DesktopBackgroundController* desktop_background_controller = 247 Shell::GetInstance()->desktop_background_controller(); 248 if (desktop_background_controller->desktop_background_mode() == 249 DesktopBackgroundController::BACKGROUND_IMAGE) { 250 desktop_background_controller->SetDesktopBackgroundSolidColorMode( 251 SK_ColorBLACK); 252 } else { 253 ash::Shell::GetInstance()->user_wallpaper_delegate()-> 254 InitializeWallpaper(); 255 } 256 return true; 257 } 258 259 bool HandleToggleRootWindowFullScreen() { 260 Shell::GetPrimaryRootWindow()->ToggleFullScreen(); 261 return true; 262 } 263 264 // Magnify the screen 265 bool HandleMagnifyScreen(int delta_index) { 266 if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) { 267 // TODO(yoshiki): Move the following logic to MagnificationController. 268 float scale = 269 ash::Shell::GetInstance()->magnification_controller()->GetScale(); 270 // Calculate rounded logarithm (base kMagnificationScaleFactor) of scale. 271 int scale_index = 272 std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5); 273 274 int new_scale_index = std::max(0, std::min(8, scale_index + delta_index)); 275 276 ash::Shell::GetInstance()->magnification_controller()-> 277 SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true); 278 } else if (ash::Shell::GetInstance()-> 279 partial_magnification_controller()->is_enabled()) { 280 float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1; 281 ash::Shell::GetInstance()->partial_magnification_controller()-> 282 SetScale(scale); 283 } 284 285 return true; 286 } 287 288 bool HandleMediaNextTrack() { 289 Shell::GetInstance()->delegate()->HandleMediaNextTrack(); 290 return true; 291 } 292 293 bool HandleMediaPlayPause() { 294 Shell::GetInstance()->delegate()->HandleMediaPlayPause(); 295 return true; 296 } 297 298 bool HandleMediaPrevTrack() { 299 Shell::GetInstance()->delegate()->HandleMediaPrevTrack(); 300 return true; 301 } 302 303 bool HandlePrintLayerHierarchy() { 304 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 305 for (size_t i = 0; i < root_windows.size(); ++i) { 306 ui::PrintLayerHierarchy(root_windows[i]->layer(), 307 root_windows[i]->GetLastMouseLocationInRoot()); 308 } 309 return true; 310 } 311 312 bool HandlePrintViewHierarchy() { 313 aura::Window* active_window = ash::wm::GetActiveWindow(); 314 if (!active_window) 315 return true; 316 views::Widget* browser_widget = 317 views::Widget::GetWidgetForNativeWindow(active_window); 318 if (!browser_widget) 319 return true; 320 views::PrintViewHierarchy(browser_widget->GetRootView()); 321 return true; 322 } 323 324 void PrintWindowHierarchy(aura::Window* window, 325 int indent, 326 std::ostringstream* out) { 327 std::string indent_str(indent, ' '); 328 std::string name(window->name()); 329 if (name.empty()) 330 name = "\"\""; 331 *out << indent_str << name << " (" << window << ")" 332 << " type=" << window->type() 333 << (wm::IsActiveWindow(window) ? " [active] " : " ") 334 << (window->IsVisible() ? " visible " : " ") 335 << window->bounds().ToString() 336 << '\n'; 337 338 for (size_t i = 0; i < window->children().size(); ++i) 339 PrintWindowHierarchy(window->children()[i], indent + 3, out); 340 } 341 342 bool HandlePrintWindowHierarchy() { 343 Shell::RootWindowControllerList controllers = 344 Shell::GetAllRootWindowControllers(); 345 for (size_t i = 0; i < controllers.size(); ++i) { 346 std::ostringstream out; 347 out << "RootWindow " << i << ":\n"; 348 PrintWindowHierarchy(controllers[i]->root_window(), 0, &out); 349 // Error so logs can be collected from end-users. 350 LOG(ERROR) << out.str(); 351 } 352 return true; 353 } 354 355 bool HandlePrintUIHierarchies() { 356 // This is a separate command so the user only has to hit one key to generate 357 // all the logs. Developers use the individual dumps repeatedly, so keep 358 // those as separate commands to avoid spamming their logs. 359 HandlePrintLayerHierarchy(); 360 HandlePrintWindowHierarchy(); 361 HandlePrintViewHierarchy(); 362 return true; 363 } 364 365 } // namespace 366 367 //////////////////////////////////////////////////////////////////////////////// 368 // AcceleratorControllerContext, public: 369 370 AcceleratorControllerContext::AcceleratorControllerContext() { 371 current_accelerator_.set_type(ui::ET_UNKNOWN); 372 previous_accelerator_.set_type(ui::ET_UNKNOWN); 373 } 374 375 void AcceleratorControllerContext::UpdateContext( 376 const ui::Accelerator& accelerator) { 377 previous_accelerator_ = current_accelerator_; 378 current_accelerator_ = accelerator; 379 } 380 381 //////////////////////////////////////////////////////////////////////////////// 382 // AcceleratorController, public: 383 384 AcceleratorController::AcceleratorController() 385 : accelerator_manager_(new ui::AcceleratorManager) { 386 Init(); 387 } 388 389 AcceleratorController::~AcceleratorController() { 390 } 391 392 void AcceleratorController::Init() { 393 for (size_t i = 0; i < kActionsAllowedAtLoginOrLockScreenLength; ++i) { 394 actions_allowed_at_login_screen_.insert( 395 kActionsAllowedAtLoginOrLockScreen[i]); 396 actions_allowed_at_lock_screen_.insert( 397 kActionsAllowedAtLoginOrLockScreen[i]); 398 } 399 for (size_t i = 0; i < kActionsAllowedAtLockScreenLength; ++i) 400 actions_allowed_at_lock_screen_.insert(kActionsAllowedAtLockScreen[i]); 401 for (size_t i = 0; i < kActionsAllowedAtModalWindowLength; ++i) 402 actions_allowed_at_modal_window_.insert(kActionsAllowedAtModalWindow[i]); 403 for (size_t i = 0; i < kReservedActionsLength; ++i) 404 reserved_actions_.insert(kReservedActions[i]); 405 for (size_t i = 0; i < kNonrepeatableActionsLength; ++i) 406 nonrepeatable_actions_.insert(kNonrepeatableActions[i]); 407 for (size_t i = 0; i < kActionsAllowedInAppModeLength; ++i) 408 actions_allowed_in_app_mode_.insert(kActionsAllowedInAppMode[i]); 409 410 RegisterAccelerators(kAcceleratorData, kAcceleratorDataLength); 411 412 #if !defined(NDEBUG) 413 RegisterAccelerators(kDesktopAcceleratorData, kDesktopAcceleratorDataLength); 414 #endif 415 416 if (DebugShortcutsEnabled()) { 417 RegisterAccelerators(kDebugAcceleratorData, kDebugAcceleratorDataLength); 418 for (size_t i = 0; i < kReservedDebugActionsLength; ++i) 419 reserved_actions_.insert(kReservedDebugActions[i]); 420 } 421 422 #if defined(OS_CHROMEOS) 423 keyboard_brightness_control_delegate_.reset( 424 new KeyboardBrightnessController()); 425 #endif 426 } 427 428 void AcceleratorController::Register(const ui::Accelerator& accelerator, 429 ui::AcceleratorTarget* target) { 430 accelerator_manager_->Register(accelerator, 431 ui::AcceleratorManager::kNormalPriority, 432 target); 433 } 434 435 void AcceleratorController::Unregister(const ui::Accelerator& accelerator, 436 ui::AcceleratorTarget* target) { 437 accelerator_manager_->Unregister(accelerator, target); 438 } 439 440 void AcceleratorController::UnregisterAll(ui::AcceleratorTarget* target) { 441 accelerator_manager_->UnregisterAll(target); 442 } 443 444 bool AcceleratorController::Process(const ui::Accelerator& accelerator) { 445 if (ime_control_delegate_) { 446 return accelerator_manager_->Process( 447 ime_control_delegate_->RemapAccelerator(accelerator)); 448 } 449 return accelerator_manager_->Process(accelerator); 450 } 451 452 bool AcceleratorController::IsRegistered( 453 const ui::Accelerator& accelerator) const { 454 return accelerator_manager_->GetCurrentTarget(accelerator) != NULL; 455 } 456 457 bool AcceleratorController::IsReservedAccelerator( 458 const ui::Accelerator& accelerator) const { 459 const ui::Accelerator remapped_accelerator = ime_control_delegate_.get() ? 460 ime_control_delegate_->RemapAccelerator(accelerator) : accelerator; 461 462 std::map<ui::Accelerator, int>::const_iterator iter = 463 accelerators_.find(remapped_accelerator); 464 if (iter == accelerators_.end()) 465 return false; // not an accelerator. 466 467 return reserved_actions_.find(iter->second) != reserved_actions_.end(); 468 } 469 470 bool AcceleratorController::PerformAction(int action, 471 const ui::Accelerator& accelerator) { 472 ash::Shell* shell = ash::Shell::GetInstance(); 473 if (!shell->session_state_delegate()->IsActiveUserSessionStarted() && 474 actions_allowed_at_login_screen_.find(action) == 475 actions_allowed_at_login_screen_.end()) { 476 return false; 477 } 478 if (shell->session_state_delegate()->IsScreenLocked() && 479 actions_allowed_at_lock_screen_.find(action) == 480 actions_allowed_at_lock_screen_.end()) { 481 return false; 482 } 483 if (shell->IsSystemModalWindowOpen() && 484 actions_allowed_at_modal_window_.find(action) == 485 actions_allowed_at_modal_window_.end()) { 486 // Note: we return true. This indicates the shortcut is handled 487 // and will not be passed to the modal window. This is important 488 // for things like Alt+Tab that would cause an undesired effect 489 // in the modal window by cycling through its window elements. 490 return true; 491 } 492 if (shell->delegate()->IsRunningInForcedAppMode() && 493 actions_allowed_in_app_mode_.find(action) == 494 actions_allowed_in_app_mode_.end()) { 495 return false; 496 } 497 498 const ui::KeyboardCode key_code = accelerator.key_code(); 499 // PerformAction() is performed from gesture controllers and passes 500 // empty Accelerator() instance as the second argument. Such events 501 // should never be suspended. 502 const bool gesture_event = key_code == ui::VKEY_UNKNOWN; 503 504 // Ignore accelerators invoked as repeated (while holding a key for a long 505 // time, if their handling is nonrepeatable. 506 if (nonrepeatable_actions_.find(action) != nonrepeatable_actions_.end() && 507 context_.repeated() && !gesture_event) { 508 return true; 509 } 510 // Type of the previous accelerator. Used by NEXT_IME and DISABLE_CAPS_LOCK. 511 const ui::EventType previous_event_type = 512 context_.previous_accelerator().type(); 513 const ui::KeyboardCode previous_key_code = 514 context_.previous_accelerator().key_code(); 515 516 // You *MUST* return true when some action is performed. Otherwise, this 517 // function might be called *twice*, via BrowserView::PreHandleKeyboardEvent 518 // and BrowserView::HandleKeyboardEvent, for a single accelerator press. 519 switch (action) { 520 case ACCESSIBLE_FOCUS_NEXT: 521 return HandleAccessibleFocusCycle(false); 522 case ACCESSIBLE_FOCUS_PREVIOUS: 523 return HandleAccessibleFocusCycle(true); 524 case CYCLE_BACKWARD_MRU: 525 if (key_code == ui::VKEY_TAB) 526 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_PREVWINDOW_TAB); 527 if (CommandLine::ForCurrentProcess()->HasSwitch( 528 switches::kAshEnableOverviewMode)) { 529 return HandleCycleWindowOverviewMRU(WindowSelector::BACKWARD); 530 } 531 return HandleCycleWindowMRU(WindowCycleController::BACKWARD, 532 accelerator.IsAltDown()); 533 case CYCLE_FORWARD_MRU: 534 if (key_code == ui::VKEY_TAB) 535 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEXTWINDOW_TAB); 536 if (CommandLine::ForCurrentProcess()->HasSwitch( 537 switches::kAshEnableOverviewMode)) { 538 return HandleCycleWindowOverviewMRU(WindowSelector::FORWARD); 539 } 540 return HandleCycleWindowMRU(WindowCycleController::FORWARD, 541 accelerator.IsAltDown()); 542 case CYCLE_BACKWARD_LINEAR: 543 if (CommandLine::ForCurrentProcess()->HasSwitch( 544 switches::kAshEnableOverviewMode)) { 545 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_OVERVIEW_F5); 546 ToggleOverviewMode(); 547 return true; 548 } 549 if (key_code == ui::VKEY_MEDIA_LAUNCH_APP1) 550 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_PREVWINDOW_F5); 551 HandleCycleWindowLinear(CYCLE_BACKWARD); 552 return true; 553 case CYCLE_FORWARD_LINEAR: 554 if (CommandLine::ForCurrentProcess()->HasSwitch( 555 switches::kAshEnableOverviewMode)) { 556 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_OVERVIEW_F5); 557 ToggleOverviewMode(); 558 return true; 559 } 560 if (key_code == ui::VKEY_MEDIA_LAUNCH_APP1) 561 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEXTWINDOW_F5); 562 HandleCycleWindowLinear(CYCLE_FORWARD); 563 return true; 564 #if defined(OS_CHROMEOS) 565 case ADD_REMOVE_DISPLAY: 566 Shell::GetInstance()->display_manager()->AddRemoveDisplay(); 567 return true; 568 case TOGGLE_MIRROR_MODE: 569 Shell::GetInstance()->display_controller()->ToggleMirrorMode(); 570 return true; 571 case LOCK_SCREEN: 572 if (key_code == ui::VKEY_L) 573 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_LOCK_SCREEN_L); 574 return HandleLock(); 575 case OPEN_FILE_DIALOG: 576 return HandleFileManager(true /* as_dialog */); 577 case OPEN_FILE_MANAGER: 578 return HandleFileManager(false /* as_dialog */); 579 case OPEN_CROSH: 580 return HandleCrosh(); 581 case SILENCE_SPOKEN_FEEDBACK: 582 HandleSilenceSpokenFeedback(); 583 break; 584 case SWAP_PRIMARY_DISPLAY: 585 Shell::GetInstance()->display_controller()->SwapPrimaryDisplay(); 586 return true; 587 case TOGGLE_SPOKEN_FEEDBACK: 588 return HandleToggleSpokenFeedback(); 589 case TOGGLE_WIFI: 590 Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi(); 591 return true; 592 case TOUCH_HUD_CLEAR: { 593 internal::RootWindowController* controller = 594 internal::RootWindowController::ForActiveRootWindow(); 595 if (controller->touch_hud_debug()) { 596 controller->touch_hud_debug()->Clear(); 597 return true; 598 } 599 return false; 600 } 601 case TOUCH_HUD_MODE_CHANGE: { 602 internal::RootWindowController* controller = 603 internal::RootWindowController::ForActiveRootWindow(); 604 if (controller->touch_hud_debug()) { 605 controller->touch_hud_debug()->ChangeToNextMode(); 606 return true; 607 } 608 return false; 609 } 610 case TOUCH_HUD_PROJECTION_TOGGLE: { 611 bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled(); 612 Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled); 613 return true; 614 } 615 case DISABLE_GPU_WATCHDOG: 616 content::GpuDataManager::GetInstance()->DisableGpuWatchdog(); 617 return true; 618 #endif 619 case OPEN_FEEDBACK_PAGE: 620 ash::Shell::GetInstance()->delegate()->OpenFeedbackPage(); 621 return true; 622 case EXIT: 623 // UMA metrics are recorded in the handler. 624 exit_warning_handler_.HandleAccelerator(); 625 return true; 626 case NEW_INCOGNITO_WINDOW: 627 Shell::GetInstance()->delegate()->NewWindow(true /* is_incognito */); 628 return true; 629 case NEW_TAB: 630 if (key_code == ui::VKEY_T) 631 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEWTAB_T); 632 Shell::GetInstance()->delegate()->NewTab(); 633 return true; 634 case NEW_WINDOW: 635 Shell::GetInstance()->delegate()->NewWindow(false /* is_incognito */); 636 return true; 637 case RESTORE_TAB: 638 Shell::GetInstance()->delegate()->RestoreTab(); 639 return true; 640 case TAKE_SCREENSHOT: 641 if (screenshot_delegate_.get() && 642 screenshot_delegate_->CanTakeScreenshot()) { 643 screenshot_delegate_->HandleTakeScreenshotForAllRootWindows(); 644 } 645 // Return true to prevent propagation of the key event. 646 return true; 647 case TAKE_PARTIAL_SCREENSHOT: 648 if (screenshot_delegate_) { 649 ash::PartialScreenshotView::StartPartialScreenshot( 650 screenshot_delegate_.get()); 651 } 652 // Return true to prevent propagation of the key event because 653 // this key combination is reserved for partial screenshot. 654 return true; 655 case TOGGLE_APP_LIST: 656 // If something else was pressed between the Search key (LWIN) 657 // being pressed and released, then ignore the release of the 658 // Search key. 659 if (key_code == ui::VKEY_LWIN && 660 (previous_event_type == ui::ET_KEY_RELEASED || 661 previous_key_code != ui::VKEY_LWIN)) 662 return false; 663 if (key_code == ui::VKEY_LWIN) 664 shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_SEARCH_LWIN); 665 // When spoken feedback is enabled, we should neither toggle the list nor 666 // consume the key since Search+Shift is one of the shortcuts the a11y 667 // feature uses. crbug.com/132296 668 DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code()); 669 if (Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled()) 670 return false; 671 ash::Shell::GetInstance()->ToggleAppList(NULL); 672 return true; 673 case DISABLE_CAPS_LOCK: 674 if (previous_event_type == ui::ET_KEY_RELEASED || 675 (previous_key_code != ui::VKEY_LSHIFT && 676 previous_key_code != ui::VKEY_SHIFT && 677 previous_key_code != ui::VKEY_RSHIFT)) { 678 // If something else was pressed between the Shift key being pressed 679 // and released, then ignore the release of the Shift key. 680 return false; 681 } 682 if (shell->caps_lock_delegate()->IsCapsLockEnabled()) { 683 shell->caps_lock_delegate()->SetCapsLockEnabled(false); 684 return true; 685 } 686 return false; 687 case TOGGLE_CAPS_LOCK: 688 if (key_code == ui::VKEY_LWIN) { 689 // If something else was pressed between the Search key (LWIN) 690 // being pressed and released, then ignore the release of the 691 // Search key. 692 // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495 693 if (previous_event_type == ui::ET_KEY_RELEASED || 694 previous_key_code != ui::VKEY_LWIN) 695 return false; 696 } 697 shell->caps_lock_delegate()->ToggleCapsLock(); 698 return true; 699 case BRIGHTNESS_DOWN: 700 if (brightness_control_delegate_) 701 return brightness_control_delegate_->HandleBrightnessDown(accelerator); 702 break; 703 case BRIGHTNESS_UP: 704 if (brightness_control_delegate_) 705 return brightness_control_delegate_->HandleBrightnessUp(accelerator); 706 break; 707 case KEYBOARD_BRIGHTNESS_DOWN: 708 if (keyboard_brightness_control_delegate_) 709 return keyboard_brightness_control_delegate_-> 710 HandleKeyboardBrightnessDown(accelerator); 711 break; 712 case KEYBOARD_BRIGHTNESS_UP: 713 if (keyboard_brightness_control_delegate_) 714 return keyboard_brightness_control_delegate_-> 715 HandleKeyboardBrightnessUp(accelerator); 716 break; 717 case VOLUME_MUTE: 718 return shell->system_tray_delegate()->GetVolumeControlDelegate()-> 719 HandleVolumeMute(accelerator); 720 break; 721 case VOLUME_DOWN: 722 return shell->system_tray_delegate()->GetVolumeControlDelegate()-> 723 HandleVolumeDown(accelerator); 724 break; 725 case VOLUME_UP: 726 return shell->system_tray_delegate()->GetVolumeControlDelegate()-> 727 HandleVolumeUp(accelerator); 728 break; 729 case FOCUS_LAUNCHER: 730 return shell->focus_cycler()->FocusWidget( 731 Launcher::ForPrimaryDisplay()->shelf_widget()); 732 break; 733 case FOCUS_NEXT_PANE: 734 return HandleRotatePaneFocus(Shell::FORWARD); 735 case FOCUS_PREVIOUS_PANE: 736 return HandleRotatePaneFocus(Shell::BACKWARD); 737 case SHOW_KEYBOARD_OVERLAY: 738 ash::Shell::GetInstance()->delegate()->ShowKeyboardOverlay(); 739 return true; 740 case SHOW_OAK: 741 if (CommandLine::ForCurrentProcess()->HasSwitch( 742 switches::kAshEnableOak)) { 743 oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow()); 744 return true; 745 } 746 break; 747 case SHOW_SYSTEM_TRAY_BUBBLE: { 748 internal::RootWindowController* controller = 749 internal::RootWindowController::ForActiveRootWindow(); 750 if (!controller->GetSystemTray()->HasSystemBubble()) 751 controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW); 752 break; 753 } 754 case SHOW_MESSAGE_CENTER_BUBBLE: { 755 internal::RootWindowController* controller = 756 internal::RootWindowController::ForActiveRootWindow(); 757 internal::StatusAreaWidget* status_area_widget = 758 controller->shelf()->status_area_widget(); 759 if (status_area_widget) { 760 WebNotificationTray* notification_tray = 761 status_area_widget->web_notification_tray(); 762 if (notification_tray->visible()) 763 notification_tray->ShowMessageCenterBubble(); 764 } 765 break; 766 } 767 case SHOW_TASK_MANAGER: 768 Shell::GetInstance()->delegate()->ShowTaskManager(); 769 return true; 770 case NEXT_IME: 771 // This check is necessary e.g. not to process the Shift+Alt+ 772 // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/ 773 // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab 774 // is released. 775 if (previous_event_type == ui::ET_KEY_RELEASED && 776 // Workaround for crbug.com/139556. CJK IME users tend to press 777 // Enter (or Space) and Shift+Alt almost at the same time to commit 778 // an IME string and then switch from the IME to the English layout. 779 // This workaround allows the user to trigger NEXT_IME even if the 780 // user presses Shift+Alt before releasing Enter. 781 // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way. 782 previous_key_code != ui::VKEY_RETURN && 783 previous_key_code != ui::VKEY_SPACE) { 784 // We totally ignore this accelerator. 785 // TODO(mazda): Fix crbug.com/158217 786 return false; 787 } 788 if (ime_control_delegate_) 789 return ime_control_delegate_->HandleNextIme(); 790 break; 791 case PREVIOUS_IME: 792 if (ime_control_delegate_) 793 return ime_control_delegate_->HandlePreviousIme(accelerator); 794 break; 795 case PRINT_UI_HIERARCHIES: 796 return HandlePrintUIHierarchies(); 797 case SWITCH_IME: 798 if (ime_control_delegate_) 799 return ime_control_delegate_->HandleSwitchIme(accelerator); 800 break; 801 case LAUNCH_APP_0: 802 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0); 803 return true; 804 case LAUNCH_APP_1: 805 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1); 806 return true; 807 case LAUNCH_APP_2: 808 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2); 809 return true; 810 case LAUNCH_APP_3: 811 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3); 812 return true; 813 case LAUNCH_APP_4: 814 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4); 815 return true; 816 case LAUNCH_APP_5: 817 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5); 818 return true; 819 case LAUNCH_APP_6: 820 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6); 821 return true; 822 case LAUNCH_APP_7: 823 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7); 824 return true; 825 case LAUNCH_LAST_APP: 826 Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1); 827 return true; 828 case WINDOW_SNAP_LEFT: 829 case WINDOW_SNAP_RIGHT: { 830 aura::Window* window = wm::GetActiveWindow(); 831 // Disable window docking shortcut key for full screen window due to 832 // http://crbug.com/135487. 833 if (!window || 834 window->type() != aura::client::WINDOW_TYPE_NORMAL || 835 wm::IsWindowFullscreen(window)) { 836 break; 837 } 838 839 internal::SnapSizer::SnapWindow(window, 840 action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE : 841 internal::SnapSizer::RIGHT_EDGE); 842 return true; 843 } 844 case WINDOW_MINIMIZE: { 845 aura::Window* window = wm::GetActiveWindow(); 846 // Attempt to restore the window that would be cycled through next from 847 // the launcher when there is no active window. 848 if (!window) 849 return HandleCycleWindowMRU(WindowCycleController::FORWARD, false); 850 // Disable the shortcut for minimizing full screen window due to 851 // crbug.com/131709, which is a crashing issue related to minimizing 852 // full screen pepper window. 853 if (!wm::IsWindowFullscreen(window) && wm::CanMinimizeWindow(window)) { 854 ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction( 855 ash::UMA_MINIMIZE_PER_KEY); 856 wm::MinimizeWindow(window); 857 return true; 858 } 859 break; 860 } 861 case TOGGLE_FULLSCREEN: { 862 if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) { 863 shell->delegate()->RecordUserMetricsAction( 864 UMA_ACCEL_FULLSCREEN_F4); 865 } 866 shell->delegate()->ToggleFullscreen(); 867 return true; 868 } 869 case TOGGLE_MAXIMIZED: { 870 shell->delegate()->ToggleMaximized(); 871 return true; 872 } 873 case WINDOW_POSITION_CENTER: { 874 aura::Window* window = wm::GetActiveWindow(); 875 if (window) { 876 wm::CenterWindow(window); 877 return true; 878 } 879 break; 880 } 881 case SCALE_UI_UP: 882 return HandleScaleUI(true /* up */); 883 case SCALE_UI_DOWN: 884 return HandleScaleUI(false /* down */); 885 case SCALE_UI_RESET: 886 return HandleScaleReset(); 887 case ROTATE_WINDOW: 888 return HandleRotateActiveWindow(); 889 case ROTATE_SCREEN: 890 return HandleRotateScreen(); 891 case TOGGLE_DESKTOP_BACKGROUND_MODE: 892 return HandleToggleDesktopBackgroundMode(); 893 case TOGGLE_ROOT_WINDOW_FULL_SCREEN: 894 return HandleToggleRootWindowFullScreen(); 895 case DEBUG_TOGGLE_DEVICE_SCALE_FACTOR: 896 Shell::GetInstance()->display_manager()->ToggleDisplayScaleFactor(); 897 return true; 898 case DEBUG_TOGGLE_SHOW_DEBUG_BORDERS: 899 ash::debug::ToggleShowDebugBorders(); 900 return true; 901 case DEBUG_TOGGLE_SHOW_FPS_COUNTER: 902 ash::debug::ToggleShowFpsCounter(); 903 return true; 904 case DEBUG_TOGGLE_SHOW_PAINT_RECTS: 905 ash::debug::ToggleShowPaintRects(); 906 return true; 907 case MAGNIFY_SCREEN_ZOOM_IN: 908 return HandleMagnifyScreen(1); 909 case MAGNIFY_SCREEN_ZOOM_OUT: 910 return HandleMagnifyScreen(-1); 911 case MEDIA_NEXT_TRACK: 912 return HandleMediaNextTrack(); 913 case MEDIA_PLAY_PAUSE: 914 return HandleMediaPlayPause(); 915 case MEDIA_PREV_TRACK: 916 return HandleMediaPrevTrack(); 917 case POWER_PRESSED: // fallthrough 918 case POWER_RELEASED: 919 #if defined(OS_CHROMEOS) 920 if (!base::chromeos::IsRunningOnChromeOS()) { 921 // There is no powerd in linux desktop, so call the 922 // PowerButtonController here. 923 Shell::GetInstance()->power_button_controller()-> 924 OnPowerButtonEvent(action == POWER_PRESSED, base::TimeTicks()); 925 } 926 #endif 927 // We don't do anything with these at present on the device, 928 // (power button events are reported to us from powerm via 929 // D-BUS), but we consume them to prevent them from getting 930 // passed to apps -- see http://crbug.com/146609. 931 return true; 932 case LOCK_PRESSED: 933 case LOCK_RELEASED: 934 Shell::GetInstance()->power_button_controller()-> 935 OnLockButtonEvent(action == LOCK_PRESSED, base::TimeTicks()); 936 return true; 937 case PRINT_LAYER_HIERARCHY: 938 return HandlePrintLayerHierarchy(); 939 case PRINT_VIEW_HIERARCHY: 940 return HandlePrintViewHierarchy(); 941 case PRINT_WINDOW_HIERARCHY: 942 return HandlePrintWindowHierarchy(); 943 default: 944 NOTREACHED() << "Unhandled action " << action; 945 } 946 return false; 947 } 948 949 void AcceleratorController::SetBrightnessControlDelegate( 950 scoped_ptr<BrightnessControlDelegate> brightness_control_delegate) { 951 // Install brightness control delegate only when internal 952 // display exists. 953 if (Shell::GetInstance()->display_manager()->HasInternalDisplay() || 954 CommandLine::ForCurrentProcess()->HasSwitch( 955 switches::kAshEnableBrightnessControl)) { 956 brightness_control_delegate_ = brightness_control_delegate.Pass(); 957 } 958 } 959 960 void AcceleratorController::SetImeControlDelegate( 961 scoped_ptr<ImeControlDelegate> ime_control_delegate) { 962 ime_control_delegate_ = ime_control_delegate.Pass(); 963 } 964 965 void AcceleratorController::SetScreenshotDelegate( 966 scoped_ptr<ScreenshotDelegate> screenshot_delegate) { 967 screenshot_delegate_ = screenshot_delegate.Pass(); 968 } 969 970 //////////////////////////////////////////////////////////////////////////////// 971 // AcceleratorController, ui::AcceleratorTarget implementation: 972 973 bool AcceleratorController::AcceleratorPressed( 974 const ui::Accelerator& accelerator) { 975 std::map<ui::Accelerator, int>::const_iterator it = 976 accelerators_.find(accelerator); 977 DCHECK(it != accelerators_.end()); 978 return PerformAction(static_cast<AcceleratorAction>(it->second), accelerator); 979 } 980 981 void AcceleratorController::RegisterAccelerators( 982 const AcceleratorData accelerators[], 983 size_t accelerators_length) { 984 for (size_t i = 0; i < accelerators_length; ++i) { 985 ui::Accelerator accelerator(accelerators[i].keycode, 986 accelerators[i].modifiers); 987 accelerator.set_type(accelerators[i].trigger_on_press ? 988 ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED); 989 Register(accelerator, this); 990 accelerators_.insert( 991 std::make_pair(accelerator, accelerators[i].action)); 992 } 993 } 994 995 void AcceleratorController::SetKeyboardBrightnessControlDelegate( 996 scoped_ptr<KeyboardBrightnessControlDelegate> 997 keyboard_brightness_control_delegate) { 998 keyboard_brightness_control_delegate_ = 999 keyboard_brightness_control_delegate.Pass(); 1000 } 1001 1002 bool AcceleratorController::CanHandleAccelerators() const { 1003 return true; 1004 } 1005 1006 } // namespace ash 1007