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/shell.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/sys_info.h"
19 #include "base/values.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/user_manager/user_manager.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
24 #include "ui/gfx/display.h"
25 #include "ui/gfx/geometry/insets.h"
26 #include "ui/gfx/screen.h"
27 #include "url/url_canon.h"
28 #include "url/url_util.h"
33 const char kInsetsTopKey
[] = "insets_top";
34 const char kInsetsLeftKey
[] = "insets_left";
35 const char kInsetsBottomKey
[] = "insets_bottom";
36 const char kInsetsRightKey
[] = "insets_right";
38 // This kind of boilerplates should be done by base::JSONValueConverter but it
39 // doesn't support classes like gfx::Insets for now.
40 // TODO(mukai): fix base::JSONValueConverter and use it here.
41 bool ValueToInsets(const base::DictionaryValue
& value
, gfx::Insets
* insets
) {
47 if (value
.GetInteger(kInsetsTopKey
, &top
) &&
48 value
.GetInteger(kInsetsLeftKey
, &left
) &&
49 value
.GetInteger(kInsetsBottomKey
, &bottom
) &&
50 value
.GetInteger(kInsetsRightKey
, &right
)) {
51 insets
->Set(top
, left
, bottom
, right
);
57 void InsetsToValue(const gfx::Insets
& insets
, base::DictionaryValue
* value
) {
59 value
->SetInteger(kInsetsTopKey
, insets
.top());
60 value
->SetInteger(kInsetsLeftKey
, insets
.left());
61 value
->SetInteger(kInsetsBottomKey
, insets
.bottom());
62 value
->SetInteger(kInsetsRightKey
, insets
.right());
65 std::string
ColorProfileToString(ui::ColorCalibrationProfile profile
) {
67 case ui::COLOR_PROFILE_STANDARD
:
69 case ui::COLOR_PROFILE_DYNAMIC
:
71 case ui::COLOR_PROFILE_MOVIE
:
73 case ui::COLOR_PROFILE_READING
:
75 case ui::NUM_COLOR_PROFILES
:
82 ui::ColorCalibrationProfile
StringToColorProfile(std::string value
) {
83 if (value
== "standard")
84 return ui::COLOR_PROFILE_STANDARD
;
85 else if (value
== "dynamic")
86 return ui::COLOR_PROFILE_DYNAMIC
;
87 else if (value
== "movie")
88 return ui::COLOR_PROFILE_MOVIE
;
89 else if (value
== "reading")
90 return ui::COLOR_PROFILE_READING
;
92 return ui::COLOR_PROFILE_STANDARD
;
95 ash::DisplayManager
* GetDisplayManager() {
96 return ash::Shell::GetInstance()->display_manager();
99 // Returns true id the current user can write display preferences to
101 bool UserCanSaveDisplayPreference() {
102 user_manager::UserManager
* user_manager
= user_manager::UserManager::Get();
103 return user_manager
->IsUserLoggedIn() &&
104 (user_manager
->IsLoggedInAsUserWithGaiaAccount() ||
105 user_manager
->IsLoggedInAsSupervisedUser() ||
106 user_manager
->IsLoggedInAsKioskApp());
109 void LoadDisplayLayouts() {
110 PrefService
* local_state
= g_browser_process
->local_state();
111 ash::DisplayLayoutStore
* layout_store
= GetDisplayManager()->layout_store();
113 const base::DictionaryValue
* layouts
= local_state
->GetDictionary(
114 prefs::kSecondaryDisplays
);
115 for (base::DictionaryValue::Iterator
it(*layouts
);
116 !it
.IsAtEnd(); it
.Advance()) {
117 ash::DisplayLayout layout
;
118 if (!ash::DisplayLayout::ConvertFromValue(it
.value(), &layout
)) {
119 LOG(WARNING
) << "Invalid preference value for " << it
.key();
123 if (it
.key().find(",") != std::string::npos
) {
124 std::vector
<std::string
> ids
= base::SplitString(
125 it
.key(), ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
126 int64 id1
= gfx::Display::kInvalidDisplayID
;
127 int64 id2
= gfx::Display::kInvalidDisplayID
;
128 if (!base::StringToInt64(ids
[0], &id1
) ||
129 !base::StringToInt64(ids
[1], &id2
) ||
130 id1
== gfx::Display::kInvalidDisplayID
||
131 id2
== gfx::Display::kInvalidDisplayID
) {
134 layout_store
->RegisterLayoutForDisplayIdPair(id1
, id2
, layout
);
139 void LoadDisplayProperties() {
140 PrefService
* local_state
= g_browser_process
->local_state();
141 const base::DictionaryValue
* properties
= local_state
->GetDictionary(
142 prefs::kDisplayProperties
);
143 for (base::DictionaryValue::Iterator
it(*properties
);
144 !it
.IsAtEnd(); it
.Advance()) {
145 const base::DictionaryValue
* dict_value
= NULL
;
146 if (!it
.value().GetAsDictionary(&dict_value
) || dict_value
== NULL
)
148 int64 id
= gfx::Display::kInvalidDisplayID
;
149 if (!base::StringToInt64(it
.key(), &id
) ||
150 id
== gfx::Display::kInvalidDisplayID
) {
153 gfx::Display::Rotation rotation
= gfx::Display::ROTATE_0
;
154 float ui_scale
= 1.0f
;
155 const gfx::Insets
* insets_to_set
= NULL
;
157 int rotation_value
= 0;
158 if (dict_value
->GetInteger("rotation", &rotation_value
)) {
159 rotation
= static_cast<gfx::Display::Rotation
>(rotation_value
);
161 int ui_scale_value
= 0;
162 if (dict_value
->GetInteger("ui-scale", &ui_scale_value
))
163 ui_scale
= static_cast<float>(ui_scale_value
) / 1000.0f
;
165 int width
= 0, height
= 0;
166 dict_value
->GetInteger("width", &width
);
167 dict_value
->GetInteger("height", &height
);
168 gfx::Size
resolution_in_pixels(width
, height
);
170 float device_scale_factor
= 1.0;
172 if (dict_value
->GetInteger("device-scale-factor", &dsf_value
))
173 device_scale_factor
= static_cast<float>(dsf_value
) / 1000.0f
;
176 if (ValueToInsets(*dict_value
, &insets
))
177 insets_to_set
= &insets
;
179 ui::ColorCalibrationProfile color_profile
= ui::COLOR_PROFILE_STANDARD
;
180 std::string color_profile_name
;
181 if (dict_value
->GetString("color_profile_name", &color_profile_name
))
182 color_profile
= StringToColorProfile(color_profile_name
);
183 GetDisplayManager()->RegisterDisplayProperty(id
,
187 resolution_in_pixels
,
193 void LoadDisplayRotationState() {
194 PrefService
* local_state
= g_browser_process
->local_state();
195 const base::DictionaryValue
* properties
=
196 local_state
->GetDictionary(prefs::kDisplayRotationLock
);
198 bool rotation_lock
= false;
199 if (!properties
->GetBoolean("lock", &rotation_lock
))
202 int rotation
= gfx::Display::ROTATE_0
;
203 if (!properties
->GetInteger("orientation", &rotation
))
206 GetDisplayManager()->RegisterDisplayRotationProperties(rotation_lock
,
207 static_cast<gfx::Display::Rotation
>(rotation
));
210 void StoreDisplayLayoutPref(const ash::DisplayIdPair
& pair
,
211 const ash::DisplayLayout
& display_layout
) {
213 base::Int64ToString(pair
.first
) + "," + base::Int64ToString(pair
.second
);
215 PrefService
* local_state
= g_browser_process
->local_state();
216 DictionaryPrefUpdate
update(local_state
, prefs::kSecondaryDisplays
);
217 base::DictionaryValue
* pref_data
= update
.Get();
218 scoped_ptr
<base::Value
> layout_value(new base::DictionaryValue());
219 if (pref_data
->HasKey(name
)) {
220 base::Value
* value
= NULL
;
221 if (pref_data
->Get(name
, &value
) && value
!= NULL
)
222 layout_value
.reset(value
->DeepCopy());
224 if (ash::DisplayLayout::ConvertToValue(display_layout
, layout_value
.get()))
225 pref_data
->Set(name
, layout_value
.release());
228 void StoreCurrentDisplayLayoutPrefs() {
229 ash::DisplayManager
* display_manager
= GetDisplayManager();
230 if (!UserCanSaveDisplayPreference() ||
231 display_manager
->num_connected_displays() < 2) {
235 ash::DisplayIdPair pair
= display_manager
->GetCurrentDisplayIdPair();
236 ash::DisplayLayout display_layout
=
237 display_manager
->layout_store()->GetRegisteredDisplayLayout(pair
);
238 StoreDisplayLayoutPref(pair
, display_layout
);
241 void StoreCurrentDisplayProperties() {
242 ash::DisplayManager
* display_manager
= GetDisplayManager();
243 PrefService
* local_state
= g_browser_process
->local_state();
245 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayProperties
);
246 base::DictionaryValue
* pref_data
= update
.Get();
248 size_t num
= display_manager
->GetNumDisplays();
249 for (size_t i
= 0; i
< num
; ++i
) {
250 const gfx::Display
& display
= display_manager
->GetDisplayAt(i
);
251 int64 id
= display
.id();
252 ash::DisplayInfo info
= display_manager
->GetDisplayInfo(id
);
254 scoped_ptr
<base::DictionaryValue
> property_value(
255 new base::DictionaryValue());
256 property_value
->SetInteger(
258 static_cast<int>(info
.GetRotation(gfx::Display::ROTATION_SOURCE_USER
)));
259 // Don't save the ui scale in unified mode because UI scale range
260 // changes depending on the display combinations.
261 if (!display_manager
->IsInUnifiedMode()) {
262 property_value
->SetInteger(
263 "ui-scale", static_cast<int>(info
.configured_ui_scale() * 1000));
266 ash::DisplayMode mode
;
267 if (!display
.IsInternal() &&
268 display_manager
->GetSelectedModeForDisplayId(id
, &mode
) &&
270 property_value
->SetInteger("width", mode
.size
.width());
271 property_value
->SetInteger("height", mode
.size
.height());
272 property_value
->SetInteger(
273 "device-scale-factor",
274 static_cast<int>(mode
.device_scale_factor
* 1000));
276 if (!info
.overscan_insets_in_dip().empty())
277 InsetsToValue(info
.overscan_insets_in_dip(), property_value
.get());
278 if (info
.color_profile() != ui::COLOR_PROFILE_STANDARD
) {
279 property_value
->SetString(
280 "color_profile_name", ColorProfileToString(info
.color_profile()));
282 pref_data
->Set(base::Int64ToString(id
), property_value
.release());
286 typedef std::map
<chromeos::DisplayPowerState
, std::string
>
287 DisplayPowerStateToStringMap
;
289 const DisplayPowerStateToStringMap
* GetDisplayPowerStateToStringMap() {
290 // Don't save or retore ALL_OFF state. crbug.com/318456.
291 static const DisplayPowerStateToStringMap
* map
= ash::CreateToStringMap(
292 chromeos::DISPLAY_POWER_ALL_ON
, "all_on",
293 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
,
294 "internal_off_external_on",
295 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
,
296 "internal_on_external_off");
300 bool GetDisplayPowerStateFromString(const base::StringPiece
& state
,
301 chromeos::DisplayPowerState
* field
) {
302 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state
, field
))
304 LOG(ERROR
) << "Invalid display power state value:" << state
;
308 void StoreDisplayPowerState(DisplayPowerState power_state
) {
309 const DisplayPowerStateToStringMap
* map
= GetDisplayPowerStateToStringMap();
310 DisplayPowerStateToStringMap::const_iterator iter
= map
->find(power_state
);
311 if (iter
!= map
->end()) {
312 PrefService
* local_state
= g_browser_process
->local_state();
313 local_state
->SetString(prefs::kDisplayPowerState
, iter
->second
);
317 void StoreCurrentDisplayPowerState() {
318 StoreDisplayPowerState(
319 ash::Shell::GetInstance()->display_configurator()->
320 requested_power_state());
323 void StoreCurrentDisplayRotationLockPrefs() {
324 bool rotation_lock
= ash::Shell::GetInstance()->display_manager()->
325 registered_internal_display_rotation_lock();
326 StoreDisplayRotationPrefs(rotation_lock
);
331 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple
* registry
) {
332 // Per-display preference.
333 registry
->RegisterDictionaryPref(prefs::kSecondaryDisplays
);
334 registry
->RegisterDictionaryPref(prefs::kDisplayProperties
);
335 DisplayPowerStateToStringMap::const_iterator iter
=
336 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON
);
337 registry
->RegisterStringPref(prefs::kDisplayPowerState
, iter
->second
);
338 registry
->RegisterDictionaryPref(prefs::kDisplayRotationLock
);
341 void StoreDisplayPrefs() {
342 // Stores the power state regardless of the login status, because the power
343 // state respects to the current status (close/open) of the lid which can be
344 // changed in any situation. See crbug.com/285360
345 StoreCurrentDisplayPowerState();
346 StoreCurrentDisplayRotationLockPrefs();
348 // Do not store prefs when the confirmation dialog is shown.
349 if (!UserCanSaveDisplayPreference() ||
350 !ash::Shell::GetInstance()->ShouldSaveDisplaySettings()) {
354 StoreCurrentDisplayLayoutPrefs();
355 StoreCurrentDisplayProperties();
358 void StoreDisplayRotationPrefs(bool rotation_lock
) {
359 if (!gfx::Display::HasInternalDisplay())
362 PrefService
* local_state
= g_browser_process
->local_state();
363 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayRotationLock
);
364 base::DictionaryValue
* pref_data
= update
.Get();
365 pref_data
->SetBoolean("lock", rotation_lock
);
366 gfx::Display::Rotation rotation
=
368 ->GetDisplayInfo(gfx::Display::InternalDisplayId())
369 .GetRotation(gfx::Display::ROTATION_SOURCE_ACCELEROMETER
);
370 pref_data
->SetInteger("orientation", static_cast<int>(rotation
));
373 void SetCurrentDisplayLayout(const ash::DisplayLayout
& layout
) {
374 GetDisplayManager()->SetLayoutForCurrentDisplays(layout
);
377 void LoadDisplayPreferences(bool first_run_after_boot
) {
378 LoadDisplayLayouts();
379 LoadDisplayProperties();
380 LoadDisplayRotationState();
381 if (!first_run_after_boot
) {
382 PrefService
* local_state
= g_browser_process
->local_state();
383 // Restore DisplayPowerState:
384 std::string value
= local_state
->GetString(prefs::kDisplayPowerState
);
385 chromeos::DisplayPowerState power_state
;
386 if (GetDisplayPowerStateFromString(value
, &power_state
)) {
387 ash::Shell::GetInstance()->display_configurator()->SetInitialDisplayPower(
393 // Stores the display layout for given display pairs.
394 void StoreDisplayLayoutPrefForTest(int64 id1
,
396 const ash::DisplayLayout
& layout
) {
397 StoreDisplayLayoutPref(std::make_pair(id1
, id2
), layout
);
400 // Stores the given |power_state|.
401 void StoreDisplayPowerStateForTest(DisplayPowerState power_state
) {
402 StoreDisplayPowerState(power_state
);
405 } // namespace chromeos