blob: 91f2fc4d2e85d652474c584d158a49c01b76e84c [file] [log] [blame]
// Copyright 2017 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 "chromecast/graphics/cast_window_manager_aura.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "chromecast/base/cast_features.h"
#include "chromecast/chromecast_buildflags.h"
#include "chromecast/graphics/cast_focus_client_aura.h"
#include "chromecast/graphics/cast_touch_activity_observer.h"
#include "chromecast/graphics/cast_touch_event_gate.h"
#include "chromecast/graphics/cast_window_manager.h"
#include "chromecast/graphics/cast_window_tree_host_aura.h"
#include "chromecast/graphics/gestures/cast_system_gesture_event_handler.h"
#include "chromecast/graphics/gestures/side_swipe_detector.h"
#include "chromecast/graphics/rounded_window_corners.h"
#include "ui/aura/client/default_capture_client.h"
#include "ui/aura/client/focus_change_observer.h"
#include "ui/aura/env.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/window.h"
#include "ui/base/ime/init/input_method_factory.h"
#include "ui/base/ime/input_method.h"
#include "ui/display/display.h"
#include "ui/display/display_transform.h"
#include "ui/display/screen.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/platform_window/platform_window_init_properties.h"
#include "ui/wm/core/default_screen_position_client.h"
#if defined(OS_FUCHSIA)
#include "ui/platform_window/fuchsia/initialize_presenter_api_view.h"
#endif
namespace chromecast {
namespace {
gfx::Transform GetPrimaryDisplayRotationTransform() {
display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
return display::CreateRotationTransform(display.rotation(),
gfx::SizeF(display.size()));
}
gfx::Rect GetPrimaryDisplayHostBounds() {
display::Display display(display::Screen::GetScreen()->GetPrimaryDisplay());
gfx::Point display_origin_in_pixel = display.bounds().origin();
gfx::Size display_size_in_pixel = display.GetSizeInPixel();
switch (display.rotation()) {
case display::Display::ROTATE_90:
case display::Display::ROTATE_270:
return gfx::Rect(display_origin_in_pixel,
gfx::Size(display_size_in_pixel.height(),
display_size_in_pixel.width()));
case display::Display::ROTATE_0:
case display::Display::ROTATE_180:
// default:
return gfx::Rect(display_origin_in_pixel, display_size_in_pixel);
}
}
// A layout manager owned by the root window.
class CastLayoutManager : public aura::LayoutManager {
public:
CastLayoutManager(CastWindowManagerAura* window_manager,
aura::Window* parent);
~CastLayoutManager() override;
private:
// aura::LayoutManager implementation:
void OnWindowResized() override;
void OnWindowAddedToLayout(aura::Window* child) override;
void OnWillRemoveWindowFromLayout(aura::Window* child) override;
void OnWindowRemovedFromLayout(aura::Window* child) override;
void OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) override;
void SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) override;
// Reorder child windows of the root window tree to reflect changes in layout.
void ReorderChildWindows(aura::Window* changed_window);
CastWindowManagerAura* const window_manager_;
aura::Window* const parent_;
DISALLOW_COPY_AND_ASSIGN(CastLayoutManager);
};
CastLayoutManager::CastLayoutManager(CastWindowManagerAura* window_manager,
aura::Window* parent)
: window_manager_(window_manager), parent_(parent) {
DCHECK(window_manager_);
DCHECK(parent_);
}
CastLayoutManager::~CastLayoutManager() {}
void CastLayoutManager::OnWindowResized() {
// Invoked when the root window of the window tree host has been resized.
}
void CastLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
ReorderChildWindows(child);
}
void CastLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {}
void CastLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
ReorderChildWindows(child);
}
void CastLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) {
ReorderChildWindows(child);
}
void CastLayoutManager::ReorderChildWindows(aura::Window* changed_window) {
// Only update the window order if the child's parent matches the parent
// window for this layout. If the child has no parent, then that means it
// was removed from the layout and we should update the window ordering.
if (changed_window->parent() && changed_window->parent() != parent_) {
return;
}
// We set z-order here, because the window's ID could be set after the window
// is added to the layout, particularly when using views::Widget to create the
// window. The window ID must be set prior to showing the window. Since we
// only process z-order on visibility changes for top-level windows, this
// logic will execute infrequently.
// Determine z-order relative to existing windows.
aura::Window::Windows windows = parent_->children();
std::stable_sort(windows.begin(), windows.end(),
[changed_window](aura::Window* lhs, aura::Window* rhs) {
// Promote |changed_window| to the top of the stack of
// windows with the same ID.
if (lhs->id() == rhs->id() && rhs == changed_window)
return true;
return lhs->id() < rhs->id();
});
std::vector<CastWindowManager::WindowId> visible_window_order;
for (size_t i = 0; i < windows.size(); ++i) {
if (windows[i]->IsVisible()) {
// static_cast is safe since the window ID value is originally derived
// from CastWindowManager::WindowId.
visible_window_order.push_back(
static_cast<CastWindowManager::WindowId>(windows[i]->id()));
}
if (i == 0) {
parent_->StackChildAtBottom(windows[i]);
} else {
parent_->StackChildAbove(windows[i], windows[i - 1]);
}
}
window_manager_->OnWindowOrderChanged(visible_window_order);
}
void CastLayoutManager::SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) {
SetChildBoundsDirect(child, requested_bounds);
}
} // namespace
CastWindowManagerAura::CastWindowManagerAura(bool enable_input)
: enable_input_(enable_input) {}
CastWindowManagerAura::~CastWindowManagerAura() {
TearDown();
}
void CastWindowManagerAura::Setup() {
if (window_tree_host_) {
return;
}
DCHECK(display::Screen::GetScreen());
ui::InitializeInputMethodForTesting();
gfx::Rect host_bounds = GetPrimaryDisplayHostBounds();
ui::PlatformWindowInitProperties properties(host_bounds);
#if defined(OS_FUCHSIA)
// When using Scenic Ozone platform we need to supply a view_token to the
// window. This is not necessary when using the headless ozone platform.
if (ui::OzonePlatform::GetInstance()
->GetPlatformProperties()
.needs_view_token) {
ui::fuchsia::InitializeViewTokenAndPresentView(&properties);
}
#endif
LOG(INFO) << "Starting window manager, bounds: " << host_bounds.ToString();
CHECK(aura::Env::GetInstance());
window_tree_host_ = std::make_unique<CastWindowTreeHostAura>(
enable_input_, std::move(properties));
window_tree_host_->InitHost();
aura::Window* root_window = window_tree_host_->window();
root_window->SetLayoutManager(new CastLayoutManager(this, root_window));
window_tree_host_->SetRootTransform(GetPrimaryDisplayRotationTransform());
// Allow seeing through to the hardware video plane:
window_tree_host_->compositor()->SetBackgroundColor(SK_ColorTRANSPARENT);
focus_client_ = std::make_unique<CastFocusClientAura>();
aura::client::SetFocusClient(root_window, focus_client_.get());
wm::SetActivationClient(root_window, focus_client_.get());
aura::client::SetWindowParentingClient(root_window, this);
capture_client_.reset(new aura::client::DefaultCaptureClient(root_window));
screen_position_client_ =
std::make_unique<wm::DefaultScreenPositionClient>(root_window);
window_tree_host_->Show();
// Install the CastTouchEventGate before other event rewriters. It has to be
// the first in the chain.
event_gate_ = std::make_unique<CastTouchEventGate>(root_window);
system_gesture_dispatcher_ = std::make_unique<CastSystemGestureDispatcher>();
system_gesture_event_handler_ =
std::make_unique<CastSystemGestureEventHandler>(
system_gesture_dispatcher_.get(), root_window);
// TODO(rdaum): Remove side swipe detection when all services requiring it
// have been rewritten.
side_swipe_detector_ = std::make_unique<SideSwipeDetector>(
system_gesture_dispatcher_.get(), root_window);
// Add rounded corners, but defaulted to hidden until explicitly asked for by
// a component.
rounded_window_corners_ = RoundedWindowCorners::Create(this);
#if BUILDFLAG(IS_CAST_AUDIO_ONLY)
window_tree_host_->compositor()->SetDisplayVSyncParameters(
base::TimeTicks(), base::TimeDelta::FromMilliseconds(250));
#endif
}
bool CastWindowManagerAura::HasRoundedWindowCorners() const {
return rounded_window_corners_.get() != nullptr &&
rounded_window_corners_->IsEnabled();
}
void CastWindowManagerAura::OnWindowOrderChanged(
std::vector<WindowId> window_order) {
window_order_.swap(window_order);
for (auto& observer : observer_list_) {
observer.WindowOrderChanged();
}
}
CastWindowTreeHostAura* CastWindowManagerAura::window_tree_host() const {
DCHECK(window_tree_host_);
return window_tree_host_.get();
}
void CastWindowManagerAura::TearDown() {
if (!window_tree_host_) {
return;
}
event_gate_.reset();
side_swipe_detector_.reset();
capture_client_.reset();
aura::client::SetWindowParentingClient(window_tree_host_->window(), nullptr);
wm::SetActivationClient(window_tree_host_->window(), nullptr);
screen_position_client_.reset();
aura::client::SetFocusClient(window_tree_host_->window(), nullptr);
focus_client_.reset();
system_gesture_event_handler_.reset();
window_tree_host_.reset();
}
void CastWindowManagerAura::SetZOrder(gfx::NativeView window,
mojom::ZOrder z_order) {
// Use aura::Window ID to maintain z-order. When the window's visibility
// changes, we stack sibling windows based on this ID. Windows with higher
// IDs are stacked on top.
window->set_id(static_cast<int>(z_order));
}
void CastWindowManagerAura::InjectEvent(ui::Event* event) {
if (!window_tree_host_) {
return;
}
window_tree_host_->DispatchEvent(event);
}
void CastWindowManagerAura::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void CastWindowManagerAura::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
gfx::NativeView CastWindowManagerAura::GetRootWindow() {
Setup();
return window_tree_host_->window();
}
std::vector<CastWindowManager::WindowId>
CastWindowManagerAura::GetWindowOrder() {
return window_order_;
}
aura::Window* CastWindowManagerAura::GetDefaultParent(aura::Window* window,
const gfx::Rect& bounds) {
DCHECK(window_tree_host_);
return window_tree_host_->window();
}
void CastWindowManagerAura::AddWindow(gfx::NativeView child) {
LOG(INFO) << "Adding window: " << child->id() << ": " << child->GetName();
Setup();
DCHECK(child);
aura::Window* parent = window_tree_host_->window();
if (!parent->Contains(child)) {
parent->AddChild(child);
}
}
void CastWindowManagerAura::AddGestureHandler(CastGestureHandler* handler) {
DCHECK(system_gesture_event_handler_);
system_gesture_dispatcher_->AddGestureHandler(handler);
}
void CastWindowManagerAura::CastWindowManagerAura::RemoveGestureHandler(
CastGestureHandler* handler) {
DCHECK(system_gesture_event_handler_);
system_gesture_dispatcher_->RemoveGestureHandler(handler);
}
CastGestureHandler* CastWindowManagerAura::GetGestureHandler() const {
return system_gesture_dispatcher_.get();
}
void CastWindowManagerAura::SetTouchInputDisabled(bool disabled) {
event_gate_->SetEnabled(disabled);
}
void CastWindowManagerAura::AddTouchActivityObserver(
CastTouchActivityObserver* observer) {
event_gate_->AddObserver(observer);
}
void CastWindowManagerAura::RemoveTouchActivityObserver(
CastTouchActivityObserver* observer) {
event_gate_->RemoveObserver(observer);
}
void CastWindowManagerAura::SetEnableRoundedCorners(bool enable) {
DCHECK(rounded_window_corners_);
rounded_window_corners_->SetEnabled(enable);
}
void CastWindowManagerAura::NotifyColorInversionEnabled(bool enabled) {
rounded_window_corners_->SetColorInversion(enabled);
}
} // namespace chromecast