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 30 using ash::DisplayManager; 31 32 namespace chromeos { 33 namespace options { 34 namespace { 35 36 DisplayManager* GetDisplayManager() { 37 return ash::Shell::GetInstance()->display_manager(); 38 } 39 40 int64 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 57 bool CompareDisplayMode(ash::DisplayMode d1, ash::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(); 61 } 62 63 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) { 64 switch (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: 78 break; 79 } 80 81 NOTREACHED(); 82 return base::string16(); 83 } 84 85 } // namespace 86 87 DisplayOptionsHandler::DisplayOptionsHandler() { 88 ash::Shell::GetInstance()->display_controller()->AddObserver(this); 89 } 90 91 DisplayOptionsHandler::~DisplayOptionsHandler() { 92 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this); 93 } 94 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); 100 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)); 113 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)); 138 } 139 140 void DisplayOptionsHandler::InitializePage() { 141 DCHECK(web_ui()); 142 } 143 144 void DisplayOptionsHandler::RegisterMessages() { 145 web_ui()->RegisterMessageCallback( 146 "getDisplayInfo", 147 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo, 148 base::Unretained(this))); 149 web_ui()->RegisterMessageCallback( 150 "setMirroring", 151 base::Bind(&DisplayOptionsHandler::HandleMirroring, 152 base::Unretained(this))); 153 web_ui()->RegisterMessageCallback( 154 "setPrimary", 155 base::Bind(&DisplayOptionsHandler::HandleSetPrimary, 156 base::Unretained(this))); 157 web_ui()->RegisterMessageCallback( 158 "setDisplayLayout", 159 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout, 160 base::Unretained(this))); 161 web_ui()->RegisterMessageCallback( 162 "setUIScale", 163 base::Bind(&DisplayOptionsHandler::HandleSetUIScale, 164 base::Unretained(this))); 165 web_ui()->RegisterMessageCallback( 166 "setResolution", 167 base::Bind(&DisplayOptionsHandler::HandleSetResolution, 168 base::Unretained(this))); 169 web_ui()->RegisterMessageCallback( 170 "setOrientation", 171 base::Bind(&DisplayOptionsHandler::HandleSetOrientation, 172 base::Unretained(this))); 173 web_ui()->RegisterMessageCallback( 174 "setColorProfile", 175 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile, 176 base::Unretained(this))); 177 } 178 179 void DisplayOptionsHandler::OnDisplayConfigurationChanging() { 180 } 181 182 void DisplayOptionsHandler::OnDisplayConfigurationChanged() { 183 SendAllDisplayInfo(); 184 } 185 186 void DisplayOptionsHandler::SendAllDisplayInfo() { 187 DisplayManager* display_manager = GetDisplayManager(); 188 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)); 192 } 193 SendDisplayInfo(displays); 194 } 195 196 void DisplayOptionsHandler::SendDisplayInfo( 197 const std::vector<gfx::Display>& displays) { 198 DisplayManager* display_manager = GetDisplayManager(); 199 base::FundamentalValue mirroring(display_manager->IsMirrored()); 200 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::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::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); 231 } 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::DisplayMode( 236 gfx::ToFlooredSize(new_size), -1.0f, false, false)); 237 } 238 } else { 239 for (size_t i = 0; i < display_info.display_modes().size(); ++i) 240 display_modes.push_back(display_info.display_modes()[i]); 241 } 242 std::sort(display_modes.begin(), display_modes.end(), CompareDisplayMode); 243 244 base::ListValue* js_resolutions = new base::ListValue(); 245 gfx::Size current_size = display_info.bounds_in_native().size(); 246 for (size_t i = 0; i < display_modes.size(); ++i) { 247 base::DictionaryValue* resolution_info = new base::DictionaryValue(); 248 gfx::Size resolution = display_modes[i].size; 249 if (!ui_scales.empty()) { 250 resolution_info->SetDouble("scale", ui_scales[i]); 251 if (ui_scales[i] == 1.0f) 252 resolution_info->SetBoolean("isBest", true); 253 resolution_info->SetBoolean( 254 "selected", display_info.configured_ui_scale() == ui_scales[i]); 255 } else { 256 // Picks the largest one as the "best", which is the last element 257 // because |display_modes| is sorted by its area. 258 if (i == display_modes.size() - 1) 259 resolution_info->SetBoolean("isBest", true); 260 resolution_info->SetBoolean("selected", (resolution == current_size)); 261 } 262 resolution_info->SetInteger("width", resolution.width()); 263 resolution_info->SetInteger("height", resolution.height()); 264 if (display_modes[i].refresh_rate > 0.0f) { 265 resolution_info->SetDouble("refreshRate", 266 display_modes[i].refresh_rate); 267 } 268 js_resolutions->Append(resolution_info); 269 } 270 js_display->Set("resolutions", js_resolutions); 271 272 js_display->SetInteger("colorProfile", display_info.color_profile()); 273 base::ListValue* available_color_profiles = new base::ListValue(); 274 for (size_t i = 0; 275 i < display_info.available_color_profiles().size(); ++i) { 276 base::DictionaryValue* color_profile_dict = new base::DictionaryValue(); 277 ui::ColorCalibrationProfile color_profile = 278 display_info.available_color_profiles()[i]; 279 color_profile_dict->SetInteger("profileId", color_profile); 280 color_profile_dict->SetString("name", GetColorProfileName(color_profile)); 281 available_color_profiles->Append(color_profile_dict); 282 } 283 js_display->Set("availableColorProfiles", available_color_profiles); 284 js_displays.Append(js_display); 285 } 286 287 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue()); 288 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue()); 289 if (display_manager->GetNumDisplays() > 1) { 290 const ash::DisplayLayout layout = 291 display_manager->GetCurrentDisplayLayout(); 292 layout_value.reset(new base::FundamentalValue(layout.position)); 293 offset_value.reset(new base::FundamentalValue(layout.offset)); 294 } 295 296 web_ui()->CallJavascriptFunction( 297 "options.DisplayOptions.setDisplayInfo", 298 mirroring, js_displays, *layout_value.get(), *offset_value.get()); 299 } 300 301 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) { 302 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring); 303 // Not necessary to start fade-in animation. DisplayConfigurator will do that. 304 } 305 306 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished( 307 int position, int offset) { 308 SetCurrentDisplayLayout( 309 ash::DisplayLayout::FromInts(position, offset)); 310 ash::Shell::GetInstance()->display_configurator_animation()-> 311 StartFadeInAnimation(); 312 } 313 314 void DisplayOptionsHandler::HandleDisplayInfo( 315 const base::ListValue* unused_args) { 316 SendAllDisplayInfo(); 317 } 318 319 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) { 320 DCHECK(!args->empty()); 321 content::RecordAction( 322 base::UserMetricsAction("Options_DisplayToggleMirroring")); 323 bool is_mirroring = false; 324 args->GetBoolean(0, &is_mirroring); 325 ash::Shell::GetInstance()->display_configurator_animation()-> 326 StartFadeOutAnimation(base::Bind( 327 &DisplayOptionsHandler::OnFadeOutForMirroringFinished, 328 base::Unretained(this), 329 is_mirroring)); 330 } 331 332 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) { 333 DCHECK(!args->empty()); 334 int64 display_id = GetDisplayId(args); 335 if (display_id == gfx::Display::kInvalidDisplayID) 336 return; 337 338 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary")); 339 ash::Shell::GetInstance()->display_controller()-> 340 SetPrimaryDisplayId(display_id); 341 } 342 343 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) { 344 double layout = -1; 345 double offset = -1; 346 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) { 347 LOG(ERROR) << "Invalid parameter"; 348 SendAllDisplayInfo(); 349 return; 350 } 351 DCHECK_LE(ash::DisplayLayout::TOP, layout); 352 DCHECK_GE(ash::DisplayLayout::LEFT, layout); 353 content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange")); 354 ash::Shell::GetInstance()->display_configurator_animation()-> 355 StartFadeOutAnimation(base::Bind( 356 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished, 357 base::Unretained(this), 358 static_cast<int>(layout), 359 static_cast<int>(offset))); 360 } 361 362 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) { 363 DCHECK(!args->empty()); 364 365 int64 display_id = GetDisplayId(args); 366 if (display_id == gfx::Display::kInvalidDisplayID) 367 return; 368 369 double ui_scale = 0.0f; 370 if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) { 371 LOG(ERROR) << "Can't find new ui_scale"; 372 return; 373 } 374 375 GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale); 376 } 377 378 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) { 379 DCHECK(!args->empty()); 380 int64 display_id = GetDisplayId(args); 381 if (display_id == gfx::Display::kInvalidDisplayID) 382 return; 383 384 content::RecordAction( 385 base::UserMetricsAction("Options_DisplaySetResolution")); 386 double width = 0.0f; 387 double height = 0.0f; 388 if (!args->GetDouble(1, &width) || width == 0.0f) { 389 LOG(ERROR) << "Can't find new width"; 390 return; 391 } 392 if (!args->GetDouble(2, &height) || height == 0.0f) { 393 LOG(ERROR) << "Can't find new height"; 394 return; 395 } 396 397 const ash::DisplayInfo& display_info = 398 GetDisplayManager()->GetDisplayInfo(display_id); 399 gfx::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height)); 400 gfx::Size old_resolution = display_info.bounds_in_native().size(); 401 bool has_new_resolution = false; 402 bool has_old_resolution = false; 403 for (size_t i = 0; i < display_info.display_modes().size(); ++i) { 404 ash::DisplayMode display_mode = display_info.display_modes()[i]; 405 if (display_mode.size == new_resolution) 406 has_new_resolution = true; 407 if (display_mode.size == old_resolution) 408 has_old_resolution = true; 409 } 410 if (!has_new_resolution) { 411 LOG(ERROR) << "No new resolution " << new_resolution.ToString() 412 << " is found in the display info " << display_info.ToString(); 413 return; 414 } 415 if (!has_old_resolution) { 416 LOG(ERROR) << "No old resolution " << old_resolution.ToString() 417 << " is found in the display info " << display_info.ToString(); 418 return; 419 } 420 421 ash::Shell::GetInstance()->resolution_notification_controller()-> 422 SetDisplayResolutionAndNotify( 423 display_id, old_resolution, new_resolution, 424 base::Bind(&StoreDisplayPrefs)); 425 } 426 427 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) { 428 DCHECK(!args->empty()); 429 430 int64 display_id = GetDisplayId(args); 431 if (display_id == gfx::Display::kInvalidDisplayID) 432 return; 433 434 std::string rotation_value; 435 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0; 436 if (!args->GetString(1, &rotation_value)) { 437 LOG(ERROR) << "Can't find new orientation"; 438 return; 439 } 440 if (rotation_value == "90") 441 new_rotation = gfx::Display::ROTATE_90; 442 else if (rotation_value == "180") 443 new_rotation = gfx::Display::ROTATE_180; 444 else if (rotation_value == "270") 445 new_rotation = gfx::Display::ROTATE_270; 446 else if (rotation_value != "0") 447 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0"; 448 449 content::RecordAction( 450 base::UserMetricsAction("Options_DisplaySetOrientation")); 451 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation); 452 } 453 454 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) { 455 DCHECK(!args->empty()); 456 int64 display_id = GetDisplayId(args); 457 if (display_id == gfx::Display::kInvalidDisplayID) 458 return; 459 460 std::string profile_value; 461 if (!args->GetString(1, &profile_value)) { 462 LOG(ERROR) << "Invalid profile_value"; 463 return; 464 } 465 466 int profile_id; 467 if (!base::StringToInt(profile_value, &profile_id)) { 468 LOG(ERROR) << "Invalid profile: " << profile_value; 469 return; 470 } 471 472 if (profile_id < ui::COLOR_PROFILE_STANDARD || 473 profile_id > ui::COLOR_PROFILE_READING) { 474 LOG(ERROR) << "Invalid profile_id: " << profile_id; 475 return; 476 } 477 478 GetDisplayManager()->SetColorCalibrationProfile( 479 display_id, static_cast<ui::ColorCalibrationProfile>(profile_id)); 480 SendAllDisplayInfo(); 481 } 482 483 } // namespace options 484 } // namespace chromeos 485