1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/rounded_display/rounded_display_provider.h"
12 #include "ash/display/window_tree_host_manager.h"
13 #include "ash/rounded_display/rounded_display_gutter.h"
14 #include "ash/rounded_display/rounded_display_gutter_factory.h"
15 #include "ash/rounded_display/rounded_display_host.h"
16 #include "ash/screen_util.h"
17 #include "ash/shell.h"
18 #include "base/check.h"
19 #include "base/functional/bind.h"
20 #include "base/notreached.h"
21 #include "ui/display/display.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/geometry/rounded_corners_f.h"
24 #include "ui/gfx/geometry/size.h"
29 using Gutters = std::vector<RoundedDisplayGutter*>;
31 bool IsRadiiHorizontallyUniform(const gfx::RoundedCornersF& radii) {
32 return radii.upper_left() == radii.upper_right() &&
33 radii.lower_left() == radii.lower_right();
36 bool IsRadiiVerticallyUniform(const gfx::RoundedCornersF& radii) {
37 return radii.upper_left() == radii.lower_left() &&
38 radii.upper_right() == radii.lower_right();
41 bool IsRadiiValid(const gfx::RoundedCornersF radii) {
42 return !radii.IsEmpty() &&
43 (IsRadiiHorizontallyUniform(radii) || IsRadiiVerticallyUniform(radii));
46 // Returns the display's panel size in pixels.
47 gfx::Size GetPanelSizeInPixels(const display::Display& display) {
48 gfx::Size display_size_in_pixels = display.GetSizeInPixel();
50 if (display.panel_rotation() == display::Display::ROTATE_90 ||
51 display.panel_rotation() == display::Display::ROTATE_270) {
52 display_size_in_pixels.Transpose();
55 return display_size_in_pixels;
58 const display::Display& GetDisplay(int64_t display_id) {
59 return Shell::Get()->display_manager()->GetDisplayForId(display_id);
62 aura::Window* GetRootWindow(int64_t display_id) {
63 return Shell::GetRootWindowForDisplayId(display_id);
69 std::unique_ptr<RoundedDisplayProvider> RoundedDisplayProvider::Create(
71 auto gutter_factory = std::make_unique<RoundedDisplayGutterFactory>();
73 return std::make_unique<RoundedDisplayProvider>(display_id,
74 std::move(gutter_factory));
77 RoundedDisplayProvider::RoundedDisplayProvider(
79 std::unique_ptr<RoundedDisplayGutterFactory> gutter_factory)
80 : display_id_(display_id), gutter_factory_(std::move(gutter_factory)) {}
82 RoundedDisplayProvider::~RoundedDisplayProvider() {
84 aura::Window* root_window = GetRootWindow(display_id_);
85 DCHECK(root_window) << "The provider needs to be destroyed first before "
86 "the root window is destroyed";
88 // `host_window_` needs to outlive the `host_`.
89 DCHECK(root_window->Contains(host_window_.get()));
90 root_window->RemoveChild(host_window_.get());
94 void RoundedDisplayProvider::Init(const gfx::RoundedCornersF& panel_radii,
97 NOTREACHED() << "Provider is already initialized";
100 DCHECK(IsRadiiValid(panel_radii));
102 current_panel_radii_ = panel_radii;
103 strategy_ = strategy;
105 const display::Display& display = GetDisplay(display_id_);
106 CreateGutters(display, panel_radii);
111 void RoundedDisplayProvider::InitializeHost() {
112 host_ = std::make_unique<RoundedDisplayHost>(
113 base::BindRepeating(&RoundedDisplayProvider::GetGuttersInDrawOrder,
114 weak_ptr_factory_.GetWeakPtr()));
116 // TODO(zoraiznaeem): Change the default color to transparent when we fail to
118 host_window_ = std::make_unique<aura::Window>(/*delegate=*/nullptr);
119 host_window_->set_owned_by_parent(false);
120 host_window_->Init(ui::LAYER_SOLID_COLOR);
121 host_window_->SetName("RoundedDisplayHost");
122 host_window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
123 host_window_->SetTransparent(true);
124 host_window_->Show();
126 aura::Window* root_window = GetRootWindow(display_id_);
127 root_window->AddChild(host_window_.get());
129 host_->Init(host_window_.get());
132 void RoundedDisplayProvider::UpdateHostParent() {
133 DCHECK(host_) << "Call Init() before calling UpdateHostParent";
135 aura::Window* new_display_root = GetRootWindow(display_id_);
136 aura::Window* current_display_root = host_window_->GetRootWindow();
138 if (new_display_root == current_display_root) {
142 current_display_root->RemoveChild(host_window_.get());
143 new_display_root->AddChild(host_window_.get());
146 bool RoundedDisplayProvider::UpdateRoundedDisplaySurface() {
147 DCHECK(host_) << "Call Init() before calling UpdateRoundedDisplay";
149 const display::Display& display = GetDisplay(display_id_);
151 if (!ShouldSubmitNewCompositorFrame(display)) {
155 // We need to adjust the bounds of host_window to account for the 1px offset
156 // introduced for certain device scale factor values due to conversion between
157 // dip and pixel values.
158 host_window_->SetBounds(screen_util::SnapBoundsToDisplayEdge(
159 gfx::Rect(display.bounds().size()), GetRootWindow(display_id_)));
161 gfx::Rect content_rect(host_window_->bounds());
162 gfx::Rect damage_rect;
164 // Submit a compositor frame to update the surface. Textures will be reused,
165 // unless we decide to create new gutters, otherwise the positions of the
166 // existing textures will be updated.
167 host_->UpdateSurface(content_rect, damage_rect, /*synchronous_draw=*/true);
169 current_device_scale_factor_ = display.device_scale_factor();
170 current_logical_rotation_ = display.rotation();
175 bool RoundedDisplayProvider::ShouldSubmitNewCompositorFrame(
176 const display::Display& display) const {
177 return display.device_scale_factor() != current_device_scale_factor_ ||
178 display.rotation() != current_logical_rotation_;
181 void RoundedDisplayProvider::GetGuttersInDrawOrder(Gutters& gutters) const {
182 for (const auto& gutter : overlay_gutters_) {
183 gutters.push_back(gutter.get());
187 bool RoundedDisplayProvider::CreateGutters(
188 const display::Display& display,
189 const gfx::RoundedCornersF& panel_radii) {
190 gfx::Size panel_size = GetPanelSizeInPixels(display);
192 // Scanout direction is left to right wrt to the panel. Therefore horizontal
193 // gutters are in the direction of scanout and vertical gutters are in the
195 bool create_vertical_gutters = strategy_ != Strategy::kScanout;
197 overlay_gutters_ = gutter_factory_->CreateOverlayGutters(
198 panel_size, panel_radii, create_vertical_gutters);