// Copyright (c) 2012 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/window_tree_host_manager.h" #include #include #include #include #include #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h" #include "ash/accessibility/magnifier/partial_magnifier_controller.h" #include "ash/display/cursor_window_controller.h" #include "ash/display/mirror_window_controller.h" #include "ash/display/root_window_transformers.h" #include "ash/frame_throttler/frame_throttling_controller.h" #include "ash/host/ash_window_tree_host.h" #include "ash/host/ash_window_tree_host_init_params.h" #include "ash/host/root_window_transformer.h" #include "ash/root_window_controller.h" #include "ash/root_window_settings.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/system/status_area_widget.h" #include "ash/system/unified/unified_system_tray.h" #include "ash/wm/window_util.h" #include "base/bind.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_functions.h" #include "base/notreached.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tracker.h" #include "ui/aura/window_tree_host.h" #include "ui/base/class_property.h" #include "ui/base/ime/init/input_method_factory.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_features.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/display/display.h" #include "ui/display/display_features.h" #include "ui/display/display_layout.h" #include "ui/display/display_transform.h" #include "ui/display/manager/display_configurator.h" #include "ui/display/manager/display_layout_store.h" #include "ui/display/manager/display_manager.h" #include "ui/display/screen.h" #include "ui/display/types/display_constants.h" #include "ui/display/util/display_util.h" #include "ui/gfx/geometry/rounded_corners_f.h" #include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/public/activation_client.h" namespace ash { namespace { // Primary display stored in global object as it can be // accessed after Shell is deleted. A separate display instance is created // during the shutdown instead of always keeping two display instances // (one here and another one in display_manager) in sync, which is error prone. // This is initialized in the constructor, and then in CreatePrimaryHost(). int64_t primary_display_id = -1; // The compositor memory limit when display size is larger than a threshold. constexpr int kUICompositorLargeMemoryLimitMB = 1024; // The display size threshold, above which the larger memory limit is used. // Pixel size was chosen to trigger for 4K+ displays. See: crbug.com/1261776 constexpr int kUICompositorMemoryLimitDisplaySizeThreshold = 3500; // An UMA signal for the current effective resolution is sent at this rate. This // keeps track of the effective resolution most used on internal display by the // user. constexpr base::TimeDelta kEffectiveResolutionRepeatingDelay = base::Minutes(30); display::DisplayManager* GetDisplayManager() { return Shell::Get()->display_manager(); } void SetDisplayPropertiesOnHost(AshWindowTreeHost* ash_host, const display::Display& display) { const display::Display::Rotation effective_rotation = display.panel_rotation(); aura::WindowTreeHost* host = ash_host->AsWindowTreeHost(); ash_host->UpdateCursorConfig(); ash_host->SetRootWindowTransformer( CreateRootWindowTransformerForDisplay(display)); host->SetDisplayTransformHint( display::DisplayRotationToOverlayTransform(effective_rotation)); const display::ManagedDisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(display.id()); if (display::features::IsRoundedDisplayEnabled()) { // Set/Update rounded corners on the display. ui::Layer* root_layer = host->window()->layer(); DCHECK(root_layer); root_layer->SetRoundedCornerRadius(display_info.rounded_corners_radii()); // If root_layer does not have rounded corners, setting the fast rounded // corner optimization on does not have any effect. root_layer->SetIsFastRoundedCorner( !display_info.rounded_corners_radii().IsEmpty()); } // Just moving the display requires the full redraw. // chrome-os-partner:33558. host->compositor()->ScheduleFullRedraw(); } void ClearDisplayPropertiesOnHost(AshWindowTreeHost* ash_host) { ash_host->ClearCursorConfig(); } aura::Window* GetWindow(AshWindowTreeHost* ash_host) { CHECK(ash_host->AsWindowTreeHost()); return ash_host->AsWindowTreeHost()->window(); } // Returns the index to the enum - |EffectiveResolution|. The enum value // represents the resolution that exactly matches the primary display's // effective resolution. int GetEffectiveResolutionUMAIndex(const display::Display& display) { const gfx::Size effective_size = display.size(); // The UMA enum index for portrait mode has 1 subtracted from itself. This // differentiates it from the landscape mode. return effective_size.width() > effective_size.height() ? effective_size.width() * effective_size.height() : effective_size.width() * effective_size.height() - 1; } void RepeatingEffectiveResolutionUMA(base::RepeatingTimer* timer, bool is_first_run) { display::Display internal_display; const auto* session_controller = Shell::Get()->session_controller(); // Record the UMA only when this is an active user session and the // internal display is present. if (display::HasInternalDisplay() && display::Screen::GetScreen()->GetDisplayWithDisplayId( display::Display::InternalDisplayId(), &internal_display) && session_controller->IsActiveUserSessionStarted() && session_controller->GetSessionState() == session_manager::SessionState::ACTIVE) { base::UmaHistogramSparse( "Ash.Display.InternalDisplay.ActiveEffectiveResolution", GetEffectiveResolutionUMAIndex(internal_display)); } // The first run of the repeating timer is half the actual delay. Reset the // timer after the first run with the correct delay. if (is_first_run && timer) { timer->Start( FROM_HERE, kEffectiveResolutionRepeatingDelay, base::BindRepeating(&RepeatingEffectiveResolutionUMA, nullptr /*timer=*/, false /*is_first_run=*/)); } } } // namespace // A utility class to store/restore focused/active window // when the display configuration has changed. class FocusActivationStore { public: FocusActivationStore() : activation_client_(nullptr), capture_client_(nullptr), focus_client_(nullptr), focused_(nullptr), active_(nullptr) {} FocusActivationStore(const FocusActivationStore&) = delete; FocusActivationStore& operator=(const FocusActivationStore&) = delete; void Store(bool clear_focus) { if (!activation_client_) { aura::Window* root = Shell::GetPrimaryRootWindow(); activation_client_ = ::wm::GetActivationClient(root); capture_client_ = aura::client::GetCaptureClient(root); focus_client_ = aura::client::GetFocusClient(root); } focused_ = focus_client_->GetFocusedWindow(); if (focused_) tracker_.Add(focused_); active_ = activation_client_->GetActiveWindow(); if (active_ && focused_ != active_) tracker_.Add(active_); // Deactivate the window to close menu / bubble windows. Deactivating by // setting active window to nullptr to avoid side effects of activating an // arbitrary window, such as covering |active_| before Restore(). if (clear_focus && active_) activation_client_->ActivateWindow(nullptr); // Release capture if any. capture_client_->SetCapture(nullptr); // Clear the focused window if any. This is necessary because a // window may be deleted when losing focus (fullscreen flash for // example). If the focused window is still alive after move, it'll // be re-focused below. if (clear_focus) focus_client_->FocusWindow(nullptr); } void Restore() { // Restore focused or active window if it's still alive. if (focused_ && tracker_.Contains(focused_)) { focus_client_->FocusWindow(focused_); } else if (active_ && tracker_.Contains(active_)) { activation_client_->ActivateWindow(active_); } if (focused_) tracker_.Remove(focused_); if (active_) tracker_.Remove(active_); focused_ = nullptr; active_ = nullptr; } private: ::wm::ActivationClient* activation_client_; aura::client::CaptureClient* capture_client_; aura::client::FocusClient* focus_client_; aura::WindowTracker tracker_; aura::Window* focused_; aura::Window* active_; }; //////////////////////////////////////////////////////////////////////////////// // WindowTreeHostManager WindowTreeHostManager::WindowTreeHostManager() : primary_tree_host_for_replace_(nullptr), focus_activation_store_(new FocusActivationStore()), cursor_window_controller_(new CursorWindowController()), mirror_window_controller_(new MirrorWindowController()), cursor_display_id_for_restore_(display::kInvalidDisplayId) { // Reset primary display to make sure that tests don't use // stale display info from previous tests. primary_display_id = display::kInvalidDisplayId; } WindowTreeHostManager::~WindowTreeHostManager() = default; void WindowTreeHostManager::Start() { display_observer_.emplace(this); Shell::Get() ->display_configurator() ->content_protection_manager() ->AddObserver(this); Shell::Get()->display_manager()->set_delegate(this); // Start a repeating timer to send UMA at fixed intervals. The first run is at // half the delay time. effective_resolution_UMA_timer_ = std::make_unique(); effective_resolution_UMA_timer_->Start( FROM_HERE, kEffectiveResolutionRepeatingDelay / 2, base::BindRepeating(&RepeatingEffectiveResolutionUMA, effective_resolution_UMA_timer_.get(), true /*is_first_run=*/)); } void WindowTreeHostManager::Shutdown() { for (auto& observer : observers_) observer.OnWindowTreeHostManagerShutdown(); effective_resolution_UMA_timer_->Reset(); // Unset the display manager's delegate here because // DisplayManager outlives WindowTreeHostManager. Shell::Get()->display_manager()->set_delegate(nullptr); cursor_window_controller_.reset(); mirror_window_controller_.reset(); Shell::Get() ->display_configurator() ->content_protection_manager() ->RemoveObserver(this); display_observer_.reset(); int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); // Delete non primary root window controllers first, then // delete the primary root window controller. aura::Window::Windows root_windows = WindowTreeHostManager::GetAllRootWindows(); std::vector to_delete; RootWindowController* primary_rwc = nullptr; for (aura::Window::Windows::iterator iter = root_windows.begin(); iter != root_windows.end(); ++iter) { RootWindowController* rwc = RootWindowController::ForWindow(*iter); if (GetRootWindowSettings(*iter)->display_id == primary_id) primary_rwc = rwc; else to_delete.push_back(rwc); } CHECK(primary_rwc); Shell::SetRootWindowForNewWindows(nullptr); for (auto* rwc : to_delete) delete rwc; delete primary_rwc; } void WindowTreeHostManager::CreatePrimaryHost( const AshWindowTreeHostInitParams& init_params) { const display::Display& primary_candidate = GetDisplayManager()->GetPrimaryDisplayCandidate(); primary_display_id = primary_candidate.id(); CHECK_NE(display::kInvalidDisplayId, primary_display_id); AddWindowTreeHostForDisplay(primary_candidate, init_params); } void WindowTreeHostManager::InitHosts() { RootWindowController::CreateForPrimaryDisplay( window_tree_hosts_[primary_display_id]); display::DisplayManager* display_manager = GetDisplayManager(); for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { const display::Display& display = display_manager->GetDisplayAt(i); if (primary_display_id != display.id()) { AshWindowTreeHost* ash_host = AddWindowTreeHostForDisplay(display, AshWindowTreeHostInitParams()); RootWindowController::CreateForSecondaryDisplay(ash_host); } } // Record display zoom for the primary display for https://crbug.com/955071. // This can be removed after M79. const display::ManagedDisplayInfo& display_info = display_manager->GetDisplayInfo(primary_display_id); int zoom_percent = std::round(display_info.zoom_factor() * 100); constexpr int kMaxValue = 300; constexpr int kBucketSize = 5; constexpr int kBucketCount = kMaxValue / kBucketSize + 1; base::LinearHistogram::FactoryGet( "Ash.Display.PrimaryDisplayZoomAtStartup", kBucketSize, kMaxValue, kBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag) ->Add(zoom_percent); for (auto& observer : observers_) observer.OnDisplaysInitialized(); } void WindowTreeHostManager::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void WindowTreeHostManager::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } // static int64_t WindowTreeHostManager::GetPrimaryDisplayId() { CHECK_NE(display::kInvalidDisplayId, primary_display_id); return primary_display_id; } // static bool WindowTreeHostManager::HasValidPrimaryDisplayId() { return primary_display_id != display::kInvalidDisplayId; } aura::Window* WindowTreeHostManager::GetPrimaryRootWindow() { // If |primary_tree_host_for_replace_| is set, it means |primary_display_id| // is kInvalidDisplayId. if (primary_tree_host_for_replace_) return GetWindow(primary_tree_host_for_replace_); return GetRootWindowForDisplayId(primary_display_id); } aura::Window* WindowTreeHostManager::GetRootWindowForDisplayId(int64_t id) { AshWindowTreeHost* host = GetAshWindowTreeHostForDisplayId(id); return host ? GetWindow(host) : nullptr; } AshWindowTreeHost* WindowTreeHostManager::GetAshWindowTreeHostForDisplayId( int64_t display_id) { const auto host = window_tree_hosts_.find(display_id); if (host != window_tree_hosts_.end()) return host->second; return mirror_window_controller_->GetAshWindowTreeHostForDisplayId( display_id); } aura::Window::Windows WindowTreeHostManager::GetAllRootWindows() { aura::Window::Windows windows; for (WindowTreeHostMap::const_iterator it = window_tree_hosts_.begin(); it != window_tree_hosts_.end(); ++it) { DCHECK(it->second); if (RootWindowController::ForWindow(GetWindow(it->second))) windows.push_back(GetWindow(it->second)); } return windows; } gfx::Insets WindowTreeHostManager::GetOverscanInsets(int64_t display_id) const { return GetDisplayManager()->GetOverscanInsets(display_id); } void WindowTreeHostManager::SetOverscanInsets( int64_t display_id, const gfx::Insets& insets_in_dip) { GetDisplayManager()->SetOverscanInsets(display_id, insets_in_dip); } std::vector WindowTreeHostManager::GetAllRootWindowControllers() { std::vector controllers; for (WindowTreeHostMap::const_iterator it = window_tree_hosts_.begin(); it != window_tree_hosts_.end(); ++it) { RootWindowController* controller = RootWindowController::ForWindow(GetWindow(it->second)); if (controller) controllers.push_back(controller); } return controllers; } void WindowTreeHostManager::UpdateMouseLocationAfterDisplayChange() { // If the mouse is currently on a display in native location, // use the same native location. Otherwise find the display closest // to the current cursor location in screen coordinates. gfx::Point point_in_screen = display::Screen::GetScreen()->GetCursorScreenPoint(); gfx::Point target_location_in_native; int64_t closest_distance_squared = -1; display::DisplayManager* display_manager = GetDisplayManager(); aura::Window* dst_root_window = nullptr; for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { const display::Display& display = display_manager->GetDisplayAt(i); const display::ManagedDisplayInfo display_info = display_manager->GetDisplayInfo(display.id()); aura::Window* root_window = GetRootWindowForDisplayId(display.id()); if (display_info.bounds_in_native().Contains( cursor_location_in_native_coords_for_restore_)) { dst_root_window = root_window; target_location_in_native = cursor_location_in_native_coords_for_restore_; break; } gfx::Point center = display.bounds().CenterPoint(); // Use the distance squared from the center of the dislay. This is not // exactly "closest" display, but good enough to pick one // appropriate (and there are at most two displays). // We don't care about actual distance, only relative to other displays, so // using the LengthSquared() is cheaper than Length(). int64_t distance_squared = (center - point_in_screen).LengthSquared(); if (closest_distance_squared < 0 || closest_distance_squared > distance_squared) { aura::Window* root_window = GetRootWindowForDisplayId(display.id()); ::wm::ConvertPointFromScreen(root_window, ¢er); root_window->GetHost()->ConvertDIPToScreenInPixels(¢er); dst_root_window = root_window; target_location_in_native = center; closest_distance_squared = distance_squared; } } gfx::Point target_location_in_root = target_location_in_native; dst_root_window->GetHost()->ConvertScreenInPixelsToDIP( &target_location_in_root); gfx::Point target_location_in_screen = target_location_in_root; ::wm::ConvertPointToScreen(dst_root_window, &target_location_in_screen); const display::Display& target_display = display_manager->FindDisplayContainingPoint(target_location_in_screen); // If the original location isn't on any of new display, let ozone move // the cursor. if (!target_display.is_valid()) return; int64_t target_display_id = target_display.id(); // Do not move the cursor if the cursor's location did not change. This avoids // moving (and showing) the cursor: // - At startup. // - When the device is rotated in tablet mode. // |cursor_display_id_for_restore_| is checked to ensure that the cursor is // moved when the cursor's native position does not change but the display // that it is on has changed. This occurs when swapping the primary display. if (target_location_in_native != cursor_location_in_native_coords_for_restore_ || target_display_id != cursor_display_id_for_restore_) { if (Shell::Get()->cursor_manager()) { if (Shell::Get()->cursor_manager()->IsCursorVisible()) { dst_root_window->MoveCursorTo(target_location_in_root); } else if (target_display_id != cursor_display_id_for_restore_) { Shell::Get()->cursor_manager()->SetDisplay(target_display); } } } else if (target_location_in_screen != cursor_location_in_screen_coords_for_restore_) { // The cursor's native position did not change but its screen position did // change. This occurs when the scale factor or the rotation of the display // that the cursor is on changes. // TODO: conditional should not be necessary. http://crbug.com/631103. if (Shell::Get()->cursor_manager()) Shell::Get()->cursor_manager()->SetDisplay(target_display); // Update the cursor's root location. This ends up dispatching a synthetic // mouse move. The synthetic mouse move updates the composited cursor's // location and hover effects. Synthetic mouse moves do not affect the // cursor's visibility. dst_root_window->GetHost()->dispatcher()->OnCursorMovedToRootLocation( target_location_in_root); } } bool WindowTreeHostManager::UpdateWorkAreaOfDisplayNearestWindow( const aura::Window* window, const gfx::Insets& insets) { const aura::Window* root_window = window->GetRootWindow(); int64_t id = GetRootWindowSettings(root_window)->display_id; // if id is |kInvaildDisplayID|, it's being deleted. DCHECK(id != display::kInvalidDisplayId); return GetDisplayManager()->UpdateWorkAreaOfDisplay(id, insets); } void WindowTreeHostManager::OnDisplayAdded(const display::Display& display) { // If we're switching from/to offscreen WTH, we need to // create new WTH for primary display instead of reusing. if (primary_tree_host_for_replace_ && (GetRootWindowSettings(GetWindow(primary_tree_host_for_replace_)) ->display_id == display::kUnifiedDisplayId || display.id() == display::kUnifiedDisplayId)) { DCHECK_EQ(display::kInvalidDisplayId, primary_display_id); primary_display_id = display.id(); AshWindowTreeHost* ash_host = AddWindowTreeHostForDisplay(display, AshWindowTreeHostInitParams()); RootWindowController* new_root_window_controller = RootWindowController::CreateForSecondaryDisplay(ash_host); // Magnifier controllers keep pointers to the current root window. // Update them here to avoid accessing them later. Shell::Get()->fullscreen_magnifier_controller()->SwitchTargetRootWindow( ash_host->AsWindowTreeHost()->window(), false); Shell::Get() ->partial_magnifier_controller() ->SwitchTargetRootWindowIfNeeded( ash_host->AsWindowTreeHost()->window()); AshWindowTreeHost* to_delete = primary_tree_host_for_replace_; // Show the shelf if the original WTH had a visible system // tray. It may or may not be visible depending on OOBE state. RootWindowController* old_root_window_controller = RootWindowController::ForWindow( to_delete->AsWindowTreeHost()->window()); TrayBackgroundView* old_tray = old_root_window_controller->GetStatusAreaWidget() ->unified_system_tray(); TrayBackgroundView* new_tray = new_root_window_controller->GetStatusAreaWidget() ->unified_system_tray(); if (old_tray->GetWidget()->IsVisible()) { new_tray->SetVisiblePreferred(true); new_tray->GetWidget()->Show(); } // |to_delete| has already been removed from |window_tree_hosts_|. DCHECK(std::none_of( window_tree_hosts_.cbegin(), window_tree_hosts_.cend(), [to_delete](const std::pair& pair) { return pair.second == to_delete; })); DeleteHost(to_delete); DCHECK(!primary_tree_host_for_replace_); } else if (primary_tree_host_for_replace_) { // TODO(oshima): It should be possible to consolidate logic for // unified and non unified, but I'm keeping them separated to minimize // the risk in M44. I'll consolidate this in M45. DCHECK(window_tree_hosts_.empty()); AshWindowTreeHost* ash_host = primary_tree_host_for_replace_; primary_tree_host_for_replace_ = nullptr; primary_display_id = display.id(); window_tree_hosts_[display.id()] = ash_host; GetRootWindowSettings(GetWindow(ash_host))->display_id = display.id(); const display::ManagedDisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(display.id()); ash_host->AsWindowTreeHost()->SetBoundsInPixels( display_info.bounds_in_native()); SetDisplayPropertiesOnHost(ash_host, display); } else { if (primary_display_id == display::kInvalidDisplayId) primary_display_id = display.id(); DCHECK(!window_tree_hosts_.empty()); AshWindowTreeHost* ash_host = AddWindowTreeHostForDisplay(display, AshWindowTreeHostInitParams()); RootWindowController::CreateForSecondaryDisplay(ash_host); } } void WindowTreeHostManager::DeleteHost(AshWindowTreeHost* host_to_delete) { ClearDisplayPropertiesOnHost(host_to_delete); aura::Window* root_being_deleted = GetWindow(host_to_delete); RootWindowController* controller = RootWindowController::ForWindow(root_being_deleted); DCHECK(controller); // Some code relies on this being called before MoveWindowsTo(). Shell::Get()->OnRootWindowWillShutdown(root_being_deleted); aura::Window* primary_root_after_host_deletion = GetRootWindowForDisplayId(GetPrimaryDisplayId()); controller->MoveWindowsTo(primary_root_after_host_deletion); // Delete most of root window related objects, but don't delete // root window itself yet because the stack may be using it. controller->Shutdown(); if (primary_tree_host_for_replace_ == host_to_delete) primary_tree_host_for_replace_ = nullptr; DCHECK_EQ(primary_root_after_host_deletion, Shell::GetPrimaryRootWindow()); if (Shell::GetRootWindowForNewWindows() == root_being_deleted) { Shell::SetRootWindowForNewWindows(primary_root_after_host_deletion); } // NOTE: ShelfWidget is gone, but Shelf still exists until this task runs. base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, controller); } void WindowTreeHostManager::OnDisplayRemoved(const display::Display& display) { AshWindowTreeHost* host_to_delete = window_tree_hosts_[display.id()]; CHECK(host_to_delete) << display.ToString(); // When the primary root window's display is removed, move the primary // root to the other display. if (primary_display_id == display.id()) { // Temporarily store the primary root window in // |primary_root_window_for_replace_| when replacing the display. if (window_tree_hosts_.size() == 1) { primary_display_id = display::kInvalidDisplayId; primary_tree_host_for_replace_ = host_to_delete; // Display for root window will be deleted when the Primary RootWindow // is deleted by the Shell. window_tree_hosts_.erase(display.id()); return; } for (const auto& pair : window_tree_hosts_) { if (pair.first != display.id()) { primary_display_id = pair.first; break; } } CHECK_NE(display::kInvalidDisplayId, primary_display_id); AshWindowTreeHost* primary_host = host_to_delete; // Delete the other host instead. host_to_delete = window_tree_hosts_[primary_display_id]; GetRootWindowSettings(GetWindow(host_to_delete))->display_id = display.id(); // Setup primary root. window_tree_hosts_[primary_display_id] = primary_host; GetRootWindowSettings(GetWindow(primary_host))->display_id = primary_display_id; OnDisplayMetricsChanged( GetDisplayManager()->GetDisplayForId(primary_display_id), DISPLAY_METRIC_BOUNDS); } DeleteHost(host_to_delete); // The window tree host should be erased at last because some handlers can // access to the host through GetRootWindowForDisplayId() during // MoveWindowsTo(). See http://crbug.com/415222 window_tree_hosts_.erase(display.id()); } void WindowTreeHostManager::OnDisplayMetricsChanged( const display::Display& display, uint32_t metrics) { if (!(metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION | DISPLAY_METRIC_DEVICE_SCALE_FACTOR))) { return; } const display::ManagedDisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(display.id()); DCHECK(!display_info.bounds_in_native().IsEmpty()); AshWindowTreeHost* ash_host = window_tree_hosts_[display.id()]; ash_host->AsWindowTreeHost()->SetBoundsInPixels( display_info.bounds_in_native()); SetDisplayPropertiesOnHost(ash_host, display); } void WindowTreeHostManager::OnHostResized(aura::WindowTreeHost* host) { display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow(host->window()); display::DisplayManager* display_manager = GetDisplayManager(); if (display_manager->UpdateDisplayBounds(display.id(), host->GetBoundsInPixels())) { mirror_window_controller_->UpdateWindow(); cursor_window_controller_->UpdateContainer(); } } void WindowTreeHostManager::OnDisplaySecurityChanged(int64_t display_id, bool secure) { AshWindowTreeHost* host = GetAshWindowTreeHostForDisplayId(display_id); // No host for internal display in docked mode. if (!host) return; ui::Compositor* compositor = host->AsWindowTreeHost()->compositor(); compositor->SetOutputIsSecure(secure); compositor->ScheduleFullRedraw(); } void WindowTreeHostManager::CreateOrUpdateMirroringDisplay( const display::DisplayInfoList& info_list) { if (GetDisplayManager()->IsInMirrorMode() || GetDisplayManager()->IsInUnifiedMode()) { mirror_window_controller_->UpdateWindow(info_list); cursor_window_controller_->UpdateContainer(); } else { NOTREACHED(); } } void WindowTreeHostManager::CloseMirroringDisplayIfNotNecessary() { mirror_window_controller_->CloseIfNotNecessary(); // If cursor_compositing is enabled for large cursor, the cursor window is // always on the desktop display (the visible cursor on the non-desktop // display is drawn through compositor mirroring). Therefore, it's unnecessary // to handle the cursor_window at all. See: http://crbug.com/412910 if (!cursor_window_controller_->is_cursor_compositing_enabled()) cursor_window_controller_->UpdateContainer(); } void WindowTreeHostManager::PreDisplayConfigurationChange(bool clear_focus) { for (auto& observer : observers_) observer.OnDisplayConfigurationChanging(); focus_activation_store_->Store(clear_focus); display::Screen* screen = display::Screen::GetScreen(); gfx::Point point_in_screen = screen->GetCursorScreenPoint(); cursor_location_in_screen_coords_for_restore_ = point_in_screen; display::Display display = screen->GetDisplayNearestPoint(point_in_screen); cursor_display_id_for_restore_ = display.id(); gfx::Point point_in_native = point_in_screen; aura::Window* root_window = GetRootWindowForDisplayId(display.id()); ::wm::ConvertPointFromScreen(root_window, &point_in_native); root_window->GetHost()->ConvertDIPToScreenInPixels(&point_in_native); cursor_location_in_native_coords_for_restore_ = point_in_native; } void WindowTreeHostManager::SetPrimaryDisplayId(int64_t id) { // TODO(oshima): Move primary display management to DisplayManager. DCHECK_NE(display::kInvalidDisplayId, id); if (id == display::kInvalidDisplayId || primary_display_id == id || window_tree_hosts_.size() < 2) { return; } const display::Display& new_primary_display = GetDisplayManager()->GetDisplayForId(id); if (!new_primary_display.is_valid()) { LOG(ERROR) << "Invalid or non-existent display is requested:" << new_primary_display.ToString(); return; } display::DisplayManager* display_manager = GetDisplayManager(); DCHECK(new_primary_display.is_valid()); DCHECK(display_manager->GetDisplayForId(new_primary_display.id()).is_valid()); AshWindowTreeHost* non_primary_host = window_tree_hosts_[new_primary_display.id()]; LOG_IF(ERROR, !non_primary_host) << "Unknown display is requested in SetPrimaryDisplay: id=" << new_primary_display.id(); if (!non_primary_host) return; display::Display old_primary_display = display::Screen::GetScreen()->GetPrimaryDisplay(); DCHECK_EQ(old_primary_display.id(), primary_display_id); // Swap root windows between current and new primary display. AshWindowTreeHost* primary_host = window_tree_hosts_[primary_display_id]; CHECK(primary_host); CHECK_NE(primary_host, non_primary_host); aura::Window* primary_window = GetWindow(primary_host); aura::Window* non_primary_window = GetWindow(non_primary_host); window_tree_hosts_[new_primary_display.id()] = primary_host; GetRootWindowSettings(primary_window)->display_id = new_primary_display.id(); window_tree_hosts_[old_primary_display.id()] = non_primary_host; GetRootWindowSettings(non_primary_window)->display_id = old_primary_display.id(); // Ensure that color spaces for the root windows reflect those of their new // displays. If these go out of sync, we can lose the ability to composite HDR // content. primary_host->AsWindowTreeHost()->compositor()->SetDisplayColorSpaces( new_primary_display.color_spaces()); non_primary_host->AsWindowTreeHost()->compositor()->SetDisplayColorSpaces( old_primary_display.color_spaces()); std::u16string old_primary_title = primary_window->GetTitle(); primary_window->SetTitle(non_primary_window->GetTitle()); non_primary_window->SetTitle(old_primary_title); const display::DisplayLayout& layout = GetDisplayManager()->GetCurrentDisplayLayout(); // The requested primary id can be same as one in the stored layout // when the primary id is set after new displays are connected. // Only update the layout if it is requested to swap primary display. if (layout.primary_id != new_primary_display.id()) { std::unique_ptr swapped_layout = layout.Copy(); swapped_layout->SwapPrimaryDisplay(new_primary_display.id()); display::DisplayIdList list = display_manager->GetConnectedDisplayIdList(); GetDisplayManager()->layout_store()->RegisterLayoutForDisplayIdList( list, std::move(swapped_layout)); } primary_display_id = new_primary_display.id(); UpdateWorkAreaOfDisplayNearestWindow(GetWindow(primary_host), old_primary_display.GetWorkAreaInsets()); UpdateWorkAreaOfDisplayNearestWindow(GetWindow(non_primary_host), new_primary_display.GetWorkAreaInsets()); // Update the dispay manager with new display info. GetDisplayManager()->set_force_bounds_changed(true); GetDisplayManager()->UpdateDisplays(); GetDisplayManager()->set_force_bounds_changed(false); } void WindowTreeHostManager::PostDisplayConfigurationChange() { focus_activation_store_->Restore(); for (auto& observer : observers_) observer.OnDisplayConfigurationChanged(); UpdateMouseLocationAfterDisplayChange(); // Enable cursor compositing, so that cursor could be mirrored to destination // displays along with other display content. Shell::Get()->UpdateCursorCompositingEnabled(); } ui::EventDispatchDetails WindowTreeHostManager::DispatchKeyEventPostIME( ui::KeyEvent* event) { aura::Window* root_window = nullptr; if (event->target()) { root_window = static_cast(event->target())->GetRootWindow(); DCHECK(root_window); } else { // Getting the active root window to dispatch the event. This isn't // significant as the event will be sent to the window resolved by // aura::client::FocusClient which is FocusController in ash. aura::Window* active_window = window_util::GetActiveWindow(); root_window = active_window ? active_window->GetRootWindow() : Shell::GetPrimaryRootWindow(); } return root_window->GetHost()->DispatchKeyEventPostIME(event); } const display::Display* WindowTreeHostManager::GetDisplayById( int64_t display_id) const { const display::Display& display = GetDisplayManager()->GetDisplayForId(display_id); return display.is_valid() ? &display : nullptr; } void WindowTreeHostManager::SetCurrentEventTargeterSourceHost( aura::WindowTreeHost* targeter_src_host) { NOTIMPLEMENTED(); } AshWindowTreeHost* WindowTreeHostManager::AddWindowTreeHostForDisplay( const display::Display& display, const AshWindowTreeHostInitParams& init_params) { static int host_count = 0; const display::ManagedDisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(display.id()); AshWindowTreeHostInitParams params_with_bounds(init_params); params_with_bounds.initial_bounds = display_info.bounds_in_native(); if (display.id() == display::kUnifiedDisplayId) { params_with_bounds.offscreen = true; params_with_bounds.delegate = mirror_window_controller(); } else { params_with_bounds.delegate = this; } params_with_bounds.display_id = display.id(); params_with_bounds.device_scale_factor = display.device_scale_factor(); // TODO(crbug/1261776): Temporarily increase compositor memory limit for // 4K+ displays to avoid rendering corruption. // Check both width and height in case of rotated display. if (std::max(display.GetSizeInPixel().width(), display.GetSizeInPixel().height()) > kUICompositorMemoryLimitDisplaySizeThreshold) { params_with_bounds.compositor_memory_limit_mb = kUICompositorLargeMemoryLimitMB; } // The AshWindowTreeHost ends up owned by the RootWindowControllers created // by this class. AshWindowTreeHost* ash_host = AshWindowTreeHost::Create(params_with_bounds).release(); aura::WindowTreeHost* host = ash_host->AsWindowTreeHost(); Shell::Get()->frame_throttling_controller()->OnWindowTreeHostCreated(host); DCHECK(!host->has_input_method()); if (!input_method_) { // Singleton input method instance for Ash. input_method_ = ui::CreateInputMethod(this, host->GetAcceleratedWidget()); // Makes sure the input method is focused by default when created, because // Ash uses singleton InputMethod and it won't call OnFocus/OnBlur when // the active window changed. input_method_->OnFocus(); } host->SetSharedInputMethod(input_method_.get()); host->window()->SetName(base::StringPrintf( "%sRootWindow-%d", params_with_bounds.offscreen ? "Offscreen" : "", host_count++)); host->window()->SetTitle(base::UTF8ToUTF16(display_info.name())); host->compositor()->SetBackgroundColor(SK_ColorBLACK); // No need to remove our observer observer because the WindowTreeHostManager // outlives the host. host->AddObserver(this); InitRootWindowSettings(host->window())->display_id = display.id(); host->InitHost(); host->window()->Show(); window_tree_hosts_[display.id()] = ash_host; SetDisplayPropertiesOnHost(ash_host, display); ash_host->ConfineCursorToRootWindow(); return ash_host; } } // namespace ash