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/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" 30 31 namespace chromeos { 32 namespace { 33 34 const char kInsetsTopKey[] = "insets_top"; 35 const char kInsetsLeftKey[] = "insets_left"; 36 const char kInsetsBottomKey[] = "insets_bottom"; 37 const char kInsetsRightKey[] = "insets_right"; 38 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) { 43 DCHECK(insets); 44 int top = 0; 45 int left = 0; 46 int bottom = 0; 47 int right = 0; 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); 53 return true; 54 } 55 return false; 56 } 57 58 void InsetsToValue(const gfx::Insets& insets, base::DictionaryValue* value) { 59 DCHECK(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()); 64 } 65 66 ash::internal::DisplayManager* GetDisplayManager() { 67 return ash::Shell::GetInstance()->display_manager(); 68 } 69 70 // Returns true id the current user can write display preferences to 71 // Local State. 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()); 78 } 79 80 void LoadDisplayLayouts() { 81 PrefService* local_state = g_browser_process->local_state(); 82 ash::internal::DisplayLayoutStore* layout_store = 83 GetDisplayManager()->layout_store(); 84 85 const base::DictionaryValue* layouts = local_state->GetDictionary( 86 prefs::kSecondaryDisplays); 87 for (DictionaryValue::Iterator it(*layouts); !it.IsAtEnd(); it.Advance()) { 88 ash::DisplayLayout layout; 89 if (!ash::DisplayLayout::ConvertFromValue(it.value(), &layout)) { 90 LOG(WARNING) << "Invalid preference value for " << it.key(); 91 continue; 92 } 93 94 if (it.key().find(",") != std::string::npos) { 95 std::vector<std::string> ids; 96 base::SplitString(it.key(), ',', &ids); 97 int64 id1 = gfx::Display::kInvalidDisplayID; 98 int64 id2 = gfx::Display::kInvalidDisplayID; 99 if (!base::StringToInt64(ids[0], &id1) || 100 !base::StringToInt64(ids[1], &id2) || 101 id1 == gfx::Display::kInvalidDisplayID || 102 id2 == gfx::Display::kInvalidDisplayID) { 103 continue; 104 } 105 layout_store->RegisterLayoutForDisplayIdPair(id1, id2, layout); 106 } 107 } 108 } 109 110 void LoadDisplayProperties() { 111 PrefService* local_state = g_browser_process->local_state(); 112 const base::DictionaryValue* properties = local_state->GetDictionary( 113 prefs::kDisplayProperties); 114 for (DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); it.Advance()) { 115 const base::DictionaryValue* dict_value = NULL; 116 if (!it.value().GetAsDictionary(&dict_value) || dict_value == NULL) 117 continue; 118 int64 id = gfx::Display::kInvalidDisplayID; 119 if (!base::StringToInt64(it.key(), &id) || 120 id == gfx::Display::kInvalidDisplayID) { 121 continue; 122 } 123 gfx::Display::Rotation rotation = gfx::Display::ROTATE_0; 124 float ui_scale = 1.0f; 125 const gfx::Insets* insets_to_set = NULL; 126 127 int rotation_value = 0; 128 if (dict_value->GetInteger("rotation", &rotation_value)) { 129 rotation = static_cast<gfx::Display::Rotation>(rotation_value); 130 } 131 int ui_scale_value = 0; 132 if (dict_value->GetInteger("ui-scale", &ui_scale_value)) 133 ui_scale = static_cast<float>(ui_scale_value) / 1000.0f; 134 135 int width = 0, height = 0; 136 dict_value->GetInteger("width", &width); 137 dict_value->GetInteger("height", &height); 138 gfx::Size resolution_in_pixels(width, height); 139 140 gfx::Insets insets; 141 if (ValueToInsets(*dict_value, &insets)) 142 insets_to_set = &insets; 143 GetDisplayManager()->RegisterDisplayProperty(id, 144 rotation, 145 ui_scale, 146 insets_to_set, 147 resolution_in_pixels); 148 } 149 } 150 151 void StoreDisplayLayoutPref(const ash::DisplayIdPair& pair, 152 const ash::DisplayLayout& display_layout) { 153 std::string name = 154 base::Int64ToString(pair.first) + "," + base::Int64ToString(pair.second); 155 156 PrefService* local_state = g_browser_process->local_state(); 157 DictionaryPrefUpdate update(local_state, prefs::kSecondaryDisplays); 158 base::DictionaryValue* pref_data = update.Get(); 159 scoped_ptr<base::Value> layout_value(new base::DictionaryValue()); 160 if (pref_data->HasKey(name)) { 161 base::Value* value = NULL; 162 if (pref_data->Get(name, &value) && value != NULL) 163 layout_value.reset(value->DeepCopy()); 164 } 165 if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get())) 166 pref_data->Set(name, layout_value.release()); 167 } 168 169 void StoreCurrentDisplayLayoutPrefs() { 170 if (!UserCanSaveDisplayPreference() || 171 GetDisplayManager()->num_connected_displays() < 2) { 172 return; 173 } 174 175 ash::DisplayIdPair pair = GetDisplayManager()->GetCurrentDisplayIdPair(); 176 ash::DisplayLayout display_layout = 177 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair); 178 StoreDisplayLayoutPref(pair, display_layout); 179 } 180 181 void StoreCurrentDisplayProperties() { 182 ash::internal::DisplayManager* display_manager = GetDisplayManager(); 183 PrefService* local_state = g_browser_process->local_state(); 184 185 DictionaryPrefUpdate update(local_state, prefs::kDisplayProperties); 186 base::DictionaryValue* pref_data = update.Get(); 187 188 size_t num = display_manager->GetNumDisplays(); 189 for (size_t i = 0; i < num; ++i) { 190 const gfx::Display& display = display_manager->GetDisplayAt(i); 191 int64 id = display.id(); 192 ash::internal::DisplayInfo info = display_manager->GetDisplayInfo(id); 193 194 scoped_ptr<base::DictionaryValue> property_value( 195 new base::DictionaryValue()); 196 property_value->SetInteger("rotation", static_cast<int>(info.rotation())); 197 property_value->SetInteger( 198 "ui-scale", 199 static_cast<int>(info.configured_ui_scale() * 1000)); 200 gfx::Size resolution; 201 if (!display.IsInternal() && 202 display_manager->GetSelectedResolutionForDisplayId(id, &resolution)) { 203 property_value->SetInteger("width", resolution.width()); 204 property_value->SetInteger("height", resolution.height()); 205 } 206 207 if (!info.overscan_insets_in_dip().empty()) 208 InsetsToValue(info.overscan_insets_in_dip(), property_value.get()); 209 pref_data->Set(base::Int64ToString(id), property_value.release()); 210 } 211 } 212 213 typedef std::map<chromeos::DisplayPowerState, std::string> 214 DisplayPowerStateToStringMap; 215 216 const DisplayPowerStateToStringMap* GetDisplayPowerStateToStringMap() { 217 // Don't save or retore ALL_OFF state. crbug.com/318456. 218 static const DisplayPowerStateToStringMap* map = ash::CreateToStringMap( 219 chromeos::DISPLAY_POWER_ALL_ON, "all_on", 220 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, 221 "internal_off_external_on", 222 chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF, 223 "internal_on_external_off"); 224 return map; 225 } 226 227 bool GetDisplayPowerStateFromString(const base::StringPiece& state, 228 chromeos::DisplayPowerState* field) { 229 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state, field)) 230 return true; 231 LOG(ERROR) << "Invalid display power state value:" << state; 232 return false; 233 } 234 235 void StoreDisplayPowerState(DisplayPowerState power_state) { 236 const DisplayPowerStateToStringMap* map = GetDisplayPowerStateToStringMap(); 237 DisplayPowerStateToStringMap::const_iterator iter = map->find(power_state); 238 if (iter != map->end()) { 239 PrefService* local_state = g_browser_process->local_state(); 240 local_state->SetString(prefs::kDisplayPowerState, iter->second); 241 } 242 } 243 244 void StoreCurrentDisplayPowerState() { 245 StoreDisplayPowerState( 246 ash::Shell::GetInstance()->output_configurator()->power_state()); 247 } 248 249 } // namespace 250 251 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple* registry) { 252 // Per-display preference. 253 registry->RegisterDictionaryPref(prefs::kSecondaryDisplays); 254 registry->RegisterDictionaryPref(prefs::kDisplayProperties); 255 DisplayPowerStateToStringMap::const_iterator iter = 256 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON); 257 registry->RegisterStringPref(prefs::kDisplayPowerState, iter->second); 258 } 259 260 void StoreDisplayPrefs() { 261 // Stores the power state regardless of the login status, because the power 262 // state respects to the current status (close/open) of the lid which can be 263 // changed in any situation. See crbug.com/285360 264 StoreCurrentDisplayPowerState(); 265 266 // Do not store prefs when the confirmation dialog is shown. 267 if (!UserCanSaveDisplayPreference() || 268 ash::Shell::GetInstance()->resolution_notification_controller()-> 269 DoesNotificationTimeout()) { 270 return; 271 } 272 StoreCurrentDisplayLayoutPrefs(); 273 StoreCurrentDisplayProperties(); 274 } 275 276 void SetCurrentDisplayLayout(const ash::DisplayLayout& layout) { 277 GetDisplayManager()->SetLayoutForCurrentDisplays(layout); 278 } 279 280 void LoadDisplayPreferences(bool first_run_after_boot) { 281 LoadDisplayLayouts(); 282 LoadDisplayProperties(); 283 if (!first_run_after_boot) { 284 PrefService* local_state = g_browser_process->local_state(); 285 // Restore DisplayPowerState: 286 std::string value = local_state->GetString(prefs::kDisplayPowerState); 287 chromeos::DisplayPowerState power_state; 288 if (GetDisplayPowerStateFromString(value, &power_state)) { 289 ash::Shell::GetInstance()->output_configurator()->SetInitialDisplayPower( 290 power_state); 291 } 292 } 293 } 294 295 // Stores the display layout for given display pairs. 296 void StoreDisplayLayoutPrefForTest(int64 id1, 297 int64 id2, 298 const ash::DisplayLayout& layout) { 299 StoreDisplayLayoutPref(std::make_pair(id1, id2), layout); 300 } 301 302 // Stores the given |power_state|. 303 void StoreDisplayPowerStateForTest(DisplayPowerState power_state) { 304 StoreDisplayPowerState(power_state); 305 } 306 307 } // namespace chromeos 308