// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/display/display_configuration_controller.h" #include #include "ash/display/display_animator.h" #include "ash/display/display_util.h" #include "ash/display/window_tree_host_manager.h" #include "ash/public/cpp/shelf_types.h" #include "ash/root_window_controller.h" #include "ash/rotator/screen_rotation_animator.h" #include "ash/shelf/shelf.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" #include "ash/wallpaper/wallpaper_controller_impl.h" #include "base/bind.h" #include "base/time/time.h" #include "chromeos/system/devicemode.h" #include "ui/base/l10n/l10n_util.h" #include "ui/display/display_layout.h" #include "ui/display/manager/display_manager.h" namespace ash { namespace { // Specifies how long the display change should have been disabled // after each display change operations. // |kCycleDisplayThrottleTimeoutMs| is set to be longer to avoid // changing the settings while the system is still configurating // displays. It will be overriden by |kAfterDisplayChangeThrottleTimeoutMs| // when the display change happens, so the actual timeout is much shorter. const int64_t kAfterDisplayChangeThrottleTimeoutMs = 500; const int64_t kCycleDisplayThrottleTimeoutMs = 4000; const int64_t kSetPrimaryDisplayThrottleTimeoutMs = 500; bool g_disable_animator_for_test = false; display::DisplayPositionInUnifiedMatrix GetUnifiedModeShelfCellPosition() { const ShelfAlignment alignment = Shell::GetPrimaryRootWindowController()->shelf()->alignment(); switch (alignment) { case ShelfAlignment::kBottom: case ShelfAlignment::kBottomLocked: return display::DisplayPositionInUnifiedMatrix::kBottomLeft; case ShelfAlignment::kLeft: return display::DisplayPositionInUnifiedMatrix::kTopLeft; case ShelfAlignment::kRight: return display::DisplayPositionInUnifiedMatrix::kTopRight; } NOTREACHED(); return display::DisplayPositionInUnifiedMatrix::kBottomLeft; } } // namespace class DisplayConfigurationController::DisplayChangeLimiter { public: DisplayChangeLimiter() : throttle_timeout_(base::Time::Now()) {} DisplayChangeLimiter(const DisplayChangeLimiter&) = delete; DisplayChangeLimiter& operator=(const DisplayChangeLimiter&) = delete; void SetThrottleTimeout(int64_t throttle_ms) { throttle_timeout_ = base::Time::Now() + base::Milliseconds(throttle_ms); } bool IsThrottled() const { return base::Time::Now() < throttle_timeout_; } private: base::Time throttle_timeout_; }; // static void DisplayConfigurationController::DisableAnimatorForTest() { g_disable_animator_for_test = true; } DisplayConfigurationController::DisplayConfigurationController( display::DisplayManager* display_manager, WindowTreeHostManager* window_tree_host_manager) : display_manager_(display_manager), window_tree_host_manager_(window_tree_host_manager) { window_tree_host_manager_->AddObserver(this); if (chromeos::IsRunningAsSystemCompositor()) limiter_ = std::make_unique(); if (!g_disable_animator_for_test) display_animator_ = std::make_unique(); } DisplayConfigurationController::~DisplayConfigurationController() { window_tree_host_manager_->RemoveObserver(this); } void DisplayConfigurationController::SetDisplayLayout( std::unique_ptr layout) { if (display_animator_) { display_animator_->StartFadeOutAnimation( base::BindOnce(&DisplayConfigurationController::SetDisplayLayoutImpl, weak_ptr_factory_.GetWeakPtr(), std::move(layout))); } else { SetDisplayLayoutImpl(std::move(layout)); } } void DisplayConfigurationController::SetUnifiedDesktopLayoutMatrix( const display::UnifiedDesktopLayoutMatrix& matrix) { DCHECK(display_manager_->IsInUnifiedMode()); if (display_animator_) { display_animator_->StartFadeOutAnimation(base::BindOnce( &DisplayConfigurationController::SetUnifiedDesktopLayoutMatrixImpl, weak_ptr_factory_.GetWeakPtr(), matrix)); } else { SetUnifiedDesktopLayoutMatrixImpl(matrix); } } void DisplayConfigurationController::SetMirrorMode(bool mirror, bool throttle) { if (display_manager_->num_connected_displays() <= 1 || display_manager_->IsInMirrorMode() == mirror || (throttle && IsLimited())) { return; } SetThrottleTimeout(kCycleDisplayThrottleTimeoutMs); if (display_animator_) { display_animator_->StartFadeOutAnimation( base::BindOnce(&DisplayConfigurationController::SetMirrorModeImpl, weak_ptr_factory_.GetWeakPtr(), mirror)); } else { SetMirrorModeImpl(mirror); } } void DisplayConfigurationController::SetDisplayRotation( int64_t display_id, display::Display::Rotation rotation, display::Display::RotationSource source, DisplayConfigurationController::RotationAnimation mode) { // No need to apply animation if the wallpaper isn't set yet during startup. if (display_manager_->IsDisplayIdValid(display_id) && Shell::Get()->wallpaper_controller()->is_wallpaper_set()) { if (GetTargetRotation(display_id) == rotation) return; if (display_animator_) { ScreenRotationAnimator* screen_rotation_animator = GetScreenRotationAnimatorForDisplay(display_id); screen_rotation_animator->Rotate(rotation, source, mode); return; } } // Invalid |display_id| or animator is disabled; call // DisplayManager::SetDisplayRotation directly. display_manager_->SetDisplayRotation(display_id, rotation, source); } display::Display::Rotation DisplayConfigurationController::GetTargetRotation( int64_t display_id) { if (!display_manager_->IsDisplayIdValid(display_id)) return display::Display::ROTATE_0; ScreenRotationAnimator* animator = GetScreenRotationAnimatorForDisplay(display_id); if (animator->IsRotating()) return animator->GetTargetRotation(); return display_manager_->GetDisplayInfo(display_id).GetActiveRotation(); } void DisplayConfigurationController::SetPrimaryDisplayId(int64_t display_id, bool throttle) { if (display_manager_->GetNumDisplays() <= 1 || (IsLimited() && throttle)) return; SetThrottleTimeout(kSetPrimaryDisplayThrottleTimeoutMs); if (display_animator_) { display_animator_->StartFadeOutAnimation( base::BindOnce(&DisplayConfigurationController::SetPrimaryDisplayIdImpl, weak_ptr_factory_.GetWeakPtr(), display_id)); } else { SetPrimaryDisplayIdImpl(display_id); } } display::Display DisplayConfigurationController::GetPrimaryMirroringDisplayForUnifiedDesktop() const { DCHECK(display_manager_->IsInUnifiedMode()); return display_manager_->GetMirroringDisplayForUnifiedDesktop( GetUnifiedModeShelfCellPosition()); } void DisplayConfigurationController::OnDisplayConfigurationChanged() { // TODO(oshima): Stop all animations. SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs); } // Protected void DisplayConfigurationController::SetAnimatorForTest(bool enable) { if (display_animator_ && !enable) display_animator_.reset(); else if (!display_animator_ && enable) display_animator_ = std::make_unique(); } // Private void DisplayConfigurationController::SetThrottleTimeout(int64_t throttle_ms) { if (limiter_) limiter_->SetThrottleTimeout(throttle_ms); } bool DisplayConfigurationController::IsLimited() { return limiter_ && limiter_->IsThrottled(); } void DisplayConfigurationController::SetDisplayLayoutImpl( std::unique_ptr layout) { display_manager_->SetLayoutForCurrentDisplays(std::move(layout)); if (display_animator_) display_animator_->StartFadeInAnimation(); } void DisplayConfigurationController::SetMirrorModeImpl(bool mirror) { display_manager_->SetMirrorMode( mirror ? display::MirrorMode::kNormal : display::MirrorMode::kOff, absl::nullopt); if (display_animator_) display_animator_->StartFadeInAnimation(); } void DisplayConfigurationController::SetPrimaryDisplayIdImpl( int64_t display_id) { window_tree_host_manager_->SetPrimaryDisplayId(display_id); if (display_animator_) display_animator_->StartFadeInAnimation(); } void DisplayConfigurationController::SetUnifiedDesktopLayoutMatrixImpl( const display::UnifiedDesktopLayoutMatrix& matrix) { display_manager_->SetUnifiedDesktopMatrix(matrix); if (display_animator_) display_animator_->StartFadeInAnimation(); } ScreenRotationAnimator* DisplayConfigurationController::GetScreenRotationAnimatorForDisplay( int64_t display_id) { aura::Window* root_window = Shell::GetRootWindowForDisplayId(display_id); return ScreenRotationAnimator::GetForRootWindow(root_window); } } // namespace ash