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 "content/public/browser/web_ui.h"
21 #include "grit/ash_strings.h"
22 #include "grit/generated_resources.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/gfx/display.h"
25 #include "ui/gfx/rect.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/gfx/size_conversions.h"
29 using ash::internal::DisplayManager;
35 DisplayManager* GetDisplayManager() {
36 return ash::Shell::GetInstance()->display_manager();
39 int64 GetDisplayId(const base::ListValue* args) {
40 // Assumes the display ID is specified as the first argument.
42 if (!args->GetString(0, &id_value)) {
43 LOG(ERROR) << "Can't find ID";
44 return gfx::Display::kInvalidDisplayID;
47 int64 display_id = gfx::Display::kInvalidDisplayID;
48 if (!base::StringToInt64(id_value, &display_id)) {
49 LOG(ERROR) << "Invalid display id: " << id_value;
50 return gfx::Display::kInvalidDisplayID;
56 bool CompareDisplayMode(ash::internal::DisplayMode d1,
57 ash::internal::DisplayMode d2) {
58 if (d1.size.GetArea() == d2.size.GetArea())
59 return d1.refresh_rate < d2.refresh_rate;
60 return d1.size.GetArea() < d2.size.GetArea();
63 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
65 case ui::COLOR_PROFILE_STANDARD:
66 return l10n_util::GetStringUTF16(
67 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD);
68 case ui::COLOR_PROFILE_DYNAMIC:
69 return l10n_util::GetStringUTF16(
70 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC);
71 case ui::COLOR_PROFILE_MOVIE:
72 return l10n_util::GetStringUTF16(
73 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE);
74 case ui::COLOR_PROFILE_READING:
75 return l10n_util::GetStringUTF16(
76 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING);
77 case ui::NUM_COLOR_PROFILES:
82 return base::string16();
87 DisplayOptionsHandler::DisplayOptionsHandler() {
88 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
91 DisplayOptionsHandler::~DisplayOptionsHandler() {
92 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
95 void DisplayOptionsHandler::GetLocalizedValues(
96 base::DictionaryValue* localized_strings) {
97 DCHECK(localized_strings);
98 RegisterTitle(localized_strings, "displayOptionsPage",
99 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);
101 localized_strings->SetString(
102 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
103 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
104 localized_strings->SetString(
105 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
106 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
107 localized_strings->SetString(
108 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
109 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
110 localized_strings->SetString(
111 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
112 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));
114 localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16(
115 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING));
116 localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16(
117 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING));
118 localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
119 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
120 localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
121 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
122 localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
123 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
124 localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
125 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
126 localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
127 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
128 localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
129 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
130 localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
131 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
132 localized_strings->SetString(
133 "startCalibratingOverscan", l10n_util::GetStringUTF16(
134 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
135 localized_strings->SetString(
136 "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
137 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE));
140 void DisplayOptionsHandler::InitializePage() {
144 void DisplayOptionsHandler::RegisterMessages() {
145 web_ui()->RegisterMessageCallback(
147 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
148 base::Unretained(this)));
149 web_ui()->RegisterMessageCallback(
151 base::Bind(&DisplayOptionsHandler::HandleMirroring,
152 base::Unretained(this)));
153 web_ui()->RegisterMessageCallback(
155 base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
156 base::Unretained(this)));
157 web_ui()->RegisterMessageCallback(
159 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
160 base::Unretained(this)));
161 web_ui()->RegisterMessageCallback(
163 base::Bind(&DisplayOptionsHandler::HandleSetUIScale,
164 base::Unretained(this)));
165 web_ui()->RegisterMessageCallback(
167 base::Bind(&DisplayOptionsHandler::HandleSetResolution,
168 base::Unretained(this)));
169 web_ui()->RegisterMessageCallback(
171 base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
172 base::Unretained(this)));
173 web_ui()->RegisterMessageCallback(
175 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
176 base::Unretained(this)));
179 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
182 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
183 SendAllDisplayInfo();
186 void DisplayOptionsHandler::SendAllDisplayInfo() {
187 DisplayManager* display_manager = GetDisplayManager();
189 std::vector<gfx::Display> displays;
190 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
191 displays.push_back(display_manager->GetDisplayAt(i));
193 SendDisplayInfo(displays);
196 void DisplayOptionsHandler::SendDisplayInfo(
197 const std::vector<gfx::Display>& displays) {
198 DisplayManager* display_manager = GetDisplayManager();
199 base::FundamentalValue mirroring(display_manager->IsMirrored());
201 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
202 base::ListValue js_displays;
203 for (size_t i = 0; i < displays.size(); ++i) {
204 const gfx::Display& display = displays[i];
205 const ash::internal::DisplayInfo& display_info =
206 display_manager->GetDisplayInfo(display.id());
207 const gfx::Rect& bounds = display.bounds();
208 base::DictionaryValue* js_display = new base::DictionaryValue();
209 js_display->SetString("id", base::Int64ToString(display.id()));
210 js_display->SetInteger("x", bounds.x());
211 js_display->SetInteger("y", bounds.y());
212 js_display->SetInteger("width", bounds.width());
213 js_display->SetInteger("height", bounds.height());
214 js_display->SetString("name",
215 display_manager->GetDisplayNameForId(display.id()));
216 js_display->SetBoolean("isPrimary", display.id() == primary_id);
217 js_display->SetBoolean("isInternal", display.IsInternal());
218 js_display->SetInteger("orientation",
219 static_cast<int>(display_info.rotation()));
220 std::vector<ash::internal::DisplayMode> display_modes;
221 std::vector<float> ui_scales;
222 if (display.IsInternal()) {
223 ui_scales = DisplayManager::GetScalesForDisplay(display_info);
224 gfx::SizeF base_size = display_info.bounds_in_native().size();
225 base_size.Scale(1.0f / display_info.device_scale_factor());
226 if (display_info.rotation() == gfx::Display::ROTATE_90 ||
227 display_info.rotation() == gfx::Display::ROTATE_270) {
228 float tmp = base_size.width();
229 base_size.set_width(base_size.height());
230 base_size.set_height(tmp);
232 for (size_t i = 0; i < ui_scales.size(); ++i) {
233 gfx::SizeF new_size = base_size;
234 new_size.Scale(ui_scales[i]);
235 display_modes.push_back(ash::internal::DisplayMode(
236 gfx::ToFlooredSize(new_size), -1.0f, false, false));
239 for (size_t i = 0; i < display_info.display_modes().size(); ++i)
240 display_modes.push_back(display_info.display_modes()[i]);
242 std::sort(display_modes.begin(), display_modes.end(), CompareDisplayMode);
244 base::ListValue* js_resolutions = new base::ListValue();
245 gfx::Size current_size = display_info.bounds_in_native().size();
246 gfx::Insets current_overscan = display_info.GetOverscanInsetsInPixel();
247 for (size_t i = 0; i < display_modes.size(); ++i) {
248 base::DictionaryValue* resolution_info = new base::DictionaryValue();
249 gfx::Size resolution = display_modes[i].size;
250 if (!ui_scales.empty()) {
251 resolution_info->SetDouble("scale", ui_scales[i]);
252 if (ui_scales[i] == 1.0f)
253 resolution_info->SetBoolean("isBest", true);
254 resolution_info->SetBoolean(
255 "selected", display_info.configured_ui_scale() == ui_scales[i]);
257 // Picks the largest one as the "best", which is the last element
258 // because |display_modes| is sorted by its area.
259 if (i == display_modes.size() - 1)
260 resolution_info->SetBoolean("isBest", true);
261 resolution_info->SetBoolean("selected", (resolution == current_size));
263 -current_overscan.width(), -current_overscan.height());
265 resolution_info->SetInteger("width", resolution.width());
266 resolution_info->SetInteger("height", resolution.height());
267 if (display_modes[i].refresh_rate > 0.0f) {
268 resolution_info->SetDouble("refreshRate",
269 display_modes[i].refresh_rate);
271 js_resolutions->Append(resolution_info);
273 js_display->Set("resolutions", js_resolutions);
275 js_display->SetInteger("colorProfile", display_info.color_profile());
276 base::ListValue* available_color_profiles = new base::ListValue();
278 i < display_info.available_color_profiles().size(); ++i) {
279 base::DictionaryValue* color_profile_dict = new base::DictionaryValue();
280 ui::ColorCalibrationProfile color_profile =
281 display_info.available_color_profiles()[i];
282 color_profile_dict->SetInteger("profileId", color_profile);
283 color_profile_dict->SetString("name", GetColorProfileName(color_profile));
284 available_color_profiles->Append(color_profile_dict);
286 js_display->Set("availableColorProfiles", available_color_profiles);
287 js_displays.Append(js_display);
290 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue());
291 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue());
292 if (display_manager->GetNumDisplays() > 1) {
293 const ash::DisplayLayout layout =
294 display_manager->GetCurrentDisplayLayout();
295 layout_value.reset(new base::FundamentalValue(layout.position));
296 offset_value.reset(new base::FundamentalValue(layout.offset));
299 web_ui()->CallJavascriptFunction(
300 "options.DisplayOptions.setDisplayInfo",
301 mirroring, js_displays, *layout_value.get(), *offset_value.get());
304 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) {
305 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring);
306 // Not necessary to start fade-in animation. OutputConfigurator will do that.
309 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
310 int position, int offset) {
311 SetCurrentDisplayLayout(
312 ash::DisplayLayout::FromInts(position, offset));
313 ash::Shell::GetInstance()->output_configurator_animation()->
314 StartFadeInAnimation();
317 void DisplayOptionsHandler::HandleDisplayInfo(
318 const base::ListValue* unused_args) {
319 SendAllDisplayInfo();
322 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
323 DCHECK(!args->empty());
324 bool is_mirroring = false;
325 args->GetBoolean(0, &is_mirroring);
326 ash::Shell::GetInstance()->output_configurator_animation()->
327 StartFadeOutAnimation(base::Bind(
328 &DisplayOptionsHandler::OnFadeOutForMirroringFinished,
329 base::Unretained(this),
333 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
334 DCHECK(!args->empty());
335 int64 display_id = GetDisplayId(args);
336 if (display_id == gfx::Display::kInvalidDisplayID)
339 ash::Shell::GetInstance()->display_controller()->
340 SetPrimaryDisplayId(display_id);
343 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
346 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
347 LOG(ERROR) << "Invalid parameter";
348 SendAllDisplayInfo();
351 DCHECK_LE(ash::DisplayLayout::TOP, layout);
352 DCHECK_GE(ash::DisplayLayout::LEFT, layout);
353 ash::Shell::GetInstance()->output_configurator_animation()->
354 StartFadeOutAnimation(base::Bind(
355 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished,
356 base::Unretained(this),
357 static_cast<int>(layout),
358 static_cast<int>(offset)));
361 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) {
362 DCHECK(!args->empty());
364 int64 display_id = GetDisplayId(args);
365 if (display_id == gfx::Display::kInvalidDisplayID)
368 double ui_scale = 0.0f;
369 if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) {
370 LOG(ERROR) << "Can't find new ui_scale";
374 GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale);
377 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) {
378 DCHECK(!args->empty());
379 int64 display_id = GetDisplayId(args);
380 if (display_id == gfx::Display::kInvalidDisplayID)
384 double height = 0.0f;
385 if (!args->GetDouble(1, &width) || width == 0.0f) {
386 LOG(ERROR) << "Can't find new width";
389 if (!args->GetDouble(2, &height) || height == 0.0f) {
390 LOG(ERROR) << "Can't find new height";
394 const ash::internal::DisplayInfo& display_info =
395 GetDisplayManager()->GetDisplayInfo(display_id);
396 gfx::Insets current_overscan = display_info.GetOverscanInsetsInPixel();
397 gfx::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height));
398 new_resolution.Enlarge(current_overscan.width(), current_overscan.height());
399 gfx::Size old_resolution = display_info.bounds_in_native().size();
400 bool has_new_resolution = false;
401 bool has_old_resolution = false;
402 for (size_t i = 0; i < display_info.display_modes().size(); ++i) {
403 ash::internal::DisplayMode display_mode = display_info.display_modes()[i];
404 if (display_mode.size == new_resolution)
405 has_new_resolution = true;
406 if (display_mode.size == old_resolution)
407 has_old_resolution = true;
409 if (!has_new_resolution) {
410 LOG(ERROR) << "No new resolution " << new_resolution.ToString()
411 << " is found in the display info " << display_info.ToString();
414 if (!has_old_resolution) {
415 LOG(ERROR) << "No old resolution " << old_resolution.ToString()
416 << " is found in the display info " << display_info.ToString();
420 ash::Shell::GetInstance()->resolution_notification_controller()->
421 SetDisplayResolutionAndNotify(
422 display_id, old_resolution, new_resolution,
423 base::Bind(&StoreDisplayPrefs));
426 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
427 DCHECK(!args->empty());
429 int64 display_id = GetDisplayId(args);
430 if (display_id == gfx::Display::kInvalidDisplayID)
433 std::string rotation_value;
434 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
435 if (!args->GetString(1, &rotation_value)) {
436 LOG(ERROR) << "Can't find new orientation";
439 if (rotation_value == "90")
440 new_rotation = gfx::Display::ROTATE_90;
441 else if (rotation_value == "180")
442 new_rotation = gfx::Display::ROTATE_180;
443 else if (rotation_value == "270")
444 new_rotation = gfx::Display::ROTATE_270;
445 else if (rotation_value != "0")
446 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
448 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
451 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) {
452 DCHECK(!args->empty());
453 int64 display_id = GetDisplayId(args);
454 if (display_id == gfx::Display::kInvalidDisplayID)
457 std::string profile_value;
458 if (!args->GetString(1, &profile_value)) {
459 LOG(ERROR) << "Invalid profile_value";
464 if (!base::StringToInt(profile_value, &profile_id)) {
465 LOG(ERROR) << "Invalid profile: " << profile_value;
469 if (profile_id < ui::COLOR_PROFILE_STANDARD ||
470 profile_id > ui::COLOR_PROFILE_READING) {
471 LOG(ERROR) << "Invalid profile_id: " << profile_id;
475 GetDisplayManager()->SetColorCalibrationProfile(
476 display_id, static_cast<ui::ColorCalibrationProfile>(profile_id));
477 SendAllDisplayInfo();
480 } // namespace options
481 } // namespace chromeos