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_manager.h"
11 #include "ash/display/resolution_notification_controller.h"
12 #include "ash/display/window_tree_host_manager.h"
13 #include "ash/rotator/screen_rotation_animator.h"
14 #include "ash/shell.h"
15 #include "base/bind.h"
16 #include "base/logging.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/values.h"
20 #include "chrome/browser/chromeos/display/display_preferences.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "content/public/browser/user_metrics.h"
23 #include "content/public/browser/web_ui.h"
24 #include "grit/ash_strings.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/display.h"
27 #include "ui/gfx/geometry/rect.h"
28 #include "ui/gfx/geometry/size_conversions.h"
29 #include "ui/gfx/screen.h"
31 using ash::DisplayManager
;
37 DisplayManager
* GetDisplayManager() {
38 return ash::Shell::GetInstance()->display_manager();
41 int64
GetDisplayId(const base::ListValue
* args
) {
42 // Assumes the display ID is specified as the first argument.
44 if (!args
->GetString(0, &id_value
)) {
45 LOG(ERROR
) << "Can't find ID";
46 return gfx::Display::kInvalidDisplayID
;
49 int64 display_id
= gfx::Display::kInvalidDisplayID
;
50 if (!base::StringToInt64(id_value
, &display_id
)) {
51 LOG(ERROR
) << "Invalid display id: " << id_value
;
52 return gfx::Display::kInvalidDisplayID
;
58 base::string16
GetColorProfileName(ui::ColorCalibrationProfile profile
) {
60 case ui::COLOR_PROFILE_STANDARD
:
61 return l10n_util::GetStringUTF16(
62 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD
);
63 case ui::COLOR_PROFILE_DYNAMIC
:
64 return l10n_util::GetStringUTF16(
65 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC
);
66 case ui::COLOR_PROFILE_MOVIE
:
67 return l10n_util::GetStringUTF16(
68 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE
);
69 case ui::COLOR_PROFILE_READING
:
70 return l10n_util::GetStringUTF16(
71 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING
);
72 case ui::NUM_COLOR_PROFILES
:
77 return base::string16();
80 int GetIntOrDouble(const base::DictionaryValue
* dict
,
81 const std::string
& field
) {
82 double double_result
= 0;
83 if (dict
->GetDouble(field
, &double_result
))
84 return static_cast<int>(double_result
);
87 dict
->GetInteger(field
, &result
);
91 bool GetFloat(const base::DictionaryValue
* dict
,
92 const std::string
& field
,
94 double double_result
= 0;
95 if (dict
->GetDouble(field
, &double_result
)) {
96 *result
= static_cast<float>(double_result
);
102 bool ConvertValueToDisplayMode(const base::DictionaryValue
* dict
,
103 ash::DisplayMode
* mode
) {
104 mode
->size
.set_width(GetIntOrDouble(dict
, "originalWidth"));
105 mode
->size
.set_height(GetIntOrDouble(dict
, "originalHeight"));
106 if (mode
->size
.IsEmpty()) {
107 LOG(ERROR
) << "missing width or height.";
110 if (!GetFloat(dict
, "refreshRate", &mode
->refresh_rate
)) {
111 LOG(ERROR
) << "missing refreshRate.";
114 if (!GetFloat(dict
, "scale", &mode
->ui_scale
)) {
115 LOG(ERROR
) << "missing ui-scale.";
118 if (!GetFloat(dict
, "deviceScaleFactor", &mode
->device_scale_factor
)) {
119 LOG(ERROR
) << "missing deviceScaleFactor.";
125 base::DictionaryValue
* ConvertDisplayModeToValue(int64 display_id
,
126 const ash::DisplayMode
& mode
) {
127 bool is_internal
= gfx::Display::HasInternalDisplay() &&
128 gfx::Display::InternalDisplayId() == display_id
;
129 base::DictionaryValue
* result
= new base::DictionaryValue();
130 gfx::Size size_dip
= mode
.GetSizeInDIP(is_internal
);
131 result
->SetInteger("width", size_dip
.width());
132 result
->SetInteger("height", size_dip
.height());
133 result
->SetInteger("originalWidth", mode
.size
.width());
134 result
->SetInteger("originalHeight", mode
.size
.height());
135 result
->SetDouble("deviceScaleFactor", mode
.device_scale_factor
);
136 result
->SetDouble("scale", mode
.ui_scale
);
137 result
->SetDouble("refreshRate", mode
.refresh_rate
);
139 "isBest", is_internal
? (mode
.ui_scale
== 1.0f
) : mode
.native
);
140 result
->SetBoolean("isNative", mode
.native
);
142 "selected", mode
.IsEquivalent(
143 GetDisplayManager()->GetActiveModeForDisplayId(display_id
)));
149 DisplayOptionsHandler::DisplayOptionsHandler() {
150 // ash::Shell doesn't exist in Athena.
151 // See: http://crbug.com/416961
152 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
155 DisplayOptionsHandler::~DisplayOptionsHandler() {
156 // ash::Shell doesn't exist in Athena.
157 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
160 void DisplayOptionsHandler::GetLocalizedValues(
161 base::DictionaryValue
* localized_strings
) {
162 DCHECK(localized_strings
);
163 RegisterTitle(localized_strings
, "displayOptionsPage",
164 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE
);
166 localized_strings
->SetString(
167 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
168 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS
));
169 localized_strings
->SetString(
170 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
171 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION
));
172 localized_strings
->SetString(
173 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
174 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION
));
175 localized_strings
->SetString(
176 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
177 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN
));
179 localized_strings
->SetString("extendedMode", l10n_util::GetStringUTF16(
180 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_EXTENDED_MODE_LABEL
));
181 localized_strings
->SetString("mirroringMode", l10n_util::GetStringUTF16(
182 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_MODE_LABEL
));
183 localized_strings
->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
184 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME
));
185 localized_strings
->SetString("setPrimary", l10n_util::GetStringUTF16(
186 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY
));
187 localized_strings
->SetString("annotateBest", l10n_util::GetStringUTF16(
188 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST
));
189 localized_strings
->SetString("annotateNative", l10n_util::GetStringUTF16(
190 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_NATIVE
));
191 localized_strings
->SetString("orientation0", l10n_util::GetStringUTF16(
192 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION
));
193 localized_strings
->SetString("orientation90", l10n_util::GetStringUTF16(
194 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90
));
195 localized_strings
->SetString("orientation180", l10n_util::GetStringUTF16(
196 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180
));
197 localized_strings
->SetString("orientation270", l10n_util::GetStringUTF16(
198 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270
));
199 localized_strings
->SetString(
200 "startCalibratingOverscan", l10n_util::GetStringUTF16(
201 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN
));
202 localized_strings
->SetString(
203 "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
204 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE
));
205 localized_strings
->SetString(
206 "enableUnifiedDesktop",
207 l10n_util::GetStringUTF16(
208 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ENABLE_UNIFIED_DESKTOP
));
211 void DisplayOptionsHandler::InitializePage() {
213 UpdateDisplaySettingsEnabled();
216 void DisplayOptionsHandler::RegisterMessages() {
217 web_ui()->RegisterMessageCallback(
219 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo
,
220 base::Unretained(this)));
221 web_ui()->RegisterMessageCallback(
223 base::Bind(&DisplayOptionsHandler::HandleMirroring
,
224 base::Unretained(this)));
225 web_ui()->RegisterMessageCallback(
227 base::Bind(&DisplayOptionsHandler::HandleSetPrimary
,
228 base::Unretained(this)));
229 web_ui()->RegisterMessageCallback(
231 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout
,
232 base::Unretained(this)));
233 web_ui()->RegisterMessageCallback(
235 base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode
,
236 base::Unretained(this)));
237 web_ui()->RegisterMessageCallback(
239 base::Bind(&DisplayOptionsHandler::HandleSetOrientation
,
240 base::Unretained(this)));
241 web_ui()->RegisterMessageCallback(
243 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile
,
244 base::Unretained(this)));
245 web_ui()->RegisterMessageCallback(
246 "setUnifiedDesktopEnabled",
247 base::Bind(&DisplayOptionsHandler::HandleSetUnifiedDesktopEnabled
,
248 base::Unretained(this)));
251 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
254 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
255 UpdateDisplaySettingsEnabled();
256 SendAllDisplayInfo();
259 void DisplayOptionsHandler::SendAllDisplayInfo() {
260 DisplayManager
* display_manager
= GetDisplayManager();
262 std::vector
<gfx::Display
> displays
;
263 for (size_t i
= 0; i
< display_manager
->GetNumDisplays(); ++i
)
264 displays
.push_back(display_manager
->GetDisplayAt(i
));
265 SendDisplayInfo(displays
);
268 void DisplayOptionsHandler::SendDisplayInfo(
269 const std::vector
<gfx::Display
>& displays
) {
270 DisplayManager
* display_manager
= GetDisplayManager();
271 base::FundamentalValue
mode(
272 display_manager
->IsInMirrorMode() ? DisplayManager::MIRRORING
:
273 (display_manager
->IsInUnifiedMode() ? DisplayManager::UNIFIED
:
274 DisplayManager::EXTENDED
));
276 int64 primary_id
= ash::Shell::GetScreen()->GetPrimaryDisplay().id();
277 base::ListValue js_displays
;
278 for (const gfx::Display
& display
: displays
) {
279 const ash::DisplayInfo
& display_info
=
280 display_manager
->GetDisplayInfo(display
.id());
281 const gfx::Rect
& bounds
= display
.bounds();
282 base::DictionaryValue
* js_display
= new base::DictionaryValue();
283 js_display
->SetString("id", base::Int64ToString(display
.id()));
284 js_display
->SetInteger("x", bounds
.x());
285 js_display
->SetInteger("y", bounds
.y());
286 js_display
->SetInteger("width", bounds
.width());
287 js_display
->SetInteger("height", bounds
.height());
288 js_display
->SetString("name",
289 display_manager
->GetDisplayNameForId(display
.id()));
290 js_display
->SetBoolean("isPrimary", display
.id() == primary_id
);
291 js_display
->SetBoolean("isInternal", display
.IsInternal());
292 js_display
->SetInteger("orientation",
293 static_cast<int>(display_info
.GetActiveRotation()));
295 base::ListValue
* js_resolutions
= new base::ListValue();
296 for (const ash::DisplayMode
& display_mode
: display_info
.display_modes()) {
297 js_resolutions
->Append(
298 ConvertDisplayModeToValue(display
.id(), display_mode
));
300 js_display
->Set("resolutions", js_resolutions
);
302 js_display
->SetInteger("colorProfile", display_info
.color_profile());
303 base::ListValue
* available_color_profiles
= new base::ListValue();
304 for (const auto& color_profile
: display_info
.available_color_profiles()) {
305 const base::string16 profile_name
= GetColorProfileName(color_profile
);
306 if (profile_name
.empty())
308 base::DictionaryValue
* color_profile_dict
= new base::DictionaryValue();
309 color_profile_dict
->SetInteger("profileId", color_profile
);
310 color_profile_dict
->SetString("name", profile_name
);
311 available_color_profiles
->Append(color_profile_dict
);
313 js_display
->Set("availableColorProfiles", available_color_profiles
);
314 js_displays
.Append(js_display
);
317 scoped_ptr
<base::Value
> layout_value
= base::Value::CreateNullValue();
318 scoped_ptr
<base::Value
> offset_value
= base::Value::CreateNullValue();
319 if (display_manager
->GetNumDisplays() > 1) {
320 const ash::DisplayLayout layout
=
321 display_manager
->GetCurrentDisplayLayout();
322 layout_value
.reset(new base::FundamentalValue(layout
.position
));
323 offset_value
.reset(new base::FundamentalValue(layout
.offset
));
326 web_ui()->CallJavascriptFunction(
327 "options.DisplayOptions.setDisplayInfo",
328 mode
, js_displays
, *layout_value
.get(), *offset_value
.get());
331 void DisplayOptionsHandler::UpdateDisplaySettingsEnabled() {
332 bool enabled
= GetDisplayManager()->num_connected_displays() <= 2;
333 bool show_unified_desktop
= GetDisplayManager()->unified_desktop_enabled();
335 web_ui()->CallJavascriptFunction(
336 "options.BrowserOptions.enableDisplaySettings",
337 base::FundamentalValue(enabled
),
338 base::FundamentalValue(show_unified_desktop
));
341 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring
) {
342 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring
);
343 // Not necessary to start fade-in animation. DisplayConfigurator will do that.
346 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
347 int position
, int offset
) {
348 SetCurrentDisplayLayout(
349 ash::DisplayLayout::FromInts(position
, offset
));
350 ash::Shell::GetInstance()->display_configurator_animation()->
351 StartFadeInAnimation();
354 void DisplayOptionsHandler::HandleDisplayInfo(
355 const base::ListValue
* unused_args
) {
356 SendAllDisplayInfo();
359 void DisplayOptionsHandler::HandleMirroring(const base::ListValue
* args
) {
360 DCHECK(!args
->empty());
361 content::RecordAction(
362 base::UserMetricsAction("Options_DisplayToggleMirroring"));
363 bool is_mirroring
= false;
364 args
->GetBoolean(0, &is_mirroring
);
365 ash::Shell::GetInstance()->display_configurator_animation()->
366 StartFadeOutAnimation(base::Bind(
367 &DisplayOptionsHandler::OnFadeOutForMirroringFinished
,
368 base::Unretained(this),
372 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue
* args
) {
373 DCHECK(!args
->empty());
374 int64 display_id
= GetDisplayId(args
);
375 if (display_id
== gfx::Display::kInvalidDisplayID
)
378 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
379 ash::Shell::GetInstance()->window_tree_host_manager()->SetPrimaryDisplayId(
383 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue
* args
) {
386 if (!args
->GetDouble(0, &layout
) || !args
->GetDouble(1, &offset
)) {
387 LOG(ERROR
) << "Invalid parameter";
388 SendAllDisplayInfo();
391 DCHECK_LE(ash::DisplayLayout::TOP
, layout
);
392 DCHECK_GE(ash::DisplayLayout::LEFT
, layout
);
393 content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
394 ash::Shell::GetInstance()->display_configurator_animation()->
395 StartFadeOutAnimation(base::Bind(
396 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished
,
397 base::Unretained(this),
398 static_cast<int>(layout
),
399 static_cast<int>(offset
)));
402 void DisplayOptionsHandler::HandleSetDisplayMode(const base::ListValue
* args
) {
403 DCHECK(!args
->empty());
405 int64 display_id
= GetDisplayId(args
);
406 if (display_id
== gfx::Display::kInvalidDisplayID
)
409 const base::DictionaryValue
* mode_data
= nullptr;
410 if (!args
->GetDictionary(1, &mode_data
)) {
411 LOG(ERROR
) << "Failed to get mode data";
415 ash::DisplayMode mode
;
416 if (!ConvertValueToDisplayMode(mode_data
, &mode
))
419 content::RecordAction(
420 base::UserMetricsAction("Options_DisplaySetResolution"));
421 ash::DisplayManager
* display_manager
= GetDisplayManager();
422 ash::DisplayMode current_mode
=
423 display_manager
->GetActiveModeForDisplayId(display_id
);
424 if (display_manager
->SetDisplayMode(display_id
, mode
)) {
425 ash::Shell::GetInstance()->resolution_notification_controller()->
427 display_id
, current_mode
, mode
, base::Bind(&StoreDisplayPrefs
));
431 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue
* args
) {
432 DCHECK(!args
->empty());
434 int64 display_id
= GetDisplayId(args
);
435 if (display_id
== gfx::Display::kInvalidDisplayID
)
438 std::string rotation_value
;
439 gfx::Display::Rotation new_rotation
= gfx::Display::ROTATE_0
;
440 if (!args
->GetString(1, &rotation_value
)) {
441 LOG(ERROR
) << "Can't find new orientation";
444 if (rotation_value
== "90")
445 new_rotation
= gfx::Display::ROTATE_90
;
446 else if (rotation_value
== "180")
447 new_rotation
= gfx::Display::ROTATE_180
;
448 else if (rotation_value
== "270")
449 new_rotation
= gfx::Display::ROTATE_270
;
450 else if (rotation_value
!= "0")
451 LOG(ERROR
) << "Invalid rotation: " << rotation_value
<< " Falls back to 0";
453 content::RecordAction(
454 base::UserMetricsAction("Options_DisplaySetOrientation"));
455 ash::ScreenRotationAnimator(display_id
)
456 .Rotate(new_rotation
, gfx::Display::ROTATION_SOURCE_USER
);
459 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue
* args
) {
460 DCHECK(!args
->empty());
461 int64 display_id
= GetDisplayId(args
);
462 if (display_id
== gfx::Display::kInvalidDisplayID
)
465 std::string profile_value
;
466 if (!args
->GetString(1, &profile_value
)) {
467 LOG(ERROR
) << "Invalid profile_value";
472 if (!base::StringToInt(profile_value
, &profile_id
)) {
473 LOG(ERROR
) << "Invalid profile: " << profile_value
;
477 if (profile_id
< ui::COLOR_PROFILE_STANDARD
||
478 profile_id
> ui::COLOR_PROFILE_READING
) {
479 LOG(ERROR
) << "Invalid profile_id: " << profile_id
;
483 GetDisplayManager()->SetColorCalibrationProfile(
484 display_id
, static_cast<ui::ColorCalibrationProfile
>(profile_id
));
485 SendAllDisplayInfo();
488 void DisplayOptionsHandler::HandleSetUnifiedDesktopEnabled(
489 const base::ListValue
* args
) {
490 DCHECK(GetDisplayManager()->unified_desktop_enabled());
492 if (args
->GetBoolean(0, &enable
)) {
493 GetDisplayManager()->SetDefaultMultiDisplayModeForCurrentDisplays(
494 enable
? DisplayManager::UNIFIED
: DisplayManager::EXTENDED
);
495 GetDisplayManager()->ReconfigureDisplays();
499 } // namespace options
500 } // namespace chromeos