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 "third_party/cros_system_api/dbus/service_constants.h"
24 #include "ui/gfx/display.h"
25 #include "ui/gfx/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 UserManager* user_manager = UserManager::Get();
103 return user_manager->IsUserLoggedIn() &&
104 (user_manager->IsLoggedInAsRegularUser() ||
105 user_manager->IsLoggedInAsLocallyManagedUser() ||
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;
125 base::SplitString(it.key(), ',', &ids);
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);
171 if (ValueToInsets(*dict_value, &insets))
172 insets_to_set = &insets;
174 ui::ColorCalibrationProfile color_profile = ui::COLOR_PROFILE_STANDARD;
175 std::string color_profile_name;
176 if (dict_value->GetString("color_profile_name", &color_profile_name))
177 color_profile = StringToColorProfile(color_profile_name);
178 GetDisplayManager()->RegisterDisplayProperty(id,
182 resolution_in_pixels,
187 void StoreDisplayLayoutPref(const ash::DisplayIdPair& pair,
188 const ash::DisplayLayout& display_layout) {
190 base::Int64ToString(pair.first) + "," + base::Int64ToString(pair.second);
192 PrefService* local_state = g_browser_process->local_state();
193 DictionaryPrefUpdate update(local_state, prefs::kSecondaryDisplays);
194 base::DictionaryValue* pref_data = update.Get();
195 scoped_ptr<base::Value> layout_value(new base::DictionaryValue());
196 if (pref_data->HasKey(name)) {
197 base::Value* value = NULL;
198 if (pref_data->Get(name, &value) && value != NULL)
199 layout_value.reset(value->DeepCopy());
201 if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get()))
202 pref_data->Set(name, layout_value.release());
205 void StoreCurrentDisplayLayoutPrefs() {
206 if (!UserCanSaveDisplayPreference() ||
207 GetDisplayManager()->num_connected_displays() < 2) {
211 ash::DisplayIdPair pair = GetDisplayManager()->GetCurrentDisplayIdPair();
212 ash::DisplayLayout display_layout =
213 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair);
214 StoreDisplayLayoutPref(pair, display_layout);
217 void StoreCurrentDisplayProperties() {
218 ash::DisplayManager* display_manager = GetDisplayManager();
219 PrefService* local_state = g_browser_process->local_state();
221 DictionaryPrefUpdate update(local_state, prefs::kDisplayProperties);
222 base::DictionaryValue* pref_data = update.Get();
224 size_t num = display_manager->GetNumDisplays();
225 for (size_t i = 0; i < num; ++i) {
226 const gfx::Display& display = display_manager->GetDisplayAt(i);
227 int64 id = display.id();
228 ash::DisplayInfo info = display_manager->GetDisplayInfo(id);
230 scoped_ptr<base::DictionaryValue> property_value(
231 new base::DictionaryValue());
232 property_value->SetInteger("rotation", static_cast<int>(info.rotation()));
233 property_value->SetInteger(
235 static_cast<int>(info.configured_ui_scale() * 1000));
236 ash::DisplayMode mode;
237 if (!display.IsInternal() &&
238 display_manager->GetSelectedModeForDisplayId(id, &mode) &&
240 property_value->SetInteger("width", mode.size.width());
241 property_value->SetInteger("height", mode.size.height());
243 if (!info.overscan_insets_in_dip().empty())
244 InsetsToValue(info.overscan_insets_in_dip(), property_value.get());
245 if (info.color_profile() != ui::COLOR_PROFILE_STANDARD) {
246 property_value->SetString(
247 "color_profile_name", ColorProfileToString(info.color_profile()));
249 pref_data->Set(base::Int64ToString(id), property_value.release());
253 typedef std::map<chromeos::DisplayPowerState, std::string>
254 DisplayPowerStateToStringMap;
256 const DisplayPowerStateToStringMap* GetDisplayPowerStateToStringMap() {
257 // Don't save or retore ALL_OFF state. crbug.com/318456.
258 static const DisplayPowerStateToStringMap* map = ash::CreateToStringMap(
259 chromeos::DISPLAY_POWER_ALL_ON, "all_on",
260 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
261 "internal_off_external_on",
262 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF,
263 "internal_on_external_off");
267 bool GetDisplayPowerStateFromString(const base::StringPiece& state,
268 chromeos::DisplayPowerState* field) {
269 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state, field))
271 LOG(ERROR) << "Invalid display power state value:" << state;
275 void StoreDisplayPowerState(DisplayPowerState power_state) {
276 const DisplayPowerStateToStringMap* map = GetDisplayPowerStateToStringMap();
277 DisplayPowerStateToStringMap::const_iterator iter = map->find(power_state);
278 if (iter != map->end()) {
279 PrefService* local_state = g_browser_process->local_state();
280 local_state->SetString(prefs::kDisplayPowerState, iter->second);
284 void StoreCurrentDisplayPowerState() {
285 StoreDisplayPowerState(
286 ash::Shell::GetInstance()->display_configurator()->power_state());
291 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple* registry) {
292 // Per-display preference.
293 registry->RegisterDictionaryPref(prefs::kSecondaryDisplays);
294 registry->RegisterDictionaryPref(prefs::kDisplayProperties);
295 DisplayPowerStateToStringMap::const_iterator iter =
296 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON);
297 registry->RegisterStringPref(prefs::kDisplayPowerState, iter->second);
300 void StoreDisplayPrefs() {
301 // Stores the power state regardless of the login status, because the power
302 // state respects to the current status (close/open) of the lid which can be
303 // changed in any situation. See crbug.com/285360
304 StoreCurrentDisplayPowerState();
306 // Do not store prefs when the confirmation dialog is shown.
307 if (!UserCanSaveDisplayPreference() ||
308 ash::Shell::GetInstance()->resolution_notification_controller()->
309 DoesNotificationTimeout()) {
312 StoreCurrentDisplayLayoutPrefs();
313 StoreCurrentDisplayProperties();
316 void SetCurrentDisplayLayout(const ash::DisplayLayout& layout) {
317 GetDisplayManager()->SetLayoutForCurrentDisplays(layout);
320 void LoadDisplayPreferences(bool first_run_after_boot) {
321 LoadDisplayLayouts();
322 LoadDisplayProperties();
323 if (!first_run_after_boot) {
324 PrefService* local_state = g_browser_process->local_state();
325 // Restore DisplayPowerState:
326 std::string value = local_state->GetString(prefs::kDisplayPowerState);
327 chromeos::DisplayPowerState power_state;
328 if (GetDisplayPowerStateFromString(value, &power_state)) {
329 ash::Shell::GetInstance()->display_configurator()->SetInitialDisplayPower(
335 // Stores the display layout for given display pairs.
336 void StoreDisplayLayoutPrefForTest(int64 id1,
338 const ash::DisplayLayout& layout) {
339 StoreDisplayLayoutPref(std::make_pair(id1, id2), layout);
342 // Stores the given |power_state|.
343 void StoreDisplayPowerStateForTest(DisplayPowerState power_state) {
344 StoreDisplayPowerState(power_state);
347 } // namespace chromeos