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.
5 #include "chrome/browser/ui/webui/options/chromeos/display_options_handler.h"
9 #include "ash/display/display_configurator_animation.h"
10 #include "ash/display/display_controller.h"
11 #include "ash/display/display_manager.h"
12 #include "ash/display/resolution_notification_controller.h"
13 #include "ash/shell.h"
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/values.h"
19 #include "chrome/browser/chromeos/display/display_preferences.h"
20 #include "content/public/browser/user_metrics.h"
21 #include "content/public/browser/web_ui.h"
22 #include "grit/ash_strings.h"
23 #include "grit/generated_resources.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/gfx/display.h"
26 #include "ui/gfx/rect.h"
27 #include "ui/gfx/screen.h"
28 #include "ui/gfx/size_conversions.h"
30 using ash::DisplayManager;
36 DisplayManager* GetDisplayManager() {
37 return ash::Shell::GetInstance()->display_manager();
40 int64 GetDisplayId(const base::ListValue* args) {
41 // Assumes the display ID is specified as the first argument.
43 if (!args->GetString(0, &id_value)) {
44 LOG(ERROR) << "Can't find ID";
45 return gfx::Display::kInvalidDisplayID;
48 int64 display_id = gfx::Display::kInvalidDisplayID;
49 if (!base::StringToInt64(id_value, &display_id)) {
50 LOG(ERROR) << "Invalid display id: " << id_value;
51 return gfx::Display::kInvalidDisplayID;
57 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
59 case ui::COLOR_PROFILE_STANDARD:
60 return l10n_util::GetStringUTF16(
61 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD);
62 case ui::COLOR_PROFILE_DYNAMIC:
63 return l10n_util::GetStringUTF16(
64 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC);
65 case ui::COLOR_PROFILE_MOVIE:
66 return l10n_util::GetStringUTF16(
67 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE);
68 case ui::COLOR_PROFILE_READING:
69 return l10n_util::GetStringUTF16(
70 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING);
71 case ui::NUM_COLOR_PROFILES:
76 return base::string16();
79 int GetIntOrDouble(const base::DictionaryValue* dict,
80 const std::string& field) {
81 double double_result = 0;
82 if (dict->GetDouble(field, &double_result))
83 return static_cast<int>(double_result);
86 dict->GetInteger(field, &result);
90 bool GetFloat(const base::DictionaryValue* dict,
91 const std::string& field,
93 double double_result = 0;
94 if (dict->GetDouble(field, &double_result)) {
95 *result = static_cast<float>(double_result);
101 bool ConvertValueToDisplayMode(const base::DictionaryValue* dict,
102 ash::DisplayMode* mode) {
103 mode->size.set_width(GetIntOrDouble(dict, "originalWidth"));
104 mode->size.set_height(GetIntOrDouble(dict, "originalHeight"));
105 if (mode->size.IsEmpty()) {
106 LOG(ERROR) << "missing width or height.";
109 if (!GetFloat(dict, "refreshRate", &mode->refresh_rate)) {
110 LOG(ERROR) << "missing refreshRate.";
113 if (!GetFloat(dict, "scale", &mode->ui_scale)) {
114 LOG(ERROR) << "missing ui-scale.";
117 if (!GetFloat(dict, "deviceScaleFactor", &mode->device_scale_factor)) {
118 LOG(ERROR) << "missing deviceScaleFactor.";
124 base::DictionaryValue* ConvertDisplayModeToValue(int64 display_id,
125 const ash::DisplayMode& mode) {
126 base::DictionaryValue* result = new base::DictionaryValue();
127 gfx::Size size_dip = mode.GetSizeInDIP();
128 result->SetInteger("width", size_dip.width());
129 result->SetInteger("height", size_dip.height());
130 result->SetInteger("originalWidth", mode.size.width());
131 result->SetInteger("originalHeight", mode.size.height());
132 result->SetDouble("deviceScaleFactor", mode.device_scale_factor);
133 result->SetDouble("scale", mode.ui_scale);
134 result->SetDouble("refreshRate", mode.refresh_rate);
135 result->SetBoolean("isBest", mode.native);
137 "selected", mode.IsEquivalent(
138 GetDisplayManager()->GetActiveModeForDisplayId(display_id)));
144 DisplayOptionsHandler::DisplayOptionsHandler() {
145 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
148 DisplayOptionsHandler::~DisplayOptionsHandler() {
149 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
152 void DisplayOptionsHandler::GetLocalizedValues(
153 base::DictionaryValue* localized_strings) {
154 DCHECK(localized_strings);
155 RegisterTitle(localized_strings, "displayOptionsPage",
156 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);
158 localized_strings->SetString(
159 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
160 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
161 localized_strings->SetString(
162 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
163 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
164 localized_strings->SetString(
165 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
166 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
167 localized_strings->SetString(
168 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
169 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));
171 localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16(
172 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING));
173 localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16(
174 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING));
175 localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
176 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
177 localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
178 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
179 localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
180 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
181 localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
182 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
183 localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
184 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
185 localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
186 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
187 localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
188 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
189 localized_strings->SetString(
190 "startCalibratingOverscan", l10n_util::GetStringUTF16(
191 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
192 localized_strings->SetString(
193 "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
194 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE));
197 void DisplayOptionsHandler::InitializePage() {
201 void DisplayOptionsHandler::RegisterMessages() {
202 web_ui()->RegisterMessageCallback(
204 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
205 base::Unretained(this)));
206 web_ui()->RegisterMessageCallback(
208 base::Bind(&DisplayOptionsHandler::HandleMirroring,
209 base::Unretained(this)));
210 web_ui()->RegisterMessageCallback(
212 base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
213 base::Unretained(this)));
214 web_ui()->RegisterMessageCallback(
216 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
217 base::Unretained(this)));
218 web_ui()->RegisterMessageCallback(
220 base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode,
221 base::Unretained(this)));
222 web_ui()->RegisterMessageCallback(
224 base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
225 base::Unretained(this)));
226 web_ui()->RegisterMessageCallback(
228 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
229 base::Unretained(this)));
232 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
235 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
236 SendAllDisplayInfo();
239 void DisplayOptionsHandler::SendAllDisplayInfo() {
240 DisplayManager* display_manager = GetDisplayManager();
242 std::vector<gfx::Display> displays;
243 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
244 displays.push_back(display_manager->GetDisplayAt(i));
246 SendDisplayInfo(displays);
249 void DisplayOptionsHandler::SendDisplayInfo(
250 const std::vector<gfx::Display>& displays) {
251 DisplayManager* display_manager = GetDisplayManager();
252 base::FundamentalValue mirroring(display_manager->IsMirrored());
254 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
255 base::ListValue js_displays;
256 for (size_t i = 0; i < displays.size(); ++i) {
257 const gfx::Display& display = displays[i];
258 const ash::DisplayInfo& display_info =
259 display_manager->GetDisplayInfo(display.id());
260 const gfx::Rect& bounds = display.bounds();
261 base::DictionaryValue* js_display = new base::DictionaryValue();
262 js_display->SetString("id", base::Int64ToString(display.id()));
263 js_display->SetInteger("x", bounds.x());
264 js_display->SetInteger("y", bounds.y());
265 js_display->SetInteger("width", bounds.width());
266 js_display->SetInteger("height", bounds.height());
267 js_display->SetString("name",
268 display_manager->GetDisplayNameForId(display.id()));
269 js_display->SetBoolean("isPrimary", display.id() == primary_id);
270 js_display->SetBoolean("isInternal", display.IsInternal());
271 js_display->SetInteger("orientation",
272 static_cast<int>(display_info.rotation()));
274 base::ListValue* js_resolutions = new base::ListValue();
275 const std::vector<ash::DisplayMode>& display_modes =
276 display_info.display_modes();
277 for (size_t i = 0; i < display_modes.size(); ++i) {
278 js_resolutions->Append(
279 ConvertDisplayModeToValue(display.id(), display_modes[i]));
281 js_display->Set("resolutions", js_resolutions);
283 js_display->SetInteger("colorProfile", display_info.color_profile());
284 base::ListValue* available_color_profiles = new base::ListValue();
286 i < display_info.available_color_profiles().size(); ++i) {
287 base::DictionaryValue* color_profile_dict = new base::DictionaryValue();
288 ui::ColorCalibrationProfile color_profile =
289 display_info.available_color_profiles()[i];
290 color_profile_dict->SetInteger("profileId", color_profile);
291 color_profile_dict->SetString("name", GetColorProfileName(color_profile));
292 available_color_profiles->Append(color_profile_dict);
294 js_display->Set("availableColorProfiles", available_color_profiles);
295 js_displays.Append(js_display);
298 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue());
299 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue());
300 if (display_manager->GetNumDisplays() > 1) {
301 const ash::DisplayLayout layout =
302 display_manager->GetCurrentDisplayLayout();
303 layout_value.reset(new base::FundamentalValue(layout.position));
304 offset_value.reset(new base::FundamentalValue(layout.offset));
307 web_ui()->CallJavascriptFunction(
308 "options.DisplayOptions.setDisplayInfo",
309 mirroring, js_displays, *layout_value.get(), *offset_value.get());
312 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) {
313 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring);
314 // Not necessary to start fade-in animation. DisplayConfigurator will do that.
317 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
318 int position, int offset) {
319 SetCurrentDisplayLayout(
320 ash::DisplayLayout::FromInts(position, offset));
321 ash::Shell::GetInstance()->display_configurator_animation()->
322 StartFadeInAnimation();
325 void DisplayOptionsHandler::HandleDisplayInfo(
326 const base::ListValue* unused_args) {
327 SendAllDisplayInfo();
330 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
331 DCHECK(!args->empty());
332 content::RecordAction(
333 base::UserMetricsAction("Options_DisplayToggleMirroring"));
334 bool is_mirroring = false;
335 args->GetBoolean(0, &is_mirroring);
336 ash::Shell::GetInstance()->display_configurator_animation()->
337 StartFadeOutAnimation(base::Bind(
338 &DisplayOptionsHandler::OnFadeOutForMirroringFinished,
339 base::Unretained(this),
343 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
344 DCHECK(!args->empty());
345 int64 display_id = GetDisplayId(args);
346 if (display_id == gfx::Display::kInvalidDisplayID)
349 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
350 ash::Shell::GetInstance()->display_controller()->
351 SetPrimaryDisplayId(display_id);
354 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
357 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
358 LOG(ERROR) << "Invalid parameter";
359 SendAllDisplayInfo();
362 DCHECK_LE(ash::DisplayLayout::TOP, layout);
363 DCHECK_GE(ash::DisplayLayout::LEFT, layout);
364 content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
365 ash::Shell::GetInstance()->display_configurator_animation()->
366 StartFadeOutAnimation(base::Bind(
367 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished,
368 base::Unretained(this),
369 static_cast<int>(layout),
370 static_cast<int>(offset)));
373 void DisplayOptionsHandler::HandleSetDisplayMode(const base::ListValue* args) {
374 DCHECK(!args->empty());
376 int64 display_id = GetDisplayId(args);
377 if (display_id == gfx::Display::kInvalidDisplayID)
380 const base::DictionaryValue* mode_data = NULL;
381 if (!args->GetDictionary(1, &mode_data)) {
382 LOG(ERROR) << "Failed to get mode data";
386 ash::DisplayMode mode;
387 if (!ConvertValueToDisplayMode(mode_data, &mode))
390 content::RecordAction(
391 base::UserMetricsAction("Options_DisplaySetResolution"));
392 ash::DisplayManager* display_manager = GetDisplayManager();
393 ash::DisplayMode current_mode =
394 display_manager->GetActiveModeForDisplayId(display_id);
395 if (display_manager->SetDisplayMode(display_id, mode)) {
396 ash::Shell::GetInstance()->resolution_notification_controller()->
398 display_id, current_mode, mode, base::Bind(&StoreDisplayPrefs));
402 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
403 DCHECK(!args->empty());
405 int64 display_id = GetDisplayId(args);
406 if (display_id == gfx::Display::kInvalidDisplayID)
409 std::string rotation_value;
410 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
411 if (!args->GetString(1, &rotation_value)) {
412 LOG(ERROR) << "Can't find new orientation";
415 if (rotation_value == "90")
416 new_rotation = gfx::Display::ROTATE_90;
417 else if (rotation_value == "180")
418 new_rotation = gfx::Display::ROTATE_180;
419 else if (rotation_value == "270")
420 new_rotation = gfx::Display::ROTATE_270;
421 else if (rotation_value != "0")
422 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
424 content::RecordAction(
425 base::UserMetricsAction("Options_DisplaySetOrientation"));
426 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
429 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) {
430 DCHECK(!args->empty());
431 int64 display_id = GetDisplayId(args);
432 if (display_id == gfx::Display::kInvalidDisplayID)
435 std::string profile_value;
436 if (!args->GetString(1, &profile_value)) {
437 LOG(ERROR) << "Invalid profile_value";
442 if (!base::StringToInt(profile_value, &profile_id)) {
443 LOG(ERROR) << "Invalid profile: " << profile_value;
447 if (profile_id < ui::COLOR_PROFILE_STANDARD ||
448 profile_id > ui::COLOR_PROFILE_READING) {
449 LOG(ERROR) << "Invalid profile_id: " << profile_id;
453 GetDisplayManager()->SetColorCalibrationProfile(
454 display_id, static_cast<ui::ColorCalibrationProfile>(profile_id));
455 SendAllDisplayInfo();
458 } // namespace options
459 } // namespace chromeos