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