display_options_handler.cc revision 010d83a9304c5a91596085d917d248abff47903a
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. 4 5#include "chrome/browser/ui/webui/options/chromeos/display_options_handler.h" 6 7#include <string> 8 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" 29 30using ash::DisplayManager; 31 32namespace chromeos { 33namespace options { 34namespace { 35 36DisplayManager* GetDisplayManager() { 37 return ash::Shell::GetInstance()->display_manager(); 38} 39 40int64 GetDisplayId(const base::ListValue* args) { 41 // Assumes the display ID is specified as the first argument. 42 std::string id_value; 43 if (!args->GetString(0, &id_value)) { 44 LOG(ERROR) << "Can't find ID"; 45 return gfx::Display::kInvalidDisplayID; 46 } 47 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; 52 } 53 54 return display_id; 55} 56 57bool 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; 68 } 69 if (d2->GetDouble("scaleFactor", &scale_factor2)) { 70 width2 /= scale_factor2; 71 height2 /= scale_factor2; 72 } 73 74 if (width1 * height1 == width2 * height2) { 75 if (scale_factor1 != scale_factor2) 76 return scale_factor1 < scale_factor2; 77 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; 82 } 83 return width1 * height1 < width2 * height2; 84} 85 86base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) { 87 switch (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: 101 break; 102 } 103 104 NOTREACHED(); 105 return base::string16(); 106} 107 108scoped_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); 120 } 121 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); 135 } 136 137 return js_resolutions.Pass(); 138} 139 140scoped_ptr<base::ListValue> GetResolutionsForExternalDisplay( 141 const ash::DisplayInfo& display_info) { 142 scoped_ptr<base::ListValue> js_resolutions(new base::ListValue); 143 144 gfx::Size current_size = display_info.bounds_in_native().size(); 145 int largest_index = -1; 146 int largest_area = -1; 147 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; 152 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); 160 } 161 largest_index = i; 162 } 163 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); 170 } 171 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); 176 } 177 178 return js_resolutions.Pass(); 179} 180 181} // namespace 182 183DisplayOptionsHandler::DisplayOptionsHandler() { 184 ash::Shell::GetInstance()->display_controller()->AddObserver(this); 185} 186 187DisplayOptionsHandler::~DisplayOptionsHandler() { 188 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this); 189} 190 191void DisplayOptionsHandler::GetLocalizedValues( 192 base::DictionaryValue* localized_strings) { 193 DCHECK(localized_strings); 194 RegisterTitle(localized_strings, "displayOptionsPage", 195 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE); 196 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)); 209 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)); 234} 235 236void DisplayOptionsHandler::InitializePage() { 237 DCHECK(web_ui()); 238} 239 240void DisplayOptionsHandler::RegisterMessages() { 241 web_ui()->RegisterMessageCallback( 242 "getDisplayInfo", 243 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo, 244 base::Unretained(this))); 245 web_ui()->RegisterMessageCallback( 246 "setMirroring", 247 base::Bind(&DisplayOptionsHandler::HandleMirroring, 248 base::Unretained(this))); 249 web_ui()->RegisterMessageCallback( 250 "setPrimary", 251 base::Bind(&DisplayOptionsHandler::HandleSetPrimary, 252 base::Unretained(this))); 253 web_ui()->RegisterMessageCallback( 254 "setDisplayLayout", 255 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout, 256 base::Unretained(this))); 257 web_ui()->RegisterMessageCallback( 258 "setUIScale", 259 base::Bind(&DisplayOptionsHandler::HandleSetUIScale, 260 base::Unretained(this))); 261 web_ui()->RegisterMessageCallback( 262 "setResolution", 263 base::Bind(&DisplayOptionsHandler::HandleSetResolution, 264 base::Unretained(this))); 265 web_ui()->RegisterMessageCallback( 266 "setOrientation", 267 base::Bind(&DisplayOptionsHandler::HandleSetOrientation, 268 base::Unretained(this))); 269 web_ui()->RegisterMessageCallback( 270 "setColorProfile", 271 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile, 272 base::Unretained(this))); 273} 274 275void DisplayOptionsHandler::OnDisplayConfigurationChanging() { 276} 277 278void DisplayOptionsHandler::OnDisplayConfigurationChanged() { 279 SendAllDisplayInfo(); 280} 281 282void DisplayOptionsHandler::SendAllDisplayInfo() { 283 DisplayManager* display_manager = GetDisplayManager(); 284 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)); 288 } 289 SendDisplayInfo(displays); 290} 291 292void DisplayOptionsHandler::SendDisplayInfo( 293 const std::vector<gfx::Display>& displays) { 294 DisplayManager* display_manager = GetDisplayManager(); 295 base::FundamentalValue mirroring(display_manager->IsMirrored()); 296 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())); 316 317 scoped_ptr<base::ListValue> js_resolutions = display.IsInternal() ? 318 GetResolutionsForInternalDisplay(display_info) : 319 GetResolutionsForExternalDisplay(display_info); 320 std::sort( 321 js_resolutions->begin(), js_resolutions->end(), CompareResolution); 322 js_display->Set("resolutions", js_resolutions.release()); 323 324 js_display->SetInteger("colorProfile", display_info.color_profile()); 325 base::ListValue* available_color_profiles = new base::ListValue(); 326 for (size_t i = 0; 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); 334 } 335 js_display->Set("availableColorProfiles", available_color_profiles); 336 js_displays.Append(js_display); 337 } 338 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)); 346 } 347 348 web_ui()->CallJavascriptFunction( 349 "options.DisplayOptions.setDisplayInfo", 350 mirroring, js_displays, *layout_value.get(), *offset_value.get()); 351} 352 353void 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. 356} 357 358void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished( 359 int position, int offset) { 360 SetCurrentDisplayLayout( 361 ash::DisplayLayout::FromInts(position, offset)); 362 ash::Shell::GetInstance()->display_configurator_animation()-> 363 StartFadeInAnimation(); 364} 365 366void DisplayOptionsHandler::HandleDisplayInfo( 367 const base::ListValue* unused_args) { 368 SendAllDisplayInfo(); 369} 370 371void 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), 381 is_mirroring)); 382} 383 384void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) { 385 DCHECK(!args->empty()); 386 int64 display_id = GetDisplayId(args); 387 if (display_id == gfx::Display::kInvalidDisplayID) 388 return; 389 390 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary")); 391 ash::Shell::GetInstance()->display_controller()-> 392 SetPrimaryDisplayId(display_id); 393} 394 395void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) { 396 double layout = -1; 397 double offset = -1; 398 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) { 399 LOG(ERROR) << "Invalid parameter"; 400 SendAllDisplayInfo(); 401 return; 402 } 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))); 412} 413 414void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) { 415 DCHECK(!args->empty()); 416 417 int64 display_id = GetDisplayId(args); 418 if (display_id == gfx::Display::kInvalidDisplayID) 419 return; 420 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"; 424 return; 425 } 426 427 GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale); 428} 429 430void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) { 431 DCHECK(!args->empty()); 432 int64 display_id = GetDisplayId(args); 433 if (display_id == gfx::Display::kInvalidDisplayID) 434 return; 435 436 content::RecordAction( 437 base::UserMetricsAction("Options_DisplaySetResolution")); 438 double width = 0.0f; 439 double height = 0.0f; 440 if (!args->GetDouble(1, &width) || width == 0.0f) { 441 LOG(ERROR) << "Can't find new width"; 442 return; 443 } 444 if (!args->GetDouble(2, &height) || height == 0.0f) { 445 LOG(ERROR) << "Can't find new height"; 446 return; 447 } 448 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; 461 } 462 if (!has_new_resolution) { 463 LOG(ERROR) << "No new resolution " << new_resolution.ToString() 464 << " is found in the display info " << display_info.ToString(); 465 return; 466 } 467 if (!has_old_resolution) { 468 LOG(ERROR) << "No old resolution " << old_resolution.ToString() 469 << " is found in the display info " << display_info.ToString(); 470 return; 471 } 472 473 ash::Shell::GetInstance()->resolution_notification_controller()-> 474 SetDisplayResolutionAndNotify( 475 display_id, old_resolution, new_resolution, 476 base::Bind(&StoreDisplayPrefs)); 477} 478 479void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) { 480 DCHECK(!args->empty()); 481 482 int64 display_id = GetDisplayId(args); 483 if (display_id == gfx::Display::kInvalidDisplayID) 484 return; 485 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"; 490 return; 491 } 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"; 500 501 content::RecordAction( 502 base::UserMetricsAction("Options_DisplaySetOrientation")); 503 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation); 504} 505 506void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) { 507 DCHECK(!args->empty()); 508 int64 display_id = GetDisplayId(args); 509 if (display_id == gfx::Display::kInvalidDisplayID) 510 return; 511 512 std::string profile_value; 513 if (!args->GetString(1, &profile_value)) { 514 LOG(ERROR) << "Invalid profile_value"; 515 return; 516 } 517 518 int profile_id; 519 if (!base::StringToInt(profile_value, &profile_id)) { 520 LOG(ERROR) << "Invalid profile: " << profile_value; 521 return; 522 } 523 524 if (profile_id < ui::COLOR_PROFILE_STANDARD || 525 profile_id > ui::COLOR_PROFILE_READING) { 526 LOG(ERROR) << "Invalid profile_id: " << profile_id; 527 return; 528 } 529 530 GetDisplayManager()->SetColorCalibrationProfile( 531 display_id, static_cast<ui::ColorCalibrationProfile>(profile_id)); 532 SendAllDisplayInfo(); 533} 534 535} // namespace options 536} // namespace chromeos 537