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. 4 5 #include "chrome/browser/chromeos/display/display_preferences.h" 6 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" 28 29 namespace chromeos { 30 namespace { 31 32 const char kInsetsTopKey[] = "insets_top"; 33 const char kInsetsLeftKey[] = "insets_left"; 34 const char kInsetsBottomKey[] = "insets_bottom"; 35 const char kInsetsRightKey[] = "insets_right"; 36 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) { 41 DCHECK(insets); 42 int top = 0; 43 int left = 0; 44 int bottom = 0; 45 int right = 0; 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); 51 return true; 52 } 53 return false; 54 } 55 56 void InsetsToValue(const gfx::Insets& insets, base::DictionaryValue* value) { 57 DCHECK(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()); 62 } 63 64 std::string ColorProfileToString(ui::ColorCalibrationProfile profile) { 65 switch (profile) { 66 case ui::COLOR_PROFILE_STANDARD: 67 return "standard"; 68 case ui::COLOR_PROFILE_DYNAMIC: 69 return "dynamic"; 70 case ui::COLOR_PROFILE_MOVIE: 71 return "movie"; 72 case ui::COLOR_PROFILE_READING: 73 return "reading"; 74 case ui::NUM_COLOR_PROFILES: 75 break; 76 } 77 NOTREACHED(); 78 return ""; 79 } 80 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; 90 NOTREACHED(); 91 return ui::COLOR_PROFILE_STANDARD; 92 } 93 94 ash::DisplayManager* GetDisplayManager() { 95 return ash::Shell::GetInstance()->display_manager(); 96 } 97 98 // Returns true id the current user can write display preferences to 99 // Local State. 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()); 106 } 107 108 void LoadDisplayLayouts() { 109 PrefService* local_state = g_browser_process->local_state(); 110 ash::DisplayLayoutStore* layout_store = GetDisplayManager()->layout_store(); 111 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(); 119 continue; 120 } 121 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) { 131 continue; 132 } 133 layout_store->RegisterLayoutForDisplayIdPair(id1, id2, layout); 134 } 135 } 136 } 137 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) 146 continue; 147 int64 id = gfx::Display::kInvalidDisplayID; 148 if (!base::StringToInt64(it.key(), &id) || 149 id == gfx::Display::kInvalidDisplayID) { 150 continue; 151 } 152 gfx::Display::Rotation rotation = gfx::Display::ROTATE_0; 153 float ui_scale = 1.0f; 154 const gfx::Insets* insets_to_set = NULL; 155 156 int rotation_value = 0; 157 if (dict_value->GetInteger("rotation", &rotation_value)) { 158 rotation = static_cast<gfx::Display::Rotation>(rotation_value); 159 } 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; 163 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); 168 169 float device_scale_factor = 1.0; 170 int dsf_value = 0; 171 if (dict_value->GetInteger("device-scale-factor", &dsf_value)) 172 device_scale_factor = static_cast<float>(dsf_value) / 1000.0f; 173 174 gfx::Insets insets; 175 if (ValueToInsets(*dict_value, &insets)) 176 insets_to_set = &insets; 177 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, 183 rotation, 184 ui_scale, 185 insets_to_set, 186 resolution_in_pixels, 187 device_scale_factor, 188 color_profile); 189 } 190 } 191 192 void LoadDisplayRotationState() { 193 PrefService* local_state = g_browser_process->local_state(); 194 const base::DictionaryValue* properties = 195 local_state->GetDictionary(prefs::kDisplayRotationLock); 196 197 bool rotation_lock = false; 198 if (!properties->GetBoolean("lock", &rotation_lock)) 199 return; 200 201 int rotation = gfx::Display::ROTATE_0; 202 if (!properties->GetInteger("orientation", &rotation)) 203 return; 204 205 GetDisplayManager()->RegisterDisplayRotationProperties(rotation_lock, 206 static_cast<gfx::Display::Rotation>(rotation)); 207 } 208 209 void StoreDisplayLayoutPref(const ash::DisplayIdPair& pair, 210 const ash::DisplayLayout& display_layout) { 211 std::string name = 212 base::Int64ToString(pair.first) + "," + base::Int64ToString(pair.second); 213 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()); 222 } 223 if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get())) 224 pref_data->Set(name, layout_value.release()); 225 } 226 227 void StoreCurrentDisplayLayoutPrefs() { 228 if (!UserCanSaveDisplayPreference() || 229 GetDisplayManager()->num_connected_displays() < 2) { 230 return; 231 } 232 233 ash::DisplayIdPair pair = GetDisplayManager()->GetCurrentDisplayIdPair(); 234 ash::DisplayLayout display_layout = 235 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair); 236 StoreDisplayLayoutPref(pair, display_layout); 237 } 238 239 void StoreCurrentDisplayProperties() { 240 ash::DisplayManager* display_manager = GetDisplayManager(); 241 PrefService* local_state = g_browser_process->local_state(); 242 243 DictionaryPrefUpdate update(local_state, prefs::kDisplayProperties); 244 base::DictionaryValue* pref_data = update.Get(); 245 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); 251 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( 256 "ui-scale", 257 static_cast<int>(info.configured_ui_scale() * 1000)); 258 ash::DisplayMode mode; 259 if (!display.IsInternal() && 260 display_manager->GetSelectedModeForDisplayId(id, &mode) && 261 !mode.native) { 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)); 267 } 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())); 273 } 274 pref_data->Set(base::Int64ToString(id), property_value.release()); 275 } 276 } 277 278 typedef std::map<chromeos::DisplayPowerState, std::string> 279 DisplayPowerStateToStringMap; 280 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"); 289 return map; 290 } 291 292 bool GetDisplayPowerStateFromString(const base::StringPiece& state, 293 chromeos::DisplayPowerState* field) { 294 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state, field)) 295 return true; 296 LOG(ERROR) << "Invalid display power state value:" << state; 297 return false; 298 } 299 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); 306 } 307 } 308 309 void StoreCurrentDisplayPowerState() { 310 StoreDisplayPowerState( 311 ash::Shell::GetInstance()->display_configurator()-> 312 requested_power_state()); 313 } 314 315 void StoreCurrentDisplayRotationLockPrefs() { 316 bool rotation_lock = ash::Shell::GetInstance()->display_manager()-> 317 registered_internal_display_rotation_lock(); 318 StoreDisplayRotationPrefs(rotation_lock); 319 } 320 321 } // namespace 322 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); 331 } 332 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(); 339 340 // Do not store prefs when the confirmation dialog is shown. 341 if (!UserCanSaveDisplayPreference() || 342 !ash::Shell::GetInstance()->ShouldSaveDisplaySettings()) { 343 return; 344 } 345 346 StoreCurrentDisplayLayoutPrefs(); 347 StoreCurrentDisplayProperties(); 348 } 349 350 void StoreDisplayRotationPrefs(bool rotation_lock) { 351 ash::DisplayManager* display_manager = GetDisplayManager(); 352 if (!display_manager->HasInternalDisplay()) 353 return; 354 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)); 362 } 363 364 void SetCurrentDisplayLayout(const ash::DisplayLayout& layout) { 365 GetDisplayManager()->SetLayoutForCurrentDisplays(layout); 366 } 367 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( 379 power_state); 380 } 381 } 382 } 383 384 // Stores the display layout for given display pairs. 385 void StoreDisplayLayoutPrefForTest(int64 id1, 386 int64 id2, 387 const ash::DisplayLayout& layout) { 388 StoreDisplayLayoutPref(std::make_pair(id1, id2), layout); 389 } 390 391 // Stores the given |power_state|. 392 void StoreDisplayPowerStateForTest(DisplayPowerState power_state) { 393 StoreDisplayPowerState(power_state); 394 } 395 396 } // namespace chromeos 397