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