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_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/screen_ash.h" 14 #include "ash/shell.h" 15 #include "base/bind.h" 16 #include "base/logging.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/values.h" 20 #include "chrome/browser/chromeos/display/display_preferences.h" 21 #include "chromeos/display/output_configurator.h" 22 #include "content/public/browser/web_ui.h" 23 #include "grit/ash_strings.h" 24 #include "grit/generated_resources.h" 25 #include "ui/base/l10n/l10n_util.h" 26 #include "ui/gfx/display.h" 27 #include "ui/gfx/rect.h" 28 #include "ui/gfx/screen.h" 29 #include "ui/gfx/size_conversions.h" 30 31 using ash::internal::DisplayManager; 32 33 namespace chromeos { 34 namespace options { 35 namespace { 36 37 DisplayManager* GetDisplayManager() { 38 return ash::Shell::GetInstance()->display_manager(); 39 } 40 41 int64 GetDisplayId(const base::ListValue* args) { 42 // Assumes the display ID is specified as the first argument. 43 std::string id_value; 44 if (!args->GetString(0, &id_value)) { 45 LOG(ERROR) << "Can't find ID"; 46 return gfx::Display::kInvalidDisplayID; 47 } 48 49 int64 display_id = gfx::Display::kInvalidDisplayID; 50 if (!base::StringToInt64(id_value, &display_id)) { 51 LOG(ERROR) << "Invalid display id: " << id_value; 52 return gfx::Display::kInvalidDisplayID; 53 } 54 55 return display_id; 56 } 57 58 bool CompareResolution(ash::internal::Resolution r1, 59 ash::internal::Resolution r2) { 60 return r1.size.GetArea() < r2.size.GetArea(); 61 } 62 63 } // namespace 64 65 DisplayOptionsHandler::DisplayOptionsHandler() { 66 ash::Shell::GetInstance()->display_controller()->AddObserver(this); 67 } 68 69 DisplayOptionsHandler::~DisplayOptionsHandler() { 70 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this); 71 } 72 73 void DisplayOptionsHandler::GetLocalizedValues( 74 DictionaryValue* localized_strings) { 75 DCHECK(localized_strings); 76 RegisterTitle(localized_strings, "displayOptionsPage", 77 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE); 78 79 localized_strings->SetString( 80 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16( 81 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS)); 82 localized_strings->SetString( 83 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16( 84 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION)); 85 localized_strings->SetString( 86 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16( 87 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION)); 88 localized_strings->SetString( 89 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16( 90 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN)); 91 92 localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16( 93 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING)); 94 localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16( 95 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING)); 96 localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16( 97 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME)); 98 localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16( 99 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY)); 100 localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16( 101 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST)); 102 localized_strings->SetString("orientation0", l10n_util::GetStringUTF16( 103 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION)); 104 localized_strings->SetString("orientation90", l10n_util::GetStringUTF16( 105 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90)); 106 localized_strings->SetString("orientation180", l10n_util::GetStringUTF16( 107 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180)); 108 localized_strings->SetString("orientation270", l10n_util::GetStringUTF16( 109 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270)); 110 localized_strings->SetString( 111 "startCalibratingOverscan", l10n_util::GetStringUTF16( 112 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN)); 113 } 114 115 void DisplayOptionsHandler::InitializePage() { 116 DCHECK(web_ui()); 117 } 118 119 void DisplayOptionsHandler::RegisterMessages() { 120 web_ui()->RegisterMessageCallback( 121 "getDisplayInfo", 122 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo, 123 base::Unretained(this))); 124 web_ui()->RegisterMessageCallback( 125 "setMirroring", 126 base::Bind(&DisplayOptionsHandler::HandleMirroring, 127 base::Unretained(this))); 128 web_ui()->RegisterMessageCallback( 129 "setPrimary", 130 base::Bind(&DisplayOptionsHandler::HandleSetPrimary, 131 base::Unretained(this))); 132 web_ui()->RegisterMessageCallback( 133 "setDisplayLayout", 134 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout, 135 base::Unretained(this))); 136 web_ui()->RegisterMessageCallback( 137 "setUIScale", 138 base::Bind(&DisplayOptionsHandler::HandleSetUIScale, 139 base::Unretained(this))); 140 web_ui()->RegisterMessageCallback( 141 "setResolution", 142 base::Bind(&DisplayOptionsHandler::HandleSetResolution, 143 base::Unretained(this))); 144 web_ui()->RegisterMessageCallback( 145 "setOrientation", 146 base::Bind(&DisplayOptionsHandler::HandleSetOrientation, 147 base::Unretained(this))); 148 } 149 150 void DisplayOptionsHandler::OnDisplayConfigurationChanging() { 151 } 152 153 void DisplayOptionsHandler::OnDisplayConfigurationChanged() { 154 SendAllDisplayInfo(); 155 } 156 157 void DisplayOptionsHandler::SendAllDisplayInfo() { 158 DisplayManager* display_manager = GetDisplayManager(); 159 160 std::vector<gfx::Display> displays; 161 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { 162 displays.push_back(display_manager->GetDisplayAt(i)); 163 } 164 SendDisplayInfo(displays); 165 } 166 167 void DisplayOptionsHandler::SendDisplayInfo( 168 const std::vector<gfx::Display>& displays) { 169 DisplayManager* display_manager = GetDisplayManager(); 170 ash::DisplayController* display_controller = 171 ash::Shell::GetInstance()->display_controller(); 172 base::FundamentalValue mirroring(display_manager->IsMirrored()); 173 174 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id(); 175 base::ListValue js_displays; 176 for (size_t i = 0; i < displays.size(); ++i) { 177 const gfx::Display& display = displays[i]; 178 const ash::internal::DisplayInfo& display_info = 179 display_manager->GetDisplayInfo(display.id()); 180 const gfx::Rect& bounds = display.bounds(); 181 base::DictionaryValue* js_display = new base::DictionaryValue(); 182 js_display->SetString("id", base::Int64ToString(display.id())); 183 js_display->SetInteger("x", bounds.x()); 184 js_display->SetInteger("y", bounds.y()); 185 js_display->SetInteger("width", bounds.width()); 186 js_display->SetInteger("height", bounds.height()); 187 js_display->SetString("name", 188 display_manager->GetDisplayNameForId(display.id())); 189 js_display->SetBoolean("isPrimary", display.id() == primary_id); 190 js_display->SetBoolean("isInternal", display.IsInternal()); 191 js_display->SetInteger("orientation", 192 static_cast<int>(display_info.rotation())); 193 std::vector<ash::internal::Resolution> resolutions; 194 std::vector<float> ui_scales; 195 if (display.IsInternal()) { 196 ui_scales = DisplayManager::GetScalesForDisplay(display_info); 197 gfx::SizeF base_size = display_info.bounds_in_pixel().size(); 198 base_size.Scale(1.0f / display.device_scale_factor()); 199 if (display_info.rotation() == gfx::Display::ROTATE_90 || 200 display_info.rotation() == gfx::Display::ROTATE_270) { 201 float tmp = base_size.width(); 202 base_size.set_width(base_size.height()); 203 base_size.set_height(tmp); 204 } 205 for (size_t i = 0; i < ui_scales.size(); ++i) { 206 gfx::SizeF new_size = base_size; 207 new_size.Scale(ui_scales[i]); 208 resolutions.push_back(ash::internal::Resolution( 209 gfx::ToFlooredSize(new_size), false /* interlaced */)); 210 } 211 } else { 212 for (size_t i = 0; i < display_info.resolutions().size(); ++i) 213 resolutions.push_back(display_info.resolutions()[i]); 214 } 215 std::sort(resolutions.begin(), resolutions.end(), CompareResolution); 216 217 base::ListValue* js_resolutions = new base::ListValue(); 218 gfx::Size current_size(bounds.width() * display.device_scale_factor(), 219 bounds.height() * display.device_scale_factor()); 220 for (size_t i = 0; i < resolutions.size(); ++i) { 221 base::DictionaryValue* resolution_info = new base::DictionaryValue(); 222 if (!ui_scales.empty()) { 223 resolution_info->SetDouble("scale", ui_scales[i]); 224 if (ui_scales[i] == 1.0f) 225 resolution_info->SetBoolean("isBest", true); 226 resolution_info->SetBoolean( 227 "selected", display_info.ui_scale() == ui_scales[i]); 228 } else { 229 // Picks the largest one as the "best", which is the last element 230 // because |resolutions| is sorted by its area. 231 if (i == resolutions.size() - 1) 232 resolution_info->SetBoolean("isBest", true); 233 resolution_info->SetBoolean( 234 "selected", (resolutions[i].size == current_size)); 235 } 236 resolution_info->SetInteger("width", resolutions[i].size.width()); 237 resolution_info->SetInteger("height",resolutions[i].size.height()); 238 js_resolutions->Append(resolution_info); 239 } 240 js_display->Set("resolutions", js_resolutions); 241 js_displays.Append(js_display); 242 } 243 244 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue()); 245 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue()); 246 if (display_manager->GetNumDisplays() > 1) { 247 const ash::DisplayLayout layout = 248 display_controller->GetCurrentDisplayLayout(); 249 layout_value.reset(new base::FundamentalValue(layout.position)); 250 offset_value.reset(new base::FundamentalValue(layout.offset)); 251 } 252 253 web_ui()->CallJavascriptFunction( 254 "options.DisplayOptions.setDisplayInfo", 255 mirroring, js_displays, *layout_value.get(), *offset_value.get()); 256 } 257 258 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) { 259 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring); 260 // Not necessary to start fade-in animation. OutputConfigurator will do that. 261 } 262 263 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished( 264 int position, int offset) { 265 SetCurrentDisplayLayout( 266 ash::DisplayLayout::FromInts(position, offset)); 267 ash::Shell::GetInstance()->output_configurator_animation()-> 268 StartFadeInAnimation(); 269 } 270 271 void DisplayOptionsHandler::HandleDisplayInfo( 272 const base::ListValue* unused_args) { 273 SendAllDisplayInfo(); 274 } 275 276 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) { 277 DCHECK(!args->empty()); 278 bool is_mirroring = false; 279 args->GetBoolean(0, &is_mirroring); 280 ash::Shell::GetInstance()->output_configurator_animation()-> 281 StartFadeOutAnimation(base::Bind( 282 &DisplayOptionsHandler::OnFadeOutForMirroringFinished, 283 base::Unretained(this), 284 is_mirroring)); 285 } 286 287 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) { 288 DCHECK(!args->empty()); 289 int64 display_id = GetDisplayId(args); 290 if (display_id == gfx::Display::kInvalidDisplayID) 291 return; 292 293 ash::Shell::GetInstance()->display_controller()-> 294 SetPrimaryDisplayId(display_id); 295 } 296 297 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) { 298 double layout = -1; 299 double offset = -1; 300 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) { 301 LOG(ERROR) << "Invalid parameter"; 302 SendAllDisplayInfo(); 303 return; 304 } 305 DCHECK_LE(ash::DisplayLayout::TOP, layout); 306 DCHECK_GE(ash::DisplayLayout::LEFT, layout); 307 ash::Shell::GetInstance()->output_configurator_animation()-> 308 StartFadeOutAnimation(base::Bind( 309 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished, 310 base::Unretained(this), 311 static_cast<int>(layout), 312 static_cast<int>(offset))); 313 } 314 315 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) { 316 DCHECK(!args->empty()); 317 318 int64 display_id = GetDisplayId(args); 319 if (display_id == gfx::Display::kInvalidDisplayID) 320 return; 321 322 double ui_scale = 0.0f; 323 if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) { 324 LOG(ERROR) << "Can't find new ui_scale"; 325 return; 326 } 327 328 GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale); 329 } 330 331 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) { 332 DCHECK(!args->empty()); 333 int64 display_id = GetDisplayId(args); 334 if (display_id == gfx::Display::kInvalidDisplayID) 335 return; 336 337 double width = 0.0f; 338 double height = 0.0f; 339 if (!args->GetDouble(1, &width) || width == 0.0f) { 340 LOG(ERROR) << "Can't find new width"; 341 return; 342 } 343 if (!args->GetDouble(2, &height) || height == 0.0f) { 344 LOG(ERROR) << "Can't find new height"; 345 return; 346 } 347 348 const ash::internal::DisplayInfo& display_info = 349 GetDisplayManager()->GetDisplayInfo(display_id); 350 gfx::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height)); 351 gfx::Size old_resolution = display_info.size_in_pixel(); 352 bool has_new_resolution = false; 353 bool has_old_resolution = false; 354 for (size_t i = 0; i < display_info.resolutions().size(); ++i) { 355 ash::internal::Resolution resolution = display_info.resolutions()[i]; 356 if (resolution.size == new_resolution) 357 has_new_resolution = true; 358 if (resolution.size == old_resolution) 359 has_old_resolution = true; 360 } 361 if (!has_new_resolution) { 362 LOG(ERROR) << "No new resolution " << new_resolution.ToString() 363 << " is found in the display info " << display_info.ToString(); 364 return; 365 } 366 if (!has_old_resolution) { 367 LOG(ERROR) << "No old resolution " << old_resolution.ToString() 368 << " is found in the display info " << display_info.ToString(); 369 return; 370 } 371 372 ash::Shell::GetInstance()->resolution_notification_controller()-> 373 SetDisplayResolutionAndNotify( 374 display_id, old_resolution, new_resolution, 375 base::Bind(&StoreDisplayPrefs)); 376 } 377 378 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) { 379 DCHECK(!args->empty()); 380 381 int64 display_id = GetDisplayId(args); 382 if (display_id == gfx::Display::kInvalidDisplayID) 383 return; 384 385 std::string rotation_value; 386 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0; 387 if (!args->GetString(1, &rotation_value)) { 388 LOG(ERROR) << "Can't find new orientation"; 389 return; 390 } 391 if (rotation_value == "90") 392 new_rotation = gfx::Display::ROTATE_90; 393 else if (rotation_value == "180") 394 new_rotation = gfx::Display::ROTATE_180; 395 else if (rotation_value == "270") 396 new_rotation = gfx::Display::ROTATE_270; 397 else if (rotation_value != "0") 398 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0"; 399 400 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation); 401 } 402 403 } // namespace options 404 } // namespace chromeos 405