1 // Copyright 2014 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 "ui/display/chromeos/display_configurator.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/logging.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/stringprintf.h" 12 #include "base/sys_info.h" 13 #include "base/time/time.h" 14 #include "ui/display/display_switches.h" 15 #include "ui/display/types/display_mode.h" 16 #include "ui/display/types/display_snapshot.h" 17 #include "ui/display/types/native_display_delegate.h" 18 19 namespace ui { 20 21 namespace { 22 23 typedef std::vector<const DisplayMode*> DisplayModeList; 24 25 // The delay to perform configuration after RRNotify. See the comment for 26 // |configure_timer_|. 27 const int kConfigureDelayMs = 500; 28 29 // The delay spent before reading the display configuration after coming out of 30 // suspend. While coming out of suspend the display state may be updating. This 31 // is used to wait until the hardware had a chance to update the display state 32 // such that we read an up to date state. 33 const int kResumeDelayMs = 500; 34 35 // Returns a string describing |state|. 36 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) { 37 switch (state) { 38 case chromeos::DISPLAY_POWER_ALL_ON: 39 return "ALL_ON"; 40 case chromeos::DISPLAY_POWER_ALL_OFF: 41 return "ALL_OFF"; 42 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON: 43 return "INTERNAL_OFF_EXTERNAL_ON"; 44 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF: 45 return "INTERNAL_ON_EXTERNAL_OFF"; 46 default: 47 return "unknown (" + base::IntToString(state) + ")"; 48 } 49 } 50 51 // Returns a string describing |state|. 52 std::string DisplayStateToString(MultipleDisplayState state) { 53 switch (state) { 54 case MULTIPLE_DISPLAY_STATE_INVALID: 55 return "INVALID"; 56 case MULTIPLE_DISPLAY_STATE_HEADLESS: 57 return "HEADLESS"; 58 case MULTIPLE_DISPLAY_STATE_SINGLE: 59 return "SINGLE"; 60 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: 61 return "DUAL_MIRROR"; 62 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: 63 return "DUAL_EXTENDED"; 64 } 65 NOTREACHED() << "Unknown state " << state; 66 return "INVALID"; 67 } 68 69 // Returns the number of displays in |displays| that should be turned on, per 70 // |state|. If |display_power| is non-NULL, it is updated to contain the 71 // on/off state of each corresponding entry in |displays|. 72 int GetDisplayPower( 73 const std::vector<DisplayConfigurator::DisplayState>& display_states, 74 chromeos::DisplayPowerState state, 75 std::vector<bool>* display_power) { 76 int num_on_displays = 0; 77 if (display_power) 78 display_power->resize(display_states.size()); 79 80 for (size_t i = 0; i < display_states.size(); ++i) { 81 bool internal = 82 display_states[i].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; 83 bool on = 84 state == chromeos::DISPLAY_POWER_ALL_ON || 85 (state == chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON && 86 !internal) || 87 (state == chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && internal); 88 if (display_power) 89 (*display_power)[i] = on; 90 if (on) 91 num_on_displays++; 92 } 93 return num_on_displays; 94 } 95 96 } // namespace 97 98 99 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0; 100 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0; 101 const int 102 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1; 103 104 DisplayConfigurator::DisplayState::DisplayState() 105 : display(NULL), 106 selected_mode(NULL), 107 mirror_mode(NULL) {} 108 109 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() { 110 if (configurator_->configure_timer_.IsRunning()) { 111 configurator_->configure_timer_.user_task().Run(); 112 configurator_->configure_timer_.Stop(); 113 return true; 114 } else { 115 return false; 116 } 117 } 118 119 // static 120 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize( 121 const DisplaySnapshot& display, 122 const gfx::Size& size) { 123 const DisplayMode* best_mode = NULL; 124 for (DisplayModeList::const_iterator it = display.modes().begin(); 125 it != display.modes().end(); 126 ++it) { 127 const DisplayMode* mode = *it; 128 129 if (mode->size() != size) 130 continue; 131 132 if (!best_mode) { 133 best_mode = mode; 134 continue; 135 } 136 137 if (mode->is_interlaced()) { 138 if (!best_mode->is_interlaced()) 139 continue; 140 } else { 141 // Reset the best rate if the non interlaced is 142 // found the first time. 143 if (best_mode->is_interlaced()) { 144 best_mode = mode; 145 continue; 146 } 147 } 148 if (mode->refresh_rate() < best_mode->refresh_rate()) 149 continue; 150 151 best_mode = mode; 152 } 153 154 return best_mode; 155 } 156 157 DisplayConfigurator::DisplayConfigurator() 158 : state_controller_(NULL), 159 mirroring_controller_(NULL), 160 is_panel_fitting_enabled_(false), 161 configure_display_(base::SysInfo::IsRunningOnChromeOS()), 162 display_state_(MULTIPLE_DISPLAY_STATE_INVALID), 163 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON), 164 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON), 165 next_display_protection_client_id_(1) {} 166 167 DisplayConfigurator::~DisplayConfigurator() { 168 if (native_display_delegate_) 169 native_display_delegate_->RemoveObserver(this); 170 } 171 172 void DisplayConfigurator::SetDelegateForTesting( 173 scoped_ptr<NativeDisplayDelegate> display_delegate) { 174 DCHECK(!native_display_delegate_); 175 176 native_display_delegate_ = display_delegate.Pass(); 177 configure_display_ = true; 178 } 179 180 void DisplayConfigurator::SetInitialDisplayPower( 181 chromeos::DisplayPowerState power_state) { 182 DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID); 183 requested_power_state_ = current_power_state_ = power_state; 184 } 185 186 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) { 187 is_panel_fitting_enabled_ = is_panel_fitting_enabled; 188 if (!configure_display_) 189 return; 190 191 // If the delegate is already initialized don't update it (For example, tests 192 // set their own delegates). 193 if (!native_display_delegate_) { 194 native_display_delegate_ = CreatePlatformNativeDisplayDelegate(); 195 native_display_delegate_->AddObserver(this); 196 } 197 } 198 199 void DisplayConfigurator::ForceInitialConfigure( 200 uint32_t background_color_argb) { 201 if (!configure_display_) 202 return; 203 204 native_display_delegate_->GrabServer(); 205 native_display_delegate_->Initialize(); 206 207 UpdateCachedDisplays(); 208 if (cached_displays_.size() > 1 && background_color_argb) 209 native_display_delegate_->SetBackgroundColor(background_color_argb); 210 const MultipleDisplayState new_state = ChooseDisplayState( 211 requested_power_state_); 212 const bool success = EnterStateOrFallBackToSoftwareMirroring( 213 new_state, requested_power_state_); 214 215 // Force the DPMS on chrome startup as the driver doesn't always detect 216 // that all displays are on when signing out. 217 native_display_delegate_->ForceDPMSOn(); 218 native_display_delegate_->UngrabServer(); 219 NotifyObservers(success, new_state); 220 } 221 222 bool DisplayConfigurator::IsMirroring() const { 223 return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR || 224 (mirroring_controller_ && 225 mirroring_controller_->SoftwareMirroringEnabled()); 226 } 227 228 bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) { 229 for (DisplayStateList::const_iterator it = cached_displays_.begin(); 230 it != cached_displays_.end(); 231 ++it) { 232 uint32_t all_desired = 0; 233 234 // In mirror mode, protection request of all displays need to be fulfilled. 235 // In non-mirror mode, only request of client's display needs to be 236 // fulfilled. 237 ContentProtections::const_iterator request_it; 238 if (IsMirroring()) { 239 for (request_it = requests.begin(); 240 request_it != requests.end(); 241 ++request_it) 242 all_desired |= request_it->second; 243 } else { 244 request_it = requests.find(it->display->display_id()); 245 if (request_it != requests.end()) 246 all_desired = request_it->second; 247 } 248 249 switch (it->display->type()) { 250 case DISPLAY_CONNECTION_TYPE_UNKNOWN: 251 return false; 252 // DisplayPort, DVI, and HDMI all support HDCP. 253 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: 254 case DISPLAY_CONNECTION_TYPE_DVI: 255 case DISPLAY_CONNECTION_TYPE_HDMI: { 256 HDCPState current_state; 257 // Need to poll the driver for updates since other applications may 258 // have updated the state. 259 if (!native_display_delegate_->GetHDCPState(*it->display, 260 ¤t_state)) 261 return false; 262 bool current_desired = (current_state != HDCP_STATE_UNDESIRED); 263 bool new_desired = (all_desired & CONTENT_PROTECTION_METHOD_HDCP); 264 // Don't enable again if HDCP is already active. Some buggy drivers 265 // may disable and enable if setting "desired" in active state. 266 if (current_desired != new_desired) { 267 HDCPState new_state = 268 new_desired ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED; 269 if (!native_display_delegate_->SetHDCPState(*it->display, new_state)) 270 return false; 271 } 272 break; 273 } 274 case DISPLAY_CONNECTION_TYPE_INTERNAL: 275 case DISPLAY_CONNECTION_TYPE_VGA: 276 case DISPLAY_CONNECTION_TYPE_NETWORK: 277 // No protections for these types. Do nothing. 278 break; 279 case DISPLAY_CONNECTION_TYPE_NONE: 280 NOTREACHED(); 281 break; 282 } 283 } 284 285 return true; 286 } 287 288 DisplayConfigurator::ContentProtectionClientId 289 DisplayConfigurator::RegisterContentProtectionClient() { 290 if (!configure_display_) 291 return kInvalidClientId; 292 293 return next_display_protection_client_id_++; 294 } 295 296 void DisplayConfigurator::UnregisterContentProtectionClient( 297 ContentProtectionClientId client_id) { 298 client_protection_requests_.erase(client_id); 299 300 ContentProtections protections; 301 for (ProtectionRequests::const_iterator it = 302 client_protection_requests_.begin(); 303 it != client_protection_requests_.end(); 304 ++it) { 305 for (ContentProtections::const_iterator it2 = it->second.begin(); 306 it2 != it->second.end(); 307 ++it2) { 308 protections[it2->first] |= it2->second; 309 } 310 } 311 312 ApplyProtections(protections); 313 } 314 315 bool DisplayConfigurator::QueryContentProtectionStatus( 316 ContentProtectionClientId client_id, 317 int64_t display_id, 318 uint32_t* link_mask, 319 uint32_t* protection_mask) { 320 if (!configure_display_) 321 return false; 322 323 uint32_t enabled = 0; 324 uint32_t unfulfilled = 0; 325 *link_mask = 0; 326 for (DisplayStateList::const_iterator it = cached_displays_.begin(); 327 it != cached_displays_.end(); 328 ++it) { 329 // Query display if it is in mirror mode or client on the same display. 330 if (!IsMirroring() && it->display->display_id() != display_id) 331 continue; 332 333 *link_mask |= it->display->type(); 334 switch (it->display->type()) { 335 case DISPLAY_CONNECTION_TYPE_UNKNOWN: 336 return false; 337 // DisplayPort, DVI, and HDMI all support HDCP. 338 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: 339 case DISPLAY_CONNECTION_TYPE_DVI: 340 case DISPLAY_CONNECTION_TYPE_HDMI: { 341 HDCPState state; 342 if (!native_display_delegate_->GetHDCPState(*it->display, &state)) 343 return false; 344 if (state == HDCP_STATE_ENABLED) 345 enabled |= CONTENT_PROTECTION_METHOD_HDCP; 346 else 347 unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP; 348 break; 349 } 350 case DISPLAY_CONNECTION_TYPE_INTERNAL: 351 case DISPLAY_CONNECTION_TYPE_VGA: 352 case DISPLAY_CONNECTION_TYPE_NETWORK: 353 // No protections for these types. Do nothing. 354 break; 355 case DISPLAY_CONNECTION_TYPE_NONE: 356 NOTREACHED(); 357 break; 358 } 359 } 360 361 // Don't reveal protections requested by other clients. 362 ProtectionRequests::iterator it = client_protection_requests_.find(client_id); 363 if (it != client_protection_requests_.end()) { 364 uint32_t requested_mask = 0; 365 if (it->second.find(display_id) != it->second.end()) 366 requested_mask = it->second[display_id]; 367 *protection_mask = enabled & ~unfulfilled & requested_mask; 368 } else { 369 *protection_mask = 0; 370 } 371 return true; 372 } 373 374 bool DisplayConfigurator::EnableContentProtection( 375 ContentProtectionClientId client_id, 376 int64_t display_id, 377 uint32_t desired_method_mask) { 378 if (!configure_display_) 379 return false; 380 381 ContentProtections protections; 382 for (ProtectionRequests::const_iterator it = 383 client_protection_requests_.begin(); 384 it != client_protection_requests_.end(); 385 ++it) { 386 for (ContentProtections::const_iterator it2 = it->second.begin(); 387 it2 != it->second.end(); 388 ++it2) { 389 if (it->first == client_id && it2->first == display_id) 390 continue; 391 protections[it2->first] |= it2->second; 392 } 393 } 394 protections[display_id] |= desired_method_mask; 395 396 if (!ApplyProtections(protections)) 397 return false; 398 399 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) { 400 if (client_protection_requests_.find(client_id) != 401 client_protection_requests_.end()) { 402 client_protection_requests_[client_id].erase(display_id); 403 if (client_protection_requests_[client_id].size() == 0) 404 client_protection_requests_.erase(client_id); 405 } 406 } else { 407 client_protection_requests_[client_id][display_id] = desired_method_mask; 408 } 409 410 return true; 411 } 412 413 std::vector<ui::ColorCalibrationProfile> 414 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) { 415 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( 416 switches::kDisableDisplayColorCalibration)) { 417 for (size_t i = 0; i < cached_displays_.size(); ++i) { 418 if (cached_displays_[i].display && 419 cached_displays_[i].display->display_id() == display_id) { 420 return native_display_delegate_->GetAvailableColorCalibrationProfiles( 421 *cached_displays_[i].display); 422 } 423 } 424 } 425 426 return std::vector<ui::ColorCalibrationProfile>(); 427 } 428 429 bool DisplayConfigurator::SetColorCalibrationProfile( 430 int64_t display_id, 431 ui::ColorCalibrationProfile new_profile) { 432 for (size_t i = 0; i < cached_displays_.size(); ++i) { 433 if (cached_displays_[i].display && 434 cached_displays_[i].display->display_id() == display_id) { 435 return native_display_delegate_->SetColorCalibrationProfile( 436 *cached_displays_[i].display, new_profile); 437 } 438 } 439 440 return false; 441 } 442 443 void DisplayConfigurator::PrepareForExit() { 444 configure_display_ = false; 445 } 446 447 bool DisplayConfigurator::SetDisplayPower( 448 chromeos::DisplayPowerState power_state, 449 int flags) { 450 if (!configure_display_) 451 return false; 452 453 VLOG(1) << "SetDisplayPower: power_state=" 454 << DisplayPowerStateToString(power_state) << " flags=" << flags 455 << ", configure timer=" 456 << (configure_timer_.IsRunning() ? "Running" : "Stopped"); 457 if (power_state == current_power_state_ && 458 !(flags & kSetDisplayPowerForceProbe)) 459 return true; 460 461 native_display_delegate_->GrabServer(); 462 UpdateCachedDisplays(); 463 464 const MultipleDisplayState new_state = ChooseDisplayState(power_state); 465 bool attempted_change = false; 466 bool success = false; 467 468 bool only_if_single_internal_display = 469 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; 470 bool single_internal_display = 471 cached_displays_.size() == 1 && 472 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; 473 if (single_internal_display || !only_if_single_internal_display) { 474 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state); 475 attempted_change = true; 476 477 // Force the DPMS on since the driver doesn't always detect that it 478 // should turn on. This is needed when coming back from idle suspend. 479 if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF) 480 native_display_delegate_->ForceDPMSOn(); 481 } 482 483 native_display_delegate_->UngrabServer(); 484 if (attempted_change) 485 NotifyObservers(success, new_state); 486 return success; 487 } 488 489 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) { 490 if (!configure_display_) 491 return false; 492 493 VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state); 494 if (display_state_ == new_state) { 495 // Cancel software mirroring if the state is moving from 496 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to 497 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED. 498 if (mirroring_controller_ && 499 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) 500 mirroring_controller_->SetSoftwareMirroring(false); 501 NotifyObservers(true, new_state); 502 return true; 503 } 504 505 native_display_delegate_->GrabServer(); 506 UpdateCachedDisplays(); 507 const bool success = EnterStateOrFallBackToSoftwareMirroring( 508 new_state, requested_power_state_); 509 native_display_delegate_->UngrabServer(); 510 511 NotifyObservers(success, new_state); 512 return success; 513 } 514 515 void DisplayConfigurator::OnConfigurationChanged() { 516 // Configure displays with |kConfigureDelayMs| delay, 517 // so that time-consuming ConfigureDisplays() won't be called multiple times. 518 if (configure_timer_.IsRunning()) { 519 // Note: when the timer is running it is possible that a different task 520 // (SetDisplayPower()) is scheduled. In these cases, prefer the already 521 // scheduled task to ConfigureDisplays() since ConfigureDisplays() performs 522 // only basic configuration while SetDisplayPower() will perform additional 523 // operations. 524 configure_timer_.Reset(); 525 } else { 526 configure_timer_.Start( 527 FROM_HERE, 528 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), 529 this, 530 &DisplayConfigurator::ConfigureDisplays); 531 } 532 } 533 534 void DisplayConfigurator::AddObserver(Observer* observer) { 535 observers_.AddObserver(observer); 536 } 537 538 void DisplayConfigurator::RemoveObserver(Observer* observer) { 539 observers_.RemoveObserver(observer); 540 } 541 542 void DisplayConfigurator::SuspendDisplays() { 543 // If the display is off due to user inactivity and there's only a single 544 // internal display connected, switch to the all-on state before 545 // suspending. This shouldn't be very noticeable to the user since the 546 // backlight is off at this point, and doing this lets us resume directly 547 // into the "on" state, which greatly reduces resume times. 548 if (requested_power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) { 549 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, 550 kSetDisplayPowerOnlyIfSingleInternalDisplay); 551 552 // We need to make sure that the monitor configuration we just did actually 553 // completes before we return, because otherwise the X message could be 554 // racing with the HandleSuspendReadiness message. 555 native_display_delegate_->SyncWithServer(); 556 } 557 } 558 559 void DisplayConfigurator::ResumeDisplays() { 560 // Force probing to ensure that we pick up any changes that were made 561 // while the system was suspended. 562 configure_timer_.Start( 563 FROM_HERE, 564 base::TimeDelta::FromMilliseconds(kResumeDelayMs), 565 base::Bind(base::IgnoreResult(&DisplayConfigurator::SetDisplayPower), 566 base::Unretained(this), 567 requested_power_state_, 568 kSetDisplayPowerForceProbe)); 569 } 570 571 void DisplayConfigurator::UpdateCachedDisplays() { 572 std::vector<DisplaySnapshot*> snapshots = 573 native_display_delegate_->GetDisplays(); 574 575 cached_displays_.clear(); 576 for (size_t i = 0; i < snapshots.size(); ++i) { 577 DisplayState display_state; 578 display_state.display = snapshots[i]; 579 cached_displays_.push_back(display_state); 580 } 581 582 // Set |selected_mode| fields. 583 for (size_t i = 0; i < cached_displays_.size(); ++i) { 584 DisplayState* display_state = &cached_displays_[i]; 585 if (display_state->display->has_proper_display_id()) { 586 gfx::Size size; 587 if (state_controller_ && 588 state_controller_->GetResolutionForDisplayId( 589 display_state->display->display_id(), &size)) { 590 display_state->selected_mode = 591 FindDisplayModeMatchingSize(*display_state->display, size); 592 } 593 } 594 // Fall back to native mode. 595 if (!display_state->selected_mode) 596 display_state->selected_mode = display_state->display->native_mode(); 597 } 598 599 // Set |mirror_mode| fields. 600 if (cached_displays_.size() == 2) { 601 bool one_is_internal = 602 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; 603 bool two_is_internal = 604 cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; 605 int internal_displays = 606 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0); 607 DCHECK_LT(internal_displays, 2); 608 LOG_IF(WARNING, internal_displays == 2) 609 << "Two internal displays detected."; 610 611 bool can_mirror = false; 612 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { 613 // Try preserving external display's aspect ratio on the first attempt. 614 // If that fails, fall back to the highest matching resolution. 615 bool preserve_aspect = attempt == 0; 616 617 if (internal_displays == 1) { 618 if (one_is_internal) { 619 can_mirror = FindMirrorMode(&cached_displays_[0], 620 &cached_displays_[1], 621 is_panel_fitting_enabled_, 622 preserve_aspect); 623 } else { 624 DCHECK(two_is_internal); 625 can_mirror = FindMirrorMode(&cached_displays_[1], 626 &cached_displays_[0], 627 is_panel_fitting_enabled_, 628 preserve_aspect); 629 } 630 } else { // if (internal_displays == 0) 631 // No panel fitting for external displays, so fall back to exact match. 632 can_mirror = FindMirrorMode( 633 &cached_displays_[0], &cached_displays_[1], false, preserve_aspect); 634 if (!can_mirror && preserve_aspect) { 635 // FindMirrorMode() will try to preserve aspect ratio of what it 636 // thinks is external display, so if it didn't succeed with one, maybe 637 // it will succeed with the other. This way we will have the correct 638 // aspect ratio on at least one of them. 639 can_mirror = FindMirrorMode(&cached_displays_[1], 640 &cached_displays_[0], 641 false, 642 preserve_aspect); 643 } 644 } 645 } 646 } 647 } 648 649 bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display, 650 DisplayState* external_display, 651 bool try_panel_fitting, 652 bool preserve_aspect) { 653 const DisplayMode* internal_native_info = 654 internal_display->display->native_mode(); 655 const DisplayMode* external_native_info = 656 external_display->display->native_mode(); 657 if (!internal_native_info || !external_native_info) 658 return false; 659 660 // Check if some external display resolution can be mirrored on internal. 661 // Prefer the modes in the order they're present in DisplaySnapshot, assuming 662 // this is the order in which they look better on the monitor. 663 for (DisplayModeList::const_iterator external_it = 664 external_display->display->modes().begin(); 665 external_it != external_display->display->modes().end(); 666 ++external_it) { 667 const DisplayMode& external_info = **external_it; 668 bool is_native_aspect_ratio = 669 external_native_info->size().width() * external_info.size().height() == 670 external_native_info->size().height() * external_info.size().width(); 671 if (preserve_aspect && !is_native_aspect_ratio) 672 continue; // Allow only aspect ratio preserving modes for mirroring. 673 674 // Try finding an exact match. 675 for (DisplayModeList::const_iterator internal_it = 676 internal_display->display->modes().begin(); 677 internal_it != internal_display->display->modes().end(); 678 ++internal_it) { 679 const DisplayMode& internal_info = **internal_it; 680 if (internal_info.size().width() == external_info.size().width() && 681 internal_info.size().height() == external_info.size().height() && 682 internal_info.is_interlaced() == external_info.is_interlaced()) { 683 internal_display->mirror_mode = *internal_it; 684 external_display->mirror_mode = *external_it; 685 return true; // Mirror mode found. 686 } 687 } 688 689 // Try to create a matching internal display mode by panel fitting. 690 if (try_panel_fitting) { 691 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks 692 // ugly, so, can fit == can upscale. Also, internal panels don't support 693 // fitting interlaced modes. 694 bool can_fit = internal_native_info->size().width() >= 695 external_info.size().width() && 696 internal_native_info->size().height() >= 697 external_info.size().height() && 698 !external_info.is_interlaced(); 699 if (can_fit) { 700 native_display_delegate_->AddMode(*internal_display->display, 701 *external_it); 702 internal_display->display->add_mode(*external_it); 703 internal_display->mirror_mode = *external_it; 704 external_display->mirror_mode = *external_it; 705 return true; // Mirror mode created. 706 } 707 } 708 } 709 710 return false; 711 } 712 713 void DisplayConfigurator::ConfigureDisplays() { 714 if (!configure_display_) 715 return; 716 717 native_display_delegate_->GrabServer(); 718 UpdateCachedDisplays(); 719 const MultipleDisplayState new_state = ChooseDisplayState( 720 requested_power_state_); 721 const bool success = EnterStateOrFallBackToSoftwareMirroring( 722 new_state, requested_power_state_); 723 native_display_delegate_->UngrabServer(); 724 725 NotifyObservers(success, new_state); 726 } 727 728 void DisplayConfigurator::NotifyObservers( 729 bool success, 730 MultipleDisplayState attempted_state) { 731 if (success) { 732 FOR_EACH_OBSERVER( 733 Observer, observers_, OnDisplayModeChanged(cached_displays_)); 734 } else { 735 FOR_EACH_OBSERVER( 736 Observer, observers_, OnDisplayModeChangeFailed(attempted_state)); 737 } 738 } 739 740 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring( 741 MultipleDisplayState display_state, 742 chromeos::DisplayPowerState power_state) { 743 bool success = EnterState(display_state, power_state); 744 if (mirroring_controller_) { 745 bool enable_software_mirroring = false; 746 if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) { 747 if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED || 748 current_power_state_ != power_state) 749 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state); 750 enable_software_mirroring = success = 751 display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED; 752 } 753 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring); 754 } 755 return success; 756 } 757 758 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state, 759 chromeos::DisplayPowerState power_state) { 760 std::vector<bool> display_power; 761 int num_on_displays = 762 GetDisplayPower(cached_displays_, power_state, &display_power); 763 VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state) 764 << " power=" << DisplayPowerStateToString(power_state); 765 766 // Save the requested state so we'll try to use it next time even if we fail. 767 requested_power_state_ = power_state; 768 769 // Framebuffer dimensions. 770 gfx::Size size; 771 772 std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point()); 773 std::vector<const DisplayMode*> new_mode; 774 for (size_t i = 0; i < cached_displays_.size(); ++i) 775 new_mode.push_back(cached_displays_[i].display->current_mode()); 776 777 switch (display_state) { 778 case MULTIPLE_DISPLAY_STATE_INVALID: 779 NOTREACHED() << "Ignoring request to enter invalid state with " 780 << cached_displays_.size() << " connected display(s)"; 781 return false; 782 case MULTIPLE_DISPLAY_STATE_HEADLESS: 783 if (cached_displays_.size() != 0) { 784 LOG(WARNING) << "Ignoring request to enter headless mode with " 785 << cached_displays_.size() << " connected display(s)"; 786 return false; 787 } 788 break; 789 case MULTIPLE_DISPLAY_STATE_SINGLE: { 790 // If there are multiple displays connected, only one should be turned on. 791 if (cached_displays_.size() != 1 && num_on_displays != 1) { 792 LOG(WARNING) << "Ignoring request to enter single mode with " 793 << cached_displays_.size() << " connected displays and " 794 << num_on_displays << " turned on"; 795 return false; 796 } 797 798 for (size_t i = 0; i < cached_displays_.size(); ++i) { 799 DisplayState* state = &cached_displays_[i]; 800 new_mode[i] = display_power[i] ? state->selected_mode : NULL; 801 802 if (display_power[i] || cached_displays_.size() == 1) { 803 const DisplayMode* mode_info = state->selected_mode; 804 if (!mode_info) { 805 LOG(WARNING) << "No selected mode when configuring display: " 806 << state->display->ToString(); 807 return false; 808 } 809 if (mode_info->size() == gfx::Size(1024, 768)) { 810 VLOG(1) << "Potentially misdetecting display(1024x768):" 811 << " displays size=" << cached_displays_.size() 812 << ", num_on_displays=" << num_on_displays 813 << ", current size:" << size.width() << "x" << size.height() 814 << ", i=" << i << ", display=" << state->display->ToString() 815 << ", display_mode=" << mode_info->ToString(); 816 } 817 size = mode_info->size(); 818 } 819 } 820 break; 821 } 822 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: { 823 if (cached_displays_.size() != 2 || 824 (num_on_displays != 0 && num_on_displays != 2)) { 825 LOG(WARNING) << "Ignoring request to enter mirrored mode with " 826 << cached_displays_.size() << " connected display(s) and " 827 << num_on_displays << " turned on"; 828 return false; 829 } 830 831 const DisplayMode* mode_info = cached_displays_[0].mirror_mode; 832 if (!mode_info) { 833 LOG(WARNING) << "No mirror mode when configuring display: " 834 << cached_displays_[0].display->ToString(); 835 return false; 836 } 837 size = mode_info->size(); 838 839 for (size_t i = 0; i < cached_displays_.size(); ++i) { 840 DisplayState* state = &cached_displays_[i]; 841 new_mode[i] = display_power[i] ? state->mirror_mode : NULL; 842 } 843 break; 844 } 845 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: { 846 if (cached_displays_.size() != 2 || 847 (num_on_displays != 0 && num_on_displays != 2)) { 848 LOG(WARNING) << "Ignoring request to enter extended mode with " 849 << cached_displays_.size() << " connected display(s) and " 850 << num_on_displays << " turned on"; 851 return false; 852 } 853 854 for (size_t i = 0; i < cached_displays_.size(); ++i) { 855 DisplayState* state = &cached_displays_[i]; 856 new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0); 857 new_mode[i] = display_power[i] ? state->selected_mode : NULL; 858 859 // Retain the full screen size even if all displays are off so the 860 // same desktop configuration can be restored when the displays are 861 // turned back on. 862 const DisplayMode* mode_info = cached_displays_[i].selected_mode; 863 if (!mode_info) { 864 LOG(WARNING) << "No selected mode when configuring display: " 865 << state->display->ToString(); 866 return false; 867 } 868 869 size.set_width(std::max<int>(size.width(), mode_info->size().width())); 870 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) + 871 mode_info->size().height()); 872 } 873 break; 874 } 875 } 876 877 // Finally, apply the desired changes. 878 bool all_succeeded = true; 879 if (!cached_displays_.empty()) { 880 native_display_delegate_->CreateFrameBuffer(size); 881 for (size_t i = 0; i < cached_displays_.size(); ++i) { 882 const DisplayState& state = cached_displays_[i]; 883 bool configure_succeeded = false; 884 885 while (true) { 886 if (native_display_delegate_->Configure( 887 *state.display, new_mode[i], new_origins[i])) { 888 state.display->set_current_mode(new_mode[i]); 889 state.display->set_origin(new_origins[i]); 890 891 configure_succeeded = true; 892 break; 893 } 894 895 const DisplayMode* mode_info = new_mode[i]; 896 if (!mode_info) 897 break; 898 899 // Find the mode with the next-best resolution and see if that can 900 // be set. 901 int best_mode_pixels = 0; 902 903 int current_mode_pixels = mode_info->size().GetArea(); 904 for (DisplayModeList::const_iterator it = 905 state.display->modes().begin(); 906 it != state.display->modes().end(); 907 it++) { 908 int pixel_count = (*it)->size().GetArea(); 909 if ((pixel_count < current_mode_pixels) && 910 (pixel_count > best_mode_pixels)) { 911 new_mode[i] = *it; 912 best_mode_pixels = pixel_count; 913 } 914 } 915 916 if (best_mode_pixels == 0) 917 break; 918 } 919 920 if (!configure_succeeded) 921 all_succeeded = false; 922 923 // If we are trying to set mirror mode and one of the modesets fails, 924 // then the two monitors will be mis-matched. In this case, return 925 // false to let the observers be aware. 926 if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR && 927 display_power[i] && 928 state.display->current_mode() != state.mirror_mode) 929 all_succeeded = false; 930 } 931 } 932 933 if (all_succeeded) { 934 display_state_ = display_state; 935 current_power_state_ = power_state; 936 framebuffer_size_ = size; 937 } 938 return all_succeeded; 939 } 940 941 MultipleDisplayState DisplayConfigurator::ChooseDisplayState( 942 chromeos::DisplayPowerState power_state) const { 943 int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL); 944 switch (cached_displays_.size()) { 945 case 0: 946 return MULTIPLE_DISPLAY_STATE_HEADLESS; 947 case 1: 948 return MULTIPLE_DISPLAY_STATE_SINGLE; 949 case 2: { 950 if (num_on_displays == 1) { 951 // If only one display is currently turned on, return the "single" 952 // state so that its native mode will be used. 953 return MULTIPLE_DISPLAY_STATE_SINGLE; 954 } else { 955 if (!state_controller_) 956 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED; 957 // With either both displays on or both displays off, use one of the 958 // dual modes. 959 std::vector<int64_t> display_ids; 960 for (size_t i = 0; i < cached_displays_.size(); ++i) { 961 // If display id isn't available, switch to extended mode. 962 if (!cached_displays_[i].display->has_proper_display_id()) 963 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED; 964 display_ids.push_back(cached_displays_[i].display->display_id()); 965 } 966 return state_controller_->GetStateForDisplayIds(display_ids); 967 } 968 } 969 default: 970 NOTREACHED(); 971 } 972 return MULTIPLE_DISPLAY_STATE_INVALID; 973 } 974 975 } // namespace ui 976