| // Copyright (c) 2013 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/mirror_window_controller.h" |
| |
| #include <utility> |
| |
| #include "ash/display/cursor_window_controller.h" |
| #include "ash/display/display_util.h" |
| #include "ash/display/root_window_transformers.h" |
| #include "ash/display/screen_position_controller.h" |
| #include "ash/display/window_tree_host_manager.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_settings.h" |
| #include "ash/shell.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "components/viz/common/surfaces/surface_id.h" |
| #include "ui/aura/client/capture_client.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_delegate.h" |
| #include "ui/aura/window_event_dispatcher.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/base/layout.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/compositor/compositor.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/display/display_layout.h" |
| #include "ui/display/display_transform.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/manager/managed_display_info.h" |
| #include "ui/display/screen.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/native_widget_types.h" |
| |
| namespace ash { |
| namespace { |
| |
| // ScreenPositionClient for mirroring windows. |
| class MirroringScreenPositionClient |
| : public aura::client::ScreenPositionClient { |
| public: |
| explicit MirroringScreenPositionClient(MirrorWindowController* controller) |
| : controller_(controller) {} |
| |
| MirroringScreenPositionClient(const MirroringScreenPositionClient&) = delete; |
| MirroringScreenPositionClient& operator=( |
| const MirroringScreenPositionClient&) = delete; |
| |
| // aura::client::ScreenPositionClient: |
| void ConvertPointToScreen(const aura::Window* window, |
| gfx::PointF* point) override { |
| const aura::Window* root = window->GetRootWindow(); |
| aura::Window::ConvertPointToTarget(window, root, point); |
| const display::Display& display = |
| controller_->GetDisplayForRootWindow(root); |
| const gfx::Point display_origin = display.bounds().origin(); |
| point->Offset(display_origin.x(), display_origin.y()); |
| } |
| |
| void ConvertPointFromScreen(const aura::Window* window, |
| gfx::PointF* point) override { |
| const aura::Window* root = window->GetRootWindow(); |
| const display::Display& display = |
| controller_->GetDisplayForRootWindow(root); |
| const gfx::Point display_origin = display.bounds().origin(); |
| point->Offset(-display_origin.x(), -display_origin.y()); |
| aura::Window::ConvertPointToTarget(root, window, point); |
| } |
| |
| void ConvertHostPointToScreen(aura::Window* root_window, |
| gfx::Point* point) override { |
| aura::Window* not_used; |
| ScreenPositionController::ConvertHostPointToRelativeToRootWindow( |
| root_window, controller_->GetAllRootWindows(), point, ¬_used); |
| aura::client::ScreenPositionClient::ConvertPointToScreen(root_window, |
| point); |
| } |
| |
| void SetBounds(aura::Window* window, |
| const gfx::Rect& bounds, |
| const display::Display& display) override { |
| NOTREACHED(); |
| } |
| |
| protected: |
| // aura::client::ScreenPositionClient: |
| gfx::Point GetRootWindowOriginInScreen( |
| const aura::Window* root_window) override { |
| DCHECK(root_window->IsRootWindow()); |
| const display::Display& display = |
| controller_->GetDisplayForRootWindow(root_window); |
| return display.bounds().origin(); |
| } |
| |
| private: |
| MirrorWindowController* controller_; // not owned. |
| }; |
| |
| // A trivial CaptureClient that does nothing. That is, calls to set/release |
| // capture are dropped. |
| class NoneCaptureClient : public aura::client::CaptureClient { |
| public: |
| NoneCaptureClient() = default; |
| |
| NoneCaptureClient(const NoneCaptureClient&) = delete; |
| NoneCaptureClient& operator=(const NoneCaptureClient&) = delete; |
| |
| ~NoneCaptureClient() override = default; |
| |
| private: |
| // aura::client::CaptureClient: |
| void SetCapture(aura::Window* window) override {} |
| void ReleaseCapture(aura::Window* window) override {} |
| aura::Window* GetCaptureWindow() override { return nullptr; } |
| aura::Window* GetGlobalCaptureWindow() override { return nullptr; } |
| void AddObserver(aura::client::CaptureClientObserver* observer) override {} |
| void RemoveObserver(aura::client::CaptureClientObserver* observer) override {} |
| }; |
| |
| display::DisplayManager::MultiDisplayMode GetCurrentMultiDisplayMode() { |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| return display_manager->IsInUnifiedMode() |
| ? display::DisplayManager::UNIFIED |
| : (display_manager->IsInSoftwareMirrorMode() |
| ? display::DisplayManager::MIRRORING |
| : display::DisplayManager::EXTENDED); |
| } |
| |
| int64_t GetCurrentReflectingSourceId() { |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| if (display_manager->IsInUnifiedMode()) |
| return display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| if (display_manager->IsInSoftwareMirrorMode()) |
| return display_manager->mirroring_source_id(); |
| return display::kInvalidDisplayId; |
| } |
| |
| } // namespace |
| |
| struct MirrorWindowController::MirroringHostInfo { |
| MirroringHostInfo(); |
| ~MirroringHostInfo(); |
| std::unique_ptr<AshWindowTreeHost> ash_host; |
| gfx::Size mirror_window_host_size; |
| aura::Window* mirror_window = nullptr; |
| }; |
| |
| MirrorWindowController::MirroringHostInfo::MirroringHostInfo() = default; |
| MirrorWindowController::MirroringHostInfo::~MirroringHostInfo() = default; |
| |
| MirrorWindowController::MirrorWindowController() |
| : current_event_targeter_src_host_(nullptr), |
| multi_display_mode_(display::DisplayManager::EXTENDED), |
| screen_position_client_(new MirroringScreenPositionClient(this)) {} |
| |
| MirrorWindowController::~MirrorWindowController() { |
| // Make sure the root window gets deleted before cursor_window_delegate. |
| Close(false); |
| } |
| |
| void MirrorWindowController::UpdateWindow( |
| const std::vector<display::ManagedDisplayInfo>& display_info_list) { |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| DCHECK(display_manager->IsInSoftwareMirrorMode() || |
| display_manager->IsInUnifiedMode()); |
| static int mirror_host_count = 0; |
| |
| multi_display_mode_ = GetCurrentMultiDisplayMode(); |
| reflecting_source_id_ = GetCurrentReflectingSourceId(); |
| viz::SurfaceId reflecting_surface_id = |
| Shell::GetRootWindowForDisplayId(reflecting_source_id_)->GetSurfaceId(); |
| |
| for (const display::ManagedDisplayInfo& display_info : display_info_list) { |
| std::unique_ptr<RootWindowTransformer> transformer; |
| if (display_manager->IsInSoftwareMirrorMode()) { |
| transformer = CreateRootWindowTransformerForMirroredDisplay( |
| display_manager->GetDisplayInfo(reflecting_source_id_), display_info); |
| } else { |
| DCHECK(display_manager->IsInUnifiedMode()); |
| display::Display display = |
| display_manager->GetMirroringDisplayById(display_info.id()); |
| transformer = CreateRootWindowTransformerForUnifiedDesktop( |
| display::Screen::GetScreen()->GetPrimaryDisplay().bounds(), display); |
| } |
| |
| if (mirroring_host_info_map_.find(display_info.id()) == |
| mirroring_host_info_map_.end()) { |
| AshWindowTreeHostInitParams init_params; |
| init_params.initial_bounds = display_info.bounds_in_native(); |
| init_params.display_id = display_info.id(); |
| init_params.delegate = this; |
| init_params.mirroring_unified = display_manager->IsInUnifiedMode(); |
| init_params.device_scale_factor = display_info.device_scale_factor(); |
| MirroringHostInfo* host_info = new MirroringHostInfo; |
| host_info->ash_host = AshWindowTreeHost::Create(init_params); |
| mirroring_host_info_map_[display_info.id()] = host_info; |
| |
| aura::WindowTreeHost* host = host_info->ash_host->AsWindowTreeHost(); |
| DCHECK(!host->has_input_method()); |
| host->SetSharedInputMethod( |
| Shell::Get()->window_tree_host_manager()->input_method()); |
| host->window()->SetName( |
| base::StringPrintf("MirrorRootWindow-%d", mirror_host_count++)); |
| host->compositor()->SetBackgroundColor(SK_ColorBLACK); |
| // No need to remove the observer because the WindowTreeHostManager |
| // outlives the host. |
| host->AddObserver(Shell::Get()->window_tree_host_manager()); |
| host->AddObserver(this); |
| // TODO(oshima): TouchHUD is using idkey. |
| InitRootWindowSettings(host->window())->display_id = display_info.id(); |
| host->InitHost(); |
| host->window()->Show(); |
| |
| if (display_manager->IsInUnifiedMode()) { |
| host_info->ash_host->ConfineCursorToRootWindow(); |
| AshWindowTreeHost* unified_ash_host = |
| Shell::Get() |
| ->window_tree_host_manager() |
| ->GetAshWindowTreeHostForDisplayId(reflecting_source_id_); |
| unified_ash_host->RegisterMirroringHost(host_info->ash_host.get()); |
| aura::client::SetScreenPositionClient(host->window(), |
| screen_position_client_.get()); |
| } |
| |
| aura::client::SetCaptureClient(host->window(), new NoneCaptureClient()); |
| host->Show(); |
| |
| aura::Window* mirror_window = host_info->mirror_window = |
| new aura::Window(nullptr); |
| mirror_window->Init(ui::LAYER_SOLID_COLOR); |
| host->window()->AddChild(mirror_window); |
| host_info->ash_host->SetRootWindowTransformer(std::move(transformer)); |
| |
| const display::Display::Rotation effective_rotation = |
| display_info.GetLogicalActiveRotation(); |
| host->SetDisplayTransformHint( |
| display::DisplayRotationToOverlayTransform(effective_rotation)); |
| |
| // The accelerated widget is created synchronously. |
| DCHECK_NE(gfx::kNullAcceleratedWidget, host->GetAcceleratedWidget()); |
| } else { |
| AshWindowTreeHost* ash_host = |
| mirroring_host_info_map_[display_info.id()]->ash_host.get(); |
| aura::WindowTreeHost* host = ash_host->AsWindowTreeHost(); |
| GetRootWindowSettings(host->window())->display_id = display_info.id(); |
| ash_host->SetRootWindowTransformer(std::move(transformer)); |
| host->SetBoundsInPixels(display_info.bounds_in_native()); |
| |
| // TODO(oshima): Consolidate the code above. |
| const display::Display::Rotation effective_rotation = |
| display_info.GetLogicalActiveRotation(); |
| host->SetDisplayTransformHint( |
| display::DisplayRotationToOverlayTransform(effective_rotation)); |
| } |
| |
| // |mirror_size| is the size of the compositor of the mirror source in |
| // physical pixels. The RootWindowTransformer corrects the scale of the |
| // mirrored display and the location of input events. |
| ui::Compositor* source_compositor = |
| Shell::GetRootWindowForDisplayId(reflecting_source_id_) |
| ->GetHost() |
| ->compositor(); |
| gfx::Size mirror_size = source_compositor->size(); |
| |
| auto* mirroring_host_info = mirroring_host_info_map_[display_info.id()]; |
| |
| const bool should_undo_rotation = ShouldUndoRotationForMirror(); |
| |
| if (!should_undo_rotation && !display_manager->IsInUnifiedMode()) { |
| // Use the rotation from source display without panel orientation |
| // applied instead of the display transform hint in |source_compositor| |
| // so that panel orientation is not applied to the mirror host. |
| mirroring_host_info->ash_host->AsWindowTreeHost() |
| ->SetDisplayTransformHint(display::DisplayRotationToOverlayTransform( |
| display_manager->GetDisplayInfo(reflecting_source_id_) |
| .GetActiveRotation())); |
| } |
| |
| aura::Window* mirror_window = mirroring_host_info->mirror_window; |
| mirror_window->SetBounds(gfx::Rect(mirror_size)); |
| mirror_window->Show(); |
| mirror_window->layer()->SetShowReflectedSurface(reflecting_surface_id, |
| mirror_size); |
| } |
| |
| // Deleting WTHs for disconnected displays. |
| if (mirroring_host_info_map_.size() > display_info_list.size()) { |
| for (MirroringHostInfoMap::iterator iter = mirroring_host_info_map_.begin(); |
| iter != mirroring_host_info_map_.end();) { |
| if (std::find_if(display_info_list.begin(), display_info_list.end(), |
| [iter](const display::ManagedDisplayInfo& info) { |
| return info.id() == iter->first; |
| }) == display_info_list.end()) { |
| CloseAndDeleteHost(iter->second, true); |
| iter = mirroring_host_info_map_.erase(iter); |
| } else { |
| ++iter; |
| } |
| } |
| } |
| |
| } |
| |
| void MirrorWindowController::UpdateWindow() { |
| if (mirroring_host_info_map_.empty()) |
| return; |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| std::vector<display::ManagedDisplayInfo> display_info_list; |
| // Prune the window on the removed displays. |
| for (auto& pair : mirroring_host_info_map_) { |
| MirroringHostInfo* info = pair.second; |
| if (screen |
| ->GetDisplayNearestWindow( |
| info->ash_host->AsWindowTreeHost()->window()) |
| .is_valid()) { |
| display_info_list.push_back(display_manager->GetDisplayInfo(pair.first)); |
| } |
| } |
| UpdateWindow(display_info_list); |
| } |
| |
| void MirrorWindowController::CloseIfNotNecessary() { |
| display::DisplayManager::MultiDisplayMode new_mode = |
| GetCurrentMultiDisplayMode(); |
| int64_t new_reflecting_source_id = GetCurrentReflectingSourceId(); |
| if (multi_display_mode_ != new_mode || |
| reflecting_source_id_ != new_reflecting_source_id) { |
| Close(true); |
| } else { |
| UpdateWindow(); |
| } |
| } |
| |
| void MirrorWindowController::Close(bool delay_host_deletion) { |
| for (auto& info : mirroring_host_info_map_) |
| CloseAndDeleteHost(info.second, delay_host_deletion); |
| mirroring_host_info_map_.clear(); |
| } |
| |
| void MirrorWindowController::OnHostResized(aura::WindowTreeHost* host) { |
| for (auto& pair : mirroring_host_info_map_) { |
| MirroringHostInfo* info = pair.second; |
| if (info->ash_host->AsWindowTreeHost() == host) { |
| if (info->mirror_window_host_size == host->GetBoundsInPixels().size()) |
| return; |
| info->mirror_window_host_size = host->GetBoundsInPixels().size(); |
| // No need to update the transformer as new transformer is already set |
| // in UpdateWindow. |
| Shell::Get() |
| ->window_tree_host_manager() |
| ->cursor_window_controller() |
| ->UpdateLocation(); |
| return; |
| } |
| } |
| } |
| |
| display::Display MirrorWindowController::GetDisplayForRootWindow( |
| const aura::Window* root) const { |
| for (const auto& pair : mirroring_host_info_map_) { |
| if (pair.second->ash_host->AsWindowTreeHost()->window() == root) { |
| // Sanity check to catch an error early. |
| const int64_t id = pair.first; |
| const display::Display* display = GetDisplayById(id); |
| DCHECK(display); |
| if (display) |
| return *display; |
| } |
| } |
| return display::Display(); |
| } |
| |
| AshWindowTreeHost* MirrorWindowController::GetAshWindowTreeHostForDisplayId( |
| int64_t id) { |
| if (mirroring_host_info_map_.count(id) == 0) |
| return nullptr; |
| return mirroring_host_info_map_[id]->ash_host.get(); |
| } |
| |
| aura::Window::Windows MirrorWindowController::GetAllRootWindows() const { |
| aura::Window::Windows root_windows; |
| for (const auto& pair : mirroring_host_info_map_) |
| root_windows.push_back(pair.second->ash_host->AsWindowTreeHost()->window()); |
| return root_windows; |
| } |
| |
| const display::Display* MirrorWindowController::GetDisplayById( |
| int64_t display_id) const { |
| const display::Displays& list = |
| Shell::Get()->display_manager()->software_mirroring_display_list(); |
| for (const auto& display : list) { |
| if (display.id() == display_id) |
| return &display; |
| } |
| |
| return nullptr; |
| } |
| |
| void MirrorWindowController::SetCurrentEventTargeterSourceHost( |
| aura::WindowTreeHost* targeter_src_host) { |
| current_event_targeter_src_host_ = targeter_src_host; |
| } |
| |
| void MirrorWindowController::CloseAndDeleteHost(MirroringHostInfo* host_info, |
| bool delay_host_deletion) { |
| aura::WindowTreeHost* host = host_info->ash_host->AsWindowTreeHost(); |
| |
| aura::client::SetScreenPositionClient(host->window(), nullptr); |
| |
| NoneCaptureClient* capture_client = static_cast<NoneCaptureClient*>( |
| aura::client::GetCaptureClient(host->window())); |
| aura::client::SetCaptureClient(host->window(), nullptr); |
| delete capture_client; |
| |
| host->RemoveObserver(Shell::Get()->window_tree_host_manager()); |
| host->RemoveObserver(this); |
| host_info->ash_host->PrepareForShutdown(); |
| |
| // EventProcessor may be accessed after this call if the mirroring window |
| // was deleted as a result of input event (e.g. shortcut), so don't delete |
| // now. |
| if (delay_host_deletion) |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, host_info); |
| else |
| delete host_info; |
| } |
| |
| } // namespace ash |