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 "chrome/grit/generated_resources.h" 21 #include "content/public/browser/user_metrics.h" 22 #include "content/public/browser/web_ui.h" 23 #include "grit/ash_strings.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 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) { 58 switch (profile) { 59 case ui::COLOR_PROFILE_STANDARD: 60 return l10n_util::GetStringUTF16( 61 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD); 62 case ui::COLOR_PROFILE_DYNAMIC: 63 return l10n_util::GetStringUTF16( 64 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC); 65 case ui::COLOR_PROFILE_MOVIE: 66 return l10n_util::GetStringUTF16( 67 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE); 68 case ui::COLOR_PROFILE_READING: 69 return l10n_util::GetStringUTF16( 70 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING); 71 case ui::NUM_COLOR_PROFILES: 72 break; 73 } 74 75 NOTREACHED(); 76 return base::string16(); 77 } 78 79 int GetIntOrDouble(const base::DictionaryValue* dict, 80 const std::string& field) { 81 double double_result = 0; 82 if (dict->GetDouble(field, &double_result)) 83 return static_cast<int>(double_result); 84 85 int result = 0; 86 dict->GetInteger(field, &result); 87 return result; 88 } 89 90 bool GetFloat(const base::DictionaryValue* dict, 91 const std::string& field, 92 float* result) { 93 double double_result = 0; 94 if (dict->GetDouble(field, &double_result)) { 95 *result = static_cast<float>(double_result); 96 return true; 97 } 98 return false; 99 } 100 101 bool ConvertValueToDisplayMode(const base::DictionaryValue* dict, 102 ash::DisplayMode* mode) { 103 mode->size.set_width(GetIntOrDouble(dict, "originalWidth")); 104 mode->size.set_height(GetIntOrDouble(dict, "originalHeight")); 105 if (mode->size.IsEmpty()) { 106 LOG(ERROR) << "missing width or height."; 107 return false; 108 } 109 if (!GetFloat(dict, "refreshRate", &mode->refresh_rate)) { 110 LOG(ERROR) << "missing refreshRate."; 111 return false; 112 } 113 if (!GetFloat(dict, "scale", &mode->ui_scale)) { 114 LOG(ERROR) << "missing ui-scale."; 115 return false; 116 } 117 if (!GetFloat(dict, "deviceScaleFactor", &mode->device_scale_factor)) { 118 LOG(ERROR) << "missing deviceScaleFactor."; 119 return false; 120 } 121 return true; 122 } 123 124 base::DictionaryValue* ConvertDisplayModeToValue(int64 display_id, 125 const ash::DisplayMode& mode) { 126 bool is_internal = GetDisplayManager()->IsInternalDisplayId(display_id); 127 base::DictionaryValue* result = new base::DictionaryValue(); 128 gfx::Size size_dip = mode.GetSizeInDIP(); 129 result->SetInteger("width", size_dip.width()); 130 result->SetInteger("height", size_dip.height()); 131 result->SetInteger("originalWidth", mode.size.width()); 132 result->SetInteger("originalHeight", mode.size.height()); 133 result->SetDouble("deviceScaleFactor", mode.device_scale_factor); 134 result->SetDouble("scale", mode.ui_scale); 135 result->SetDouble("refreshRate", mode.refresh_rate); 136 result->SetBoolean( 137 "isBest", is_internal ? (mode.ui_scale == 1.0f) : mode.native); 138 result->SetBoolean("isNative", mode.native); 139 result->SetBoolean( 140 "selected", mode.IsEquivalent( 141 GetDisplayManager()->GetActiveModeForDisplayId(display_id))); 142 return result; 143 } 144 145 } // namespace 146 147 DisplayOptionsHandler::DisplayOptionsHandler() { 148 #if !defined(USE_ATHENA) 149 // ash::Shell doesn't exist in Athena. 150 // See: http://crbug.com/416961 151 ash::Shell::GetInstance()->display_controller()->AddObserver(this); 152 #endif 153 } 154 155 DisplayOptionsHandler::~DisplayOptionsHandler() { 156 #if !defined(USE_ATHENA) 157 // ash::Shell doesn't exist in Athena. 158 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this); 159 #endif 160 } 161 162 void DisplayOptionsHandler::GetLocalizedValues( 163 base::DictionaryValue* localized_strings) { 164 DCHECK(localized_strings); 165 RegisterTitle(localized_strings, "displayOptionsPage", 166 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE); 167 168 localized_strings->SetString( 169 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16( 170 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS)); 171 localized_strings->SetString( 172 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16( 173 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION)); 174 localized_strings->SetString( 175 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16( 176 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION)); 177 localized_strings->SetString( 178 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16( 179 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN)); 180 181 localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16( 182 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING)); 183 localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16( 184 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING)); 185 localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16( 186 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME)); 187 localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16( 188 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY)); 189 localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16( 190 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST)); 191 localized_strings->SetString("annotateNative", l10n_util::GetStringUTF16( 192 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_NATIVE)); 193 localized_strings->SetString("orientation0", l10n_util::GetStringUTF16( 194 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION)); 195 localized_strings->SetString("orientation90", l10n_util::GetStringUTF16( 196 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90)); 197 localized_strings->SetString("orientation180", l10n_util::GetStringUTF16( 198 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180)); 199 localized_strings->SetString("orientation270", l10n_util::GetStringUTF16( 200 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270)); 201 localized_strings->SetString( 202 "startCalibratingOverscan", l10n_util::GetStringUTF16( 203 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN)); 204 localized_strings->SetString( 205 "selectedDisplayColorProfile", l10n_util::GetStringUTF16( 206 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE)); 207 } 208 209 void DisplayOptionsHandler::InitializePage() { 210 DCHECK(web_ui()); 211 #if !defined(USE_ATHENA) 212 web_ui()->CallJavascriptFunction( 213 "options.BrowserOptions.enableDisplayButton", 214 base::FundamentalValue(true)); 215 #endif 216 } 217 218 void DisplayOptionsHandler::RegisterMessages() { 219 web_ui()->RegisterMessageCallback( 220 "getDisplayInfo", 221 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo, 222 base::Unretained(this))); 223 web_ui()->RegisterMessageCallback( 224 "setMirroring", 225 base::Bind(&DisplayOptionsHandler::HandleMirroring, 226 base::Unretained(this))); 227 web_ui()->RegisterMessageCallback( 228 "setPrimary", 229 base::Bind(&DisplayOptionsHandler::HandleSetPrimary, 230 base::Unretained(this))); 231 web_ui()->RegisterMessageCallback( 232 "setDisplayLayout", 233 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout, 234 base::Unretained(this))); 235 web_ui()->RegisterMessageCallback( 236 "setDisplayMode", 237 base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode, 238 base::Unretained(this))); 239 web_ui()->RegisterMessageCallback( 240 "setOrientation", 241 base::Bind(&DisplayOptionsHandler::HandleSetOrientation, 242 base::Unretained(this))); 243 web_ui()->RegisterMessageCallback( 244 "setColorProfile", 245 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile, 246 base::Unretained(this))); 247 } 248 249 void DisplayOptionsHandler::OnDisplayConfigurationChanging() { 250 } 251 252 void DisplayOptionsHandler::OnDisplayConfigurationChanged() { 253 SendAllDisplayInfo(); 254 } 255 256 void DisplayOptionsHandler::SendAllDisplayInfo() { 257 DisplayManager* display_manager = GetDisplayManager(); 258 259 std::vector<gfx::Display> displays; 260 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { 261 displays.push_back(display_manager->GetDisplayAt(i)); 262 } 263 SendDisplayInfo(displays); 264 } 265 266 void DisplayOptionsHandler::SendDisplayInfo( 267 const std::vector<gfx::Display>& displays) { 268 DisplayManager* display_manager = GetDisplayManager(); 269 base::FundamentalValue mirroring(display_manager->IsMirrored()); 270 271 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id(); 272 base::ListValue js_displays; 273 for (size_t i = 0; i < displays.size(); ++i) { 274 const gfx::Display& display = displays[i]; 275 const ash::DisplayInfo& display_info = 276 display_manager->GetDisplayInfo(display.id()); 277 const gfx::Rect& bounds = display.bounds(); 278 base::DictionaryValue* js_display = new base::DictionaryValue(); 279 js_display->SetString("id", base::Int64ToString(display.id())); 280 js_display->SetInteger("x", bounds.x()); 281 js_display->SetInteger("y", bounds.y()); 282 js_display->SetInteger("width", bounds.width()); 283 js_display->SetInteger("height", bounds.height()); 284 js_display->SetString("name", 285 display_manager->GetDisplayNameForId(display.id())); 286 js_display->SetBoolean("isPrimary", display.id() == primary_id); 287 js_display->SetBoolean("isInternal", display.IsInternal()); 288 js_display->SetInteger("orientation", 289 static_cast<int>(display_info.rotation())); 290 291 base::ListValue* js_resolutions = new base::ListValue(); 292 const std::vector<ash::DisplayMode>& display_modes = 293 display_info.display_modes(); 294 for (size_t i = 0; i < display_modes.size(); ++i) { 295 js_resolutions->Append( 296 ConvertDisplayModeToValue(display.id(), display_modes[i])); 297 } 298 js_display->Set("resolutions", js_resolutions); 299 300 js_display->SetInteger("colorProfile", display_info.color_profile()); 301 base::ListValue* available_color_profiles = new base::ListValue(); 302 for (size_t i = 0; 303 i < display_info.available_color_profiles().size(); ++i) { 304 base::DictionaryValue* color_profile_dict = new base::DictionaryValue(); 305 ui::ColorCalibrationProfile color_profile = 306 display_info.available_color_profiles()[i]; 307 color_profile_dict->SetInteger("profileId", color_profile); 308 color_profile_dict->SetString("name", GetColorProfileName(color_profile)); 309 available_color_profiles->Append(color_profile_dict); 310 } 311 js_display->Set("availableColorProfiles", available_color_profiles); 312 js_displays.Append(js_display); 313 } 314 315 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue()); 316 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue()); 317 if (display_manager->GetNumDisplays() > 1) { 318 const ash::DisplayLayout layout = 319 display_manager->GetCurrentDisplayLayout(); 320 layout_value.reset(new base::FundamentalValue(layout.position)); 321 offset_value.reset(new base::FundamentalValue(layout.offset)); 322 } 323 324 web_ui()->CallJavascriptFunction( 325 "options.DisplayOptions.setDisplayInfo", 326 mirroring, js_displays, *layout_value.get(), *offset_value.get()); 327 } 328 329 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) { 330 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring); 331 // Not necessary to start fade-in animation. DisplayConfigurator will do that. 332 } 333 334 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished( 335 int position, int offset) { 336 SetCurrentDisplayLayout( 337 ash::DisplayLayout::FromInts(position, offset)); 338 ash::Shell::GetInstance()->display_configurator_animation()-> 339 StartFadeInAnimation(); 340 } 341 342 void DisplayOptionsHandler::HandleDisplayInfo( 343 const base::ListValue* unused_args) { 344 SendAllDisplayInfo(); 345 } 346 347 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) { 348 DCHECK(!args->empty()); 349 content::RecordAction( 350 base::UserMetricsAction("Options_DisplayToggleMirroring")); 351 bool is_mirroring = false; 352 args->GetBoolean(0, &is_mirroring); 353 ash::Shell::GetInstance()->display_configurator_animation()-> 354 StartFadeOutAnimation(base::Bind( 355 &DisplayOptionsHandler::OnFadeOutForMirroringFinished, 356 base::Unretained(this), 357 is_mirroring)); 358 } 359 360 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) { 361 DCHECK(!args->empty()); 362 int64 display_id = GetDisplayId(args); 363 if (display_id == gfx::Display::kInvalidDisplayID) 364 return; 365 366 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary")); 367 ash::Shell::GetInstance()->display_controller()-> 368 SetPrimaryDisplayId(display_id); 369 } 370 371 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) { 372 double layout = -1; 373 double offset = -1; 374 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) { 375 LOG(ERROR) << "Invalid parameter"; 376 SendAllDisplayInfo(); 377 return; 378 } 379 DCHECK_LE(ash::DisplayLayout::TOP, layout); 380 DCHECK_GE(ash::DisplayLayout::LEFT, layout); 381 content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange")); 382 ash::Shell::GetInstance()->display_configurator_animation()-> 383 StartFadeOutAnimation(base::Bind( 384 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished, 385 base::Unretained(this), 386 static_cast<int>(layout), 387 static_cast<int>(offset))); 388 } 389 390 void DisplayOptionsHandler::HandleSetDisplayMode(const base::ListValue* args) { 391 DCHECK(!args->empty()); 392 393 int64 display_id = GetDisplayId(args); 394 if (display_id == gfx::Display::kInvalidDisplayID) 395 return; 396 397 const base::DictionaryValue* mode_data = NULL; 398 if (!args->GetDictionary(1, &mode_data)) { 399 LOG(ERROR) << "Failed to get mode data"; 400 return; 401 } 402 403 ash::DisplayMode mode; 404 if (!ConvertValueToDisplayMode(mode_data, &mode)) 405 return; 406 407 content::RecordAction( 408 base::UserMetricsAction("Options_DisplaySetResolution")); 409 ash::DisplayManager* display_manager = GetDisplayManager(); 410 ash::DisplayMode current_mode = 411 display_manager->GetActiveModeForDisplayId(display_id); 412 if (display_manager->SetDisplayMode(display_id, mode)) { 413 ash::Shell::GetInstance()->resolution_notification_controller()-> 414 PrepareNotification( 415 display_id, current_mode, mode, base::Bind(&StoreDisplayPrefs)); 416 } 417 } 418 419 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) { 420 DCHECK(!args->empty()); 421 422 int64 display_id = GetDisplayId(args); 423 if (display_id == gfx::Display::kInvalidDisplayID) 424 return; 425 426 std::string rotation_value; 427 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0; 428 if (!args->GetString(1, &rotation_value)) { 429 LOG(ERROR) << "Can't find new orientation"; 430 return; 431 } 432 if (rotation_value == "90") 433 new_rotation = gfx::Display::ROTATE_90; 434 else if (rotation_value == "180") 435 new_rotation = gfx::Display::ROTATE_180; 436 else if (rotation_value == "270") 437 new_rotation = gfx::Display::ROTATE_270; 438 else if (rotation_value != "0") 439 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0"; 440 441 content::RecordAction( 442 base::UserMetricsAction("Options_DisplaySetOrientation")); 443 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation); 444 } 445 446 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) { 447 DCHECK(!args->empty()); 448 int64 display_id = GetDisplayId(args); 449 if (display_id == gfx::Display::kInvalidDisplayID) 450 return; 451 452 std::string profile_value; 453 if (!args->GetString(1, &profile_value)) { 454 LOG(ERROR) << "Invalid profile_value"; 455 return; 456 } 457 458 int profile_id; 459 if (!base::StringToInt(profile_value, &profile_id)) { 460 LOG(ERROR) << "Invalid profile: " << profile_value; 461 return; 462 } 463 464 if (profile_id < ui::COLOR_PROFILE_STANDARD || 465 profile_id > ui::COLOR_PROFILE_READING) { 466 LOG(ERROR) << "Invalid profile_id: " << profile_id; 467 return; 468 } 469 470 GetDisplayManager()->SetColorCalibrationProfile( 471 display_id, static_cast<ui::ColorCalibrationProfile>(profile_id)); 472 SendAllDisplayInfo(); 473 } 474 475 } // namespace options 476 } // namespace chromeos 477