1 // Copyright (c) 2012 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.
5 #include "chrome/browser/chromeos/display/display_preferences.h"
7 #include "ash/display/display_layout_store.h"
8 #include "ash/display/display_manager.h"
9 #include "ash/display/display_pref_util.h"
10 #include "ash/display/display_util.h"
11 #include "ash/shell.h"
12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/sys_info.h"
20 #include "base/values.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/common/pref_names.h"
23 #include "components/user_manager/user_manager.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
25 #include "ui/gfx/display.h"
26 #include "ui/gfx/geometry/insets.h"
27 #include "ui/gfx/screen.h"
28 #include "url/url_canon.h"
29 #include "url/url_util.h"
34 const char kInsetsTopKey
[] = "insets_top";
35 const char kInsetsLeftKey
[] = "insets_left";
36 const char kInsetsBottomKey
[] = "insets_bottom";
37 const char kInsetsRightKey
[] = "insets_right";
39 // This kind of boilerplates should be done by base::JSONValueConverter but it
40 // doesn't support classes like gfx::Insets for now.
41 // TODO(mukai): fix base::JSONValueConverter and use it here.
42 bool ValueToInsets(const base::DictionaryValue
& value
, gfx::Insets
* insets
) {
48 if (value
.GetInteger(kInsetsTopKey
, &top
) &&
49 value
.GetInteger(kInsetsLeftKey
, &left
) &&
50 value
.GetInteger(kInsetsBottomKey
, &bottom
) &&
51 value
.GetInteger(kInsetsRightKey
, &right
)) {
52 insets
->Set(top
, left
, bottom
, right
);
58 void InsetsToValue(const gfx::Insets
& insets
, base::DictionaryValue
* value
) {
60 value
->SetInteger(kInsetsTopKey
, insets
.top());
61 value
->SetInteger(kInsetsLeftKey
, insets
.left());
62 value
->SetInteger(kInsetsBottomKey
, insets
.bottom());
63 value
->SetInteger(kInsetsRightKey
, insets
.right());
66 std::string
ColorProfileToString(ui::ColorCalibrationProfile profile
) {
68 case ui::COLOR_PROFILE_STANDARD
:
70 case ui::COLOR_PROFILE_DYNAMIC
:
72 case ui::COLOR_PROFILE_MOVIE
:
74 case ui::COLOR_PROFILE_READING
:
76 case ui::NUM_COLOR_PROFILES
:
83 ui::ColorCalibrationProfile
StringToColorProfile(std::string value
) {
84 if (value
== "standard")
85 return ui::COLOR_PROFILE_STANDARD
;
86 else if (value
== "dynamic")
87 return ui::COLOR_PROFILE_DYNAMIC
;
88 else if (value
== "movie")
89 return ui::COLOR_PROFILE_MOVIE
;
90 else if (value
== "reading")
91 return ui::COLOR_PROFILE_READING
;
93 return ui::COLOR_PROFILE_STANDARD
;
96 ash::DisplayManager
* GetDisplayManager() {
97 return ash::Shell::GetInstance()->display_manager();
100 // Returns true id the current user can write display preferences to
102 bool UserCanSaveDisplayPreference() {
103 user_manager::UserManager
* user_manager
= user_manager::UserManager::Get();
104 return user_manager
->IsUserLoggedIn() &&
105 (user_manager
->IsLoggedInAsUserWithGaiaAccount() ||
106 user_manager
->IsLoggedInAsSupervisedUser() ||
107 user_manager
->IsLoggedInAsKioskApp());
110 void LoadDisplayLayouts() {
111 PrefService
* local_state
= g_browser_process
->local_state();
112 ash::DisplayLayoutStore
* layout_store
= GetDisplayManager()->layout_store();
114 const base::DictionaryValue
* layouts
= local_state
->GetDictionary(
115 prefs::kSecondaryDisplays
);
116 for (base::DictionaryValue::Iterator
it(*layouts
);
117 !it
.IsAtEnd(); it
.Advance()) {
118 ash::DisplayLayout layout
;
119 if (!ash::DisplayLayout::ConvertFromValue(it
.value(), &layout
)) {
120 LOG(WARNING
) << "Invalid preference value for " << it
.key();
124 if (it
.key().find(",") != std::string::npos
) {
125 std::vector
<std::string
> ids
= base::SplitString(
126 it
.key(), ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
127 int64 id1
= gfx::Display::kInvalidDisplayID
;
128 int64 id2
= gfx::Display::kInvalidDisplayID
;
129 if (!base::StringToInt64(ids
[0], &id1
) ||
130 !base::StringToInt64(ids
[1], &id2
) ||
131 id1
== gfx::Display::kInvalidDisplayID
||
132 id2
== gfx::Display::kInvalidDisplayID
) {
135 layout_store
->RegisterLayoutForDisplayIdPair(id1
, id2
, layout
);
140 void LoadDisplayProperties() {
141 PrefService
* local_state
= g_browser_process
->local_state();
142 const base::DictionaryValue
* properties
= local_state
->GetDictionary(
143 prefs::kDisplayProperties
);
144 for (base::DictionaryValue::Iterator
it(*properties
);
145 !it
.IsAtEnd(); it
.Advance()) {
146 const base::DictionaryValue
* dict_value
= NULL
;
147 if (!it
.value().GetAsDictionary(&dict_value
) || dict_value
== NULL
)
149 int64 id
= gfx::Display::kInvalidDisplayID
;
150 if (!base::StringToInt64(it
.key(), &id
) ||
151 id
== gfx::Display::kInvalidDisplayID
) {
154 gfx::Display::Rotation rotation
= gfx::Display::ROTATE_0
;
155 float ui_scale
= 1.0f
;
156 const gfx::Insets
* insets_to_set
= NULL
;
158 int rotation_value
= 0;
159 if (dict_value
->GetInteger("rotation", &rotation_value
)) {
160 rotation
= static_cast<gfx::Display::Rotation
>(rotation_value
);
162 int ui_scale_value
= 0;
163 if (dict_value
->GetInteger("ui-scale", &ui_scale_value
))
164 ui_scale
= static_cast<float>(ui_scale_value
) / 1000.0f
;
166 int width
= 0, height
= 0;
167 dict_value
->GetInteger("width", &width
);
168 dict_value
->GetInteger("height", &height
);
169 gfx::Size
resolution_in_pixels(width
, height
);
171 float device_scale_factor
= 1.0;
173 if (dict_value
->GetInteger("device-scale-factor", &dsf_value
))
174 device_scale_factor
= static_cast<float>(dsf_value
) / 1000.0f
;
177 if (ValueToInsets(*dict_value
, &insets
))
178 insets_to_set
= &insets
;
180 ui::ColorCalibrationProfile color_profile
= ui::COLOR_PROFILE_STANDARD
;
181 std::string color_profile_name
;
182 if (dict_value
->GetString("color_profile_name", &color_profile_name
))
183 color_profile
= StringToColorProfile(color_profile_name
);
184 GetDisplayManager()->RegisterDisplayProperty(id
,
188 resolution_in_pixels
,
194 void LoadDisplayRotationState() {
195 PrefService
* local_state
= g_browser_process
->local_state();
196 const base::DictionaryValue
* properties
=
197 local_state
->GetDictionary(prefs::kDisplayRotationLock
);
199 bool rotation_lock
= false;
200 if (!properties
->GetBoolean("lock", &rotation_lock
))
203 int rotation
= gfx::Display::ROTATE_0
;
204 if (!properties
->GetInteger("orientation", &rotation
))
207 GetDisplayManager()->RegisterDisplayRotationProperties(rotation_lock
,
208 static_cast<gfx::Display::Rotation
>(rotation
));
211 void StoreDisplayLayoutPref(const ash::DisplayIdPair
& pair
,
212 const ash::DisplayLayout
& display_layout
) {
214 base::Int64ToString(pair
.first
) + "," + base::Int64ToString(pair
.second
);
216 PrefService
* local_state
= g_browser_process
->local_state();
217 DictionaryPrefUpdate
update(local_state
, prefs::kSecondaryDisplays
);
218 base::DictionaryValue
* pref_data
= update
.Get();
219 scoped_ptr
<base::Value
> layout_value(new base::DictionaryValue());
220 if (pref_data
->HasKey(name
)) {
221 base::Value
* value
= NULL
;
222 if (pref_data
->Get(name
, &value
) && value
!= NULL
)
223 layout_value
.reset(value
->DeepCopy());
225 if (ash::DisplayLayout::ConvertToValue(display_layout
, layout_value
.get()))
226 pref_data
->Set(name
, layout_value
.release());
229 void StoreCurrentDisplayLayoutPrefs() {
230 ash::DisplayManager
* display_manager
= GetDisplayManager();
231 if (!UserCanSaveDisplayPreference() ||
232 display_manager
->num_connected_displays() < 2) {
236 ash::DisplayIdPair pair
= display_manager
->GetCurrentDisplayIdPair();
237 ash::DisplayLayout display_layout
=
238 display_manager
->layout_store()->GetRegisteredDisplayLayout(pair
);
239 StoreDisplayLayoutPref(pair
, display_layout
);
242 void StoreCurrentDisplayProperties() {
243 ash::DisplayManager
* display_manager
= GetDisplayManager();
244 PrefService
* local_state
= g_browser_process
->local_state();
246 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayProperties
);
247 base::DictionaryValue
* pref_data
= update
.Get();
249 size_t num
= display_manager
->GetNumDisplays();
250 for (size_t i
= 0; i
< num
; ++i
) {
251 const gfx::Display
& display
= display_manager
->GetDisplayAt(i
);
252 int64 id
= display
.id();
253 ash::DisplayInfo info
= display_manager
->GetDisplayInfo(id
);
255 scoped_ptr
<base::DictionaryValue
> property_value(
256 new base::DictionaryValue());
257 // Don't save the display preference in unified mode because its
258 // size and modes can change depending on the combination of displays.
259 if (display_manager
->IsInUnifiedMode())
261 property_value
->SetInteger(
263 static_cast<int>(info
.GetRotation(gfx::Display::ROTATION_SOURCE_USER
)));
264 property_value
->SetInteger(
265 "ui-scale", static_cast<int>(info
.configured_ui_scale() * 1000));
267 ash::DisplayMode mode
;
268 if (!display
.IsInternal() &&
269 display_manager
->GetSelectedModeForDisplayId(id
, &mode
) &&
271 property_value
->SetInteger("width", mode
.size
.width());
272 property_value
->SetInteger("height", mode
.size
.height());
273 property_value
->SetInteger(
274 "device-scale-factor",
275 static_cast<int>(mode
.device_scale_factor
* 1000));
277 if (!info
.overscan_insets_in_dip().empty())
278 InsetsToValue(info
.overscan_insets_in_dip(), property_value
.get());
279 if (info
.color_profile() != ui::COLOR_PROFILE_STANDARD
) {
280 property_value
->SetString(
281 "color_profile_name", ColorProfileToString(info
.color_profile()));
283 pref_data
->Set(base::Int64ToString(id
), property_value
.release());
287 typedef std::map
<chromeos::DisplayPowerState
, std::string
>
288 DisplayPowerStateToStringMap
;
290 const DisplayPowerStateToStringMap
* GetDisplayPowerStateToStringMap() {
291 // Don't save or retore ALL_OFF state. crbug.com/318456.
292 static const DisplayPowerStateToStringMap
* map
= ash::CreateToStringMap(
293 chromeos::DISPLAY_POWER_ALL_ON
, "all_on",
294 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
,
295 "internal_off_external_on",
296 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
,
297 "internal_on_external_off");
301 bool GetDisplayPowerStateFromString(const base::StringPiece
& state
,
302 chromeos::DisplayPowerState
* field
) {
303 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state
, field
))
305 LOG(ERROR
) << "Invalid display power state value:" << state
;
309 void StoreDisplayPowerState(DisplayPowerState power_state
) {
310 const DisplayPowerStateToStringMap
* map
= GetDisplayPowerStateToStringMap();
311 DisplayPowerStateToStringMap::const_iterator iter
= map
->find(power_state
);
312 if (iter
!= map
->end()) {
313 PrefService
* local_state
= g_browser_process
->local_state();
314 local_state
->SetString(prefs::kDisplayPowerState
, iter
->second
);
318 void StoreCurrentDisplayPowerState() {
319 StoreDisplayPowerState(
320 ash::Shell::GetInstance()->display_configurator()->
321 requested_power_state());
324 void StoreCurrentDisplayRotationLockPrefs() {
325 bool rotation_lock
= ash::Shell::GetInstance()->display_manager()->
326 registered_internal_display_rotation_lock();
327 StoreDisplayRotationPrefs(rotation_lock
);
332 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple
* registry
) {
333 // Per-display preference.
334 registry
->RegisterDictionaryPref(prefs::kSecondaryDisplays
);
335 registry
->RegisterDictionaryPref(prefs::kDisplayProperties
);
336 DisplayPowerStateToStringMap::const_iterator iter
=
337 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON
);
338 registry
->RegisterStringPref(prefs::kDisplayPowerState
, iter
->second
);
339 registry
->RegisterDictionaryPref(prefs::kDisplayRotationLock
);
342 void StoreDisplayPrefs() {
343 // Stores the power state regardless of the login status, because the power
344 // state respects to the current status (close/open) of the lid which can be
345 // changed in any situation. See crbug.com/285360
346 StoreCurrentDisplayPowerState();
347 StoreCurrentDisplayRotationLockPrefs();
349 // Do not store prefs when the confirmation dialog is shown.
350 if (!UserCanSaveDisplayPreference() ||
351 !ash::Shell::GetInstance()->ShouldSaveDisplaySettings()) {
355 StoreCurrentDisplayLayoutPrefs();
356 StoreCurrentDisplayProperties();
359 void StoreDisplayRotationPrefs(bool rotation_lock
) {
360 if (!gfx::Display::HasInternalDisplay())
363 PrefService
* local_state
= g_browser_process
->local_state();
364 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayRotationLock
);
365 base::DictionaryValue
* pref_data
= update
.Get();
366 pref_data
->SetBoolean("lock", rotation_lock
);
367 gfx::Display::Rotation rotation
=
369 ->GetDisplayInfo(gfx::Display::InternalDisplayId())
370 .GetRotation(gfx::Display::ROTATION_SOURCE_ACCELEROMETER
);
371 pref_data
->SetInteger("orientation", static_cast<int>(rotation
));
374 void SetCurrentDisplayLayout(const ash::DisplayLayout
& layout
) {
375 GetDisplayManager()->SetLayoutForCurrentDisplays(layout
);
378 void LoadDisplayPreferences(bool first_run_after_boot
) {
379 LoadDisplayLayouts();
380 LoadDisplayProperties();
381 LoadDisplayRotationState();
382 if (!first_run_after_boot
) {
383 PrefService
* local_state
= g_browser_process
->local_state();
384 // Restore DisplayPowerState:
385 std::string value
= local_state
->GetString(prefs::kDisplayPowerState
);
386 chromeos::DisplayPowerState power_state
;
387 if (GetDisplayPowerStateFromString(value
, &power_state
)) {
388 ash::Shell::GetInstance()->display_configurator()->SetInitialDisplayPower(
394 // Stores the display layout for given display pairs.
395 void StoreDisplayLayoutPrefForTest(int64 id1
,
397 const ash::DisplayLayout
& layout
) {
398 StoreDisplayLayoutPref(ash::CreateDisplayIdPair(id1
, id2
), layout
);
401 // Stores the given |power_state|.
402 void StoreDisplayPowerStateForTest(DisplayPowerState power_state
) {
403 StoreDisplayPowerState(power_state
);
406 } // namespace chromeos