// Copyright 2013 The Chromium Authors
// 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 "base/memory/raw_ptr.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/containers/contains.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.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/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:
raw_ptr<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;
raw_ptr<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 (!base::Contains(mirroring_host_info_map_, display_info.id())) {
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 (!base::Contains(display_info_list, iter->first,
&display::ManagedDisplayInfo::id)) {
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::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
host_info);
else
delete host_info;
}
} // namespace ash