display_manager.cc revision f2477e01787aa58f445919b809d89e252beef54f
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 "ash/display/display_manager.h" 6 7#include <cmath> 8#include <set> 9#include <string> 10#include <vector> 11 12#include "ash/ash_switches.h" 13#include "ash/display/display_layout_store.h" 14#include "ash/screen_ash.h" 15#include "ash/shell.h" 16#include "base/auto_reset.h" 17#include "base/command_line.h" 18#include "base/logging.h" 19#include "base/strings/string_number_conversions.h" 20#include "base/strings/string_split.h" 21#include "base/strings/stringprintf.h" 22#include "base/strings/utf_string_conversions.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#if defined(USE_X11) 31#include "ui/base/x/x11_util.h" 32#endif 33 34#if defined(OS_CHROMEOS) 35#include "ash/display/output_configurator_animation.h" 36#include "base/sys_info.h" 37#include "chromeos/display/output_configurator.h" 38#endif 39 40#if defined(OS_WIN) 41#include "base/win/windows_version.h" 42#endif 43 44namespace ash { 45namespace internal { 46typedef std::vector<gfx::Display> DisplayList; 47typedef std::vector<DisplayInfo> DisplayInfoList; 48 49namespace { 50 51// The number of pixels to overlap between the primary and secondary displays, 52// in case that the offset value is too large. 53const int kMinimumOverlapForInvalidOffset = 100; 54 55// List of value UI Scale values. Scales for 2x are equivalent to 640, 56// 800, 1024, 1280, 1440, 1600 and 1920 pixel width respectively on 57// 2560 pixel width 2x density display. Please see crbug.com/233375 58// for the full list of resolutions. 59const float kUIScalesFor2x[] = 60 {0.5f, 0.625f, 0.8f, 1.0f, 1.125f, 1.25f, 1.5f, 2.0f}; 61const float kUIScalesFor1280[] = {0.5f, 0.625f, 0.8f, 1.0f, 1.125f }; 62const float kUIScalesFor1366[] = {0.5f, 0.6f, 0.75f, 1.0f, 1.125f }; 63 64struct DisplaySortFunctor { 65 bool operator()(const gfx::Display& a, const gfx::Display& b) { 66 return a.id() < b.id(); 67 } 68}; 69 70struct DisplayInfoSortFunctor { 71 bool operator()(const DisplayInfo& a, const DisplayInfo& b) { 72 return a.id() < b.id(); 73 } 74}; 75 76struct ResolutionMatcher { 77 ResolutionMatcher(const gfx::Size& size) : size(size) {} 78 bool operator()(const Resolution& resolution) { 79 return resolution.size == size; 80 } 81 gfx::Size size; 82}; 83 84struct ScaleComparator { 85 ScaleComparator(float s) : scale(s) {} 86 87 bool operator()(float s) const { 88 const float kEpsilon = 0.0001f; 89 return std::abs(scale - s) < kEpsilon; 90 } 91 float scale; 92}; 93 94gfx::Display& GetInvalidDisplay() { 95 static gfx::Display* invalid_display = new gfx::Display(); 96 return *invalid_display; 97} 98 99void MaybeInitInternalDisplay(int64 id) { 100 CommandLine* command_line = CommandLine::ForCurrentProcess(); 101 if (command_line->HasSwitch(switches::kAshUseFirstDisplayAsInternal)) 102 gfx::Display::SetInternalDisplayId(id); 103} 104 105// Scoped objects used to either create or close the non desktop window 106// at specific timing. 107class NonDesktopDisplayUpdater { 108 public: 109 NonDesktopDisplayUpdater(DisplayManager* manager, 110 DisplayManager::Delegate* delegate) 111 : manager_(manager), 112 delegate_(delegate), 113 enabled_(manager_->second_display_mode() != DisplayManager::EXTENDED && 114 manager_->non_desktop_display().is_valid()) { 115 } 116 117 ~NonDesktopDisplayUpdater() { 118 if (!delegate_) 119 return; 120 121 if (enabled_) { 122 DisplayInfo display_info = manager_->GetDisplayInfo( 123 manager_->non_desktop_display().id()); 124 delegate_->CreateOrUpdateNonDesktopDisplay(display_info); 125 } else { 126 delegate_->CloseNonDesktopDisplay(); 127 } 128 } 129 130 bool enabled() const { return enabled_; } 131 132 private: 133 DisplayManager* manager_; 134 DisplayManager::Delegate* delegate_; 135 bool enabled_; 136 DISALLOW_COPY_AND_ASSIGN(NonDesktopDisplayUpdater); 137}; 138 139} // namespace 140 141using std::string; 142using std::vector; 143 144DisplayManager::DisplayManager() 145 : delegate_(NULL), 146 layout_store_(new DisplayLayoutStore), 147 first_display_id_(gfx::Display::kInvalidDisplayID), 148 num_connected_displays_(0), 149 force_bounds_changed_(false), 150 change_display_upon_host_resize_(false), 151 second_display_mode_(EXTENDED), 152 mirrored_display_id_(gfx::Display::kInvalidDisplayID) { 153#if defined(OS_CHROMEOS) 154 change_display_upon_host_resize_ = !base::SysInfo::IsRunningOnChromeOS(); 155#endif 156} 157 158DisplayManager::~DisplayManager() { 159} 160 161// static 162std::vector<float> DisplayManager::GetScalesForDisplay( 163 const DisplayInfo& info) { 164 std::vector<float> ret; 165 if (info.device_scale_factor() == 2.0f) { 166 ret.assign(kUIScalesFor2x, kUIScalesFor2x + arraysize(kUIScalesFor2x)); 167 return ret; 168 } 169 switch (info.bounds_in_native().width()) { 170 case 1280: 171 ret.assign(kUIScalesFor1280, 172 kUIScalesFor1280 + arraysize(kUIScalesFor1280)); 173 break; 174 case 1366: 175 ret.assign(kUIScalesFor1366, 176 kUIScalesFor1366 + arraysize(kUIScalesFor1366)); 177 break; 178 default: 179 ret.assign(kUIScalesFor1280, 180 kUIScalesFor1280 + arraysize(kUIScalesFor1280)); 181#if defined(OS_CHROMEOS) 182 if (base::SysInfo::IsRunningOnChromeOS()) 183 NOTREACHED() << "Unknown resolution:" << info.ToString(); 184#endif 185 } 186 return ret; 187} 188 189// static 190float DisplayManager::GetNextUIScale(const DisplayInfo& info, bool up) { 191 float scale = info.configured_ui_scale(); 192 std::vector<float> scales = GetScalesForDisplay(info); 193 for (size_t i = 0; i < scales.size(); ++i) { 194 if (ScaleComparator(scales[i])(scale)) { 195 if (up && i != scales.size() - 1) 196 return scales[i + 1]; 197 if (!up && i != 0) 198 return scales[i - 1]; 199 return scales[i]; 200 } 201 } 202 // Fallback to 1.0f if the |scale| wasn't in the list. 203 return 1.0f; 204} 205 206bool DisplayManager::InitFromCommandLine() { 207 DisplayInfoList info_list; 208 CommandLine* command_line = CommandLine::ForCurrentProcess(); 209 if (!command_line->HasSwitch(switches::kAshHostWindowBounds)) 210 return false; 211 const string size_str = 212 command_line->GetSwitchValueASCII(switches::kAshHostWindowBounds); 213 vector<string> parts; 214 base::SplitString(size_str, ',', &parts); 215 for (vector<string>::const_iterator iter = parts.begin(); 216 iter != parts.end(); ++iter) { 217 info_list.push_back(DisplayInfo::CreateFromSpec(*iter)); 218 } 219 MaybeInitInternalDisplay(info_list[0].id()); 220 if (info_list.size() > 1 && 221 command_line->HasSwitch(switches::kAshEnableSoftwareMirroring)) { 222 SetSecondDisplayMode(MIRRORING); 223 } 224 OnNativeDisplaysChanged(info_list); 225 return true; 226} 227 228void DisplayManager::InitDefaultDisplay() { 229 DisplayInfoList info_list; 230 info_list.push_back(DisplayInfo::CreateFromSpec(std::string())); 231 MaybeInitInternalDisplay(info_list[0].id()); 232 OnNativeDisplaysChanged(info_list); 233} 234 235// static 236void DisplayManager::UpdateDisplayBoundsForLayoutById( 237 const DisplayLayout& layout, 238 const gfx::Display& primary_display, 239 int64 secondary_display_id) { 240 DCHECK_NE(gfx::Display::kInvalidDisplayID, secondary_display_id); 241 UpdateDisplayBoundsForLayout( 242 layout, primary_display, 243 Shell::GetInstance()->display_manager()-> 244 FindDisplayForId(secondary_display_id)); 245} 246 247bool DisplayManager::IsActiveDisplay(const gfx::Display& display) const { 248 for (DisplayList::const_iterator iter = displays_.begin(); 249 iter != displays_.end(); ++iter) { 250 if ((*iter).id() == display.id()) 251 return true; 252 } 253 return false; 254} 255 256bool DisplayManager::HasInternalDisplay() const { 257 return gfx::Display::InternalDisplayId() != gfx::Display::kInvalidDisplayID; 258} 259 260bool DisplayManager::IsInternalDisplayId(int64 id) const { 261 return gfx::Display::InternalDisplayId() == id; 262} 263 264DisplayLayout DisplayManager::GetCurrentDisplayLayout() { 265 DCHECK_EQ(2U, num_connected_displays()); 266 // Invert if the primary was swapped. 267 if (num_connected_displays() > 1) { 268 DisplayIdPair pair = GetCurrentDisplayIdPair(); 269 return layout_store_->ComputeDisplayLayoutForDisplayIdPair(pair); 270 } 271 NOTREACHED() << "DisplayLayout is requested for single display"; 272 // On release build, just fallback to default instead of blowing up. 273 DisplayLayout layout = 274 layout_store_->default_display_layout(); 275 layout.primary_id = displays_[0].id(); 276 return layout; 277} 278 279DisplayIdPair DisplayManager::GetCurrentDisplayIdPair() const { 280 if (IsMirrored()) { 281 DCHECK_LE(2u, num_connected_displays()); 282 return std::make_pair(displays_[0].id(), mirrored_display_id_); 283 } else { 284 CHECK_GE(2u, displays_.size()); 285 int64 id_at_zero = displays_[0].id(); 286 if (id_at_zero == gfx::Display::InternalDisplayId() || 287 id_at_zero == first_display_id()) { 288 return std::make_pair(id_at_zero, displays_[1].id()); 289 } else { 290 return std::make_pair(displays_[1].id(), id_at_zero); 291 } 292 } 293} 294 295void DisplayManager::SetLayoutForCurrentDisplays( 296 const DisplayLayout& layout_relative_to_primary) { 297 DCHECK_EQ(2U, GetNumDisplays()); 298 if (GetNumDisplays() < 2) 299 return; 300 const gfx::Display& primary = Shell::GetScreen()->GetPrimaryDisplay(); 301 const DisplayIdPair pair = GetCurrentDisplayIdPair(); 302 // Invert if the primary was swapped. 303 DisplayLayout to_set = pair.first == primary.id() ? 304 layout_relative_to_primary : layout_relative_to_primary.Invert(); 305 306 DisplayLayout current_layout = 307 layout_store_->GetRegisteredDisplayLayout(pair); 308 if (to_set.position != current_layout.position || 309 to_set.offset != current_layout.offset) { 310 to_set.primary_id = primary.id(); 311 layout_store_->RegisterLayoutForDisplayIdPair( 312 pair.first, pair.second, to_set); 313 if (delegate_) 314 delegate_->PreDisplayConfigurationChange(false); 315 // PreDisplayConfigurationChange(false); 316 // TODO(oshima): Call UpdateDisplays instead. 317 const DisplayLayout layout = GetCurrentDisplayLayout(); 318 UpdateDisplayBoundsForLayoutById( 319 layout, primary, 320 ScreenAsh::GetSecondaryDisplay().id()); 321 322 //UpdateCurrentDisplayBoundsForLayout(); 323 // Primary's bounds stay the same. Just notify bounds change 324 // on the secondary. 325 Shell::GetInstance()->screen()->NotifyBoundsChanged( 326 ScreenAsh::GetSecondaryDisplay()); 327 if (delegate_) 328 delegate_->PostDisplayConfigurationChange(); 329 } 330} 331 332const gfx::Display& DisplayManager::GetDisplayForId(int64 id) const { 333 gfx::Display* display = 334 const_cast<DisplayManager*>(this)->FindDisplayForId(id); 335 return display ? *display : GetInvalidDisplay(); 336} 337 338const gfx::Display& DisplayManager::FindDisplayContainingPoint( 339 const gfx::Point& point_in_screen) const { 340 for (DisplayList::const_iterator iter = displays_.begin(); 341 iter != displays_.end(); ++iter) { 342 const gfx::Display& display = *iter; 343 if (display.bounds().Contains(point_in_screen)) 344 return display; 345 } 346 return GetInvalidDisplay(); 347} 348 349bool DisplayManager::UpdateWorkAreaOfDisplay(int64 display_id, 350 const gfx::Insets& insets) { 351 gfx::Display* display = FindDisplayForId(display_id); 352 DCHECK(display); 353 gfx::Rect old_work_area = display->work_area(); 354 display->UpdateWorkAreaFromInsets(insets); 355 return old_work_area != display->work_area(); 356} 357 358void DisplayManager::SetOverscanInsets(int64 display_id, 359 const gfx::Insets& insets_in_dip) { 360 display_info_[display_id].SetOverscanInsets(insets_in_dip); 361 DisplayInfoList display_info_list; 362 for (DisplayList::const_iterator iter = displays_.begin(); 363 iter != displays_.end(); ++iter) { 364 display_info_list.push_back(GetDisplayInfo(iter->id())); 365 } 366 AddMirrorDisplayInfoIfAny(&display_info_list); 367 UpdateDisplays(display_info_list); 368} 369 370void DisplayManager::SetDisplayRotation(int64 display_id, 371 gfx::Display::Rotation rotation) { 372 DisplayInfoList display_info_list; 373 for (DisplayList::const_iterator iter = displays_.begin(); 374 iter != displays_.end(); ++iter) { 375 DisplayInfo info = GetDisplayInfo(iter->id()); 376 if (info.id() == display_id) { 377 if (info.rotation() == rotation) 378 return; 379 info.set_rotation(rotation); 380 } 381 display_info_list.push_back(info); 382 } 383 AddMirrorDisplayInfoIfAny(&display_info_list); 384 if (virtual_keyboard_root_window_enabled() && 385 display_id == non_desktop_display_.id()) { 386 DisplayInfo info = GetDisplayInfo(display_id); 387 info.set_rotation(rotation); 388 display_info_list.push_back(info); 389 } 390 UpdateDisplays(display_info_list); 391} 392 393void DisplayManager::SetDisplayUIScale(int64 display_id, 394 float ui_scale) { 395 if (!IsDisplayUIScalingEnabled() || 396 gfx::Display::InternalDisplayId() != display_id) { 397 return; 398 } 399 400 DisplayInfoList display_info_list; 401 for (DisplayList::const_iterator iter = displays_.begin(); 402 iter != displays_.end(); ++iter) { 403 DisplayInfo info = GetDisplayInfo(iter->id()); 404 if (info.id() == display_id) { 405 if (info.configured_ui_scale() == ui_scale) 406 return; 407 std::vector<float> scales = GetScalesForDisplay(info); 408 ScaleComparator comparator(ui_scale); 409 if (std::find_if(scales.begin(), scales.end(), comparator) == 410 scales.end()) { 411 return; 412 } 413 info.set_configured_ui_scale(ui_scale); 414 } 415 display_info_list.push_back(info); 416 } 417 AddMirrorDisplayInfoIfAny(&display_info_list); 418 UpdateDisplays(display_info_list); 419} 420 421void DisplayManager::SetDisplayResolution(int64 display_id, 422 const gfx::Size& resolution) { 423 DCHECK_NE(gfx::Display::InternalDisplayId(), display_id); 424 if (gfx::Display::InternalDisplayId() == display_id) 425 return; 426 const DisplayInfo& display_info = GetDisplayInfo(display_id); 427 const std::vector<Resolution>& resolutions = display_info.resolutions(); 428 DCHECK_NE(0u, resolutions.size()); 429 std::vector<Resolution>::const_iterator iter = 430 std::find_if(resolutions.begin(), 431 resolutions.end(), 432 ResolutionMatcher(resolution)); 433 if (iter == resolutions.end()) { 434 LOG(WARNING) << "Unsupported resolution was requested:" 435 << resolution.ToString(); 436 return; 437 } else if (iter == resolutions.begin()) { 438 // The best resolution was set, so forget it. 439 resolutions_.erase(display_id); 440 } else { 441 resolutions_[display_id] = resolution; 442 } 443#if defined(OS_CHROMEOS) && defined(USE_X11) 444 if (base::SysInfo::IsRunningOnChromeOS()) 445 Shell::GetInstance()->output_configurator()->ScheduleConfigureOutputs(); 446#endif 447} 448 449void DisplayManager::RegisterDisplayProperty( 450 int64 display_id, 451 gfx::Display::Rotation rotation, 452 float ui_scale, 453 const gfx::Insets* overscan_insets, 454 const gfx::Size& resolution_in_pixels) { 455 if (display_info_.find(display_id) == display_info_.end()) { 456 display_info_[display_id] = 457 DisplayInfo(display_id, std::string(""), false); 458 } 459 460 display_info_[display_id].set_rotation(rotation); 461 // Just in case the preference file was corrupted. 462 if (0.5f <= ui_scale && ui_scale <= 2.0f) 463 display_info_[display_id].set_configured_ui_scale(ui_scale); 464 if (overscan_insets) 465 display_info_[display_id].SetOverscanInsets(*overscan_insets); 466 if (!resolution_in_pixels.IsEmpty()) 467 resolutions_[display_id] = resolution_in_pixels; 468} 469 470bool DisplayManager::GetSelectedResolutionForDisplayId( 471 int64 id, 472 gfx::Size* resolution_out) const { 473 std::map<int64, gfx::Size>::const_iterator iter = 474 resolutions_.find(id); 475 if (iter == resolutions_.end()) 476 return false; 477 *resolution_out = iter->second; 478 return true; 479} 480 481bool DisplayManager::IsDisplayUIScalingEnabled() const { 482 return GetDisplayIdForUIScaling() != gfx::Display::kInvalidDisplayID; 483} 484 485gfx::Insets DisplayManager::GetOverscanInsets(int64 display_id) const { 486 std::map<int64, DisplayInfo>::const_iterator it = 487 display_info_.find(display_id); 488 return (it != display_info_.end()) ? 489 it->second.overscan_insets_in_dip() : gfx::Insets(); 490} 491 492void DisplayManager::OnNativeDisplaysChanged( 493 const std::vector<DisplayInfo>& updated_displays) { 494 if (updated_displays.empty()) { 495 VLOG(1) << "OnNativeDisplayChanged(0): # of current displays=" 496 << displays_.size(); 497 // If the device is booted without display, or chrome is started 498 // without --ash-host-window-bounds on linux desktop, use the 499 // default display. 500 if (displays_.empty()) { 501 std::vector<DisplayInfo> init_displays; 502 init_displays.push_back(DisplayInfo::CreateFromSpec(std::string())); 503 MaybeInitInternalDisplay(init_displays[0].id()); 504 OnNativeDisplaysChanged(init_displays); 505 } else { 506 // Otherwise don't update the displays when all displays are disconnected. 507 // This happens when: 508 // - the device is idle and powerd requested to turn off all displays. 509 // - the device is suspended. (kernel turns off all displays) 510 // - the internal display's brightness is set to 0 and no external 511 // display is connected. 512 // - the internal display's brightness is 0 and external display is 513 // disconnected. 514 // The display will be updated when one of displays is turned on, and the 515 // display list will be updated correctly. 516 } 517 return; 518 } 519 first_display_id_ = updated_displays[0].id(); 520 std::set<gfx::Point> origins; 521 522 if (updated_displays.size() == 1) { 523 VLOG(1) << "OnNativeDisplaysChanged(1):" << updated_displays[0].ToString(); 524 } else { 525 VLOG(1) << "OnNativeDisplaysChanged(" << updated_displays.size() 526 << ") [0]=" << updated_displays[0].ToString() 527 << ", [1]=" << updated_displays[1].ToString(); 528 } 529 530 bool internal_display_connected = false; 531 num_connected_displays_ = updated_displays.size(); 532 mirrored_display_id_ = gfx::Display::kInvalidDisplayID; 533 non_desktop_display_ = gfx::Display(); 534 DisplayInfoList new_display_info_list; 535 for (DisplayInfoList::const_iterator iter = updated_displays.begin(); 536 iter != updated_displays.end(); 537 ++iter) { 538 if (!internal_display_connected) 539 internal_display_connected = IsInternalDisplayId(iter->id()); 540 // Mirrored monitors have the same origins. 541 gfx::Point origin = iter->bounds_in_native().origin(); 542 if (origins.find(origin) != origins.end()) { 543 InsertAndUpdateDisplayInfo(*iter); 544 mirrored_display_id_ = iter->id(); 545 } else { 546 origins.insert(origin); 547 new_display_info_list.push_back(*iter); 548 } 549 } 550 if (HasInternalDisplay() && 551 !internal_display_connected && 552 display_info_.find(gfx::Display::InternalDisplayId()) == 553 display_info_.end()) { 554 DisplayInfo internal_display_info( 555 gfx::Display::InternalDisplayId(), 556 l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME), 557 false /*Internal display must not have overscan */); 558 internal_display_info.SetBounds(gfx::Rect(0, 0, 800, 600)); 559 display_info_[gfx::Display::InternalDisplayId()] = internal_display_info; 560 } 561 UpdateDisplays(new_display_info_list); 562} 563 564void DisplayManager::UpdateDisplays() { 565 DisplayInfoList display_info_list; 566 for (DisplayList::const_iterator iter = displays_.begin(); 567 iter != displays_.end(); ++iter) { 568 display_info_list.push_back(GetDisplayInfo(iter->id())); 569 } 570 AddMirrorDisplayInfoIfAny(&display_info_list); 571 UpdateDisplays(display_info_list); 572} 573 574void DisplayManager::UpdateDisplays( 575 const std::vector<DisplayInfo>& updated_display_info_list) { 576#if defined(OS_WIN) 577 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 578 DCHECK_EQ(1u, updated_display_info_list.size()) << 579 "Multiple display test does not work on Win8 bots. Please " 580 "skip (don't disable) the test using SupportsMultipleDisplays()"; 581 } 582#endif 583 584 DisplayInfoList new_display_info_list = updated_display_info_list; 585 std::sort(displays_.begin(), displays_.end(), DisplaySortFunctor()); 586 std::sort(new_display_info_list.begin(), 587 new_display_info_list.end(), 588 DisplayInfoSortFunctor()); 589 DisplayList removed_displays; 590 std::vector<size_t> changed_display_indices; 591 std::vector<size_t> added_display_indices; 592 593 DisplayList::iterator curr_iter = displays_.begin(); 594 DisplayInfoList::const_iterator new_info_iter = new_display_info_list.begin(); 595 596 DisplayList new_displays; 597 598 // Use the internal display or 1st as the mirror source, then scale 599 // the root window so that it matches the external display's 600 // resolution. This is necessary in order for scaling to work while 601 // mirrored. 602 int64 non_desktop_display_id = gfx::Display::kInvalidDisplayID; 603 604 if (second_display_mode_ != EXTENDED && new_display_info_list.size() == 2) { 605 bool zero_is_source = 606 first_display_id_ == new_display_info_list[0].id() || 607 gfx::Display::InternalDisplayId() == new_display_info_list[0].id(); 608 if (second_display_mode_ == MIRRORING) { 609 mirrored_display_id_ = new_display_info_list[zero_is_source ? 1 : 0].id(); 610 non_desktop_display_id = mirrored_display_id_; 611 } else { 612 // TODO(oshima|bshe): The virtual keyboard is currently assigned to 613 // the 1st display. 614 non_desktop_display_id = 615 new_display_info_list[zero_is_source ? 0 : 1].id(); 616 } 617 } 618 619 while (curr_iter != displays_.end() || 620 new_info_iter != new_display_info_list.end()) { 621 if (new_info_iter != new_display_info_list.end() && 622 non_desktop_display_id == new_info_iter->id()) { 623 DisplayInfo info = *new_info_iter; 624 info.SetOverscanInsets(gfx::Insets()); 625 InsertAndUpdateDisplayInfo(info); 626 non_desktop_display_ = 627 CreateDisplayFromDisplayInfoById(non_desktop_display_id); 628 ++new_info_iter; 629 // Remove existing external dispaly if it is going to be used as 630 // non desktop. 631 if (curr_iter != displays_.end() && 632 curr_iter->id() == non_desktop_display_id) { 633 removed_displays.push_back(*curr_iter); 634 ++curr_iter; 635 } 636 continue; 637 } 638 639 if (curr_iter == displays_.end()) { 640 // more displays in new list. 641 added_display_indices.push_back(new_displays.size()); 642 InsertAndUpdateDisplayInfo(*new_info_iter); 643 new_displays.push_back( 644 CreateDisplayFromDisplayInfoById(new_info_iter->id())); 645 ++new_info_iter; 646 } else if (new_info_iter == new_display_info_list.end()) { 647 // more displays in current list. 648 removed_displays.push_back(*curr_iter); 649 ++curr_iter; 650 } else if (curr_iter->id() == new_info_iter->id()) { 651 const gfx::Display& current_display = *curr_iter; 652 // Copy the info because |CreateDisplayFromInfo| updates the instance. 653 const DisplayInfo current_display_info = 654 GetDisplayInfo(current_display.id()); 655 InsertAndUpdateDisplayInfo(*new_info_iter); 656 gfx::Display new_display = 657 CreateDisplayFromDisplayInfoById(new_info_iter->id()); 658 const DisplayInfo& new_display_info = GetDisplayInfo(new_display.id()); 659 660 bool host_window_bounds_changed = 661 current_display_info.bounds_in_native() != 662 new_display_info.bounds_in_native(); 663 664 if (force_bounds_changed_ || 665 host_window_bounds_changed || 666 (current_display.device_scale_factor() != 667 new_display.device_scale_factor()) || 668 (current_display_info.size_in_pixel() != 669 new_display.GetSizeInPixel()) || 670 (current_display.rotation() != new_display.rotation())) { 671 672 changed_display_indices.push_back(new_displays.size()); 673 } 674 675 new_display.UpdateWorkAreaFromInsets(current_display.GetWorkAreaInsets()); 676 new_displays.push_back(new_display); 677 ++curr_iter; 678 ++new_info_iter; 679 } else if (curr_iter->id() < new_info_iter->id()) { 680 // more displays in current list between ids, which means it is deleted. 681 removed_displays.push_back(*curr_iter); 682 ++curr_iter; 683 } else { 684 // more displays in new list between ids, which means it is added. 685 added_display_indices.push_back(new_displays.size()); 686 InsertAndUpdateDisplayInfo(*new_info_iter); 687 new_displays.push_back( 688 CreateDisplayFromDisplayInfoById(new_info_iter->id())); 689 ++new_info_iter; 690 } 691 } 692 693 scoped_ptr<NonDesktopDisplayUpdater> non_desktop_display_updater( 694 new NonDesktopDisplayUpdater(this, delegate_)); 695 696 // Do not update |displays_| if there's nothing to be updated. Without this, 697 // it will not update the display layout, which causes the bug 698 // http://crbug.com/155948. 699 if (changed_display_indices.empty() && added_display_indices.empty() && 700 removed_displays.empty()) { 701 return; 702 } 703 // Clear focus if the display has been removed, but don't clear focus if 704 // the destkop has been moved from one display to another 705 // (mirror -> docked, docked -> single internal). 706 bool clear_focus = 707 !removed_displays.empty() && 708 !(removed_displays.size() == 1 && added_display_indices.size() == 1); 709 if (delegate_) 710 delegate_->PreDisplayConfigurationChange(clear_focus); 711 712 size_t updated_index; 713 if (UpdateSecondaryDisplayBoundsForLayout(&new_displays, &updated_index) && 714 std::find(added_display_indices.begin(), 715 added_display_indices.end(), 716 updated_index) == added_display_indices.end() && 717 std::find(changed_display_indices.begin(), 718 changed_display_indices.end(), 719 updated_index) == changed_display_indices.end()) { 720 changed_display_indices.push_back(updated_index); 721 } 722 723 displays_ = new_displays; 724 725 base::AutoReset<bool> resetter(&change_display_upon_host_resize_, false); 726 727 // Temporarily add displays to be removed because display object 728 // being removed are accessed during shutting down the root. 729 displays_.insert(displays_.end(), removed_displays.begin(), 730 removed_displays.end()); 731 732 for (DisplayList::const_reverse_iterator iter = removed_displays.rbegin(); 733 iter != removed_displays.rend(); ++iter) { 734 Shell::GetInstance()->screen()->NotifyDisplayRemoved(displays_.back()); 735 displays_.pop_back(); 736 } 737 // Close the non desktop window here to avoid creating two compositor on 738 // one display. 739 if (!non_desktop_display_updater->enabled()) 740 non_desktop_display_updater.reset(); 741 for (std::vector<size_t>::iterator iter = added_display_indices.begin(); 742 iter != added_display_indices.end(); ++iter) { 743 Shell::GetInstance()->screen()->NotifyDisplayAdded(displays_[*iter]); 744 } 745 // Create the non destkop window after all displays are added so that 746 // it can mirror the display newly added. This can happen when switching 747 // from dock mode to software mirror mode. 748 non_desktop_display_updater.reset(); 749 for (std::vector<size_t>::iterator iter = changed_display_indices.begin(); 750 iter != changed_display_indices.end(); ++iter) { 751 Shell::GetInstance()->screen()->NotifyBoundsChanged(displays_[*iter]); 752 } 753 if (delegate_) 754 delegate_->PostDisplayConfigurationChange(); 755 756#if defined(USE_X11) && defined(OS_CHROMEOS) 757 if (!changed_display_indices.empty() && base::SysInfo::IsRunningOnChromeOS()) 758 ui::ClearX11DefaultRootWindow(); 759#endif 760} 761 762const gfx::Display& DisplayManager::GetDisplayAt(size_t index) const { 763 DCHECK_LT(index, displays_.size()); 764 return displays_[index]; 765} 766 767const gfx::Display& DisplayManager::GetPrimaryDisplayCandidate() const { 768 if (GetNumDisplays() == 1) 769 return displays_[0]; 770 DisplayLayout layout = layout_store_->GetRegisteredDisplayLayout( 771 GetCurrentDisplayIdPair()); 772 return GetDisplayForId(layout.primary_id); 773} 774 775size_t DisplayManager::GetNumDisplays() const { 776 return displays_.size(); 777} 778 779bool DisplayManager::IsMirrored() const { 780 return mirrored_display_id_ != gfx::Display::kInvalidDisplayID; 781} 782 783const DisplayInfo& DisplayManager::GetDisplayInfo(int64 display_id) const { 784 std::map<int64, DisplayInfo>::const_iterator iter = 785 display_info_.find(display_id); 786 CHECK(iter != display_info_.end()) << display_id; 787 return iter->second; 788} 789 790std::string DisplayManager::GetDisplayNameForId(int64 id) { 791 if (id == gfx::Display::kInvalidDisplayID) 792 return l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME); 793 794 std::map<int64, DisplayInfo>::const_iterator iter = display_info_.find(id); 795 if (iter != display_info_.end() && !iter->second.name().empty()) 796 return iter->second.name(); 797 798 return base::StringPrintf("Display %d", static_cast<int>(id)); 799} 800 801int64 DisplayManager::GetDisplayIdForUIScaling() const { 802 // UI Scaling is effective only on internal display. 803 int64 display_id = gfx::Display::InternalDisplayId(); 804#if defined(OS_WIN) 805 display_id = first_display_id(); 806#endif 807 return display_id; 808} 809 810void DisplayManager::SetMirrorMode(bool mirrored) { 811 if (num_connected_displays() <= 1) 812 return; 813 814#if defined(OS_CHROMEOS) 815 if (base::SysInfo::IsRunningOnChromeOS()) { 816 chromeos::OutputState new_state = mirrored ? 817 chromeos::STATE_DUAL_MIRROR : chromeos::STATE_DUAL_EXTENDED; 818 Shell::GetInstance()->output_configurator()->SetDisplayMode(new_state); 819 return; 820 } 821#endif 822 // This is fallback path to emulate mirroroing on desktop. 823 SetSecondDisplayMode(mirrored ? MIRRORING : EXTENDED); 824 DisplayInfoList display_info_list; 825 int count = 0; 826 for (std::map<int64, DisplayInfo>::const_iterator iter = 827 display_info_.begin(); 828 count < 2; ++iter, ++count) { 829 display_info_list.push_back(GetDisplayInfo(iter->second.id())); 830 } 831 UpdateDisplays(display_info_list); 832#if defined(OS_CHROMEOS) 833 if (Shell::GetInstance()->output_configurator_animation()) { 834 Shell::GetInstance()->output_configurator_animation()-> 835 StartFadeInAnimation(); 836 } 837#endif 838} 839 840void DisplayManager::AddRemoveDisplay() { 841 DCHECK(!displays_.empty()); 842 std::vector<DisplayInfo> new_display_info_list; 843 const DisplayInfo& first_display = GetDisplayInfo(displays_[0].id()); 844 new_display_info_list.push_back(first_display); 845 // Add if there is only one display connected. 846 if (num_connected_displays() == 1) { 847 // Layout the 2nd display below the primary as with the real device. 848 gfx::Rect host_bounds = first_display.bounds_in_native(); 849 new_display_info_list.push_back(DisplayInfo::CreateFromSpec( 850 base::StringPrintf( 851 "%d+%d-500x400", host_bounds.x(), host_bounds.bottom()))); 852 } 853 num_connected_displays_ = new_display_info_list.size(); 854 mirrored_display_id_ = gfx::Display::kInvalidDisplayID; 855 non_desktop_display_ = gfx::Display(); 856 UpdateDisplays(new_display_info_list); 857} 858 859void DisplayManager::ToggleDisplayScaleFactor() { 860 DCHECK(!displays_.empty()); 861 std::vector<DisplayInfo> new_display_info_list; 862 for (DisplayList::const_iterator iter = displays_.begin(); 863 iter != displays_.end(); ++iter) { 864 DisplayInfo display_info = GetDisplayInfo(iter->id()); 865 display_info.set_device_scale_factor( 866 display_info.device_scale_factor() == 1.0f ? 2.0f : 1.0f); 867 new_display_info_list.push_back(display_info); 868 } 869 AddMirrorDisplayInfoIfAny(&new_display_info_list); 870 UpdateDisplays(new_display_info_list); 871} 872 873#if defined(OS_CHROMEOS) 874void DisplayManager::SetSoftwareMirroring(bool enabled) { 875 // TODO(oshima|bshe): Support external display on the system 876 // that has virtual keyboard display. 877 if (second_display_mode_ == VIRTUAL_KEYBOARD) 878 return; 879 SetSecondDisplayMode(enabled ? MIRRORING : EXTENDED); 880} 881#endif 882 883void DisplayManager::SetSecondDisplayMode(SecondDisplayMode mode) { 884 second_display_mode_ = mode; 885 mirrored_display_id_ = gfx::Display::kInvalidDisplayID; 886 non_desktop_display_ = gfx::Display(); 887} 888 889bool DisplayManager::UpdateDisplayBounds(int64 display_id, 890 const gfx::Rect& new_bounds) { 891 if (change_display_upon_host_resize_) { 892 display_info_[display_id].SetBounds(new_bounds); 893 // Don't notify observers if the mirrored window has changed. 894 if (software_mirroring_enabled() && mirrored_display_id_ == display_id) 895 return false; 896 gfx::Display* display = FindDisplayForId(display_id); 897 display->SetSize(display_info_[display_id].size_in_pixel()); 898 Shell::GetInstance()->screen()->NotifyBoundsChanged(*display); 899 return true; 900 } 901 return false; 902} 903 904void DisplayManager::CreateMirrorWindowIfAny() { 905 NonDesktopDisplayUpdater updater(this, delegate_); 906} 907 908gfx::Display* DisplayManager::FindDisplayForId(int64 id) { 909 for (DisplayList::iterator iter = displays_.begin(); 910 iter != displays_.end(); ++iter) { 911 if ((*iter).id() == id) 912 return &(*iter); 913 } 914 DLOG(WARNING) << "Could not find display:" << id; 915 return NULL; 916} 917 918void DisplayManager::AddMirrorDisplayInfoIfAny( 919 std::vector<DisplayInfo>* display_info_list) { 920 if (software_mirroring_enabled() && IsMirrored()) 921 display_info_list->push_back(GetDisplayInfo(mirrored_display_id_)); 922} 923 924void DisplayManager::InsertAndUpdateDisplayInfo(const DisplayInfo& new_info) { 925 std::map<int64, DisplayInfo>::iterator info = 926 display_info_.find(new_info.id()); 927 if (info != display_info_.end()) 928 info->second.Copy(new_info); 929 else { 930 display_info_[new_info.id()] = new_info; 931 display_info_[new_info.id()].set_native(false); 932 } 933 display_info_[new_info.id()].UpdateDisplaySize(); 934} 935 936gfx::Display DisplayManager::CreateDisplayFromDisplayInfoById(int64 id) { 937 DCHECK(display_info_.find(id) != display_info_.end()); 938 const DisplayInfo& display_info = display_info_[id]; 939 940 gfx::Display new_display(display_info.id()); 941 gfx::Rect bounds_in_native(display_info.size_in_pixel()); 942 float device_scale_factor = display_info.device_scale_factor(); 943 if (device_scale_factor == 2.0f && display_info.configured_ui_scale() == 2.0f) 944 device_scale_factor = 1.0f; 945 946 // Simply set the origin to (0,0). The primary display's origin is 947 // always (0,0) and the secondary display's bounds will be updated 948 // in |UpdateSecondaryDisplayBoundsForLayout| called in |UpdateDisplay|. 949 new_display.SetScaleAndBounds( 950 device_scale_factor, gfx::Rect(bounds_in_native.size())); 951 new_display.set_rotation(display_info.rotation()); 952 new_display.set_touch_support(display_info.touch_support()); 953 return new_display; 954} 955 956bool DisplayManager::UpdateSecondaryDisplayBoundsForLayout( 957 DisplayList* displays, 958 size_t* updated_index) const { 959 if (displays->size() != 2U) 960 return false; 961 962 int64 id_at_zero = displays->at(0).id(); 963 DisplayIdPair pair = 964 (id_at_zero == first_display_id_ || 965 id_at_zero == gfx::Display::InternalDisplayId()) ? 966 std::make_pair(id_at_zero, displays->at(1).id()) : 967 std::make_pair(displays->at(1).id(), id_at_zero) ; 968 DisplayLayout layout = 969 layout_store_->ComputeDisplayLayoutForDisplayIdPair(pair); 970 971 // Ignore if a user has a old format (should be extremely rare) 972 // and this will be replaced with DCHECK. 973 if (layout.primary_id != gfx::Display::kInvalidDisplayID) { 974 size_t primary_index, secondary_index; 975 if (displays->at(0).id() == layout.primary_id) { 976 primary_index = 0; 977 secondary_index = 1; 978 } else { 979 primary_index = 1; 980 secondary_index = 0; 981 } 982 // This function may be called before the secondary display is 983 // registered. The bounds is empty in that case and will 984 // return true. 985 gfx::Rect bounds = 986 GetDisplayForId(displays->at(secondary_index).id()).bounds(); 987 UpdateDisplayBoundsForLayout( 988 layout, displays->at(primary_index), &displays->at(secondary_index)); 989 *updated_index = secondary_index; 990 return bounds != displays->at(secondary_index).bounds(); 991 } 992 return false; 993} 994 995// static 996void DisplayManager::UpdateDisplayBoundsForLayout( 997 const DisplayLayout& layout, 998 const gfx::Display& primary_display, 999 gfx::Display* secondary_display) { 1000 DCHECK_EQ("0,0", primary_display.bounds().origin().ToString()); 1001 1002 const gfx::Rect& primary_bounds = primary_display.bounds(); 1003 const gfx::Rect& secondary_bounds = secondary_display->bounds(); 1004 gfx::Point new_secondary_origin = primary_bounds.origin(); 1005 1006 DisplayLayout::Position position = layout.position; 1007 1008 // Ignore the offset in case the secondary display doesn't share edges with 1009 // the primary display. 1010 int offset = layout.offset; 1011 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) { 1012 offset = std::min( 1013 offset, primary_bounds.width() - kMinimumOverlapForInvalidOffset); 1014 offset = std::max( 1015 offset, -secondary_bounds.width() + kMinimumOverlapForInvalidOffset); 1016 } else { 1017 offset = std::min( 1018 offset, primary_bounds.height() - kMinimumOverlapForInvalidOffset); 1019 offset = std::max( 1020 offset, -secondary_bounds.height() + kMinimumOverlapForInvalidOffset); 1021 } 1022 switch (position) { 1023 case DisplayLayout::TOP: 1024 new_secondary_origin.Offset(offset, -secondary_bounds.height()); 1025 break; 1026 case DisplayLayout::RIGHT: 1027 new_secondary_origin.Offset(primary_bounds.width(), offset); 1028 break; 1029 case DisplayLayout::BOTTOM: 1030 new_secondary_origin.Offset(offset, primary_bounds.height()); 1031 break; 1032 case DisplayLayout::LEFT: 1033 new_secondary_origin.Offset(-secondary_bounds.width(), offset); 1034 break; 1035 } 1036 gfx::Insets insets = secondary_display->GetWorkAreaInsets(); 1037 secondary_display->set_bounds( 1038 gfx::Rect(new_secondary_origin, secondary_bounds.size())); 1039 secondary_display->UpdateWorkAreaFromInsets(insets); 1040} 1041 1042} // namespace internal 1043} // namespace ash 1044