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_controller.h"
10 #include "ash/display/display_manager.h"
11 #include "ash/display/output_configurator_animation.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 "chromeos/display/output_configurator.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::internal::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(ash::internal::Resolution r1
,
58 ash::internal::Resolution r2
) {
59 return r1
.size
.GetArea() < r2
.size
.GetArea();
64 DisplayOptionsHandler::DisplayOptionsHandler() {
65 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
68 DisplayOptionsHandler::~DisplayOptionsHandler() {
69 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
72 void DisplayOptionsHandler::GetLocalizedValues(
73 base::DictionaryValue
* localized_strings
) {
74 DCHECK(localized_strings
);
75 RegisterTitle(localized_strings
, "displayOptionsPage",
76 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE
);
78 localized_strings
->SetString(
79 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
80 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS
));
81 localized_strings
->SetString(
82 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
83 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION
));
84 localized_strings
->SetString(
85 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
86 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION
));
87 localized_strings
->SetString(
88 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
89 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN
));
91 localized_strings
->SetString("startMirroring", l10n_util::GetStringUTF16(
92 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING
));
93 localized_strings
->SetString("stopMirroring", l10n_util::GetStringUTF16(
94 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING
));
95 localized_strings
->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
96 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME
));
97 localized_strings
->SetString("setPrimary", l10n_util::GetStringUTF16(
98 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY
));
99 localized_strings
->SetString("annotateBest", l10n_util::GetStringUTF16(
100 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST
));
101 localized_strings
->SetString("orientation0", l10n_util::GetStringUTF16(
102 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION
));
103 localized_strings
->SetString("orientation90", l10n_util::GetStringUTF16(
104 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90
));
105 localized_strings
->SetString("orientation180", l10n_util::GetStringUTF16(
106 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180
));
107 localized_strings
->SetString("orientation270", l10n_util::GetStringUTF16(
108 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270
));
109 localized_strings
->SetString(
110 "startCalibratingOverscan", l10n_util::GetStringUTF16(
111 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN
));
114 void DisplayOptionsHandler::InitializePage() {
118 void DisplayOptionsHandler::RegisterMessages() {
119 web_ui()->RegisterMessageCallback(
121 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo
,
122 base::Unretained(this)));
123 web_ui()->RegisterMessageCallback(
125 base::Bind(&DisplayOptionsHandler::HandleMirroring
,
126 base::Unretained(this)));
127 web_ui()->RegisterMessageCallback(
129 base::Bind(&DisplayOptionsHandler::HandleSetPrimary
,
130 base::Unretained(this)));
131 web_ui()->RegisterMessageCallback(
133 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout
,
134 base::Unretained(this)));
135 web_ui()->RegisterMessageCallback(
137 base::Bind(&DisplayOptionsHandler::HandleSetUIScale
,
138 base::Unretained(this)));
139 web_ui()->RegisterMessageCallback(
141 base::Bind(&DisplayOptionsHandler::HandleSetResolution
,
142 base::Unretained(this)));
143 web_ui()->RegisterMessageCallback(
145 base::Bind(&DisplayOptionsHandler::HandleSetOrientation
,
146 base::Unretained(this)));
149 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
152 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
153 SendAllDisplayInfo();
156 void DisplayOptionsHandler::SendAllDisplayInfo() {
157 DisplayManager
* display_manager
= GetDisplayManager();
159 std::vector
<gfx::Display
> displays
;
160 for (size_t i
= 0; i
< display_manager
->GetNumDisplays(); ++i
) {
161 displays
.push_back(display_manager
->GetDisplayAt(i
));
163 SendDisplayInfo(displays
);
166 void DisplayOptionsHandler::SendDisplayInfo(
167 const std::vector
<gfx::Display
>& displays
) {
168 DisplayManager
* display_manager
= GetDisplayManager();
169 base::FundamentalValue
mirroring(display_manager
->IsMirrored());
171 int64 primary_id
= ash::Shell::GetScreen()->GetPrimaryDisplay().id();
172 base::ListValue js_displays
;
173 for (size_t i
= 0; i
< displays
.size(); ++i
) {
174 const gfx::Display
& display
= displays
[i
];
175 const ash::internal::DisplayInfo
& display_info
=
176 display_manager
->GetDisplayInfo(display
.id());
177 const gfx::Rect
& bounds
= display
.bounds();
178 base::DictionaryValue
* js_display
= new base::DictionaryValue();
179 js_display
->SetString("id", base::Int64ToString(display
.id()));
180 js_display
->SetInteger("x", bounds
.x());
181 js_display
->SetInteger("y", bounds
.y());
182 js_display
->SetInteger("width", bounds
.width());
183 js_display
->SetInteger("height", bounds
.height());
184 js_display
->SetString("name",
185 display_manager
->GetDisplayNameForId(display
.id()));
186 js_display
->SetBoolean("isPrimary", display
.id() == primary_id
);
187 js_display
->SetBoolean("isInternal", display
.IsInternal());
188 js_display
->SetInteger("orientation",
189 static_cast<int>(display_info
.rotation()));
190 std::vector
<ash::internal::Resolution
> resolutions
;
191 std::vector
<float> ui_scales
;
192 if (display
.IsInternal()) {
193 ui_scales
= DisplayManager::GetScalesForDisplay(display_info
);
194 gfx::SizeF base_size
= display_info
.bounds_in_native().size();
195 base_size
.Scale(1.0f
/ display
.device_scale_factor());
196 if (display_info
.rotation() == gfx::Display::ROTATE_90
||
197 display_info
.rotation() == gfx::Display::ROTATE_270
) {
198 float tmp
= base_size
.width();
199 base_size
.set_width(base_size
.height());
200 base_size
.set_height(tmp
);
202 for (size_t i
= 0; i
< ui_scales
.size(); ++i
) {
203 gfx::SizeF new_size
= base_size
;
204 new_size
.Scale(ui_scales
[i
]);
205 resolutions
.push_back(ash::internal::Resolution(
206 gfx::ToFlooredSize(new_size
), false /* interlaced */));
209 for (size_t i
= 0; i
< display_info
.resolutions().size(); ++i
)
210 resolutions
.push_back(display_info
.resolutions()[i
]);
212 std::sort(resolutions
.begin(), resolutions
.end(), CompareResolution
);
214 base::ListValue
* js_resolutions
= new base::ListValue();
215 gfx::Size current_size
= display_info
.bounds_in_native().size();
216 gfx::Insets current_overscan
= display_info
.GetOverscanInsetsInPixel();
217 for (size_t i
= 0; i
< resolutions
.size(); ++i
) {
218 base::DictionaryValue
* resolution_info
= new base::DictionaryValue();
219 gfx::Size resolution
= resolutions
[i
].size
;
220 if (!ui_scales
.empty()) {
221 resolution_info
->SetDouble("scale", ui_scales
[i
]);
222 if (ui_scales
[i
] == 1.0f
)
223 resolution_info
->SetBoolean("isBest", true);
224 resolution_info
->SetBoolean(
225 "selected", display_info
.configured_ui_scale() == ui_scales
[i
]);
227 // Picks the largest one as the "best", which is the last element
228 // because |resolutions| is sorted by its area.
229 if (i
== resolutions
.size() - 1)
230 resolution_info
->SetBoolean("isBest", true);
231 resolution_info
->SetBoolean("selected", (resolution
== current_size
));
233 -current_overscan
.width(), -current_overscan
.height());
235 resolution_info
->SetInteger("width", resolution
.width());
236 resolution_info
->SetInteger("height", resolution
.height());
237 js_resolutions
->Append(resolution_info
);
239 js_display
->Set("resolutions", js_resolutions
);
240 js_displays
.Append(js_display
);
243 scoped_ptr
<base::Value
> layout_value(base::Value::CreateNullValue());
244 scoped_ptr
<base::Value
> offset_value(base::Value::CreateNullValue());
245 if (display_manager
->GetNumDisplays() > 1) {
246 const ash::DisplayLayout layout
=
247 display_manager
->GetCurrentDisplayLayout();
248 layout_value
.reset(new base::FundamentalValue(layout
.position
));
249 offset_value
.reset(new base::FundamentalValue(layout
.offset
));
252 web_ui()->CallJavascriptFunction(
253 "options.DisplayOptions.setDisplayInfo",
254 mirroring
, js_displays
, *layout_value
.get(), *offset_value
.get());
257 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring
) {
258 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring
);
259 // Not necessary to start fade-in animation. OutputConfigurator will do that.
262 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
263 int position
, int offset
) {
264 SetCurrentDisplayLayout(
265 ash::DisplayLayout::FromInts(position
, offset
));
266 ash::Shell::GetInstance()->output_configurator_animation()->
267 StartFadeInAnimation();
270 void DisplayOptionsHandler::HandleDisplayInfo(
271 const base::ListValue
* unused_args
) {
272 SendAllDisplayInfo();
275 void DisplayOptionsHandler::HandleMirroring(const base::ListValue
* args
) {
276 DCHECK(!args
->empty());
277 bool is_mirroring
= false;
278 args
->GetBoolean(0, &is_mirroring
);
279 ash::Shell::GetInstance()->output_configurator_animation()->
280 StartFadeOutAnimation(base::Bind(
281 &DisplayOptionsHandler::OnFadeOutForMirroringFinished
,
282 base::Unretained(this),
286 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue
* args
) {
287 DCHECK(!args
->empty());
288 int64 display_id
= GetDisplayId(args
);
289 if (display_id
== gfx::Display::kInvalidDisplayID
)
292 ash::Shell::GetInstance()->display_controller()->
293 SetPrimaryDisplayId(display_id
);
296 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue
* args
) {
299 if (!args
->GetDouble(0, &layout
) || !args
->GetDouble(1, &offset
)) {
300 LOG(ERROR
) << "Invalid parameter";
301 SendAllDisplayInfo();
304 DCHECK_LE(ash::DisplayLayout::TOP
, layout
);
305 DCHECK_GE(ash::DisplayLayout::LEFT
, layout
);
306 ash::Shell::GetInstance()->output_configurator_animation()->
307 StartFadeOutAnimation(base::Bind(
308 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished
,
309 base::Unretained(this),
310 static_cast<int>(layout
),
311 static_cast<int>(offset
)));
314 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue
* args
) {
315 DCHECK(!args
->empty());
317 int64 display_id
= GetDisplayId(args
);
318 if (display_id
== gfx::Display::kInvalidDisplayID
)
321 double ui_scale
= 0.0f
;
322 if (!args
->GetDouble(1, &ui_scale
) || ui_scale
== 0.0f
) {
323 LOG(ERROR
) << "Can't find new ui_scale";
327 GetDisplayManager()->SetDisplayUIScale(display_id
, ui_scale
);
330 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue
* args
) {
331 DCHECK(!args
->empty());
332 int64 display_id
= GetDisplayId(args
);
333 if (display_id
== gfx::Display::kInvalidDisplayID
)
337 double height
= 0.0f
;
338 if (!args
->GetDouble(1, &width
) || width
== 0.0f
) {
339 LOG(ERROR
) << "Can't find new width";
342 if (!args
->GetDouble(2, &height
) || height
== 0.0f
) {
343 LOG(ERROR
) << "Can't find new height";
347 const ash::internal::DisplayInfo
& display_info
=
348 GetDisplayManager()->GetDisplayInfo(display_id
);
349 gfx::Insets current_overscan
= display_info
.GetOverscanInsetsInPixel();
350 gfx::Size new_resolution
= gfx::ToFlooredSize(gfx::SizeF(width
, height
));
351 new_resolution
.Enlarge(current_overscan
.width(), current_overscan
.height());
352 gfx::Size old_resolution
= display_info
.bounds_in_native().size();
353 bool has_new_resolution
= false;
354 bool has_old_resolution
= false;
355 for (size_t i
= 0; i
< display_info
.resolutions().size(); ++i
) {
356 ash::internal::Resolution resolution
= display_info
.resolutions()[i
];
357 if (resolution
.size
== new_resolution
)
358 has_new_resolution
= true;
359 if (resolution
.size
== old_resolution
)
360 has_old_resolution
= true;
362 if (!has_new_resolution
) {
363 LOG(ERROR
) << "No new resolution " << new_resolution
.ToString()
364 << " is found in the display info " << display_info
.ToString();
367 if (!has_old_resolution
) {
368 LOG(ERROR
) << "No old resolution " << old_resolution
.ToString()
369 << " is found in the display info " << display_info
.ToString();
373 ash::Shell::GetInstance()->resolution_notification_controller()->
374 SetDisplayResolutionAndNotify(
375 display_id
, old_resolution
, new_resolution
,
376 base::Bind(&StoreDisplayPrefs
));
379 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue
* args
) {
380 DCHECK(!args
->empty());
382 int64 display_id
= GetDisplayId(args
);
383 if (display_id
== gfx::Display::kInvalidDisplayID
)
386 std::string rotation_value
;
387 gfx::Display::Rotation new_rotation
= gfx::Display::ROTATE_0
;
388 if (!args
->GetString(1, &rotation_value
)) {
389 LOG(ERROR
) << "Can't find new orientation";
392 if (rotation_value
== "90")
393 new_rotation
= gfx::Display::ROTATE_90
;
394 else if (rotation_value
== "180")
395 new_rotation
= gfx::Display::ROTATE_180
;
396 else if (rotation_value
== "270")
397 new_rotation
= gfx::Display::ROTATE_270
;
398 else if (rotation_value
!= "0")
399 LOG(ERROR
) << "Invalid rotation: " << rotation_value
<< " Falls back to 0";
401 GetDisplayManager()->SetDisplayRotation(display_id
, new_rotation
);
404 } // namespace options
405 } // namespace chromeos