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/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