blob: fca496d01aa48e592b255ddc5e7c764892f578f7 [file] [log] [blame]
// 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/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 "ash/window_factory.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/env.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/base/ui_base_switches_util.h"
#include "ui/compositor/reflector.h"
#include "ui/display/display_layout.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) {}
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, &not_used);
aura::client::ScreenPositionClient::ConvertPointToScreen(root_window,
point);
}
void SetBounds(aura::Window* window,
const gfx::Rect& bounds,
const display::Display& display) override {
NOTREACHED();
}
private:
MirrorWindowController* controller_; // not owned.
DISALLOW_COPY_AND_ASSIGN(MirroringScreenPositionClient);
};
// 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() 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 {}
DISALLOW_COPY_AND_ASSIGN(NoneCaptureClient);
};
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;
}
ui::ContextFactoryPrivate* GetContextFactoryPrivate() {
return Shell::Get()->aura_env()->context_factory_private();
}
} // 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();
for (const display::ManagedDisplayInfo& display_info : display_info_list) {
std::unique_ptr<RootWindowTransformer> transformer;
if (display_manager->IsInSoftwareMirrorMode()) {
transformer.reset(CreateRootWindowTransformerForMirroredDisplay(
display_manager->GetDisplayInfo(reflecting_source_id_),
display_info));
} else {
DCHECK(display_manager->IsInUnifiedMode());
display::Display display =
display_manager->GetMirroringDisplayById(display_info.id());
transformer.reset(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.mirroring_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 =
window_factory::NewWindow().release();
mirror_window->Init(ui::LAYER_SOLID_COLOR);
host->window()->AddChild(mirror_window);
host_info->ash_host->SetRootWindowTransformer(std::move(transformer));
mirror_window->SetBounds(host->window()->bounds());
mirror_window->Show();
// The accelerated widget is created synchronously.
DCHECK_NE(gfx::kNullAcceleratedWidget, host->GetAcceleratedWidget());
if (reflector_) {
reflector_->AddMirroringLayer(mirror_window->layer());
} else if (GetContextFactoryPrivate()) {
reflector_ = GetContextFactoryPrivate()->CreateReflector(
Shell::GetRootWindowForDisplayId(reflecting_source_id_)
->GetHost()
->compositor(),
mirror_window->layer());
}
} 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());
}
}
// 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;
}
}
}
if (mirroring_host_info_map_.empty() && reflector_) {
// Close the mirror window if all displays are disconnected.
GetContextFactoryPrivate()->RemoveReflector(reflector_.get());
reflector_.reset();
}
}
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) {
if (reflector_) {
GetContextFactoryPrivate()->RemoveReflector(reflector_.get());
reflector_.reset();
}
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();
if (reflector_)
reflector_->OnMirroringCompositorResized();
// 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 = GetMirroringDisplayById(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::GetMirroringDisplayById(
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();
// |reflector_| may be null during display disconnect or shutdown.
if (reflector_ && host_info->mirror_window->layer()->GetCompositor())
reflector_->RemoveMirroringLayer(host_info->mirror_window->layer());
// 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