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/resolution_notification_controller.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/values.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chromeos/login/user_manager.h"
22 #include "chrome/common/pref_names.h"
23 #include "chromeos/display/output_configurator.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
25 #include "ui/gfx/display.h"
26 #include "ui/gfx/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 ash::internal::DisplayManager
* GetDisplayManager() {
67 return ash::Shell::GetInstance()->display_manager();
70 // Returns true id the current user can write display preferences to
72 bool UserCanSaveDisplayPreference() {
73 UserManager
* user_manager
= UserManager::Get();
74 return user_manager
->IsUserLoggedIn() &&
75 (user_manager
->IsLoggedInAsRegularUser() ||
76 user_manager
->IsLoggedInAsLocallyManagedUser() ||
77 user_manager
->IsLoggedInAsKioskApp());
80 void LoadDisplayLayouts() {
81 PrefService
* local_state
= g_browser_process
->local_state();
82 ash::internal::DisplayLayoutStore
* layout_store
=
83 GetDisplayManager()->layout_store();
85 const base::DictionaryValue
* layouts
= local_state
->GetDictionary(
86 prefs::kSecondaryDisplays
);
87 for (base::DictionaryValue::Iterator
it(*layouts
);
88 !it
.IsAtEnd(); it
.Advance()) {
89 ash::DisplayLayout layout
;
90 if (!ash::DisplayLayout::ConvertFromValue(it
.value(), &layout
)) {
91 LOG(WARNING
) << "Invalid preference value for " << it
.key();
95 if (it
.key().find(",") != std::string::npos
) {
96 std::vector
<std::string
> ids
;
97 base::SplitString(it
.key(), ',', &ids
);
98 int64 id1
= gfx::Display::kInvalidDisplayID
;
99 int64 id2
= gfx::Display::kInvalidDisplayID
;
100 if (!base::StringToInt64(ids
[0], &id1
) ||
101 !base::StringToInt64(ids
[1], &id2
) ||
102 id1
== gfx::Display::kInvalidDisplayID
||
103 id2
== gfx::Display::kInvalidDisplayID
) {
106 layout_store
->RegisterLayoutForDisplayIdPair(id1
, id2
, layout
);
111 void LoadDisplayProperties() {
112 PrefService
* local_state
= g_browser_process
->local_state();
113 const base::DictionaryValue
* properties
= local_state
->GetDictionary(
114 prefs::kDisplayProperties
);
115 for (base::DictionaryValue::Iterator
it(*properties
);
116 !it
.IsAtEnd(); it
.Advance()) {
117 const base::DictionaryValue
* dict_value
= NULL
;
118 if (!it
.value().GetAsDictionary(&dict_value
) || dict_value
== NULL
)
120 int64 id
= gfx::Display::kInvalidDisplayID
;
121 if (!base::StringToInt64(it
.key(), &id
) ||
122 id
== gfx::Display::kInvalidDisplayID
) {
125 gfx::Display::Rotation rotation
= gfx::Display::ROTATE_0
;
126 float ui_scale
= 1.0f
;
127 const gfx::Insets
* insets_to_set
= NULL
;
129 int rotation_value
= 0;
130 if (dict_value
->GetInteger("rotation", &rotation_value
)) {
131 rotation
= static_cast<gfx::Display::Rotation
>(rotation_value
);
133 int ui_scale_value
= 0;
134 if (dict_value
->GetInteger("ui-scale", &ui_scale_value
))
135 ui_scale
= static_cast<float>(ui_scale_value
) / 1000.0f
;
137 int width
= 0, height
= 0;
138 dict_value
->GetInteger("width", &width
);
139 dict_value
->GetInteger("height", &height
);
140 gfx::Size
resolution_in_pixels(width
, height
);
143 if (ValueToInsets(*dict_value
, &insets
))
144 insets_to_set
= &insets
;
145 GetDisplayManager()->RegisterDisplayProperty(id
,
149 resolution_in_pixels
);
153 void StoreDisplayLayoutPref(const ash::DisplayIdPair
& pair
,
154 const ash::DisplayLayout
& display_layout
) {
156 base::Int64ToString(pair
.first
) + "," + base::Int64ToString(pair
.second
);
158 PrefService
* local_state
= g_browser_process
->local_state();
159 DictionaryPrefUpdate
update(local_state
, prefs::kSecondaryDisplays
);
160 base::DictionaryValue
* pref_data
= update
.Get();
161 scoped_ptr
<base::Value
> layout_value(new base::DictionaryValue());
162 if (pref_data
->HasKey(name
)) {
163 base::Value
* value
= NULL
;
164 if (pref_data
->Get(name
, &value
) && value
!= NULL
)
165 layout_value
.reset(value
->DeepCopy());
167 if (ash::DisplayLayout::ConvertToValue(display_layout
, layout_value
.get()))
168 pref_data
->Set(name
, layout_value
.release());
171 void StoreCurrentDisplayLayoutPrefs() {
172 if (!UserCanSaveDisplayPreference() ||
173 GetDisplayManager()->num_connected_displays() < 2) {
177 ash::DisplayIdPair pair
= GetDisplayManager()->GetCurrentDisplayIdPair();
178 ash::DisplayLayout display_layout
=
179 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair
);
180 StoreDisplayLayoutPref(pair
, display_layout
);
183 void StoreCurrentDisplayProperties() {
184 ash::internal::DisplayManager
* display_manager
= GetDisplayManager();
185 PrefService
* local_state
= g_browser_process
->local_state();
187 DictionaryPrefUpdate
update(local_state
, prefs::kDisplayProperties
);
188 base::DictionaryValue
* pref_data
= update
.Get();
190 size_t num
= display_manager
->GetNumDisplays();
191 for (size_t i
= 0; i
< num
; ++i
) {
192 const gfx::Display
& display
= display_manager
->GetDisplayAt(i
);
193 int64 id
= display
.id();
194 ash::internal::DisplayInfo info
= display_manager
->GetDisplayInfo(id
);
196 scoped_ptr
<base::DictionaryValue
> property_value(
197 new base::DictionaryValue());
198 property_value
->SetInteger("rotation", static_cast<int>(info
.rotation()));
199 property_value
->SetInteger(
201 static_cast<int>(info
.configured_ui_scale() * 1000));
202 gfx::Size resolution
;
203 if (!display
.IsInternal() &&
204 display_manager
->GetSelectedResolutionForDisplayId(id
, &resolution
)) {
205 property_value
->SetInteger("width", resolution
.width());
206 property_value
->SetInteger("height", resolution
.height());
209 if (!info
.overscan_insets_in_dip().empty())
210 InsetsToValue(info
.overscan_insets_in_dip(), property_value
.get());
211 pref_data
->Set(base::Int64ToString(id
), property_value
.release());
215 typedef std::map
<chromeos::DisplayPowerState
, std::string
>
216 DisplayPowerStateToStringMap
;
218 const DisplayPowerStateToStringMap
* GetDisplayPowerStateToStringMap() {
219 // Don't save or retore ALL_OFF state. crbug.com/318456.
220 static const DisplayPowerStateToStringMap
* map
= ash::CreateToStringMap(
221 chromeos::DISPLAY_POWER_ALL_ON
, "all_on",
222 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
,
223 "internal_off_external_on",
224 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
,
225 "internal_on_external_off");
229 bool GetDisplayPowerStateFromString(const base::StringPiece
& state
,
230 chromeos::DisplayPowerState
* field
) {
231 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state
, field
))
233 LOG(ERROR
) << "Invalid display power state value:" << state
;
237 void StoreDisplayPowerState(DisplayPowerState power_state
) {
238 const DisplayPowerStateToStringMap
* map
= GetDisplayPowerStateToStringMap();
239 DisplayPowerStateToStringMap::const_iterator iter
= map
->find(power_state
);
240 if (iter
!= map
->end()) {
241 PrefService
* local_state
= g_browser_process
->local_state();
242 local_state
->SetString(prefs::kDisplayPowerState
, iter
->second
);
246 void StoreCurrentDisplayPowerState() {
247 StoreDisplayPowerState(
248 ash::Shell::GetInstance()->output_configurator()->power_state());
253 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple
* registry
) {
254 // Per-display preference.
255 registry
->RegisterDictionaryPref(prefs::kSecondaryDisplays
);
256 registry
->RegisterDictionaryPref(prefs::kDisplayProperties
);
257 DisplayPowerStateToStringMap::const_iterator iter
=
258 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON
);
259 registry
->RegisterStringPref(prefs::kDisplayPowerState
, iter
->second
);
262 void StoreDisplayPrefs() {
263 // Stores the power state regardless of the login status, because the power
264 // state respects to the current status (close/open) of the lid which can be
265 // changed in any situation. See crbug.com/285360
266 StoreCurrentDisplayPowerState();
268 // Do not store prefs when the confirmation dialog is shown.
269 if (!UserCanSaveDisplayPreference() ||
270 ash::Shell::GetInstance()->resolution_notification_controller()->
271 DoesNotificationTimeout()) {
274 StoreCurrentDisplayLayoutPrefs();
275 StoreCurrentDisplayProperties();
278 void SetCurrentDisplayLayout(const ash::DisplayLayout
& layout
) {
279 GetDisplayManager()->SetLayoutForCurrentDisplays(layout
);
282 void LoadDisplayPreferences(bool first_run_after_boot
) {
283 LoadDisplayLayouts();
284 LoadDisplayProperties();
285 if (!first_run_after_boot
) {
286 PrefService
* local_state
= g_browser_process
->local_state();
287 // Restore DisplayPowerState:
288 std::string value
= local_state
->GetString(prefs::kDisplayPowerState
);
289 chromeos::DisplayPowerState power_state
;
290 if (GetDisplayPowerStateFromString(value
, &power_state
)) {
291 ash::Shell::GetInstance()->output_configurator()->SetInitialDisplayPower(
297 // Stores the display layout for given display pairs.
298 void StoreDisplayLayoutPrefForTest(int64 id1
,
300 const ash::DisplayLayout
& layout
) {
301 StoreDisplayLayoutPref(std::make_pair(id1
, id2
), layout
);
304 // Stores the given |power_state|.
305 void StoreDisplayPowerStateForTest(DisplayPowerState power_state
) {
306 StoreDisplayPowerState(power_state
);
309 } // namespace chromeos