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_controller.h" 8 #include "ash/display/display_layout_store.h" 9 #include "ash/display/display_manager.h" 10 #include "ash/display/display_pref_util.h" 11 #include "ash/display/resolution_notification_controller.h" 12 #include "ash/shell.h" 13 #include "base/prefs/pref_registry_simple.h" 14 #include "base/prefs/pref_service.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/browser/prefs/scoped_user_pref_update.h" 23 #include "chrome/common/pref_names.h" 24 #include "chromeos/display/output_configurator.h" 25 #include "third_party/cros_system_api/dbus/service_constants.h" 26 #include "ui/gfx/display.h" 27 #include "ui/gfx/insets.h" 28 #include "ui/gfx/screen.h" 29 #include "url/url_canon.h" 30 #include "url/url_util.h" 31 32 namespace chromeos { 33 namespace { 34 35 const char kInsetsTopKey[] = "insets_top"; 36 const char kInsetsLeftKey[] = "insets_left"; 37 const char kInsetsBottomKey[] = "insets_bottom"; 38 const char kInsetsRightKey[] = "insets_right"; 39 40 // This kind of boilerplates should be done by base::JSONValueConverter but it 41 // doesn't support classes like gfx::Insets for now. 42 // TODO(mukai): fix base::JSONValueConverter and use it here. 43 bool ValueToInsets(const base::DictionaryValue& value, gfx::Insets* insets) { 44 DCHECK(insets); 45 int top = 0; 46 int left = 0; 47 int bottom = 0; 48 int right = 0; 49 if (value.GetInteger(kInsetsTopKey, &top) && 50 value.GetInteger(kInsetsLeftKey, &left) && 51 value.GetInteger(kInsetsBottomKey, &bottom) && 52 value.GetInteger(kInsetsRightKey, &right)) { 53 insets->Set(top, left, bottom, right); 54 return true; 55 } 56 return false; 57 } 58 59 void InsetsToValue(const gfx::Insets& insets, base::DictionaryValue* value) { 60 DCHECK(value); 61 value->SetInteger(kInsetsTopKey, insets.top()); 62 value->SetInteger(kInsetsLeftKey, insets.left()); 63 value->SetInteger(kInsetsBottomKey, insets.bottom()); 64 value->SetInteger(kInsetsRightKey, insets.right()); 65 } 66 67 ash::internal::DisplayManager* GetDisplayManager() { 68 return ash::Shell::GetInstance()->display_manager(); 69 } 70 71 // Returns true id the current user can write display preferences to 72 // Local State. 73 bool IsValidUser() { 74 UserManager* user_manager = UserManager::Get(); 75 return (user_manager->IsUserLoggedIn() && 76 !user_manager->IsLoggedInAsDemoUser() && 77 !user_manager->IsLoggedInAsGuest() && 78 !user_manager->IsLoggedInAsStub()); 79 } 80 81 ash::DisplayController* GetDisplayController() { 82 return ash::Shell::GetInstance()->display_controller(); 83 } 84 85 void LoadDisplayLayouts() { 86 PrefService* local_state = g_browser_process->local_state(); 87 ash::internal::DisplayLayoutStore* layout_store = 88 GetDisplayManager()->layout_store(); 89 90 const base::DictionaryValue* layouts = local_state->GetDictionary( 91 prefs::kSecondaryDisplays); 92 for (DictionaryValue::Iterator it(*layouts); !it.IsAtEnd(); it.Advance()) { 93 ash::DisplayLayout layout; 94 if (!ash::DisplayLayout::ConvertFromValue(it.value(), &layout)) { 95 LOG(WARNING) << "Invalid preference value for " << it.key(); 96 continue; 97 } 98 99 if (it.key().find(",") != std::string::npos) { 100 std::vector<std::string> ids; 101 base::SplitString(it.key(), ',', &ids); 102 int64 id1 = gfx::Display::kInvalidDisplayID; 103 int64 id2 = gfx::Display::kInvalidDisplayID; 104 if (!base::StringToInt64(ids[0], &id1) || 105 !base::StringToInt64(ids[1], &id2) || 106 id1 == gfx::Display::kInvalidDisplayID || 107 id2 == gfx::Display::kInvalidDisplayID) { 108 continue; 109 } 110 layout_store->RegisterLayoutForDisplayIdPair(id1, id2, layout); 111 } 112 } 113 } 114 115 void LoadDisplayProperties() { 116 PrefService* local_state = g_browser_process->local_state(); 117 const base::DictionaryValue* properties = local_state->GetDictionary( 118 prefs::kDisplayProperties); 119 for (DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); it.Advance()) { 120 const base::DictionaryValue* dict_value = NULL; 121 if (!it.value().GetAsDictionary(&dict_value) || dict_value == NULL) 122 continue; 123 int64 id = gfx::Display::kInvalidDisplayID; 124 if (!base::StringToInt64(it.key(), &id) || 125 id == gfx::Display::kInvalidDisplayID) { 126 continue; 127 } 128 gfx::Display::Rotation rotation = gfx::Display::ROTATE_0; 129 float ui_scale = 1.0f; 130 const gfx::Insets* insets_to_set = NULL; 131 132 int rotation_value = 0; 133 if (dict_value->GetInteger("rotation", &rotation_value)) { 134 rotation = static_cast<gfx::Display::Rotation>(rotation_value); 135 } 136 int ui_scale_value = 0; 137 if (dict_value->GetInteger("ui-scale", &ui_scale_value)) 138 ui_scale = static_cast<float>(ui_scale_value) / 1000.0f; 139 140 int width = 0, height = 0; 141 dict_value->GetInteger("width", &width); 142 dict_value->GetInteger("height", &height); 143 gfx::Size resolution_in_pixels(width, height); 144 145 gfx::Insets insets; 146 if (ValueToInsets(*dict_value, &insets)) 147 insets_to_set = &insets; 148 GetDisplayManager()->RegisterDisplayProperty(id, 149 rotation, 150 ui_scale, 151 insets_to_set, 152 resolution_in_pixels); 153 } 154 } 155 156 void StoreDisplayLayoutPref(const ash::DisplayIdPair& pair, 157 const ash::DisplayLayout& display_layout) { 158 std::string name = 159 base::Int64ToString(pair.first) + "," + base::Int64ToString(pair.second); 160 161 PrefService* local_state = g_browser_process->local_state(); 162 DictionaryPrefUpdate update(local_state, prefs::kSecondaryDisplays); 163 base::DictionaryValue* pref_data = update.Get(); 164 scoped_ptr<base::Value> layout_value(new base::DictionaryValue()); 165 if (pref_data->HasKey(name)) { 166 base::Value* value = NULL; 167 if (pref_data->Get(name, &value) && value != NULL) 168 layout_value.reset(value->DeepCopy()); 169 } 170 if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get())) 171 pref_data->Set(name, layout_value.release()); 172 } 173 174 void StoreCurrentDisplayLayoutPrefs() { 175 if (!IsValidUser() || GetDisplayManager()->num_connected_displays() < 2) 176 return; 177 178 ash::DisplayIdPair pair = GetDisplayController()->GetCurrentDisplayIdPair(); 179 ash::DisplayLayout display_layout = 180 GetDisplayManager()->layout_store()->GetRegisteredDisplayLayout(pair); 181 StoreDisplayLayoutPref(pair, display_layout); 182 } 183 184 void StoreCurrentDisplayProperties() { 185 ash::internal::DisplayManager* display_manager = GetDisplayManager(); 186 PrefService* local_state = g_browser_process->local_state(); 187 188 DictionaryPrefUpdate update(local_state, prefs::kDisplayProperties); 189 base::DictionaryValue* pref_data = update.Get(); 190 191 size_t num = display_manager->GetNumDisplays(); 192 for (size_t i = 0; i < num; ++i) { 193 const gfx::Display& display = display_manager->GetDisplayAt(i); 194 int64 id = display.id(); 195 ash::internal::DisplayInfo info = display_manager->GetDisplayInfo(id); 196 197 scoped_ptr<base::DictionaryValue> property_value( 198 new base::DictionaryValue()); 199 property_value->SetInteger("rotation", static_cast<int>(info.rotation())); 200 property_value->SetInteger("ui-scale", 201 static_cast<int>(info.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()); 207 } 208 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()); 212 } 213 } 214 215 typedef std::map<chromeos::DisplayPowerState, std::string> 216 DisplayPowerStateToStringMap; 217 218 const DisplayPowerStateToStringMap* GetDisplayPowerStateToStringMap() { 219 static const DisplayPowerStateToStringMap* map = ash::CreateToStringMap( 220 chromeos::DISPLAY_POWER_ALL_ON, "all_on", 221 chromeos::DISPLAY_POWER_ALL_OFF, "all_off", 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"); 226 return map; 227 } 228 229 bool GetDisplayPowerStateFromString(const base::StringPiece& state, 230 chromeos::DisplayPowerState* field) { 231 if (ash::ReverseFind(GetDisplayPowerStateToStringMap(), state, field)) 232 return true; 233 LOG(ERROR) << "Invalid display power state value:" << state; 234 return false; 235 } 236 237 void StoreDisplayPowerState(DisplayPowerState power_state) { 238 const DisplayPowerStateToStringMap* map = GetDisplayPowerStateToStringMap(); 239 DisplayPowerStateToStringMap::const_iterator iter = map->find(power_state); 240 std::string value = iter != map->end() ? iter->second : std::string(); 241 PrefService* local_state = g_browser_process->local_state(); 242 local_state->SetString(prefs::kDisplayPowerState, value); 243 } 244 245 void StoreCurrentDisplayPowerState() { 246 StoreDisplayPowerState( 247 ash::Shell::GetInstance()->output_configurator()->power_state()); 248 } 249 250 } // namespace 251 252 void RegisterDisplayLocalStatePrefs(PrefRegistrySimple* registry) { 253 // Per-display preference. 254 registry->RegisterDictionaryPref(prefs::kSecondaryDisplays); 255 registry->RegisterDictionaryPref(prefs::kDisplayProperties); 256 DisplayPowerStateToStringMap::const_iterator iter = 257 GetDisplayPowerStateToStringMap()->find(chromeos::DISPLAY_POWER_ALL_ON); 258 registry->RegisterStringPref(prefs::kDisplayPowerState, iter->second); 259 } 260 261 void StoreDisplayPrefs() { 262 // Do not store prefs when the confirmation dialog is shown. 263 if (!IsValidUser() || 264 ash::Shell::GetInstance()->resolution_notification_controller()-> 265 DoesNotificationTimeout()) { 266 return; 267 } 268 StoreCurrentDisplayLayoutPrefs(); 269 StoreCurrentDisplayProperties(); 270 StoreCurrentDisplayPowerState(); 271 } 272 273 void SetCurrentDisplayLayout(const ash::DisplayLayout& layout) { 274 ash::DisplayController* display_controller = GetDisplayController(); 275 display_controller->SetLayoutForCurrentDisplays(layout); 276 } 277 278 void LoadDisplayPreferences(bool first_run_after_boot) { 279 LoadDisplayLayouts(); 280 LoadDisplayProperties(); 281 if (!first_run_after_boot) { 282 PrefService* local_state = g_browser_process->local_state(); 283 // Restore DisplayPowerState: 284 std::string value = local_state->GetString(prefs::kDisplayPowerState); 285 chromeos::DisplayPowerState power_state; 286 if (GetDisplayPowerStateFromString(value, &power_state)) { 287 ash::Shell::GetInstance()->output_configurator()->SetInitialDisplayPower( 288 power_state); 289 } 290 } 291 } 292 293 // Stores the display layout for given display pairs. 294 void StoreDisplayLayoutPrefForTest(int64 id1, 295 int64 id2, 296 const ash::DisplayLayout& layout) { 297 StoreDisplayLayoutPref(std::make_pair(id1, id2), layout); 298 } 299 300 // Stores the given |power_state|. 301 void StoreDisplayPowerStateForTest(DisplayPowerState power_state) { 302 StoreDisplayPowerState(power_state); 303 } 304 305 } // namespace chromeos 306