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/values.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/common/pref_names.h"
21 #include "components/user_manager/user_manager.h"
22 #include "third_party/cros_system_api/dbus/service_constants.h"
23 #include "ui/gfx/display.h"
24 #include "ui/gfx/insets.h"
25 #include "ui/gfx/screen.h"
26 #include "url/url_canon.h"
27 #include "url/url_util.h"
32 const char kInsetsTopKey
[] = "insets_top";
33 const char kInsetsLeftKey
[] = "insets_left";
34 const char kInsetsBottomKey
[] = "insets_bottom";
35 const char kInsetsRightKey
[] = "insets_right";
37 // This kind of boilerplates should be done by base::JSONValueConverter but it
38 // doesn't support classes like gfx::Insets for now.
39 // TODO(mukai): fix base::JSONValueConverter and use it here.
40 bool ValueToInsets(const base::DictionaryValue
& value
, gfx::Insets
* insets
) {
46 if (value
.GetInteger(kInsetsTopKey
, &top
) &&
47 value
.GetInteger(kInsetsLeftKey
, &left
) &&
48 value
.GetInteger(kInsetsBottomKey
, &bottom
) &&
49 value
.GetInteger(kInsetsRightKey
, &right
)) {
50 insets
->Set(top
, left
, bottom
, right
);
56 void InsetsToValue(const gfx::Insets
& insets
, base::DictionaryValue
* value
) {
58 value
->SetInteger(kInsetsTopKey
, insets
.top());
59 value
->SetInteger(kInsetsLeftKey
, insets
.left());
60 value
->SetInteger(kInsetsBottomKey
, insets
.bottom());
61 value
->SetInteger(kInsetsRightKey
, insets
.right());
64 std::string
ColorProfileToString(ui::ColorCalibrationProfile profile
) {
66 case ui::COLOR_PROFILE_STANDARD
:
68 case ui::COLOR_PROFILE_DYNAMIC
:
70 case ui::COLOR_PROFILE_MOVIE
:
72 case ui::COLOR_PROFILE_READING
:
74 case ui::NUM_COLOR_PROFILES
:
81 ui::ColorCalibrationProfile
StringToColorProfile(std::string value
) {
82 if (value
== "standard")
83 return ui::COLOR_PROFILE_STANDARD
;
84 else if (value
== "dynamic")
85 return ui::COLOR_PROFILE_DYNAMIC
;
86 else if (value
== "movie")
87 return ui::COLOR_PROFILE_MOVIE
;
88 else if (value
== "reading")
89 return ui::COLOR_PROFILE_READING
;
91 return ui::COLOR_PROFILE_STANDARD
;
94 ash::DisplayManager
* GetDisplayManager() {
95 return ash::Shell::GetInstance()->display_manager();
98 // Returns true id the current user can write display preferences to
100 bool UserCanSaveDisplayPreference() {
101 user_manager::UserManager
* user_manager
= user_manager::UserManager::Get();
102 return user_manager
->IsUserLoggedIn() &&
103 (user_manager
->IsLoggedInAsUserWithGaiaAccount() ||
104 user_manager
->IsLoggedInAsSupervisedUser() ||
105 user_manager
->IsLoggedInAsKioskApp());
108 void LoadDisplayLayouts() {
109 PrefService
* local_state
= g_browser_process
->local_state();
110 ash::DisplayLayoutStore
* layout_store
= GetDisplayManager()->layout_store();
112 const base::DictionaryValue
* layouts
= local_state
->GetDictionary(
113 prefs::kSecondaryDisplays
);
114 for (base::DictionaryValue::Iterator
it(*layouts
);
115 !it
.IsAtEnd(); it
.Advance()) {
116 ash::DisplayLayout layout
;
117 if (!ash::DisplayLayout::ConvertFromValue(it
.value(), &layout
)) {
118 LOG(WARNING
) << "Invalid preference value for " << it
.key();
122 if (it
.key().find(",") != std::string::npos
) {
123 std::vector
<std::string
> ids
;
124 base::SplitString(it
.key(), ',', &ids
);
125 int64 id1
= gfx::Display::kInvalidDisplayID
;
126 int64 id2
= gfx::Display::kInvalidDisplayID
;
127 if (!base::StringToInt64(ids
[0], &id1
) ||
128 !base::StringToInt64(ids
[1], &id2
) ||
129 id1
== gfx::Display::kInvalidDisplayID
||
130 id2
== gfx::Display::kInvalidDisplayID
) {
133 layout_store
->RegisterLayoutForDisplayIdPair(id1
, id2
, layout
);
138 void LoadDisplayProperties() {
139 PrefService
* local_state
= g_browser_process
->local_state();
140 const base::DictionaryValue
* properties
= local_state
->GetDictionary(
141 prefs::kDisplayProperties
);
142 for (base::DictionaryValue::Iterator
it(*properties
);
143 !it
.IsAtEnd(); it
.Advance()) {
144 const base::DictionaryValue
* dict_value
= NULL
;
145 if (!it
.value().GetAsDictionary(&dict_value
) || dict_value
== NULL
)
147 int64 id
= gfx::Display::kInvalidDisplayID
;
148 if (!base::StringToInt64(it
.key(), &id
) ||
149 id
== gfx::Display::kInvalidDisplayID
) {
152 gfx::Display::Rotation rotation
= gfx::Display::ROTATE_0
;
153 float ui_scale
= 1.0f
;
154 const gfx::Insets
* insets_to_set
= NULL
;
156 int rotation_value
= 0;
157 if (dict_value
->GetInteger("rotation", &rotation_value
)) {
158 rotation
= static_cast<gfx::Display::Rotation
>(rotation_value
);
160 int ui_scale_value
= 0;
161 if (dict_value
->GetInteger("ui-scale", &ui_scale_value
))
162 ui_scale
= static_cast<float>(ui_scale_value
) / 1000.0f
;
164 int width
= 0, height
= 0;
165 dict_value
->GetInteger("width", &width
);
166 dict_value
->GetInteger("height", &height
);
167 gfx::Size
resolution_in_pixels(width
, height
);
169 float device_scale_factor
= 1.0;
171 if (dict_value
->GetInteger("device-scale-factor", &dsf_value
))
172 device_scale_factor
= static_cast<float>(dsf_value
) / 1000.0f
;
175 if (ValueToInsets(*dict_value
, &insets
))
176 insets_to_set
= &insets
;
178 ui::ColorCalibrationProfile color_profile
= ui::COLOR_PROFILE_STANDARD
;
179 std::string color_profile_name
;
180 if (dict_value
->GetString("color_profile_name", &color_profile_name
))
181 color_profile
= StringToColorProfile(color_profile_name
);
182 GetDisplayManager()->RegisterDisplayProperty(id
,
186 resolution_in_pixels
,
192 void LoadDisplayRotationState() {
193 PrefService
* local_state
= g_browser_process
->local_state();
194 const base::DictionaryValue
* properties
=
195 local_state
->GetDictionary(prefs::kDisplayRotationLock
);
197 bool rotation_lock
= false;
198 if (!properties
->GetBoolean("lock", &rotation_lock
))
201 int rotation
= gfx::Display::ROTATE_0
;
202 if (!properties
->GetInteger("orientation", &rotation
))
205 GetDisplayManager()->RegisterDisplayRotationProperties(rotation_lock
,
206 static_cast<gfx::Display::Rotation
>(rotation
));
209 void StoreDisplayLayoutPref(const ash::DisplayIdPair
& pair
,
210 const ash::DisplayLayout
& display_layout
) {
212 base::Int64ToString(pair
.first
) + "," + base::Int64ToString(pair
.second
);
214 PrefService
* local_state
= g_browser_process
->local_state();
215 DictionaryPrefUpdate
update(local_state
, prefs::kSecondaryDisplays
);
216 base::DictionaryValue
* pref_data
= update
.Get();
217 scoped_ptr
<base::Value
> layout_value(new base::DictionaryValue());
218 if (pref_data
->HasKey(name
)) {
219 base::Value
* value
= NULL
;
220 if (pref_data
->Get(name
, &value
) && value
!= NULL
)
221 layout_value
.reset(value
->DeepCopy());
223 if (ash::DisplayLayout::ConvertToValue(display_layout
, layout_value
.get()))
224 pref_data
->Set(name
, layout_value
.release());
227 void StoreCurrentDisplayLayoutPrefs() {
228 if (!UserCanSaveDisplayPreference() ||
229 GetDisplayManager()->num_connected_displays() < 2) {
233 ash::DisplayIdPair pair
= GetDisplayManager()->GetCurrentDisplayIdPair();
234 ash::DisplayLayout display_layout
=
235 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair
);
236 StoreDisplayLayoutPref(pair
, display_layout
);
239 void StoreCurrentDisplayProperties() {
240 ash::DisplayManager
* display_manager
= GetDisplayManager();
241 PrefService
* local_state
= g_browser_process
->local_state();
243 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayProperties
);
244 base::DictionaryValue
* pref_data
= update
.Get();
246 size_t num
= display_manager
->GetNumDisplays();
247 for (size_t i
= 0; i
< num
; ++i
) {
248 const gfx::Display
& display
= display_manager
->GetDisplayAt(i
);
249 int64 id
= display
.id();
250 ash::DisplayInfo info
= display_manager
->GetDisplayInfo(id
);
252 scoped_ptr
<base::DictionaryValue
> property_value(
253 new base::DictionaryValue());
254 property_value
->SetInteger("rotation", static_cast<int>(info
.rotation()));
255 property_value
->SetInteger(
257 static_cast<int>(info
.configured_ui_scale() * 1000));
258 ash::DisplayMode mode
;
259 if (!display
.IsInternal() &&
260 display_manager
->GetSelectedModeForDisplayId(id
, &mode
) &&
262 property_value
->SetInteger("width", mode
.size
.width());
263 property_value
->SetInteger("height", mode
.size
.height());
264 property_value
->SetInteger(
265 "device-scale-factor",
266 static_cast<int>(mode
.device_scale_factor
* 1000));
268 if (!info
.overscan_insets_in_dip().empty())
269 InsetsToValue(info
.overscan_insets_in_dip(), property_value
.get());
270 if (info
.color_profile() != ui::COLOR_PROFILE_STANDARD
) {
271 property_value
->SetString(
272 "color_profile_name", ColorProfileToString(info
.color_profile()));
274 pref_data
->Set(base::Int64ToString(id
), property_value
.release());
278 typedef std::map
<chromeos::DisplayPowerState
, std::string
>
279 DisplayPowerStateToStringMap
;
281 const DisplayPowerStateToStringMap
* GetDisplayPowerStateToStringMap() {
282 // Don't save or retore ALL_OFF state. crbug.com/318456.
283 static const DisplayPowerStateToStringMap
* map
= ash::CreateToStringMap(
284 chromeos::DISPLAY_POWER_ALL_ON
, "all_on",
285 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
,
286 "internal_off_external_on",
287 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
,
288 "internal_on_external_off");
292 bool GetDisplayPowerStateFromString(const base::StringPiece
& state
,
293 chromeos::DisplayPowerState
* field
) {
294 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state
, field
))
296 LOG(ERROR
) << "Invalid display power state value:" << state
;
300 void StoreDisplayPowerState(DisplayPowerState power_state
) {
301 const DisplayPowerStateToStringMap
* map
= GetDisplayPowerStateToStringMap();
302 DisplayPowerStateToStringMap::const_iterator iter
= map
->find(power_state
);
303 if (iter
!= map
->end()) {
304 PrefService
* local_state
= g_browser_process
->local_state();
305 local_state
->SetString(prefs::kDisplayPowerState
, iter
->second
);
309 void StoreCurrentDisplayPowerState() {
310 StoreDisplayPowerState(
311 ash::Shell::GetInstance()->display_configurator()->
312 requested_power_state());
315 void StoreCurrentDisplayRotationLockPrefs() {
316 bool rotation_lock
= ash::Shell::GetInstance()->display_manager()->
317 registered_internal_display_rotation_lock();
318 StoreDisplayRotationPrefs(rotation_lock
);
323 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple
* registry
) {
324 // Per-display preference.
325 registry
->RegisterDictionaryPref(prefs::kSecondaryDisplays
);
326 registry
->RegisterDictionaryPref(prefs::kDisplayProperties
);
327 DisplayPowerStateToStringMap::const_iterator iter
=
328 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON
);
329 registry
->RegisterStringPref(prefs::kDisplayPowerState
, iter
->second
);
330 registry
->RegisterDictionaryPref(prefs::kDisplayRotationLock
);
333 void StoreDisplayPrefs() {
334 // Stores the power state regardless of the login status, because the power
335 // state respects to the current status (close/open) of the lid which can be
336 // changed in any situation. See crbug.com/285360
337 StoreCurrentDisplayPowerState();
338 StoreCurrentDisplayRotationLockPrefs();
340 // Do not store prefs when the confirmation dialog is shown.
341 if (!UserCanSaveDisplayPreference() ||
342 !ash::Shell::GetInstance()->ShouldSaveDisplaySettings()) {
346 StoreCurrentDisplayLayoutPrefs();
347 StoreCurrentDisplayProperties();
350 void StoreDisplayRotationPrefs(bool rotation_lock
) {
351 ash::DisplayManager
* display_manager
= GetDisplayManager();
352 if (!display_manager
->HasInternalDisplay())
355 PrefService
* local_state
= g_browser_process
->local_state();
356 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayRotationLock
);
357 base::DictionaryValue
* pref_data
= update
.Get();
358 pref_data
->SetBoolean("lock", rotation_lock
);
359 gfx::Display::Rotation rotation
= display_manager
->
360 GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation();
361 pref_data
->SetInteger("orientation", static_cast<int>(rotation
));
364 void SetCurrentDisplayLayout(const ash::DisplayLayout
& layout
) {
365 GetDisplayManager()->SetLayoutForCurrentDisplays(layout
);
368 void LoadDisplayPreferences(bool first_run_after_boot
) {
369 LoadDisplayLayouts();
370 LoadDisplayProperties();
371 LoadDisplayRotationState();
372 if (!first_run_after_boot
) {
373 PrefService
* local_state
= g_browser_process
->local_state();
374 // Restore DisplayPowerState:
375 std::string value
= local_state
->GetString(prefs::kDisplayPowerState
);
376 chromeos::DisplayPowerState power_state
;
377 if (GetDisplayPowerStateFromString(value
, &power_state
)) {
378 ash::Shell::GetInstance()->display_configurator()->SetInitialDisplayPower(
384 // Stores the display layout for given display pairs.
385 void StoreDisplayLayoutPrefForTest(int64 id1
,
387 const ash::DisplayLayout
& layout
) {
388 StoreDisplayLayoutPref(std::make_pair(id1
, id2
), layout
);
391 // Stores the given |power_state|.
392 void StoreDisplayPowerStateForTest(DisplayPowerState power_state
) {
393 StoreDisplayPowerState(power_state
);
396 } // namespace chromeos