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->IsLoggedInAsRegularUser() ||
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 StoreDisplayLayoutPref(const ash::DisplayIdPair& pair,
193 const ash::DisplayLayout& display_layout) {
195 base::Int64ToString(pair.first) + "," + base::Int64ToString(pair.second);
197 PrefService* local_state = g_browser_process->local_state();
198 DictionaryPrefUpdate update(local_state, prefs::kSecondaryDisplays);
199 base::DictionaryValue* pref_data = update.Get();
200 scoped_ptr<base::Value> layout_value(new base::DictionaryValue());
201 if (pref_data->HasKey(name)) {
202 base::Value* value = NULL;
203 if (pref_data->Get(name, &value) && value != NULL)
204 layout_value.reset(value->DeepCopy());
206 if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get()))
207 pref_data->Set(name, layout_value.release());
210 void StoreCurrentDisplayLayoutPrefs() {
211 if (!UserCanSaveDisplayPreference() ||
212 GetDisplayManager()->num_connected_displays() < 2) {
216 ash::DisplayIdPair pair = GetDisplayManager()->GetCurrentDisplayIdPair();
217 ash::DisplayLayout display_layout =
218 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair);
219 StoreDisplayLayoutPref(pair, display_layout);
222 void StoreCurrentDisplayProperties() {
223 ash::DisplayManager* display_manager = GetDisplayManager();
224 PrefService* local_state = g_browser_process->local_state();
226 DictionaryPrefUpdate update(local_state, prefs::kDisplayProperties);
227 base::DictionaryValue* pref_data = update.Get();
229 size_t num = display_manager->GetNumDisplays();
230 for (size_t i = 0; i < num; ++i) {
231 const gfx::Display& display = display_manager->GetDisplayAt(i);
232 int64 id = display.id();
233 ash::DisplayInfo info = display_manager->GetDisplayInfo(id);
235 scoped_ptr<base::DictionaryValue> property_value(
236 new base::DictionaryValue());
237 property_value->SetInteger("rotation", static_cast<int>(info.rotation()));
238 property_value->SetInteger(
240 static_cast<int>(info.configured_ui_scale() * 1000));
241 ash::DisplayMode mode;
242 if (!display.IsInternal() &&
243 display_manager->GetSelectedModeForDisplayId(id, &mode) &&
245 property_value->SetInteger("width", mode.size.width());
246 property_value->SetInteger("height", mode.size.height());
247 property_value->SetInteger(
248 "device-scale-factor",
249 static_cast<int>(mode.device_scale_factor * 1000));
251 if (!info.overscan_insets_in_dip().empty())
252 InsetsToValue(info.overscan_insets_in_dip(), property_value.get());
253 if (info.color_profile() != ui::COLOR_PROFILE_STANDARD) {
254 property_value->SetString(
255 "color_profile_name", ColorProfileToString(info.color_profile()));
257 pref_data->Set(base::Int64ToString(id), property_value.release());
261 typedef std::map<chromeos::DisplayPowerState, std::string>
262 DisplayPowerStateToStringMap;
264 const DisplayPowerStateToStringMap* GetDisplayPowerStateToStringMap() {
265 // Don't save or retore ALL_OFF state. crbug.com/318456.
266 static const DisplayPowerStateToStringMap* map = ash::CreateToStringMap(
267 chromeos::DISPLAY_POWER_ALL_ON, "all_on",
268 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
269 "internal_off_external_on",
270 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF,
271 "internal_on_external_off");
275 bool GetDisplayPowerStateFromString(const base::StringPiece& state,
276 chromeos::DisplayPowerState* field) {
277 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state, field))
279 LOG(ERROR) << "Invalid display power state value:" << state;
283 void StoreDisplayPowerState(DisplayPowerState power_state) {
284 const DisplayPowerStateToStringMap* map = GetDisplayPowerStateToStringMap();
285 DisplayPowerStateToStringMap::const_iterator iter = map->find(power_state);
286 if (iter != map->end()) {
287 PrefService* local_state = g_browser_process->local_state();
288 local_state->SetString(prefs::kDisplayPowerState, iter->second);
292 void StoreCurrentDisplayPowerState() {
293 StoreDisplayPowerState(
294 ash::Shell::GetInstance()->display_configurator()->power_state());
299 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple* registry) {
300 // Per-display preference.
301 registry->RegisterDictionaryPref(prefs::kSecondaryDisplays);
302 registry->RegisterDictionaryPref(prefs::kDisplayProperties);
303 DisplayPowerStateToStringMap::const_iterator iter =
304 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON);
305 registry->RegisterStringPref(prefs::kDisplayPowerState, iter->second);
308 void StoreDisplayPrefs() {
309 // Stores the power state regardless of the login status, because the power
310 // state respects to the current status (close/open) of the lid which can be
311 // changed in any situation. See crbug.com/285360
312 StoreCurrentDisplayPowerState();
314 // Do not store prefs when the confirmation dialog is shown.
315 if (!UserCanSaveDisplayPreference() ||
316 !ash::Shell::GetInstance()->ShouldSaveDisplaySettings()) {
320 StoreCurrentDisplayLayoutPrefs();
321 StoreCurrentDisplayProperties();
324 void SetCurrentDisplayLayout(const ash::DisplayLayout& layout) {
325 GetDisplayManager()->SetLayoutForCurrentDisplays(layout);
328 void LoadDisplayPreferences(bool first_run_after_boot) {
329 LoadDisplayLayouts();
330 LoadDisplayProperties();
331 if (!first_run_after_boot) {
332 PrefService* local_state = g_browser_process->local_state();
333 // Restore DisplayPowerState:
334 std::string value = local_state->GetString(prefs::kDisplayPowerState);
335 chromeos::DisplayPowerState power_state;
336 if (GetDisplayPowerStateFromString(value, &power_state)) {
337 ash::Shell::GetInstance()->display_configurator()->SetInitialDisplayPower(
343 // Stores the display layout for given display pairs.
344 void StoreDisplayLayoutPrefForTest(int64 id1,
346 const ash::DisplayLayout& layout) {
347 StoreDisplayLayoutPref(std::make_pair(id1, id2), layout);
350 // Stores the given |power_state|.
351 void StoreDisplayPowerStateForTest(DisplayPowerState power_state) {
352 StoreDisplayPowerState(power_state);
355 } // namespace chromeos