| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/display/output_configurator.h" | 5 #include "chromeos/display/output_configurator.h" |
| 6 | 6 |
| 7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
| 8 #include <X11/extensions/Xrandr.h> | 8 #include <X11/extensions/Xrandr.h> |
| 9 #include <X11/extensions/XInput2.h> | 9 #include <X11/extensions/XInput2.h> |
| 10 | 10 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 for (size_t i = 0; i < outputs.size(); ++i) | 91 for (size_t i = 0; i < outputs.size(); ++i) |
| 92 has_internal_output |= outputs[i].is_internal; | 92 has_internal_output |= outputs[i].is_internal; |
| 93 | 93 |
| 94 // "Projecting" is defined as having more than 1 output connected while at | 94 // "Projecting" is defined as having more than 1 output connected while at |
| 95 // least one of them is an internal output. | 95 // least one of them is an internal output. |
| 96 return has_internal_output && (connected_output_count > 1); | 96 return has_internal_output && (connected_output_count > 1); |
| 97 } | 97 } |
| 98 | 98 |
| 99 } // namespace | 99 } // namespace |
| 100 | 100 |
| 101 OutputConfigurator::ModeInfo::ModeInfo() |
| 102 : width(0), |
| 103 height(0), |
| 104 interlaced(false) {} |
| 105 |
| 101 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() | 106 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() |
| 102 : x_scale(1.0), | 107 : x_scale(1.0), |
| 103 x_offset(0.0), | 108 x_offset(0.0), |
| 104 y_scale(1.0), | 109 y_scale(1.0), |
| 105 y_offset(0.0) {} | 110 y_offset(0.0) {} |
| 106 | 111 |
| 107 OutputConfigurator::OutputSnapshot::OutputSnapshot() | 112 OutputConfigurator::OutputSnapshot::OutputSnapshot() |
| 108 : output(None), | 113 : output(None), |
| 109 crtc(None), | 114 crtc(None), |
| 110 current_mode(None), | 115 current_mode(None), |
| 111 native_mode(None), | 116 native_mode(None), |
| 112 mirror_mode(None), | 117 mirror_mode(None), |
| 113 selected_mode(None), | 118 selected_mode(None), |
| 114 x(0), | 119 x(0), |
| 115 y(0), | 120 y(0), |
| 121 width_mm(0), |
| 122 height_mm(0), |
| 116 is_internal(false), | 123 is_internal(false), |
| 117 is_aspect_preserving_scaling(false), | 124 is_aspect_preserving_scaling(false), |
| 118 touch_device_id(0), | 125 touch_device_id(0), |
| 119 display_id(0), | 126 display_id(0), |
| 120 has_display_id(false) {} | 127 has_display_id(false) {} |
| 121 | 128 |
| 129 OutputConfigurator::OutputSnapshot::~OutputSnapshot() {} |
| 130 |
| 122 void OutputConfigurator::TestApi::SendScreenChangeEvent() { | 131 void OutputConfigurator::TestApi::SendScreenChangeEvent() { |
| 123 XRRScreenChangeNotifyEvent event = {0}; | 132 XRRScreenChangeNotifyEvent event = {0}; |
| 124 event.type = xrandr_event_base_ + RRScreenChangeNotify; | 133 event.type = xrandr_event_base_ + RRScreenChangeNotify; |
| 125 configurator_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event)); | 134 configurator_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event)); |
| 126 } | 135 } |
| 127 | 136 |
| 128 void OutputConfigurator::TestApi::SendOutputChangeEvent(RROutput output, | 137 void OutputConfigurator::TestApi::SendOutputChangeEvent(RROutput output, |
| 129 RRCrtc crtc, | 138 RRCrtc crtc, |
| 130 RRMode mode, | 139 RRMode mode, |
| 131 bool connected) { | 140 bool connected) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 143 if (configurator_->configure_timer_.get() && | 152 if (configurator_->configure_timer_.get() && |
| 144 configurator_->configure_timer_->IsRunning()) { | 153 configurator_->configure_timer_->IsRunning()) { |
| 145 configurator_->configure_timer_.reset(); | 154 configurator_->configure_timer_.reset(); |
| 146 configurator_->ConfigureOutputs(); | 155 configurator_->ConfigureOutputs(); |
| 147 return true; | 156 return true; |
| 148 } else { | 157 } else { |
| 149 return false; | 158 return false; |
| 150 } | 159 } |
| 151 } | 160 } |
| 152 | 161 |
| 162 // static |
| 163 const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo( |
| 164 const OutputSnapshot& output, |
| 165 RRMode mode) { |
| 166 std::map<RRMode, ModeInfo>::const_iterator it = output.mode_infos.find(mode); |
| 167 if (it == output.mode_infos.end()) { |
| 168 LOG(WARNING) << "Unable to find info about mode " << mode |
| 169 << " for output " << output.output; |
| 170 return NULL; |
| 171 } |
| 172 return &it->second; |
| 173 } |
| 174 |
| 153 OutputConfigurator::OutputConfigurator() | 175 OutputConfigurator::OutputConfigurator() |
| 154 : state_controller_(NULL), | 176 : state_controller_(NULL), |
| 155 mirroring_controller_(NULL), | 177 mirroring_controller_(NULL), |
| 156 configure_display_(base::chromeos::IsRunningOnChromeOS()), | 178 configure_display_(base::chromeos::IsRunningOnChromeOS()), |
| 157 xrandr_event_base_(0), | 179 xrandr_event_base_(0), |
| 158 output_state_(STATE_INVALID), | 180 output_state_(STATE_INVALID), |
| 159 power_state_(DISPLAY_POWER_ALL_ON) { | 181 power_state_(DISPLAY_POWER_ALL_ON) { |
| 160 } | 182 } |
| 161 | 183 |
| 162 OutputConfigurator::~OutputConfigurator() {} | 184 OutputConfigurator::~OutputConfigurator() {} |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 if (success) { | 420 if (success) { |
| 399 NotifyOnDisplayChanged(); | 421 NotifyOnDisplayChanged(); |
| 400 } else { | 422 } else { |
| 401 FOR_EACH_OBSERVER( | 423 FOR_EACH_OBSERVER( |
| 402 Observer, observers_, OnDisplayModeChangeFailed(new_state)); | 424 Observer, observers_, OnDisplayModeChangeFailed(new_state)); |
| 403 } | 425 } |
| 404 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); | 426 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); |
| 405 } | 427 } |
| 406 | 428 |
| 407 void OutputConfigurator::NotifyOnDisplayChanged() { | 429 void OutputConfigurator::NotifyOnDisplayChanged() { |
| 408 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); | 430 FOR_EACH_OBSERVER(Observer, observers_, |
| 431 OnDisplayModeChanged(cached_outputs_)); |
| 409 } | 432 } |
| 410 | 433 |
| 411 bool OutputConfigurator::EnterStateOrFallBackToSoftwareMirroring( | 434 bool OutputConfigurator::EnterStateOrFallBackToSoftwareMirroring( |
| 412 OutputState output_state, | 435 OutputState output_state, |
| 413 DisplayPowerState power_state, | 436 DisplayPowerState power_state, |
| 414 const std::vector<OutputSnapshot>& outputs) { | 437 const std::vector<OutputSnapshot>& outputs) { |
| 415 bool success = EnterState(output_state, power_state, outputs); | 438 bool success = EnterState(output_state, power_state, outputs); |
| 416 if (mirroring_controller_) { | 439 if (mirroring_controller_) { |
| 417 bool enable_software_mirroring = false; | 440 bool enable_software_mirroring = false; |
| 418 if (!success && output_state == STATE_DUAL_MIRROR) { | 441 if (!success && output_state == STATE_DUAL_MIRROR) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 460 return false; | 483 return false; |
| 461 } | 484 } |
| 462 | 485 |
| 463 for (size_t i = 0; i < updated_outputs.size(); ++i) { | 486 for (size_t i = 0; i < updated_outputs.size(); ++i) { |
| 464 OutputSnapshot* output = &updated_outputs[i]; | 487 OutputSnapshot* output = &updated_outputs[i]; |
| 465 output->x = 0; | 488 output->x = 0; |
| 466 output->y = 0; | 489 output->y = 0; |
| 467 output->current_mode = output_power[i] ? output->selected_mode : None; | 490 output->current_mode = output_power[i] ? output->selected_mode : None; |
| 468 | 491 |
| 469 if (output_power[i] || outputs.size() == 1) { | 492 if (output_power[i] || outputs.size() == 1) { |
| 470 if (!delegate_->GetModeDetails( | 493 const ModeInfo* mode_info = |
| 471 output->selected_mode, &width, &height, NULL)) | 494 GetModeInfo(*output, output->selected_mode); |
| 495 if (!mode_info) |
| 472 return false; | 496 return false; |
| 497 width = mode_info->width; |
| 498 height = mode_info->height; |
| 473 } | 499 } |
| 474 } | 500 } |
| 475 break; | 501 break; |
| 476 } | 502 } |
| 477 case STATE_DUAL_MIRROR: { | 503 case STATE_DUAL_MIRROR: { |
| 478 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { | 504 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { |
| 479 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | 505 LOG(WARNING) << "Ignoring request to enter mirrored mode with " |
| 480 << outputs.size() << " connected output(s) and " | 506 << outputs.size() << " connected output(s) and " |
| 481 << num_on_outputs << " turned on"; | 507 << num_on_outputs << " turned on"; |
| 482 return false; | 508 return false; |
| 483 } | 509 } |
| 484 | 510 |
| 485 if (!delegate_->GetModeDetails( | 511 if (!outputs[0].mirror_mode) |
| 486 outputs[0].mirror_mode, &width, &height, NULL)) | |
| 487 return false; | 512 return false; |
| 513 const ModeInfo* mode_info = |
| 514 GetModeInfo(outputs[0], outputs[0].mirror_mode); |
| 515 if (!mode_info) |
| 516 return false; |
| 517 width = mode_info->width; |
| 518 height = mode_info->height; |
| 488 | 519 |
| 489 for (size_t i = 0; i < outputs.size(); ++i) { | 520 for (size_t i = 0; i < outputs.size(); ++i) { |
| 490 OutputSnapshot* output = &updated_outputs[i]; | 521 OutputSnapshot* output = &updated_outputs[i]; |
| 491 output->x = 0; | 522 output->x = 0; |
| 492 output->y = 0; | 523 output->y = 0; |
| 493 output->current_mode = output_power[i] ? output->mirror_mode : None; | 524 output->current_mode = output_power[i] ? output->mirror_mode : None; |
| 494 if (output->touch_device_id) { | 525 if (output->touch_device_id) { |
| 495 // CTM needs to be calculated if aspect preserving scaling is used. | 526 // CTM needs to be calculated if aspect preserving scaling is used. |
| 496 // Otherwise, assume it is full screen, and use identity CTM. | 527 // Otherwise, assume it is full screen, and use identity CTM. |
| 497 if (output->mirror_mode != output->native_mode && | 528 if (output->mirror_mode != output->native_mode && |
| 498 output->is_aspect_preserving_scaling) { | 529 output->is_aspect_preserving_scaling) { |
| 499 output->transform = GetMirrorModeCTM(output); | 530 output->transform = GetMirrorModeCTM(*output); |
| 500 mirrored_display_area_ratio_map_[output->touch_device_id] = | 531 mirrored_display_area_ratio_map_[output->touch_device_id] = |
| 501 GetMirroredDisplayAreaRatio(output); | 532 GetMirroredDisplayAreaRatio(*output); |
| 502 } | 533 } |
| 503 } | 534 } |
| 504 } | 535 } |
| 505 break; | 536 break; |
| 506 } | 537 } |
| 507 case STATE_DUAL_EXTENDED: { | 538 case STATE_DUAL_EXTENDED: { |
| 508 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { | 539 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { |
| 509 LOG(WARNING) << "Ignoring request to enter extended mode with " | 540 LOG(WARNING) << "Ignoring request to enter extended mode with " |
| 510 << outputs.size() << " connected output(s) and " | 541 << outputs.size() << " connected output(s) and " |
| 511 << num_on_outputs << " turned on"; | 542 << num_on_outputs << " turned on"; |
| 512 return false; | 543 return false; |
| 513 } | 544 } |
| 514 | 545 |
| 515 // Pairs are [width, height] corresponding to the given output's mode. | |
| 516 std::vector<std::pair<int, int> > mode_sizes(outputs.size()); | |
| 517 | |
| 518 for (size_t i = 0; i < outputs.size(); ++i) { | 546 for (size_t i = 0; i < outputs.size(); ++i) { |
| 519 if (!delegate_->GetModeDetails(outputs[i].selected_mode, | |
| 520 &(mode_sizes[i].first), &(mode_sizes[i].second), NULL)) { | |
| 521 return false; | |
| 522 } | |
| 523 | |
| 524 OutputSnapshot* output = &updated_outputs[i]; | 547 OutputSnapshot* output = &updated_outputs[i]; |
| 525 output->x = 0; | 548 output->x = 0; |
| 526 output->y = height ? height + kVerticalGap : 0; | 549 output->y = height ? height + kVerticalGap : 0; |
| 527 output->current_mode = output_power[i] ? output->selected_mode : None; | 550 output->current_mode = output_power[i] ? output->selected_mode : None; |
| 528 | 551 |
| 529 // Retain the full screen size even if all outputs are off so the | 552 // Retain the full screen size even if all outputs are off so the |
| 530 // same desktop configuration can be restored when the outputs are | 553 // same desktop configuration can be restored when the outputs are |
| 531 // turned back on. | 554 // turned back on. |
| 532 width = std::max<int>(width, mode_sizes[i].first); | 555 const ModeInfo* mode_info = |
| 533 height += (height ? kVerticalGap : 0) + mode_sizes[i].second; | 556 GetModeInfo(outputs[i], outputs[i].selected_mode); |
| 557 if (!mode_info) |
| 558 return false; |
| 559 width = std::max<int>(width, mode_info->width); |
| 560 height += (height ? kVerticalGap : 0) + mode_info->height; |
| 534 } | 561 } |
| 535 | 562 |
| 536 for (size_t i = 0; i < outputs.size(); ++i) { | 563 for (size_t i = 0; i < outputs.size(); ++i) { |
| 537 OutputSnapshot* output = &updated_outputs[i]; | 564 OutputSnapshot* output = &updated_outputs[i]; |
| 538 if (output->touch_device_id) { | 565 if (output->touch_device_id) { |
| 566 const ModeInfo* mode_info = |
| 567 GetModeInfo(*output, output->selected_mode); |
| 568 DCHECK(mode_info); |
| 539 CoordinateTransformation* ctm = &(output->transform); | 569 CoordinateTransformation* ctm = &(output->transform); |
| 540 ctm->x_scale = static_cast<float>(mode_sizes[i].first) / width; | 570 ctm->x_scale = static_cast<float>(mode_info->width) / width; |
| 541 ctm->x_offset = static_cast<float>(output->x) / width; | 571 ctm->x_offset = static_cast<float>(output->x) / width; |
| 542 ctm->y_scale = static_cast<float>(mode_sizes[i].second) / height; | 572 ctm->y_scale = static_cast<float>(mode_info->height) / height; |
| 543 ctm->y_offset = static_cast<float>(output->y) / height; | 573 ctm->y_offset = static_cast<float>(output->y) / height; |
| 544 } | 574 } |
| 545 } | 575 } |
| 546 break; | 576 break; |
| 547 } | 577 } |
| 548 } | 578 } |
| 549 | 579 |
| 550 // Finally, apply the desired changes. | 580 // Finally, apply the desired changes. |
| 551 DCHECK_EQ(outputs.size(), updated_outputs.size()); | 581 DCHECK_EQ(outputs.size(), updated_outputs.size()); |
| 552 if (!outputs.empty()) { | 582 if (!outputs.empty()) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 602 } | 632 } |
| 603 } | 633 } |
| 604 default: | 634 default: |
| 605 NOTREACHED(); | 635 NOTREACHED(); |
| 606 } | 636 } |
| 607 return STATE_INVALID; | 637 return STATE_INVALID; |
| 608 } | 638 } |
| 609 | 639 |
| 610 OutputConfigurator::CoordinateTransformation | 640 OutputConfigurator::CoordinateTransformation |
| 611 OutputConfigurator::GetMirrorModeCTM( | 641 OutputConfigurator::GetMirrorModeCTM( |
| 612 const OutputConfigurator::OutputSnapshot* output) { | 642 const OutputConfigurator::OutputSnapshot& output) { |
| 613 CoordinateTransformation ctm; // Default to identity | 643 CoordinateTransformation ctm; // Default to identity |
| 614 int native_mode_width = 0, native_mode_height = 0; | 644 const ModeInfo* native_mode_info = GetModeInfo(output, output.native_mode); |
| 615 int mirror_mode_width = 0, mirror_mode_height = 0; | 645 const ModeInfo* mirror_mode_info = GetModeInfo(output, output.mirror_mode); |
| 616 if (!delegate_->GetModeDetails(output->native_mode, | 646 |
| 617 &native_mode_width, &native_mode_height, NULL) || | 647 if (!native_mode_info || !mirror_mode_info || |
| 618 !delegate_->GetModeDetails(output->mirror_mode, | 648 native_mode_info->height == 0 || mirror_mode_info->height == 0 || |
| 619 &mirror_mode_width, &mirror_mode_height, NULL)) | 649 native_mode_info->width == 0 || mirror_mode_info->width == 0) |
| 620 return ctm; | 650 return ctm; |
| 621 | 651 |
| 622 if (native_mode_height == 0 || mirror_mode_height == 0 || | 652 float native_mode_ar = static_cast<float>(native_mode_info->width) / |
| 623 native_mode_width == 0 || mirror_mode_width == 0) | 653 static_cast<float>(native_mode_info->height); |
| 624 return ctm; | 654 float mirror_mode_ar = static_cast<float>(mirror_mode_info->width) / |
| 625 | 655 static_cast<float>(mirror_mode_info->height); |
| 626 float native_mode_ar = static_cast<float>(native_mode_width) / | |
| 627 static_cast<float>(native_mode_height); | |
| 628 float mirror_mode_ar = static_cast<float>(mirror_mode_width) / | |
| 629 static_cast<float>(mirror_mode_height); | |
| 630 | 656 |
| 631 if (mirror_mode_ar > native_mode_ar) { // Letterboxing | 657 if (mirror_mode_ar > native_mode_ar) { // Letterboxing |
| 632 ctm.x_scale = 1.0; | 658 ctm.x_scale = 1.0; |
| 633 ctm.x_offset = 0.0; | 659 ctm.x_offset = 0.0; |
| 634 ctm.y_scale = mirror_mode_ar / native_mode_ar; | 660 ctm.y_scale = mirror_mode_ar / native_mode_ar; |
| 635 ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5; | 661 ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5; |
| 636 return ctm; | 662 return ctm; |
| 637 } | 663 } |
| 638 if (native_mode_ar > mirror_mode_ar) { // Pillarboxing | 664 if (native_mode_ar > mirror_mode_ar) { // Pillarboxing |
| 639 ctm.y_scale = 1.0; | 665 ctm.y_scale = 1.0; |
| 640 ctm.y_offset = 0.0; | 666 ctm.y_offset = 0.0; |
| 641 ctm.x_scale = native_mode_ar / mirror_mode_ar; | 667 ctm.x_scale = native_mode_ar / mirror_mode_ar; |
| 642 ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5; | 668 ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5; |
| 643 return ctm; | 669 return ctm; |
| 644 } | 670 } |
| 645 | 671 |
| 646 return ctm; // Same aspect ratio - return identity | 672 return ctm; // Same aspect ratio - return identity |
| 647 } | 673 } |
| 648 | 674 |
| 649 float OutputConfigurator::GetMirroredDisplayAreaRatio( | 675 float OutputConfigurator::GetMirroredDisplayAreaRatio( |
| 650 const OutputConfigurator::OutputSnapshot* output) { | 676 const OutputConfigurator::OutputSnapshot& output) { |
| 651 float area_ratio = 1.0f; | 677 float area_ratio = 1.0f; |
| 652 int native_mode_width = 0, native_mode_height = 0; | 678 const ModeInfo* native_mode_info = GetModeInfo(output, output.native_mode); |
| 653 int mirror_mode_width = 0, mirror_mode_height = 0; | 679 const ModeInfo* mirror_mode_info = GetModeInfo(output, output.mirror_mode); |
| 654 if (!delegate_->GetModeDetails(output->native_mode, | 680 |
| 655 &native_mode_width, &native_mode_height, NULL) || | 681 if (!native_mode_info || !mirror_mode_info || |
| 656 !delegate_->GetModeDetails(output->mirror_mode, | 682 native_mode_info->height == 0 || mirror_mode_info->height == 0 || |
| 657 &mirror_mode_width, &mirror_mode_height, NULL)) | 683 native_mode_info->width == 0 || mirror_mode_info->width == 0) |
| 658 return area_ratio; | 684 return area_ratio; |
| 659 | 685 |
| 660 if (native_mode_height == 0 || mirror_mode_height == 0 || | 686 float width_ratio = static_cast<float>(mirror_mode_info->width) / |
| 661 native_mode_width == 0 || mirror_mode_width == 0) | 687 static_cast<float>(native_mode_info->width); |
| 662 return area_ratio; | 688 float height_ratio = static_cast<float>(mirror_mode_info->height) / |
| 663 | 689 static_cast<float>(native_mode_info->height); |
| 664 float width_ratio = static_cast<float>(mirror_mode_width) / | |
| 665 static_cast<float>(native_mode_width); | |
| 666 float height_ratio = static_cast<float>(mirror_mode_height) / | |
| 667 static_cast<float>(native_mode_height); | |
| 668 | 690 |
| 669 area_ratio = width_ratio * height_ratio; | 691 area_ratio = width_ratio * height_ratio; |
| 670 return area_ratio; | 692 return area_ratio; |
| 671 } | 693 } |
| 672 | 694 |
| 673 } // namespace chromeos | 695 } // namespace chromeos |
| OLD | NEW |