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 bool CompareResolution(base::Value
* display1
, base::Value
* display2
) {
58 base::DictionaryValue
* d1
= NULL
;
59 base::DictionaryValue
* d2
= NULL
;
60 CHECK(display1
->GetAsDictionary(&d1
) && display2
->GetAsDictionary(&d2
));
61 int width1
= 0, height1
= 0, width2
= 0, height2
= 0;
62 CHECK(d1
->GetInteger("width", &width1
) && d1
->GetInteger("height", &height1
));
63 CHECK(d2
->GetInteger("width", &width2
) && d2
->GetInteger("height", &height2
));
64 double scale_factor1
= 0, scale_factor2
= 0;
65 if (d1
->GetDouble("scaleFactor", &scale_factor1
)) {
66 width1
/= scale_factor1
;
67 height1
/= scale_factor1
;
69 if (d2
->GetDouble("scaleFactor", &scale_factor2
)) {
70 width2
/= scale_factor2
;
71 height2
/= scale_factor2
;
74 if (width1
* height1
== width2
* height2
) {
75 if (scale_factor1
!= scale_factor2
)
76 return scale_factor1
< scale_factor2
;
78 int refresh_rate1
= 0, refresh_rate2
= 0;
79 CHECK(d1
->GetInteger("refreshRate", &refresh_rate1
) ==
80 d2
->GetInteger("refreshRate", &refresh_rate2
));
81 return refresh_rate1
< refresh_rate2
;
83 return width1
* height1
< width2
* height2
;
86 base::string16
GetColorProfileName(ui::ColorCalibrationProfile profile
) {
88 case ui::COLOR_PROFILE_STANDARD
:
89 return l10n_util::GetStringUTF16(
90 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD
);
91 case ui::COLOR_PROFILE_DYNAMIC
:
92 return l10n_util::GetStringUTF16(
93 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC
);
94 case ui::COLOR_PROFILE_MOVIE
:
95 return l10n_util::GetStringUTF16(
96 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE
);
97 case ui::COLOR_PROFILE_READING
:
98 return l10n_util::GetStringUTF16(
99 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING
);
100 case ui::NUM_COLOR_PROFILES
:
105 return base::string16();
108 scoped_ptr
<base::ListValue
> GetResolutionsForInternalDisplay(
109 const ash::DisplayInfo
& display_info
) {
110 scoped_ptr
<base::ListValue
> js_resolutions(new base::ListValue
);
111 const std::vector
<float> ui_scales
=
112 DisplayManager::GetScalesForDisplay(display_info
);
113 gfx::SizeF base_size
= display_info
.bounds_in_native().size();
114 base_size
.Scale(1.0f
/ display_info
.device_scale_factor());
115 if (display_info
.rotation() == gfx::Display::ROTATE_90
||
116 display_info
.rotation() == gfx::Display::ROTATE_270
) {
117 float tmp
= base_size
.width();
118 base_size
.set_width(base_size
.height());
119 base_size
.set_height(tmp
);
122 for (size_t i
= 0; i
< ui_scales
.size(); ++i
) {
123 base::DictionaryValue
* resolution_info
= new base::DictionaryValue();
124 gfx::SizeF new_size
= base_size
;
125 new_size
.Scale(ui_scales
[i
]);
126 gfx::Size resolution
= gfx::ToFlooredSize(new_size
);
127 resolution_info
->SetDouble("scale", ui_scales
[i
]);
128 if (ui_scales
[i
] == 1.0f
)
129 resolution_info
->SetBoolean("isBest", true);
130 resolution_info
->SetBoolean(
131 "selected", display_info
.configured_ui_scale() == ui_scales
[i
]);
132 resolution_info
->SetInteger("width", resolution
.width());
133 resolution_info
->SetInteger("height", resolution
.height());
134 js_resolutions
->Append(resolution_info
);
137 return js_resolutions
.Pass();
140 scoped_ptr
<base::ListValue
> GetResolutionsForExternalDisplay(
141 const ash::DisplayInfo
& display_info
) {
142 scoped_ptr
<base::ListValue
> js_resolutions(new base::ListValue
);
144 gfx::Size current_size
= display_info
.bounds_in_native().size();
145 int largest_index
= -1;
146 int largest_area
= -1;
148 for (size_t i
= 0; i
< display_info
.display_modes().size(); ++i
) {
149 base::DictionaryValue
* resolution_info
= new base::DictionaryValue();
150 const ash::DisplayMode
& display_mode
= display_info
.display_modes()[i
];
151 gfx::Size resolution
= display_mode
.size
;
153 if (resolution
.GetArea() > largest_area
) {
154 resolution_info
->SetBoolean("isBest", true);
155 largest_area
= resolution
.GetArea();
156 if (largest_index
>= 0) {
157 base::DictionaryValue
* prev_largest
= NULL
;
158 CHECK(js_resolutions
->GetDictionary(largest_index
, &prev_largest
));
159 prev_largest
->SetBoolean("isBest", false);
164 if (resolution
== current_size
) {
165 // Right now, the scale factor for unselected resolutions is unknown.
166 // TODO(mukai): Set the scale factor for unselected ones.
167 resolution_info
->SetDouble(
168 "scaleFactor", display_info
.device_scale_factor());
169 resolution_info
->SetBoolean("selected", true);
172 resolution_info
->SetInteger("width", resolution
.width());
173 resolution_info
->SetInteger("height", resolution
.height());
174 resolution_info
->SetDouble("refreshRate", display_mode
.refresh_rate
);
175 js_resolutions
->Append(resolution_info
);
178 return js_resolutions
.Pass();
183 DisplayOptionsHandler::DisplayOptionsHandler() {
184 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
187 DisplayOptionsHandler::~DisplayOptionsHandler() {
188 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
191 void DisplayOptionsHandler::GetLocalizedValues(
192 base::DictionaryValue
* localized_strings
) {
193 DCHECK(localized_strings
);
194 RegisterTitle(localized_strings
, "displayOptionsPage",
195 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE
);
197 localized_strings
->SetString(
198 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
199 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS
));
200 localized_strings
->SetString(
201 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
202 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION
));
203 localized_strings
->SetString(
204 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
205 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION
));
206 localized_strings
->SetString(
207 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
208 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN
));
210 localized_strings
->SetString("startMirroring", l10n_util::GetStringUTF16(
211 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING
));
212 localized_strings
->SetString("stopMirroring", l10n_util::GetStringUTF16(
213 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING
));
214 localized_strings
->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
215 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME
));
216 localized_strings
->SetString("setPrimary", l10n_util::GetStringUTF16(
217 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY
));
218 localized_strings
->SetString("annotateBest", l10n_util::GetStringUTF16(
219 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST
));
220 localized_strings
->SetString("orientation0", l10n_util::GetStringUTF16(
221 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION
));
222 localized_strings
->SetString("orientation90", l10n_util::GetStringUTF16(
223 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90
));
224 localized_strings
->SetString("orientation180", l10n_util::GetStringUTF16(
225 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180
));
226 localized_strings
->SetString("orientation270", l10n_util::GetStringUTF16(
227 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270
));
228 localized_strings
->SetString(
229 "startCalibratingOverscan", l10n_util::GetStringUTF16(
230 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN
));
231 localized_strings
->SetString(
232 "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
233 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE
));
236 void DisplayOptionsHandler::InitializePage() {
240 void DisplayOptionsHandler::RegisterMessages() {
241 web_ui()->RegisterMessageCallback(
243 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo
,
244 base::Unretained(this)));
245 web_ui()->RegisterMessageCallback(
247 base::Bind(&DisplayOptionsHandler::HandleMirroring
,
248 base::Unretained(this)));
249 web_ui()->RegisterMessageCallback(
251 base::Bind(&DisplayOptionsHandler::HandleSetPrimary
,
252 base::Unretained(this)));
253 web_ui()->RegisterMessageCallback(
255 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout
,
256 base::Unretained(this)));
257 web_ui()->RegisterMessageCallback(
259 base::Bind(&DisplayOptionsHandler::HandleSetUIScale
,
260 base::Unretained(this)));
261 web_ui()->RegisterMessageCallback(
263 base::Bind(&DisplayOptionsHandler::HandleSetResolution
,
264 base::Unretained(this)));
265 web_ui()->RegisterMessageCallback(
267 base::Bind(&DisplayOptionsHandler::HandleSetOrientation
,
268 base::Unretained(this)));
269 web_ui()->RegisterMessageCallback(
271 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile
,
272 base::Unretained(this)));
275 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
278 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
279 SendAllDisplayInfo();
282 void DisplayOptionsHandler::SendAllDisplayInfo() {
283 DisplayManager
* display_manager
= GetDisplayManager();
285 std::vector
<gfx::Display
> displays
;
286 for (size_t i
= 0; i
< display_manager
->GetNumDisplays(); ++i
) {
287 displays
.push_back(display_manager
->GetDisplayAt(i
));
289 SendDisplayInfo(displays
);
292 void DisplayOptionsHandler::SendDisplayInfo(
293 const std::vector
<gfx::Display
>& displays
) {
294 DisplayManager
* display_manager
= GetDisplayManager();
295 base::FundamentalValue
mirroring(display_manager
->IsMirrored());
297 int64 primary_id
= ash::Shell::GetScreen()->GetPrimaryDisplay().id();
298 base::ListValue js_displays
;
299 for (size_t i
= 0; i
< displays
.size(); ++i
) {
300 const gfx::Display
& display
= displays
[i
];
301 const ash::DisplayInfo
& display_info
=
302 display_manager
->GetDisplayInfo(display
.id());
303 const gfx::Rect
& bounds
= display
.bounds();
304 base::DictionaryValue
* js_display
= new base::DictionaryValue();
305 js_display
->SetString("id", base::Int64ToString(display
.id()));
306 js_display
->SetInteger("x", bounds
.x());
307 js_display
->SetInteger("y", bounds
.y());
308 js_display
->SetInteger("width", bounds
.width());
309 js_display
->SetInteger("height", bounds
.height());
310 js_display
->SetString("name",
311 display_manager
->GetDisplayNameForId(display
.id()));
312 js_display
->SetBoolean("isPrimary", display
.id() == primary_id
);
313 js_display
->SetBoolean("isInternal", display
.IsInternal());
314 js_display
->SetInteger("orientation",
315 static_cast<int>(display_info
.rotation()));
317 scoped_ptr
<base::ListValue
> js_resolutions
= display
.IsInternal() ?
318 GetResolutionsForInternalDisplay(display_info
) :
319 GetResolutionsForExternalDisplay(display_info
);
321 js_resolutions
->begin(), js_resolutions
->end(), CompareResolution
);
322 js_display
->Set("resolutions", js_resolutions
.release());
324 js_display
->SetInteger("colorProfile", display_info
.color_profile());
325 base::ListValue
* available_color_profiles
= new base::ListValue();
327 i
< display_info
.available_color_profiles().size(); ++i
) {
328 base::DictionaryValue
* color_profile_dict
= new base::DictionaryValue();
329 ui::ColorCalibrationProfile color_profile
=
330 display_info
.available_color_profiles()[i
];
331 color_profile_dict
->SetInteger("profileId", color_profile
);
332 color_profile_dict
->SetString("name", GetColorProfileName(color_profile
));
333 available_color_profiles
->Append(color_profile_dict
);
335 js_display
->Set("availableColorProfiles", available_color_profiles
);
336 js_displays
.Append(js_display
);
339 scoped_ptr
<base::Value
> layout_value(base::Value::CreateNullValue());
340 scoped_ptr
<base::Value
> offset_value(base::Value::CreateNullValue());
341 if (display_manager
->GetNumDisplays() > 1) {
342 const ash::DisplayLayout layout
=
343 display_manager
->GetCurrentDisplayLayout();
344 layout_value
.reset(new base::FundamentalValue(layout
.position
));
345 offset_value
.reset(new base::FundamentalValue(layout
.offset
));
348 web_ui()->CallJavascriptFunction(
349 "options.DisplayOptions.setDisplayInfo",
350 mirroring
, js_displays
, *layout_value
.get(), *offset_value
.get());
353 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring
) {
354 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring
);
355 // Not necessary to start fade-in animation. DisplayConfigurator will do that.
358 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
359 int position
, int offset
) {
360 SetCurrentDisplayLayout(
361 ash::DisplayLayout::FromInts(position
, offset
));
362 ash::Shell::GetInstance()->display_configurator_animation()->
363 StartFadeInAnimation();
366 void DisplayOptionsHandler::HandleDisplayInfo(
367 const base::ListValue
* unused_args
) {
368 SendAllDisplayInfo();
371 void DisplayOptionsHandler::HandleMirroring(const base::ListValue
* args
) {
372 DCHECK(!args
->empty());
373 content::RecordAction(
374 base::UserMetricsAction("Options_DisplayToggleMirroring"));
375 bool is_mirroring
= false;
376 args
->GetBoolean(0, &is_mirroring
);
377 ash::Shell::GetInstance()->display_configurator_animation()->
378 StartFadeOutAnimation(base::Bind(
379 &DisplayOptionsHandler::OnFadeOutForMirroringFinished
,
380 base::Unretained(this),
384 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue
* args
) {
385 DCHECK(!args
->empty());
386 int64 display_id
= GetDisplayId(args
);
387 if (display_id
== gfx::Display::kInvalidDisplayID
)
390 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
391 ash::Shell::GetInstance()->display_controller()->
392 SetPrimaryDisplayId(display_id
);
395 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue
* args
) {
398 if (!args
->GetDouble(0, &layout
) || !args
->GetDouble(1, &offset
)) {
399 LOG(ERROR
) << "Invalid parameter";
400 SendAllDisplayInfo();
403 DCHECK_LE(ash::DisplayLayout::TOP
, layout
);
404 DCHECK_GE(ash::DisplayLayout::LEFT
, layout
);
405 content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
406 ash::Shell::GetInstance()->display_configurator_animation()->
407 StartFadeOutAnimation(base::Bind(
408 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished
,
409 base::Unretained(this),
410 static_cast<int>(layout
),
411 static_cast<int>(offset
)));
414 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue
* args
) {
415 DCHECK(!args
->empty());
417 int64 display_id
= GetDisplayId(args
);
418 if (display_id
== gfx::Display::kInvalidDisplayID
)
421 double ui_scale
= 0.0f
;
422 if (!args
->GetDouble(1, &ui_scale
) || ui_scale
== 0.0f
) {
423 LOG(ERROR
) << "Can't find new ui_scale";
427 GetDisplayManager()->SetDisplayUIScale(display_id
, ui_scale
);
430 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue
* args
) {
431 DCHECK(!args
->empty());
432 int64 display_id
= GetDisplayId(args
);
433 if (display_id
== gfx::Display::kInvalidDisplayID
)
436 content::RecordAction(
437 base::UserMetricsAction("Options_DisplaySetResolution"));
439 double height
= 0.0f
;
440 if (!args
->GetDouble(1, &width
) || width
== 0.0f
) {
441 LOG(ERROR
) << "Can't find new width";
444 if (!args
->GetDouble(2, &height
) || height
== 0.0f
) {
445 LOG(ERROR
) << "Can't find new height";
449 const ash::DisplayInfo
& display_info
=
450 GetDisplayManager()->GetDisplayInfo(display_id
);
451 gfx::Size new_resolution
= gfx::ToFlooredSize(gfx::SizeF(width
, height
));
452 gfx::Size old_resolution
= display_info
.bounds_in_native().size();
453 bool has_new_resolution
= false;
454 bool has_old_resolution
= false;
455 for (size_t i
= 0; i
< display_info
.display_modes().size(); ++i
) {
456 ash::DisplayMode display_mode
= display_info
.display_modes()[i
];
457 if (display_mode
.size
== new_resolution
)
458 has_new_resolution
= true;
459 if (display_mode
.size
== old_resolution
)
460 has_old_resolution
= true;
462 if (!has_new_resolution
) {
463 LOG(ERROR
) << "No new resolution " << new_resolution
.ToString()
464 << " is found in the display info " << display_info
.ToString();
467 if (!has_old_resolution
) {
468 LOG(ERROR
) << "No old resolution " << old_resolution
.ToString()
469 << " is found in the display info " << display_info
.ToString();
473 ash::Shell::GetInstance()->resolution_notification_controller()->
474 SetDisplayResolutionAndNotify(
475 display_id
, old_resolution
, new_resolution
,
476 base::Bind(&StoreDisplayPrefs
));
479 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue
* args
) {
480 DCHECK(!args
->empty());
482 int64 display_id
= GetDisplayId(args
);
483 if (display_id
== gfx::Display::kInvalidDisplayID
)
486 std::string rotation_value
;
487 gfx::Display::Rotation new_rotation
= gfx::Display::ROTATE_0
;
488 if (!args
->GetString(1, &rotation_value
)) {
489 LOG(ERROR
) << "Can't find new orientation";
492 if (rotation_value
== "90")
493 new_rotation
= gfx::Display::ROTATE_90
;
494 else if (rotation_value
== "180")
495 new_rotation
= gfx::Display::ROTATE_180
;
496 else if (rotation_value
== "270")
497 new_rotation
= gfx::Display::ROTATE_270
;
498 else if (rotation_value
!= "0")
499 LOG(ERROR
) << "Invalid rotation: " << rotation_value
<< " Falls back to 0";
501 content::RecordAction(
502 base::UserMetricsAction("Options_DisplaySetOrientation"));
503 GetDisplayManager()->SetDisplayRotation(display_id
, new_rotation
);
506 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue
* args
) {
507 DCHECK(!args
->empty());
508 int64 display_id
= GetDisplayId(args
);
509 if (display_id
== gfx::Display::kInvalidDisplayID
)
512 std::string profile_value
;
513 if (!args
->GetString(1, &profile_value
)) {
514 LOG(ERROR
) << "Invalid profile_value";
519 if (!base::StringToInt(profile_value
, &profile_id
)) {
520 LOG(ERROR
) << "Invalid profile: " << profile_value
;
524 if (profile_id
< ui::COLOR_PROFILE_STANDARD
||
525 profile_id
> ui::COLOR_PROFILE_READING
) {
526 LOG(ERROR
) << "Invalid profile_id: " << profile_id
;
530 GetDisplayManager()->SetColorCalibrationProfile(
531 display_id
, static_cast<ui::ColorCalibrationProfile
>(profile_id
));
532 SendAllDisplayInfo();
535 } // namespace options
536 } // namespace chromeos