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