1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromecast/browser/cast_display_configurator.h"
6
7 #include <math.h>
8 #include <algorithm>
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "chromecast/base/cast_features.h"
16 #include "chromecast/browser/cast_touch_device_manager.h"
17 #include "chromecast/chromecast_buildflags.h"
18 #include "chromecast/graphics/cast_display_util.h"
19 #include "chromecast/graphics/cast_screen.h"
20 #include "chromecast/public/graphics_properties_shlib.h"
21 #include "ui/display/types/display_snapshot.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/ozone/public/ozone_platform.h"
24
25 namespace chromecast {
26 namespace shell {
27
28 namespace {
29 constexpr int64_t kStubDisplayId = 1;
30 constexpr char kCastGraphicsHeight[] = "cast-graphics-height";
31 constexpr char kCastGraphicsWidth[] = "cast-graphics-width";
32
GetDefaultScreenResolution()33 gfx::Size GetDefaultScreenResolution() {
34 #if BUILDFLAG(IS_CAST_AUDIO_ONLY)
35 return gfx::Size(1, 1);
36 #else
37 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
38 if (!chromecast::IsFeatureEnabled(kTripleBuffer720) &&
39 GraphicsPropertiesShlib::IsSupported(GraphicsPropertiesShlib::k1080p,
40 cmd_line->argv())) {
41 return gfx::Size(1920, 1080);
42 }
43
44 return gfx::Size(1280, 720);
45 #endif
46 }
47
48 // Helper to return the screen resolution (device pixels)
49 // to use.
GetScreenResolution()50 gfx::Size GetScreenResolution() {
51 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
52 int cast_gfx_width = 0;
53 int cast_gfx_height = 0;
54 if (base::StringToInt(cmd_line->GetSwitchValueASCII(kCastGraphicsWidth),
55 &cast_gfx_width) &&
56 base::StringToInt(cmd_line->GetSwitchValueASCII(kCastGraphicsHeight),
57 &cast_gfx_height) &&
58 cast_gfx_width > 0 && cast_gfx_height > 0) {
59 return gfx::Size(cast_gfx_width, cast_gfx_height);
60 }
61
62 return GetDefaultScreenResolution();
63 }
64
RotationFromPanelOrientation(display::PanelOrientation orientation)65 display::Display::Rotation RotationFromPanelOrientation(
66 display::PanelOrientation orientation) {
67 switch (orientation) {
68 case display::kNormal:
69 return display::Display::ROTATE_0;
70 case display::kRightUp:
71 return display::Display::ROTATE_90;
72 case display::kBottomUp:
73 return display::Display::ROTATE_180;
74 case display::kLeftUp:
75 return display::Display::ROTATE_270;
76 }
77 }
78
GetScreenBounds(const gfx::Size & size_in_pixels,display::Display::Rotation rotation)79 gfx::Rect GetScreenBounds(const gfx::Size& size_in_pixels,
80 display::Display::Rotation rotation) {
81 switch (rotation) {
82 case display::Display::ROTATE_90:
83 case display::Display::ROTATE_270:
84 return gfx::Rect(
85 gfx::Size(size_in_pixels.height(), size_in_pixels.width()));
86 case display::Display::ROTATE_0:
87 case display::Display::ROTATE_180:
88 default:
89 return gfx::Rect(size_in_pixels);
90 }
91 }
92
93 } // namespace
94
CastDisplayConfigurator(CastScreen * screen)95 CastDisplayConfigurator::CastDisplayConfigurator(CastScreen* screen)
96 : delegate_(
97 #if defined(USE_OZONE) && !BUILDFLAG(IS_CAST_AUDIO_ONLY)
98 ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate()
99 #else
100 nullptr
101 #endif
102 ),
103 touch_device_manager_(std::make_unique<CastTouchDeviceManager>()),
104 display_(nullptr),
105 cast_screen_(screen),
106 weak_factory_(this) {
107 if (delegate_) {
108 delegate_->AddObserver(this);
109 delegate_->Initialize();
110 ForceInitialConfigure();
111 } else {
112 ConfigureDisplayFromCommandLine();
113 }
114 }
115
~CastDisplayConfigurator()116 CastDisplayConfigurator::~CastDisplayConfigurator() {
117 if (delegate_)
118 delegate_->RemoveObserver(this);
119 }
120
121 // display::NativeDisplayObserver interface
OnConfigurationChanged()122 void CastDisplayConfigurator::OnConfigurationChanged() {
123 DCHECK(delegate_);
124 delegate_->GetDisplays(base::BindOnce(
125 &CastDisplayConfigurator::OnDisplaysAcquired, weak_factory_.GetWeakPtr(),
126 false /* force_initial_configure */));
127 }
128
OnDisplaySnapshotsInvalidated()129 void CastDisplayConfigurator::OnDisplaySnapshotsInvalidated() {
130 display_ = nullptr;
131 }
132
EnableDisplay(display::ConfigureCallback callback)133 void CastDisplayConfigurator::EnableDisplay(
134 display::ConfigureCallback callback) {
135 if (!delegate_ || !display_)
136 return;
137
138 display::DisplayConfigurationParams display_config_params(
139 display_->display_id(), gfx::Point(), display_->native_mode());
140 std::vector<display::DisplayConfigurationParams> config_request;
141 config_request.push_back(std::move(display_config_params));
142
143 delegate_->Configure(config_request, std::move(callback));
144 }
145
DisableDisplay(display::ConfigureCallback callback)146 void CastDisplayConfigurator::DisableDisplay(
147 display::ConfigureCallback callback) {
148 if (!delegate_ || !display_)
149 return;
150
151 display::DisplayConfigurationParams display_config_params(
152 display_->display_id(), gfx::Point(), nullptr);
153 std::vector<display::DisplayConfigurationParams> config_request;
154 config_request.push_back(std::move(display_config_params));
155
156 delegate_->Configure(config_request, std::move(callback));
157 }
158
ConfigureDisplayFromCommandLine()159 void CastDisplayConfigurator::ConfigureDisplayFromCommandLine() {
160 const gfx::Size size = GetScreenResolution();
161 UpdateScreen(kStubDisplayId, gfx::Rect(size), GetDeviceScaleFactor(size),
162 display::Display::ROTATE_0);
163 }
164
SetColorMatrix(const std::vector<float> & color_matrix)165 void CastDisplayConfigurator::SetColorMatrix(
166 const std::vector<float>& color_matrix) {
167 if (!delegate_ || !display_)
168 return;
169 delegate_->SetColorMatrix(display_->display_id(), color_matrix);
170 }
171
SetGammaCorrection(const std::vector<display::GammaRampRGBEntry> & degamma_lut,const std::vector<display::GammaRampRGBEntry> & gamma_lut)172 void CastDisplayConfigurator::SetGammaCorrection(
173 const std::vector<display::GammaRampRGBEntry>& degamma_lut,
174 const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
175 if (!delegate_ || !display_)
176 return;
177
178 delegate_->SetGammaCorrection(display_->display_id(), degamma_lut, gamma_lut);
179 }
180
ForceInitialConfigure()181 void CastDisplayConfigurator::ForceInitialConfigure() {
182 if (!delegate_)
183 return;
184 delegate_->GetDisplays(base::BindOnce(
185 &CastDisplayConfigurator::OnDisplaysAcquired, weak_factory_.GetWeakPtr(),
186 true /* force_initial_configure */));
187 }
188
OnDisplaysAcquired(bool force_initial_configure,const std::vector<display::DisplaySnapshot * > & displays)189 void CastDisplayConfigurator::OnDisplaysAcquired(
190 bool force_initial_configure,
191 const std::vector<display::DisplaySnapshot*>& displays) {
192 DCHECK(delegate_);
193 if (displays.empty()) {
194 LOG(WARNING) << "No displays detected, skipping display init.";
195 return;
196 }
197
198 if (displays.size() > 1) {
199 LOG(WARNING) << "Multiple display detected, using the first one.";
200 }
201
202 display_ = displays[0];
203 if (!display_->native_mode()) {
204 LOG(WARNING) << "Display " << display_->display_id()
205 << " doesn't have a native mode.";
206 return;
207 }
208
209 gfx::Point origin;
210 gfx::Size native_size(display_->native_mode()->size());
211 if (force_initial_configure) {
212 // For initial configuration, pass the native geometry to gfx::Screen
213 // before calling Configure(), so that this information is available
214 // to chrome during startup. Otherwise we will not have a valid display
215 // during the first queries to display::Screen.
216 UpdateScreen(display_->display_id(), gfx::Rect(origin, native_size),
217 GetDeviceScaleFactor(native_size),
218 RotationFromPanelOrientation(display_->panel_orientation()));
219 }
220
221 display::DisplayConfigurationParams display_config_params(
222 display_->display_id(), origin, display_->native_mode());
223 std::vector<display::DisplayConfigurationParams> config_request;
224 config_request.push_back(std::move(display_config_params));
225
226 delegate_->Configure(
227 config_request,
228 base::BindRepeating(&CastDisplayConfigurator::OnDisplayConfigured,
229 weak_factory_.GetWeakPtr(), display_,
230 display_->native_mode(), origin));
231 }
232
OnDisplayConfigured(display::DisplaySnapshot * display,const display::DisplayMode * mode,const gfx::Point & origin,const base::flat_map<int64_t,bool> & statuses)233 void CastDisplayConfigurator::OnDisplayConfigured(
234 display::DisplaySnapshot* display,
235 const display::DisplayMode* mode,
236 const gfx::Point& origin,
237 const base::flat_map<int64_t, bool>& statuses) {
238 DCHECK(display);
239 DCHECK(mode);
240 DCHECK_EQ(display, display_);
241 DCHECK_EQ(statuses.size(), 1UL);
242
243 const gfx::Rect bounds(origin, mode->size());
244 bool success = statuses.at(display_->display_id());
245 DVLOG(1) << __func__ << " success=" << success
246 << " bounds=" << bounds.ToString();
247 if (success) {
248 // Need to update the display state otherwise it becomes stale.
249 display_->set_current_mode(mode);
250 display_->set_origin(origin);
251
252 UpdateScreen(display_->display_id(), bounds,
253 GetDeviceScaleFactor(display->native_mode()->size()),
254 RotationFromPanelOrientation(display_->panel_orientation()));
255 } else {
256 LOG(FATAL) << "Failed to configure display";
257 }
258 }
259
UpdateScreen(int64_t display_id,const gfx::Rect & bounds,float device_scale_factor,display::Display::Rotation rotation)260 void CastDisplayConfigurator::UpdateScreen(
261 int64_t display_id,
262 const gfx::Rect& bounds,
263 float device_scale_factor,
264 display::Display::Rotation rotation) {
265 cast_screen_->OnDisplayChanged(display_id, device_scale_factor, rotation,
266 GetScreenBounds(bounds.size(), rotation));
267 touch_device_manager_->OnDisplayConfigured(display_id, rotation, bounds);
268 }
269
270 } // namespace shell
271 } // namespace chromecast
272