| // Copyright 2018 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/persistent_window_controller.h" |
| |
| #include "ash/display/persistent_window_info.h" |
| #include "ash/session/session_controller.h" |
| #include "ash/shell.h" |
| #include "ash/wm/mru_window_tracker.h" |
| #include "ash/wm/window_state.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/screen.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| display::DisplayManager* GetDisplayManager() { |
| return Shell::Get()->display_manager(); |
| } |
| |
| MruWindowTracker::WindowList GetWindowList() { |
| return Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(); |
| } |
| |
| // Returns true when window cycle list can be processed to perform save/restore |
| // operations on observing display changes. |
| bool ShouldProcessWindowList() { |
| // Window cycle list exists in active user session only. |
| if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) |
| return false; |
| |
| if (GetDisplayManager()->IsInMirrorMode()) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| constexpr char PersistentWindowController::kNumOfWindowsRestoredHistogramName[]; |
| |
| PersistentWindowController::PersistentWindowController() { |
| display::Screen::GetScreen()->AddObserver(this); |
| Shell::Get()->session_controller()->AddObserver(this); |
| Shell::Get()->window_tree_host_manager()->AddObserver(this); |
| } |
| |
| PersistentWindowController::~PersistentWindowController() { |
| Shell::Get()->window_tree_host_manager()->RemoveObserver(this); |
| Shell::Get()->session_controller()->RemoveObserver(this); |
| display::Screen::GetScreen()->RemoveObserver(this); |
| } |
| |
| void PersistentWindowController::OnWillProcessDisplayChanges() { |
| if (!ShouldProcessWindowList()) |
| return; |
| |
| for (auto* window : GetWindowList()) { |
| wm::WindowState* window_state = wm::GetWindowState(window); |
| // This implies that we keep the first persistent info until they're valid |
| // to restore, or until they're cleared by user-invoked bounds change. |
| if (window_state->persistent_window_info()) |
| continue; |
| window_state->SetPersistentWindowInfo(PersistentWindowInfo(window)); |
| } |
| } |
| |
| void PersistentWindowController::OnDisplayAdded( |
| const display::Display& new_display) { |
| restore_callback_ = base::BindOnce( |
| &PersistentWindowController::MaybeRestorePersistentWindowBounds, |
| base::Unretained(this)); |
| } |
| |
| void PersistentWindowController::OnSessionStateChanged( |
| session_manager::SessionState state) { |
| MaybeRestorePersistentWindowBounds(); |
| } |
| |
| void PersistentWindowController::OnDisplayConfigurationChanged() { |
| if (restore_callback_) |
| std::move(restore_callback_).Run(); |
| } |
| |
| void PersistentWindowController::MaybeRestorePersistentWindowBounds() { |
| if (!ShouldProcessWindowList()) |
| return; |
| |
| display::Screen* screen = display::Screen::GetScreen(); |
| int window_restored_count = 0; |
| for (auto* window : GetWindowList()) { |
| wm::WindowState* window_state = wm::GetWindowState(window); |
| if (!window_state->persistent_window_info()) |
| continue; |
| PersistentWindowInfo persistent_window_info = |
| *window_state->persistent_window_info(); |
| const int64_t persistent_display_id = persistent_window_info.display_id; |
| if (persistent_display_id == screen->GetDisplayNearestWindow(window).id()) |
| continue; |
| auto* display_manager = GetDisplayManager(); |
| if (!display_manager->IsDisplayIdValid(persistent_display_id)) |
| continue; |
| const auto& display = |
| display_manager->GetDisplayForId(persistent_display_id); |
| |
| // Update |persistent_window_bounds| based on |persistent_display_bounds|'s |
| // position change. This ensures that |persistent_window_bounds| is |
| // associated with the right target display. |
| gfx::Rect persistent_window_bounds = |
| persistent_window_info.window_bounds_in_screen; |
| const auto& persistent_display_bounds = |
| persistent_window_info.display_bounds_in_screen; |
| // It is possible to have display size change, such as changing cable, bad |
| // cable signal etc., but it should be rare. |
| DCHECK(display.bounds().size() == persistent_display_bounds.size()); |
| const gfx::Vector2d offset = display.bounds().OffsetFromOrigin() - |
| persistent_display_bounds.OffsetFromOrigin(); |
| persistent_window_bounds.Offset(offset); |
| |
| window->SetBoundsInScreen(persistent_window_bounds, display); |
| // Reset persistent window info everytime the window bounds have restored. |
| window_state->ResetPersistentWindowInfo(); |
| |
| ++window_restored_count; |
| } |
| |
| if (window_restored_count != 0) { |
| UMA_HISTOGRAM_COUNTS_100( |
| PersistentWindowController::kNumOfWindowsRestoredHistogramName, |
| window_restored_count); |
| } |
| } |
| |
| |
| } // namespace ash |