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