1 // Copyright 2020 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/ui/display_settings_manager_impl.h"
6 
7 #include "base/logging.h"
8 #include "chromecast/graphics/cast_window_manager.h"
9 #include "chromecast/ui/display_settings/brightness_animation.h"
10 #include "chromecast/ui/display_settings/color_temperature_animation.h"
11 #include "ui/display/types/gamma_ramp_rgb_entry.h"
12 
13 #if defined(USE_AURA)
14 #include "chromecast/browser/cast_browser_process.h"
15 #include "chromecast/browser/cast_display_configurator.h"
16 #include "chromecast/ui/display_settings/gamma_configurator.h"
17 #endif  // defined(USE_AURA)
18 
19 namespace chromecast {
20 
21 namespace {
22 
23 constexpr base::TimeDelta kAnimationDuration = base::TimeDelta::FromSeconds(2);
24 constexpr base::TimeDelta kScreenOnOffDuration =
25     base::TimeDelta::FromMilliseconds(200);
26 
27 #if defined(USE_AURA)
28 // These delays are needed to ensure there are no visible artifacts due to the
29 // backlight turning on prior to the LCD fully initializing or vice-versa.
30 // TODO(b/161140301): Make this configurable for different products
31 // TODO(b/161268188): Remove these if the delays can be handled by the kernel
32 constexpr base::TimeDelta kDisplayPowerOnDelay =
33     base::TimeDelta::FromMilliseconds(35);
34 constexpr base::TimeDelta kDisplayPowerOffDelay =
35     base::TimeDelta::FromMilliseconds(85);
36 #endif  // defined(USE_AURA)
37 
38 const float kMinApiBrightness = 0.0f;
39 const float kMaxApiBrightness = 1.0f;
40 const float kDefaultApiBrightness = kMaxApiBrightness;
41 
42 }  // namespace
43 
DisplaySettingsManagerImpl(CastWindowManager * window_manager,const DisplaySettingsManager::ColorTemperatureConfig & color_temperature_config)44 DisplaySettingsManagerImpl::DisplaySettingsManagerImpl(
45     CastWindowManager* window_manager,
46     const DisplaySettingsManager::ColorTemperatureConfig&
47         color_temperature_config)
48     : window_manager_(window_manager),
49 #if defined(USE_AURA)
50       display_configurator_(
51           shell::CastBrowserProcess::GetInstance()->display_configurator()),
52       gamma_configurator_(
53           std::make_unique<GammaConfigurator>(window_manager_,
54                                               display_configurator_)),
55 #else
56       display_configurator_(nullptr),
57 #endif  // defined(USE_AURA)
58       brightness_(-1.0f),
59       screen_on_(true),
60 #if defined(USE_AURA)
61       screen_power_on_(true),
62       allow_screen_power_off_(false),
63 #endif  // defined(USE_AURA)
64       color_temperature_animation_(std::make_unique<ColorTemperatureAnimation>(
65           window_manager_,
66           display_configurator_,
67           color_temperature_config)),
68       weak_factory_(this) {
69   DCHECK(window_manager_);
70 #if defined(USE_AURA)
71   DCHECK(display_configurator_);
72 #endif  // defined(USE_AURA)
73 }
74 
75 DisplaySettingsManagerImpl::~DisplaySettingsManagerImpl() = default;
76 
SetDelegate(DisplaySettingsManager::Delegate * delegate)77 void DisplaySettingsManagerImpl::SetDelegate(
78     DisplaySettingsManager::Delegate* delegate) {
79   brightness_animation_ = std::make_unique<BrightnessAnimation>(delegate);
80 }
81 
ResetDelegate()82 void DisplaySettingsManagerImpl::ResetDelegate() {
83   // Skip a brightess animation to its last step and stop
84   // This is important for the final brightness to be cached on reboot
85   brightness_animation_.reset();
86 }
87 
SetGammaCalibration(const std::vector<display::GammaRampRGBEntry> & gamma)88 void DisplaySettingsManagerImpl::SetGammaCalibration(
89     const std::vector<display::GammaRampRGBEntry>& gamma) {
90 #if defined(USE_AURA)
91   gamma_configurator_->OnCalibratedGammaLoaded(gamma);
92 #endif  // defined(USE_AURA)
93 }
94 
NotifyBrightnessChanged(float new_brightness,float old_brightness)95 void DisplaySettingsManagerImpl::NotifyBrightnessChanged(float new_brightness,
96                                                          float old_brightness) {
97   for (auto& observer : observers_)
98     observer->OnDisplayBrightnessChanged(new_brightness);
99 }
100 
SetColorInversion(bool enable)101 void DisplaySettingsManagerImpl::SetColorInversion(bool enable) {
102 #if defined(USE_AURA)
103   gamma_configurator_->SetColorInversion(enable);
104 #endif  // defined(USE_AURA)
105   window_manager_->NotifyColorInversionEnabled(enable);
106 }
107 
AddReceiver(mojo::PendingReceiver<mojom::DisplaySettings> receiver)108 void DisplaySettingsManagerImpl::AddReceiver(
109     mojo::PendingReceiver<mojom::DisplaySettings> receiver) {
110   receivers_.Add(this, std::move(receiver));
111 }
112 
SetColorTemperature(float temperature)113 void DisplaySettingsManagerImpl::SetColorTemperature(float temperature) {
114   DVLOG(4) << "Setting color temperature to " << temperature << " Kelvin.";
115   color_temperature_animation_->AnimateToNewValue(temperature,
116                                                   kAnimationDuration);
117 }
118 
SetColorTemperatureSmooth(float temperature,base::TimeDelta duration)119 void DisplaySettingsManagerImpl::SetColorTemperatureSmooth(
120     float temperature,
121     base::TimeDelta duration) {
122   DVLOG(4) << "Setting color temperature to " << temperature
123            << " Kelvin. Duration: " << duration;
124   color_temperature_animation_->AnimateToNewValue(temperature, duration);
125 }
126 
ResetColorTemperature()127 void DisplaySettingsManagerImpl::ResetColorTemperature() {
128   color_temperature_animation_->AnimateToNeutral(kAnimationDuration);
129 }
130 
SetBrightness(float brightness)131 void DisplaySettingsManagerImpl::SetBrightness(float brightness) {
132   SetBrightnessSmooth(brightness, base::TimeDelta::FromSeconds(0));
133 }
134 
SetBrightnessSmooth(float brightness,base::TimeDelta duration)135 void DisplaySettingsManagerImpl::SetBrightnessSmooth(float brightness,
136                                                      base::TimeDelta duration) {
137   if (brightness < kMinApiBrightness) {
138     LOG(ERROR) << "brightness " << brightness
139                << " is less than minimum brightness " << kMinApiBrightness
140                << ".";
141     return;
142   }
143 
144   if (brightness > kMaxApiBrightness) {
145     LOG(ERROR) << "brightness " << brightness
146                << " is greater than maximum brightness " << kMaxApiBrightness
147                << ".";
148     return;
149   }
150 
151   brightness_ = brightness;
152 
153   // If the screen is off, keep the new brightness but don't apply it
154   if (!screen_on_) {
155     return;
156   }
157 
158   UpdateBrightness(duration);
159 }
160 
ResetBrightness()161 void DisplaySettingsManagerImpl::ResetBrightness() {
162   SetBrightness(kDefaultApiBrightness);
163 }
164 
165 #if defined(USE_AURA)
OnDisplayOn(const base::flat_map<int64_t,bool> & statuses)166 void DisplaySettingsManagerImpl::OnDisplayOn(
167     const base::flat_map<int64_t, bool>& statuses) {
168   for (const auto& status : statuses) {
169     bool display_success = status.second;
170     if (!display_success) {
171       // Fatal since the user has no other way of turning the screen on if this
172       // failed.
173       LOG(FATAL) << "Failed to enable screen";
174       return;
175     }
176   }
177   screen_power_on_ = true;
178   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
179       FROM_HERE,
180       base::BindOnce(&DisplaySettingsManagerImpl::OnDisplayOnTimeoutCompleted,
181                      weak_factory_.GetWeakPtr()),
182       kDisplayPowerOnDelay);
183 }
184 
OnDisplayOnTimeoutCompleted()185 void DisplaySettingsManagerImpl::OnDisplayOnTimeoutCompleted() {
186   UpdateBrightness(kScreenOnOffDuration);
187   window_manager_->SetTouchInputDisabled(false /* since screen_on = true */);
188 }
189 
OnDisplayOffTimeoutCompleted()190 void DisplaySettingsManagerImpl::OnDisplayOffTimeoutCompleted() {
191   display_configurator_->DisableDisplay(
192       base::BindOnce([](const base::flat_map<int64_t, bool>& statuses) {
193         for (const auto& status : statuses) {
194           bool display_success = status.second;
195           LOG_IF(FATAL, !display_success) << "Failed to disable display";
196         }
197       }));
198   screen_power_on_ = false;
199 }
200 
SetScreenOn(bool screen_on)201 void DisplaySettingsManagerImpl::SetScreenOn(bool screen_on) {
202   // Allow this to run if screen_on == screen_on_ == false IF
203   // previously, the screen was turned off without powering off the screen
204   // and we want to power it off this time
205   if (screen_on == screen_on_ &&
206       !(!screen_on && allow_screen_power_off_ && screen_power_on_)) {
207     return;
208   }
209 
210   LOG(INFO) << "Setting screen on to " << screen_on;
211   screen_on_ = screen_on;
212 
213   // TODO(b/161268188): This can be simplified and the delays removed
214   // if backlight timing is handled by the kernel
215   if (screen_on && !screen_power_on_) {
216     display_configurator_->EnableDisplay(base::BindOnce(
217         &DisplaySettingsManagerImpl::OnDisplayOn, weak_factory_.GetWeakPtr()));
218   } else {
219     UpdateBrightness(kScreenOnOffDuration);
220     window_manager_->SetTouchInputDisabled(!screen_on_);
221     if (!screen_on && allow_screen_power_off_) {
222       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
223           FROM_HERE,
224           base::BindOnce(
225               &DisplaySettingsManagerImpl::OnDisplayOffTimeoutCompleted,
226               weak_factory_.GetWeakPtr()),
227           kDisplayPowerOffDelay + kScreenOnOffDuration);
228     }
229   }
230 }
231 
SetAllowScreenPowerOff(bool allow_power_off)232 void DisplaySettingsManagerImpl::SetAllowScreenPowerOff(bool allow_power_off) {
233   allow_screen_power_off_ = allow_power_off;
234 }
235 #else
SetScreenOn(bool screen_on)236 void DisplaySettingsManagerImpl::SetScreenOn(bool screen_on) {
237   if (screen_on == screen_on_) {
238     return;
239   }
240 
241   LOG(INFO) << "Setting screen on to " << screen_on;
242   screen_on_ = screen_on;
243 
244   UpdateBrightness(kScreenOnOffDuration);
245   window_manager_->SetTouchInputDisabled(!screen_on_);
246 }
SetAllowScreenPowerOff(bool allow_power_off)247 void DisplaySettingsManagerImpl::SetAllowScreenPowerOff(bool allow_power_off) {}
248 #endif
249 
AddDisplaySettingsObserver(mojo::PendingRemote<mojom::DisplaySettingsObserver> observer)250 void DisplaySettingsManagerImpl::AddDisplaySettingsObserver(
251     mojo::PendingRemote<mojom::DisplaySettingsObserver> observer) {
252   mojo::Remote<mojom::DisplaySettingsObserver> observer_remote(
253       std::move(observer));
254   observers_.Add(std::move(observer_remote));
255 }
256 
UpdateBrightness(base::TimeDelta duration)257 void DisplaySettingsManagerImpl::UpdateBrightness(base::TimeDelta duration) {
258   float brightness = screen_on_ ? brightness_ : 0;
259 
260   if (brightness_animation_)
261     brightness_animation_->AnimateToNewValue(brightness, duration);
262 }
263 
264 }  // namespace chromecast
265