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 "chromeos/display/output_configurator.h" 6 7 #include <X11/Xlib.h> 8 #include <X11/extensions/Xrandr.h> 9 #include <X11/extensions/XInput2.h> 10 11 #include "base/bind.h" 12 #include "base/logging.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/sys_info.h" 16 #include "base/time/time.h" 17 #include "chromeos/display/output_util.h" 18 #include "chromeos/display/real_output_configurator_delegate.h" 19 20 namespace chromeos { 21 22 namespace { 23 24 // The delay to perform configuration after RRNotify. See the comment 25 // in |Dispatch()|. 26 const int64 kConfigureDelayMs = 500; 27 28 // Returns a string describing |state|. 29 std::string DisplayPowerStateToString(DisplayPowerState state) { 30 switch (state) { 31 case DISPLAY_POWER_ALL_ON: 32 return "ALL_ON"; 33 case DISPLAY_POWER_ALL_OFF: 34 return "ALL_OFF"; 35 case DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON: 36 return "INTERNAL_OFF_EXTERNAL_ON"; 37 case DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF: 38 return "INTERNAL_ON_EXTERNAL_OFF"; 39 default: 40 return "unknown (" + base::IntToString(state) + ")"; 41 } 42 } 43 44 // Returns a string describing |state|. 45 std::string OutputStateToString(OutputState state) { 46 switch (state) { 47 case STATE_INVALID: 48 return "INVALID"; 49 case STATE_HEADLESS: 50 return "HEADLESS"; 51 case STATE_SINGLE: 52 return "SINGLE"; 53 case STATE_DUAL_MIRROR: 54 return "DUAL_MIRROR"; 55 case STATE_DUAL_EXTENDED: 56 return "DUAL_EXTENDED"; 57 } 58 NOTREACHED() << "Unknown state " << state; 59 return "INVALID"; 60 } 61 62 // Returns a string representation of OutputSnapshot. 63 std::string OutputSnapshotToString( 64 const OutputConfigurator::OutputSnapshot* output) { 65 return base::StringPrintf( 66 "[type=%d, output=%ld, crtc=%ld, mode=%ld, dim=%dx%d]", 67 output->type, 68 output->output, 69 output->crtc, 70 output->current_mode, 71 static_cast<int>(output->width_mm), 72 static_cast<int>(output->height_mm)); 73 } 74 75 // Returns a string representation of ModeInfo. 76 std::string ModeInfoToString(const OutputConfigurator::ModeInfo* mode) { 77 return base::StringPrintf("[%dx%d %srate=%f]", 78 mode->width, 79 mode->height, 80 mode->interlaced ? "interlaced " : "", 81 mode->refresh_rate); 82 83 } 84 85 // Returns the number of outputs in |outputs| that should be turned on, per 86 // |state|. If |output_power| is non-NULL, it is updated to contain the 87 // on/off state of each corresponding entry in |outputs|. 88 int GetOutputPower( 89 const std::vector<OutputConfigurator::OutputSnapshot>& outputs, 90 DisplayPowerState state, 91 std::vector<bool>* output_power) { 92 int num_on_outputs = 0; 93 if (output_power) 94 output_power->resize(outputs.size()); 95 96 for (size_t i = 0; i < outputs.size(); ++i) { 97 bool internal = outputs[i].is_internal; 98 bool on = state == DISPLAY_POWER_ALL_ON || 99 (state == DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON && !internal) || 100 (state == DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && internal); 101 if (output_power) 102 (*output_power)[i] = on; 103 if (on) 104 num_on_outputs++; 105 } 106 return num_on_outputs; 107 } 108 109 // Determine if there is an "internal" output and how many outputs are 110 // connected. 111 bool IsProjecting( 112 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { 113 bool has_internal_output = false; 114 int connected_output_count = outputs.size(); 115 for (size_t i = 0; i < outputs.size(); ++i) 116 has_internal_output |= outputs[i].is_internal; 117 118 // "Projecting" is defined as having more than 1 output connected while at 119 // least one of them is an internal output. 120 return has_internal_output && (connected_output_count > 1); 121 } 122 123 } // namespace 124 125 OutputConfigurator::ModeInfo::ModeInfo() 126 : width(0), 127 height(0), 128 interlaced(false), 129 refresh_rate(0.0) {} 130 131 OutputConfigurator::ModeInfo::ModeInfo(int width, 132 int height, 133 bool interlaced, 134 float refresh_rate) 135 : width(width), 136 height(height), 137 interlaced(interlaced), 138 refresh_rate(refresh_rate) {} 139 140 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() 141 : x_scale(1.0), 142 x_offset(0.0), 143 y_scale(1.0), 144 y_offset(0.0) {} 145 146 OutputConfigurator::OutputSnapshot::OutputSnapshot() 147 : output(None), 148 crtc(None), 149 current_mode(None), 150 native_mode(None), 151 mirror_mode(None), 152 selected_mode(None), 153 x(0), 154 y(0), 155 width_mm(0), 156 height_mm(0), 157 is_internal(false), 158 is_aspect_preserving_scaling(false), 159 type(OUTPUT_TYPE_UNKNOWN), 160 touch_device_id(0), 161 display_id(0), 162 has_display_id(false), 163 index(0) {} 164 165 OutputConfigurator::OutputSnapshot::~OutputSnapshot() {} 166 167 void OutputConfigurator::TestApi::SendScreenChangeEvent() { 168 XRRScreenChangeNotifyEvent event = {0}; 169 event.type = xrandr_event_base_ + RRScreenChangeNotify; 170 configurator_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event)); 171 } 172 173 void OutputConfigurator::TestApi::SendOutputChangeEvent(RROutput output, 174 RRCrtc crtc, 175 RRMode mode, 176 bool connected) { 177 XRROutputChangeNotifyEvent event = {0}; 178 event.type = xrandr_event_base_ + RRNotify; 179 event.subtype = RRNotify_OutputChange; 180 event.output = output; 181 event.crtc = crtc; 182 event.mode = mode; 183 event.connection = connected ? RR_Connected : RR_Disconnected; 184 configurator_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event)); 185 } 186 187 bool OutputConfigurator::TestApi::TriggerConfigureTimeout() { 188 if (configurator_->configure_timer_.get() && 189 configurator_->configure_timer_->IsRunning()) { 190 configurator_->configure_timer_.reset(); 191 configurator_->ConfigureOutputs(); 192 return true; 193 } else { 194 return false; 195 } 196 } 197 198 // static 199 const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo( 200 const OutputSnapshot& output, 201 RRMode mode) { 202 if (mode == None) 203 return NULL; 204 205 ModeInfoMap::const_iterator it = output.mode_infos.find(mode); 206 if (it == output.mode_infos.end()) { 207 LOG(WARNING) << "Unable to find info about mode " << mode 208 << " for output " << output.output; 209 return NULL; 210 } 211 return &it->second; 212 } 213 214 // static 215 RRMode OutputConfigurator::FindOutputModeMatchingSize( 216 const OutputSnapshot& output, 217 int width, 218 int height) { 219 RRMode found = None; 220 float best_rate = 0; 221 bool non_interlaced_found = false; 222 for (ModeInfoMap::const_iterator it = output.mode_infos.begin(); 223 it != output.mode_infos.end(); ++it) { 224 RRMode mode = it->first; 225 const ModeInfo& info = it->second; 226 227 if (info.width == width && info.height == height) { 228 if (info.interlaced) { 229 if (non_interlaced_found) 230 continue; 231 } else { 232 // Reset the best rate if the non interlaced is 233 // found the first time. 234 if (!non_interlaced_found) 235 best_rate = info.refresh_rate; 236 non_interlaced_found = true; 237 } 238 if (info.refresh_rate < best_rate) 239 continue; 240 241 found = mode; 242 best_rate = info.refresh_rate; 243 } 244 } 245 return found; 246 } 247 248 OutputConfigurator::OutputConfigurator() 249 : state_controller_(NULL), 250 mirroring_controller_(NULL), 251 is_panel_fitting_enabled_(false), 252 configure_display_(base::SysInfo::IsRunningOnChromeOS()), 253 xrandr_event_base_(0), 254 output_state_(STATE_INVALID), 255 power_state_(DISPLAY_POWER_ALL_ON), 256 next_output_protection_client_id_(1) { 257 } 258 259 OutputConfigurator::~OutputConfigurator() {} 260 261 void OutputConfigurator::SetDelegateForTesting(scoped_ptr<Delegate> delegate) { 262 delegate_ = delegate.Pass(); 263 configure_display_ = true; 264 } 265 266 void OutputConfigurator::SetInitialDisplayPower(DisplayPowerState power_state) { 267 DCHECK_EQ(output_state_, STATE_INVALID); 268 power_state_ = power_state; 269 } 270 271 void OutputConfigurator::Init(bool is_panel_fitting_enabled) { 272 is_panel_fitting_enabled_ = is_panel_fitting_enabled; 273 if (!configure_display_) 274 return; 275 276 if (!delegate_) 277 delegate_.reset(new RealOutputConfiguratorDelegate()); 278 } 279 280 void OutputConfigurator::Start(uint32 background_color_argb) { 281 if (!configure_display_) 282 return; 283 284 delegate_->GrabServer(); 285 delegate_->InitXRandRExtension(&xrandr_event_base_); 286 287 UpdateCachedOutputs(); 288 if (cached_outputs_.size() > 1 && background_color_argb) 289 delegate_->SetBackgroundColor(background_color_argb); 290 const OutputState new_state = ChooseOutputState(power_state_); 291 const bool success = EnterStateOrFallBackToSoftwareMirroring( 292 new_state, power_state_); 293 294 // Force the DPMS on chrome startup as the driver doesn't always detect 295 // that all displays are on when signing out. 296 delegate_->ForceDPMSOn(); 297 delegate_->UngrabServer(); 298 delegate_->SendProjectingStateToPowerManager(IsProjecting(cached_outputs_)); 299 NotifyObservers(success, new_state); 300 } 301 302 bool OutputConfigurator::ApplyProtections(const DisplayProtections& requests) { 303 for (std::vector<OutputSnapshot>::const_iterator it = cached_outputs_.begin(); 304 it != cached_outputs_.end(); ++it) { 305 RROutput this_id = it->output; 306 uint32_t all_desired = 0; 307 DisplayProtections::const_iterator request_it = requests.find( 308 it->display_id); 309 if (request_it != requests.end()) 310 all_desired = request_it->second; 311 switch (it->type) { 312 case OUTPUT_TYPE_UNKNOWN: 313 return false; 314 // DisplayPort, DVI, and HDMI all support HDCP. 315 case OUTPUT_TYPE_DISPLAYPORT: 316 case OUTPUT_TYPE_DVI: 317 case OUTPUT_TYPE_HDMI: { 318 HDCPState new_desired_state = 319 (all_desired & OUTPUT_PROTECTION_METHOD_HDCP) ? 320 HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED; 321 if (!delegate_->SetHDCPState(this_id, new_desired_state)) 322 return false; 323 break; 324 } 325 case OUTPUT_TYPE_INTERNAL: 326 case OUTPUT_TYPE_VGA: 327 case OUTPUT_TYPE_NETWORK: 328 // No protections for these types. Do nothing. 329 break; 330 case OUTPUT_TYPE_NONE: 331 NOTREACHED(); 332 break; 333 } 334 } 335 336 return true; 337 } 338 339 OutputConfigurator::OutputProtectionClientId 340 OutputConfigurator::RegisterOutputProtectionClient() { 341 if (!configure_display_) 342 return kInvalidClientId; 343 344 return next_output_protection_client_id_++; 345 } 346 347 void OutputConfigurator::UnregisterOutputProtectionClient( 348 OutputProtectionClientId client_id) { 349 client_protection_requests_.erase(client_id); 350 351 DisplayProtections protections; 352 for (ProtectionRequests::const_iterator it = 353 client_protection_requests_.begin(); 354 it != client_protection_requests_.end(); 355 ++it) { 356 for (DisplayProtections::const_iterator it2 = it->second.begin(); 357 it2 != it->second.end(); ++it2) { 358 protections[it2->first] |= it2->second; 359 } 360 } 361 362 ApplyProtections(protections); 363 } 364 365 bool OutputConfigurator::QueryOutputProtectionStatus( 366 OutputProtectionClientId client_id, 367 int64 display_id, 368 uint32_t* link_mask, 369 uint32_t* protection_mask) { 370 if (!configure_display_) 371 return false; 372 373 uint32_t enabled = 0; 374 uint32_t unfulfilled = 0; 375 *link_mask = 0; 376 for (std::vector<OutputSnapshot>::const_iterator it = cached_outputs_.begin(); 377 it != cached_outputs_.end(); ++it) { 378 RROutput this_id = it->output; 379 if (it->display_id != display_id) 380 continue; 381 *link_mask |= it->type; 382 switch (it->type) { 383 case OUTPUT_TYPE_UNKNOWN: 384 return false; 385 // DisplayPort, DVI, and HDMI all support HDCP. 386 case OUTPUT_TYPE_DISPLAYPORT: 387 case OUTPUT_TYPE_DVI: 388 case OUTPUT_TYPE_HDMI: { 389 HDCPState state; 390 if (!delegate_->GetHDCPState(this_id, &state)) 391 return false; 392 if (state == HDCP_STATE_ENABLED) 393 enabled |= OUTPUT_PROTECTION_METHOD_HDCP; 394 else 395 unfulfilled |= OUTPUT_PROTECTION_METHOD_HDCP; 396 break; 397 } 398 case OUTPUT_TYPE_INTERNAL: 399 case OUTPUT_TYPE_VGA: 400 case OUTPUT_TYPE_NETWORK: 401 // No protections for these types. Do nothing. 402 break; 403 case OUTPUT_TYPE_NONE: 404 NOTREACHED(); 405 break; 406 } 407 } 408 409 // Don't reveal protections requested by other clients. 410 ProtectionRequests::iterator it = client_protection_requests_.find(client_id); 411 if (it != client_protection_requests_.end()) { 412 uint32_t requested_mask = 0; 413 if (it->second.find(display_id) != it->second.end()) 414 requested_mask = it->second[display_id]; 415 *protection_mask = enabled & ~unfulfilled & requested_mask; 416 } else { 417 *protection_mask = 0; 418 } 419 return true; 420 } 421 422 bool OutputConfigurator::EnableOutputProtection( 423 OutputProtectionClientId client_id, 424 int64 display_id, 425 uint32_t desired_method_mask) { 426 if (!configure_display_) 427 return false; 428 429 DisplayProtections protections; 430 for (ProtectionRequests::const_iterator it = 431 client_protection_requests_.begin(); 432 it != client_protection_requests_.end(); 433 ++it) { 434 for (DisplayProtections::const_iterator it2 = it->second.begin(); 435 it2 != it->second.end(); ++it2) { 436 if (it->first == client_id && it2->first == display_id) 437 continue; 438 protections[it2->first] |= it2->second; 439 } 440 } 441 protections[display_id] |= desired_method_mask; 442 443 if (!ApplyProtections(protections)) 444 return false; 445 446 if (desired_method_mask == OUTPUT_PROTECTION_METHOD_NONE) { 447 if (client_protection_requests_.find(client_id) != 448 client_protection_requests_.end()) { 449 client_protection_requests_[client_id].erase(display_id); 450 if (client_protection_requests_[client_id].size() == 0) 451 client_protection_requests_.erase(client_id); 452 } 453 } else { 454 client_protection_requests_[client_id][display_id] = desired_method_mask; 455 } 456 457 return true; 458 } 459 460 void OutputConfigurator::Stop() { 461 configure_display_ = false; 462 } 463 464 bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, 465 int flags) { 466 if (!configure_display_) 467 return false; 468 469 VLOG(1) << "SetDisplayPower: power_state=" 470 << DisplayPowerStateToString(power_state) << " flags=" << flags 471 << ", configure timer=" 472 << ((configure_timer_.get() && configure_timer_->IsRunning()) ? 473 "Running" : "Stopped"); 474 if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe)) 475 return true; 476 477 delegate_->GrabServer(); 478 UpdateCachedOutputs(); 479 480 const OutputState new_state = ChooseOutputState(power_state); 481 bool attempted_change = false; 482 bool success = false; 483 484 bool only_if_single_internal_display = 485 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; 486 bool single_internal_display = 487 cached_outputs_.size() == 1 && cached_outputs_[0].is_internal; 488 if (single_internal_display || !only_if_single_internal_display) { 489 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state); 490 attempted_change = true; 491 492 // Force the DPMS on since the driver doesn't always detect that it 493 // should turn on. This is needed when coming back from idle suspend. 494 if (success && power_state != DISPLAY_POWER_ALL_OFF) 495 delegate_->ForceDPMSOn(); 496 } 497 498 delegate_->UngrabServer(); 499 if (attempted_change) 500 NotifyObservers(success, new_state); 501 return true; 502 } 503 504 bool OutputConfigurator::SetDisplayMode(OutputState new_state) { 505 if (!configure_display_) 506 return false; 507 508 VLOG(1) << "SetDisplayMode: state=" << OutputStateToString(new_state); 509 if (output_state_ == new_state) { 510 // Cancel software mirroring if the state is moving from 511 // STATE_DUAL_EXTENDED to STATE_DUAL_EXTENDED. 512 if (mirroring_controller_ && new_state == STATE_DUAL_EXTENDED) 513 mirroring_controller_->SetSoftwareMirroring(false); 514 NotifyObservers(true, new_state); 515 return true; 516 } 517 518 delegate_->GrabServer(); 519 UpdateCachedOutputs(); 520 const bool success = EnterStateOrFallBackToSoftwareMirroring( 521 new_state, power_state_); 522 delegate_->UngrabServer(); 523 524 NotifyObservers(success, new_state); 525 return success; 526 } 527 528 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) { 529 if (!configure_display_) 530 return true; 531 532 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { 533 VLOG(1) << "Received RRScreenChangeNotify event"; 534 delegate_->UpdateXRandRConfiguration(event); 535 return true; 536 } 537 538 // Bail out early for everything except RRNotify_OutputChange events 539 // about an output getting connected or disconnected. 540 if (event->type - xrandr_event_base_ != RRNotify) 541 return true; 542 const XRRNotifyEvent* notify_event = reinterpret_cast<XRRNotifyEvent*>(event); 543 if (notify_event->subtype != RRNotify_OutputChange) 544 return true; 545 const XRROutputChangeNotifyEvent* output_change_event = 546 reinterpret_cast<XRROutputChangeNotifyEvent*>(event); 547 const int action = output_change_event->connection; 548 if (action != RR_Connected && action != RR_Disconnected) 549 return true; 550 551 const bool connected = (action == RR_Connected); 552 VLOG(1) << "Received RRNotify_OutputChange event:" 553 << " output=" << output_change_event->output 554 << " crtc=" << output_change_event->crtc 555 << " mode=" << output_change_event->mode 556 << " action=" << (connected ? "connected" : "disconnected"); 557 558 bool found_changed_output = false; 559 for (std::vector<OutputSnapshot>::const_iterator it = cached_outputs_.begin(); 560 it != cached_outputs_.end(); ++it) { 561 if (it->output == output_change_event->output) { 562 if (connected && it->crtc == output_change_event->crtc && 563 it->current_mode == output_change_event->mode) { 564 VLOG(1) << "Ignoring event describing already-cached state"; 565 return true; 566 } 567 found_changed_output = true; 568 break; 569 } 570 } 571 572 if (!connected && !found_changed_output) { 573 VLOG(1) << "Ignoring event describing already-disconnected output"; 574 return true; 575 } 576 577 // Connecting/disconnecting a display may generate multiple events. Defer 578 // configuring outputs to avoid grabbing X and configuring displays 579 // multiple times. 580 ScheduleConfigureOutputs(); 581 return true; 582 } 583 584 base::EventStatus OutputConfigurator::WillProcessEvent( 585 const base::NativeEvent& event) { 586 // XI_HierarchyChanged events are special. There is no window associated with 587 // these events. So process them directly from here. 588 if (configure_display_ && event->type == GenericEvent && 589 event->xgeneric.evtype == XI_HierarchyChanged) { 590 VLOG(1) << "Received XI_HierarchyChanged event"; 591 // Defer configuring outputs to not stall event processing. 592 // This also takes care of same event being received twice. 593 ScheduleConfigureOutputs(); 594 } 595 596 return base::EVENT_CONTINUE; 597 } 598 599 void OutputConfigurator::DidProcessEvent(const base::NativeEvent& event) { 600 } 601 602 void OutputConfigurator::AddObserver(Observer* observer) { 603 observers_.AddObserver(observer); 604 } 605 606 void OutputConfigurator::RemoveObserver(Observer* observer) { 607 observers_.RemoveObserver(observer); 608 } 609 610 void OutputConfigurator::SuspendDisplays() { 611 // If the display is off due to user inactivity and there's only a single 612 // internal display connected, switch to the all-on state before 613 // suspending. This shouldn't be very noticeable to the user since the 614 // backlight is off at this point, and doing this lets us resume directly 615 // into the "on" state, which greatly reduces resume times. 616 if (power_state_ == DISPLAY_POWER_ALL_OFF) { 617 SetDisplayPower(DISPLAY_POWER_ALL_ON, 618 kSetDisplayPowerOnlyIfSingleInternalDisplay); 619 620 // We need to make sure that the monitor configuration we just did actually 621 // completes before we return, because otherwise the X message could be 622 // racing with the HandleSuspendReadiness message. 623 delegate_->SyncWithServer(); 624 } 625 } 626 627 void OutputConfigurator::ResumeDisplays() { 628 // Force probing to ensure that we pick up any changes that were made 629 // while the system was suspended. 630 SetDisplayPower(power_state_, kSetDisplayPowerForceProbe); 631 } 632 633 void OutputConfigurator::ScheduleConfigureOutputs() { 634 if (configure_timer_.get()) { 635 configure_timer_->Reset(); 636 } else { 637 configure_timer_.reset(new base::OneShotTimer<OutputConfigurator>()); 638 configure_timer_->Start( 639 FROM_HERE, 640 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), 641 this, 642 &OutputConfigurator::ConfigureOutputs); 643 } 644 } 645 646 void OutputConfigurator::UpdateCachedOutputs() { 647 cached_outputs_ = delegate_->GetOutputs(); 648 649 // Set |selected_mode| fields. 650 for (size_t i = 0; i < cached_outputs_.size(); ++i) { 651 OutputSnapshot* output = &cached_outputs_[i]; 652 if (output->has_display_id) { 653 int width = 0, height = 0; 654 if (state_controller_ && 655 state_controller_->GetResolutionForDisplayId( 656 output->display_id, &width, &height)) { 657 output->selected_mode = 658 FindOutputModeMatchingSize(*output, width, height); 659 } 660 } 661 // Fall back to native mode. 662 if (output->selected_mode == None) 663 output->selected_mode = output->native_mode; 664 } 665 666 // Set |mirror_mode| fields. 667 if (cached_outputs_.size() == 2) { 668 bool one_is_internal = cached_outputs_[0].is_internal; 669 bool two_is_internal = cached_outputs_[1].is_internal; 670 int internal_outputs = (one_is_internal ? 1 : 0) + 671 (two_is_internal ? 1 : 0); 672 DCHECK_LT(internal_outputs, 2); 673 LOG_IF(WARNING, internal_outputs == 2) 674 << "Two internal outputs detected."; 675 676 bool can_mirror = false; 677 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { 678 // Try preserving external output's aspect ratio on the first attempt. 679 // If that fails, fall back to the highest matching resolution. 680 bool preserve_aspect = attempt == 0; 681 682 if (internal_outputs == 1) { 683 if (one_is_internal) { 684 can_mirror = FindMirrorMode(&cached_outputs_[0], &cached_outputs_[1], 685 is_panel_fitting_enabled_, preserve_aspect); 686 } else { 687 DCHECK(two_is_internal); 688 can_mirror = FindMirrorMode(&cached_outputs_[1], &cached_outputs_[0], 689 is_panel_fitting_enabled_, preserve_aspect); 690 } 691 } else { // if (internal_outputs == 0) 692 // No panel fitting for external outputs, so fall back to exact match. 693 can_mirror = FindMirrorMode(&cached_outputs_[0], &cached_outputs_[1], 694 false, preserve_aspect); 695 if (!can_mirror && preserve_aspect) { 696 // FindMirrorMode() will try to preserve aspect ratio of what it 697 // thinks is external display, so if it didn't succeed with one, maybe 698 // it will succeed with the other. This way we will have the correct 699 // aspect ratio on at least one of them. 700 can_mirror = FindMirrorMode(&cached_outputs_[1], &cached_outputs_[0], 701 false, preserve_aspect); 702 } 703 } 704 } 705 } 706 } 707 708 bool OutputConfigurator::FindMirrorMode(OutputSnapshot* internal_output, 709 OutputSnapshot* external_output, 710 bool try_panel_fitting, 711 bool preserve_aspect) { 712 const ModeInfo* internal_native_info = 713 GetModeInfo(*internal_output, internal_output->native_mode); 714 const ModeInfo* external_native_info = 715 GetModeInfo(*external_output, external_output->native_mode); 716 if (!internal_native_info || !external_native_info) 717 return false; 718 719 // Check if some external output resolution can be mirrored on internal. 720 // Prefer the modes in the order that X sorts them, assuming this is the order 721 // in which they look better on the monitor. 722 for (ModeInfoMap::const_iterator external_it = 723 external_output->mode_infos.begin(); 724 external_it != external_output->mode_infos.end(); ++external_it) { 725 const ModeInfo& external_info = external_it->second; 726 bool is_native_aspect_ratio = 727 external_native_info->width * external_info.height == 728 external_native_info->height * external_info.width; 729 if (preserve_aspect && !is_native_aspect_ratio) 730 continue; // Allow only aspect ratio preserving modes for mirroring. 731 732 // Try finding an exact match. 733 for (ModeInfoMap::const_iterator internal_it = 734 internal_output->mode_infos.begin(); 735 internal_it != internal_output->mode_infos.end(); ++internal_it) { 736 const ModeInfo& internal_info = internal_it->second; 737 if (internal_info.width == external_info.width && 738 internal_info.height == external_info.height && 739 internal_info.interlaced == external_info.interlaced) { 740 internal_output->mirror_mode = internal_it->first; 741 external_output->mirror_mode = external_it->first; 742 return true; // Mirror mode found. 743 } 744 } 745 746 // Try to create a matching internal output mode by panel fitting. 747 if (try_panel_fitting) { 748 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks 749 // ugly, so, can fit == can upscale. Also, internal panels don't support 750 // fitting interlaced modes. 751 bool can_fit = 752 internal_native_info->width >= external_info.width && 753 internal_native_info->height >= external_info.height && 754 !external_info.interlaced; 755 if (can_fit) { 756 RRMode mode = external_it->first; 757 delegate_->AddOutputMode(internal_output->output, mode); 758 internal_output->mode_infos.insert(std::make_pair(mode, external_info)); 759 internal_output->mirror_mode = mode; 760 external_output->mirror_mode = mode; 761 return true; // Mirror mode created. 762 } 763 } 764 } 765 766 return false; 767 } 768 769 void OutputConfigurator::ConfigureOutputs() { 770 configure_timer_.reset(); 771 772 delegate_->GrabServer(); 773 UpdateCachedOutputs(); 774 const OutputState new_state = ChooseOutputState(power_state_); 775 const bool success = EnterStateOrFallBackToSoftwareMirroring( 776 new_state, power_state_); 777 delegate_->UngrabServer(); 778 779 NotifyObservers(success, new_state); 780 delegate_->SendProjectingStateToPowerManager(IsProjecting(cached_outputs_)); 781 } 782 783 void OutputConfigurator::NotifyObservers(bool success, 784 OutputState attempted_state) { 785 if (success) { 786 FOR_EACH_OBSERVER(Observer, observers_, 787 OnDisplayModeChanged(cached_outputs_)); 788 } else { 789 FOR_EACH_OBSERVER(Observer, observers_, 790 OnDisplayModeChangeFailed(attempted_state)); 791 } 792 } 793 794 bool OutputConfigurator::EnterStateOrFallBackToSoftwareMirroring( 795 OutputState output_state, 796 DisplayPowerState power_state) { 797 bool success = EnterState(output_state, power_state); 798 if (mirroring_controller_) { 799 bool enable_software_mirroring = false; 800 if (!success && output_state == STATE_DUAL_MIRROR) { 801 if (output_state_ != STATE_DUAL_EXTENDED || power_state_ != power_state) 802 EnterState(STATE_DUAL_EXTENDED, power_state); 803 enable_software_mirroring = success = 804 output_state_ == STATE_DUAL_EXTENDED; 805 } 806 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring); 807 } 808 return success; 809 } 810 811 bool OutputConfigurator::EnterState( 812 OutputState output_state, 813 DisplayPowerState power_state) { 814 std::vector<bool> output_power; 815 int num_on_outputs = GetOutputPower( 816 cached_outputs_, power_state, &output_power); 817 VLOG(1) << "EnterState: output=" << OutputStateToString(output_state) 818 << " power=" << DisplayPowerStateToString(power_state); 819 820 // Framebuffer dimensions. 821 int width = 0, height = 0; 822 std::vector<OutputSnapshot> updated_outputs = cached_outputs_; 823 824 switch (output_state) { 825 case STATE_INVALID: 826 NOTREACHED() << "Ignoring request to enter invalid state with " 827 << updated_outputs.size() << " connected output(s)"; 828 return false; 829 case STATE_HEADLESS: 830 if (updated_outputs.size() != 0) { 831 LOG(WARNING) << "Ignoring request to enter headless mode with " 832 << updated_outputs.size() << " connected output(s)"; 833 return false; 834 } 835 break; 836 case STATE_SINGLE: { 837 // If there are multiple outputs connected, only one should be turned on. 838 if (updated_outputs.size() != 1 && num_on_outputs != 1) { 839 LOG(WARNING) << "Ignoring request to enter single mode with " 840 << updated_outputs.size() << " connected outputs and " 841 << num_on_outputs << " turned on"; 842 return false; 843 } 844 845 for (size_t i = 0; i < updated_outputs.size(); ++i) { 846 OutputSnapshot* output = &updated_outputs[i]; 847 output->x = 0; 848 output->y = 0; 849 output->current_mode = output_power[i] ? output->selected_mode : None; 850 851 if (output_power[i] || updated_outputs.size() == 1) { 852 const ModeInfo* mode_info = 853 GetModeInfo(*output, output->selected_mode); 854 if (!mode_info) 855 return false; 856 if (mode_info->width == 1024 && mode_info->height == 768) { 857 VLOG(1) << "Potentially misdetecting display(1024x768):" 858 << " outputs size=" << updated_outputs.size() 859 << ", num_on_outputs=" << num_on_outputs 860 << ", current size:" << width << "x" << height 861 << ", i=" << i 862 << ", output=" << OutputSnapshotToString(output) 863 << ", mode_info=" << ModeInfoToString(mode_info); 864 } 865 width = mode_info->width; 866 height = mode_info->height; 867 } 868 } 869 break; 870 } 871 case STATE_DUAL_MIRROR: { 872 if (updated_outputs.size() != 2 || 873 (num_on_outputs != 0 && num_on_outputs != 2)) { 874 LOG(WARNING) << "Ignoring request to enter mirrored mode with " 875 << updated_outputs.size() << " connected output(s) and " 876 << num_on_outputs << " turned on"; 877 return false; 878 } 879 880 if (!updated_outputs[0].mirror_mode) 881 return false; 882 const ModeInfo* mode_info = 883 GetModeInfo(updated_outputs[0], updated_outputs[0].mirror_mode); 884 if (!mode_info) 885 return false; 886 width = mode_info->width; 887 height = mode_info->height; 888 889 for (size_t i = 0; i < updated_outputs.size(); ++i) { 890 OutputSnapshot* output = &updated_outputs[i]; 891 output->x = 0; 892 output->y = 0; 893 output->current_mode = output_power[i] ? output->mirror_mode : None; 894 if (output->touch_device_id) { 895 // CTM needs to be calculated if aspect preserving scaling is used. 896 // Otherwise, assume it is full screen, and use identity CTM. 897 if (output->mirror_mode != output->native_mode && 898 output->is_aspect_preserving_scaling) { 899 output->transform = GetMirrorModeCTM(*output); 900 mirrored_display_area_ratio_map_[output->touch_device_id] = 901 GetMirroredDisplayAreaRatio(*output); 902 } 903 } 904 } 905 break; 906 } 907 case STATE_DUAL_EXTENDED: { 908 if (updated_outputs.size() != 2 || 909 (num_on_outputs != 0 && num_on_outputs != 2)) { 910 LOG(WARNING) << "Ignoring request to enter extended mode with " 911 << updated_outputs.size() << " connected output(s) and " 912 << num_on_outputs << " turned on"; 913 return false; 914 } 915 916 for (size_t i = 0; i < updated_outputs.size(); ++i) { 917 OutputSnapshot* output = &updated_outputs[i]; 918 output->x = 0; 919 output->y = height ? height + kVerticalGap : 0; 920 output->current_mode = output_power[i] ? output->selected_mode : None; 921 922 // Retain the full screen size even if all outputs are off so the 923 // same desktop configuration can be restored when the outputs are 924 // turned back on. 925 const ModeInfo* mode_info = 926 GetModeInfo(updated_outputs[i], updated_outputs[i].selected_mode); 927 if (!mode_info) 928 return false; 929 width = std::max<int>(width, mode_info->width); 930 height += (height ? kVerticalGap : 0) + mode_info->height; 931 } 932 933 for (size_t i = 0; i < updated_outputs.size(); ++i) { 934 OutputSnapshot* output = &updated_outputs[i]; 935 if (output->touch_device_id) 936 output->transform = GetExtendedModeCTM(*output, width, height); 937 } 938 break; 939 } 940 } 941 942 // Finally, apply the desired changes. 943 DCHECK_EQ(cached_outputs_.size(), updated_outputs.size()); 944 if (!updated_outputs.empty()) { 945 delegate_->CreateFrameBuffer(width, height, updated_outputs); 946 for (size_t i = 0; i < updated_outputs.size(); ++i) { 947 const OutputSnapshot& output = updated_outputs[i]; 948 if (delegate_->ConfigureCrtc(output.crtc, output.current_mode, 949 output.output, output.x, output.y)) { 950 if (output.touch_device_id) 951 delegate_->ConfigureCTM(output.touch_device_id, output.transform); 952 cached_outputs_[i] = updated_outputs[i]; 953 } else { 954 LOG(WARNING) << "Unable to configure CRTC " << output.crtc << ":" 955 << " mode=" << output.current_mode 956 << " output=" << output.output 957 << " x=" << output.x 958 << " y=" << output.y; 959 } 960 } 961 } 962 963 output_state_ = output_state; 964 power_state_ = power_state; 965 return true; 966 } 967 968 OutputState OutputConfigurator::ChooseOutputState( 969 DisplayPowerState power_state) const { 970 int num_on_outputs = GetOutputPower(cached_outputs_, power_state, NULL); 971 switch (cached_outputs_.size()) { 972 case 0: 973 return STATE_HEADLESS; 974 case 1: 975 return STATE_SINGLE; 976 case 2: { 977 if (num_on_outputs == 1) { 978 // If only one output is currently turned on, return the "single" 979 // state so that its native mode will be used. 980 return STATE_SINGLE; 981 } else { 982 // With either both outputs on or both outputs off, use one of the 983 // dual modes. 984 std::vector<int64> display_ids; 985 for (size_t i = 0; i < cached_outputs_.size(); ++i) { 986 // If display id isn't available, switch to extended mode. 987 if (!cached_outputs_[i].has_display_id) 988 return STATE_DUAL_EXTENDED; 989 display_ids.push_back(cached_outputs_[i].display_id); 990 } 991 return state_controller_->GetStateForDisplayIds(display_ids); 992 } 993 } 994 default: 995 NOTREACHED(); 996 } 997 return STATE_INVALID; 998 } 999 1000 OutputConfigurator::CoordinateTransformation 1001 OutputConfigurator::GetMirrorModeCTM( 1002 const OutputConfigurator::OutputSnapshot& output) { 1003 CoordinateTransformation ctm; // Default to identity 1004 const ModeInfo* native_mode_info = GetModeInfo(output, output.native_mode); 1005 const ModeInfo* mirror_mode_info = GetModeInfo(output, output.mirror_mode); 1006 1007 if (!native_mode_info || !mirror_mode_info || 1008 native_mode_info->height == 0 || mirror_mode_info->height == 0 || 1009 native_mode_info->width == 0 || mirror_mode_info->width == 0) 1010 return ctm; 1011 1012 float native_mode_ar = static_cast<float>(native_mode_info->width) / 1013 static_cast<float>(native_mode_info->height); 1014 float mirror_mode_ar = static_cast<float>(mirror_mode_info->width) / 1015 static_cast<float>(mirror_mode_info->height); 1016 1017 if (mirror_mode_ar > native_mode_ar) { // Letterboxing 1018 ctm.x_scale = 1.0; 1019 ctm.x_offset = 0.0; 1020 ctm.y_scale = mirror_mode_ar / native_mode_ar; 1021 ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5; 1022 return ctm; 1023 } 1024 if (native_mode_ar > mirror_mode_ar) { // Pillarboxing 1025 ctm.y_scale = 1.0; 1026 ctm.y_offset = 0.0; 1027 ctm.x_scale = native_mode_ar / mirror_mode_ar; 1028 ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5; 1029 return ctm; 1030 } 1031 1032 return ctm; // Same aspect ratio - return identity 1033 } 1034 1035 OutputConfigurator::CoordinateTransformation 1036 OutputConfigurator::GetExtendedModeCTM( 1037 const OutputConfigurator::OutputSnapshot& output, 1038 int framebuffer_width, 1039 int framebuffer_height) { 1040 CoordinateTransformation ctm; // Default to identity 1041 const ModeInfo* mode_info = GetModeInfo(output, output.selected_mode); 1042 DCHECK(mode_info); 1043 if (!mode_info) 1044 return ctm; 1045 // An example of how to calculate the CTM. 1046 // Suppose we have 2 monitors, the first one has size 1366 x 768. 1047 // The second one has size 2560 x 1600 1048 // The total size of framebuffer is 2560 x 2428 1049 // where 2428 = 768 + 60 (hidden gap) + 1600 1050 // and the sceond monitor is translated to Point (0, 828) in the 1051 // framebuffer. 1052 // X will first map input event location to [0, 2560) x [0, 2428), 1053 // then apply CTM on it. 1054 // So to compute CTM, for monitor1, we have 1055 // x_scale = (1366 - 1) / (2560 - 1) 1056 // x_offset = 0 / (2560 - 1) 1057 // y_scale = (768 - 1) / (2428 - 1) 1058 // y_offset = 0 / (2428 -1) 1059 // For Monitor 2, we have 1060 // x_scale = (2560 - 1) / (2560 - 1) 1061 // x_offset = 0 / (2560 - 1) 1062 // y_scale = (1600 - 1) / (2428 - 1) 1063 // y_offset = 828 / (2428 -1) 1064 // See the unittest OutputConfiguratorTest.CTMForMultiScreens. 1065 ctm.x_scale = 1066 static_cast<float>(mode_info->width - 1) / (framebuffer_width - 1); 1067 ctm.x_offset = static_cast<float>(output.x) / (framebuffer_width - 1); 1068 ctm.y_scale = 1069 static_cast<float>(mode_info->height - 1) / (framebuffer_height - 1); 1070 ctm.y_offset = static_cast<float>(output.y) / (framebuffer_height - 1); 1071 return ctm; 1072 } 1073 1074 float OutputConfigurator::GetMirroredDisplayAreaRatio( 1075 const OutputConfigurator::OutputSnapshot& output) { 1076 float area_ratio = 1.0f; 1077 const ModeInfo* native_mode_info = GetModeInfo(output, output.native_mode); 1078 const ModeInfo* mirror_mode_info = GetModeInfo(output, output.mirror_mode); 1079 1080 if (!native_mode_info || !mirror_mode_info || 1081 native_mode_info->height == 0 || mirror_mode_info->height == 0 || 1082 native_mode_info->width == 0 || mirror_mode_info->width == 0) 1083 return area_ratio; 1084 1085 float width_ratio = static_cast<float>(mirror_mode_info->width) / 1086 static_cast<float>(native_mode_info->width); 1087 float height_ratio = static_cast<float>(mirror_mode_info->height) / 1088 static_cast<float>(native_mode_info->height); 1089 1090 area_ratio = width_ratio * height_ratio; 1091 return area_ratio; 1092 } 1093 1094 } // namespace chromeos 1095