Home | History | Annotate | Download | only in display
      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