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.
5 #include "chromeos/display/output_configurator.h"
8 #include <X11/extensions/Xrandr.h>
9 #include <X11/extensions/XInput2.h>
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"
24 // The delay to perform configuration after RRNotify. See the comment
26 const int64 kConfigureDelayMs = 500;
28 // Returns a string describing |state|.
29 std::string DisplayPowerStateToString(DisplayPowerState state) {
31 case DISPLAY_POWER_ALL_ON:
33 case DISPLAY_POWER_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";
40 return "unknown (" + base::IntToString(state) + ")";
44 // Returns a string describing |state|.
45 std::string OutputStateToString(OutputState state) {
53 case STATE_DUAL_MIRROR:
55 case STATE_DUAL_EXTENDED:
56 return "DUAL_EXTENDED";
58 NOTREACHED() << "Unknown state " << state;
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]",
71 static_cast<int>(output->width_mm),
72 static_cast<int>(output->height_mm));
75 // Returns a string representation of ModeInfo.
76 std::string ModeInfoToString(const OutputConfigurator::ModeInfo* mode) {
77 return base::StringPrintf("[%dx%d %srate=%f]",
80 mode->interlaced ? "interlaced " : "",
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|.
89 const std::vector<OutputConfigurator::OutputSnapshot>& outputs,
90 DisplayPowerState state,
91 std::vector<bool>* output_power) {
92 int num_on_outputs = 0;
94 output_power->resize(outputs.size());
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);
102 (*output_power)[i] = on;
106 return num_on_outputs;
109 // Determine if there is an "internal" output and how many outputs are
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;
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);
125 OutputConfigurator::ModeInfo::ModeInfo()
131 OutputConfigurator::ModeInfo::ModeInfo(int width,
137 interlaced(interlaced),
138 refresh_rate(refresh_rate) {}
140 OutputConfigurator::CoordinateTransformation::CoordinateTransformation()
146 OutputConfigurator::OutputSnapshot::OutputSnapshot()
158 is_aspect_preserving_scaling(false),
159 type(OUTPUT_TYPE_UNKNOWN),
162 has_display_id(false),
165 OutputConfigurator::OutputSnapshot::~OutputSnapshot() {}
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));
173 void OutputConfigurator::TestApi::SendOutputChangeEvent(RROutput output,
177 XRROutputChangeNotifyEvent event = {0};
178 event.type = xrandr_event_base_ + RRNotify;
179 event.subtype = RRNotify_OutputChange;
180 event.output = output;
183 event.connection = connected ? RR_Connected : RR_Disconnected;
184 configurator_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event));
187 bool OutputConfigurator::TestApi::TriggerConfigureTimeout() {
188 if (configurator_->configure_timer_.get() &&
189 configurator_->configure_timer_->IsRunning()) {
190 configurator_->configure_timer_.reset();
191 configurator_->ConfigureOutputs();
199 const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo(
200 const OutputSnapshot& output,
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;
215 RRMode OutputConfigurator::FindOutputModeMatchingSize(
216 const OutputSnapshot& output,
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;
227 if (info.width == width && info.height == height) {
228 if (info.interlaced) {
229 if (non_interlaced_found)
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;
238 if (info.refresh_rate < best_rate)
242 best_rate = info.refresh_rate;
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) {
259 OutputConfigurator::~OutputConfigurator() {}
261 void OutputConfigurator::SetDelegateForTesting(scoped_ptr<Delegate> delegate) {
262 delegate_ = delegate.Pass();
263 configure_display_ = true;
266 void OutputConfigurator::SetInitialDisplayPower(DisplayPowerState power_state) {
267 DCHECK_EQ(output_state_, STATE_INVALID);
268 power_state_ = power_state;
271 void OutputConfigurator::Init(bool is_panel_fitting_enabled) {
272 is_panel_fitting_enabled_ = is_panel_fitting_enabled;
273 if (!configure_display_)
277 delegate_.reset(new RealOutputConfiguratorDelegate());
280 void OutputConfigurator::Start(uint32 background_color_argb) {
281 if (!configure_display_)
284 delegate_->GrabServer();
285 delegate_->InitXRandRExtension(&xrandr_event_base_);
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_);
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);
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(
309 if (request_it != requests.end())
310 all_desired = request_it->second;
312 case OUTPUT_TYPE_UNKNOWN:
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))
325 case OUTPUT_TYPE_INTERNAL:
326 case OUTPUT_TYPE_VGA:
327 case OUTPUT_TYPE_NETWORK:
328 // No protections for these types. Do nothing.
330 case OUTPUT_TYPE_NONE:
339 OutputConfigurator::OutputProtectionClientId
340 OutputConfigurator::RegisterOutputProtectionClient() {
341 if (!configure_display_)
342 return kInvalidClientId;
344 return next_output_protection_client_id_++;
347 void OutputConfigurator::UnregisterOutputProtectionClient(
348 OutputProtectionClientId client_id) {
349 client_protection_requests_.erase(client_id);
351 DisplayProtections protections;
352 for (ProtectionRequests::const_iterator it =
353 client_protection_requests_.begin();
354 it != client_protection_requests_.end();
356 for (DisplayProtections::const_iterator it2 = it->second.begin();
357 it2 != it->second.end(); ++it2) {
358 protections[it2->first] |= it2->second;
362 ApplyProtections(protections);
365 bool OutputConfigurator::QueryOutputProtectionStatus(
366 OutputProtectionClientId client_id,
369 uint32_t* protection_mask) {
370 if (!configure_display_)
373 uint32_t enabled = 0;
374 uint32_t unfulfilled = 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)
381 *link_mask |= it->type;
383 case OUTPUT_TYPE_UNKNOWN:
385 // DisplayPort, DVI, and HDMI all support HDCP.
386 case OUTPUT_TYPE_DISPLAYPORT:
387 case OUTPUT_TYPE_DVI:
388 case OUTPUT_TYPE_HDMI: {
390 if (!delegate_->GetHDCPState(this_id, &state))
392 if (state == HDCP_STATE_ENABLED)
393 enabled |= OUTPUT_PROTECTION_METHOD_HDCP;
395 unfulfilled |= OUTPUT_PROTECTION_METHOD_HDCP;
398 case OUTPUT_TYPE_INTERNAL:
399 case OUTPUT_TYPE_VGA:
400 case OUTPUT_TYPE_NETWORK:
401 // No protections for these types. Do nothing.
403 case OUTPUT_TYPE_NONE:
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;
417 *protection_mask = 0;
422 bool OutputConfigurator::EnableOutputProtection(
423 OutputProtectionClientId client_id,
425 uint32_t desired_method_mask) {
426 if (!configure_display_)
429 DisplayProtections protections;
430 for (ProtectionRequests::const_iterator it =
431 client_protection_requests_.begin();
432 it != client_protection_requests_.end();
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)
438 protections[it2->first] |= it2->second;
441 protections[display_id] |= desired_method_mask;
443 if (!ApplyProtections(protections))
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);
454 client_protection_requests_[client_id][display_id] = desired_method_mask;
460 void OutputConfigurator::Stop() {
461 configure_display_ = false;
464 bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state,
466 if (!configure_display_)
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))
477 delegate_->GrabServer();
478 UpdateCachedOutputs();
480 const OutputState new_state = ChooseOutputState(power_state);
481 bool attempted_change = false;
482 bool success = false;
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;
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();
498 delegate_->UngrabServer();
499 if (attempted_change)
500 NotifyObservers(success, new_state);
504 bool OutputConfigurator::SetDisplayMode(OutputState new_state) {
505 if (!configure_display_)
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);
518 delegate_->GrabServer();
519 UpdateCachedOutputs();
520 const bool success = EnterStateOrFallBackToSoftwareMirroring(
521 new_state, power_state_);
522 delegate_->UngrabServer();
524 NotifyObservers(success, new_state);
528 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) {
529 if (!configure_display_)
532 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
533 VLOG(1) << "Received RRScreenChangeNotify event";
534 delegate_->UpdateXRandRConfiguration(event);
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)
542 const XRRNotifyEvent* notify_event = reinterpret_cast<XRRNotifyEvent*>(event);
543 if (notify_event->subtype != RRNotify_OutputChange)
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)
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");
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";
567 found_changed_output = true;
572 if (!connected && !found_changed_output) {
573 VLOG(1) << "Ignoring event describing already-disconnected output";
577 // Connecting/disconnecting a display may generate multiple events. Defer
578 // configuring outputs to avoid grabbing X and configuring displays
580 ScheduleConfigureOutputs();
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();
596 return base::EVENT_CONTINUE;
599 void OutputConfigurator::DidProcessEvent(const base::NativeEvent& event) {
602 void OutputConfigurator::AddObserver(Observer* observer) {
603 observers_.AddObserver(observer);
606 void OutputConfigurator::RemoveObserver(Observer* observer) {
607 observers_.RemoveObserver(observer);
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);
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();
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);
633 void OutputConfigurator::ScheduleConfigureOutputs() {
634 if (configure_timer_.get()) {
635 configure_timer_->Reset();
637 configure_timer_.reset(new base::OneShotTimer<OutputConfigurator>());
638 configure_timer_->Start(
640 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
642 &OutputConfigurator::ConfigureOutputs);
646 void OutputConfigurator::UpdateCachedOutputs() {
647 cached_outputs_ = delegate_->GetOutputs();
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);
661 // Fall back to native mode.
662 if (output->selected_mode == None)
663 output->selected_mode = output->native_mode;
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.";
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;
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);
687 DCHECK(two_is_internal);
688 can_mirror = FindMirrorMode(&cached_outputs_[1], &cached_outputs_[0],
689 is_panel_fitting_enabled_, preserve_aspect);
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);
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)
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.
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.
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.
752 internal_native_info->width >= external_info.width &&
753 internal_native_info->height >= external_info.height &&
754 !external_info.interlaced;
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.
769 void OutputConfigurator::ConfigureOutputs() {
770 configure_timer_.reset();
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();
779 NotifyObservers(success, new_state);
780 delegate_->SendProjectingStateToPowerManager(IsProjecting(cached_outputs_));
783 void OutputConfigurator::NotifyObservers(bool success,
784 OutputState attempted_state) {
786 FOR_EACH_OBSERVER(Observer, observers_,
787 OnDisplayModeChanged(cached_outputs_));
789 FOR_EACH_OBSERVER(Observer, observers_,
790 OnDisplayModeChangeFailed(attempted_state));
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;
806 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
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);
820 // Framebuffer dimensions.
821 int width = 0, height = 0;
822 std::vector<OutputSnapshot> updated_outputs = cached_outputs_;
824 switch (output_state) {
826 NOTREACHED() << "Ignoring request to enter invalid state with "
827 << updated_outputs.size() << " connected output(s)";
830 if (updated_outputs.size() != 0) {
831 LOG(WARNING) << "Ignoring request to enter headless mode with "
832 << updated_outputs.size() << " connected output(s)";
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";
845 for (size_t i = 0; i < updated_outputs.size(); ++i) {
846 OutputSnapshot* output = &updated_outputs[i];
849 output->current_mode = output_power[i] ? output->selected_mode : None;
851 if (output_power[i] || updated_outputs.size() == 1) {
852 const ModeInfo* mode_info =
853 GetModeInfo(*output, output->selected_mode);
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
862 << ", output=" << OutputSnapshotToString(output)
863 << ", mode_info=" << ModeInfoToString(mode_info);
865 width = mode_info->width;
866 height = mode_info->height;
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";
880 if (!updated_outputs[0].mirror_mode)
882 const ModeInfo* mode_info =
883 GetModeInfo(updated_outputs[0], updated_outputs[0].mirror_mode);
886 width = mode_info->width;
887 height = mode_info->height;
889 for (size_t i = 0; i < updated_outputs.size(); ++i) {
890 OutputSnapshot* output = &updated_outputs[i];
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);
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";
916 for (size_t i = 0; i < updated_outputs.size(); ++i) {
917 OutputSnapshot* output = &updated_outputs[i];
919 output->y = height ? height + kVerticalGap : 0;
920 output->current_mode = output_power[i] ? output->selected_mode : None;
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
925 const ModeInfo* mode_info =
926 GetModeInfo(updated_outputs[i], updated_outputs[i].selected_mode);
929 width = std::max<int>(width, mode_info->width);
930 height += (height ? kVerticalGap : 0) + mode_info->height;
933 for (size_t i = 0; i < updated_outputs.size(); ++i) {
934 OutputSnapshot* output = &updated_outputs[i];
935 if (output->touch_device_id) {
936 const ModeInfo* mode_info =
937 GetModeInfo(*output, output->selected_mode);
939 CoordinateTransformation* ctm = &(output->transform);
940 ctm->x_scale = static_cast<float>(mode_info->width) / width;
941 ctm->x_offset = static_cast<float>(output->x) / width;
942 ctm->y_scale = static_cast<float>(mode_info->height) / height;
943 ctm->y_offset = static_cast<float>(output->y) / height;
950 // Finally, apply the desired changes.
951 DCHECK_EQ(cached_outputs_.size(), updated_outputs.size());
952 if (!updated_outputs.empty()) {
953 delegate_->CreateFrameBuffer(width, height, updated_outputs);
954 for (size_t i = 0; i < updated_outputs.size(); ++i) {
955 const OutputSnapshot& output = updated_outputs[i];
956 if (delegate_->ConfigureCrtc(output.crtc, output.current_mode,
957 output.output, output.x, output.y)) {
958 if (output.touch_device_id)
959 delegate_->ConfigureCTM(output.touch_device_id, output.transform);
960 cached_outputs_[i] = updated_outputs[i];
962 LOG(WARNING) << "Unable to configure CRTC " << output.crtc << ":"
963 << " mode=" << output.current_mode
964 << " output=" << output.output
966 << " y=" << output.y;
971 output_state_ = output_state;
972 power_state_ = power_state;
976 OutputState OutputConfigurator::ChooseOutputState(
977 DisplayPowerState power_state) const {
978 int num_on_outputs = GetOutputPower(cached_outputs_, power_state, NULL);
979 switch (cached_outputs_.size()) {
981 return STATE_HEADLESS;
985 if (num_on_outputs == 1) {
986 // If only one output is currently turned on, return the "single"
987 // state so that its native mode will be used.
990 // With either both outputs on or both outputs off, use one of the
992 std::vector<int64> display_ids;
993 for (size_t i = 0; i < cached_outputs_.size(); ++i) {
994 // If display id isn't available, switch to extended mode.
995 if (!cached_outputs_[i].has_display_id)
996 return STATE_DUAL_EXTENDED;
997 display_ids.push_back(cached_outputs_[i].display_id);
999 return state_controller_->GetStateForDisplayIds(display_ids);
1005 return STATE_INVALID;
1008 OutputConfigurator::CoordinateTransformation
1009 OutputConfigurator::GetMirrorModeCTM(
1010 const OutputConfigurator::OutputSnapshot& output) {
1011 CoordinateTransformation ctm; // Default to identity
1012 const ModeInfo* native_mode_info = GetModeInfo(output, output.native_mode);
1013 const ModeInfo* mirror_mode_info = GetModeInfo(output, output.mirror_mode);
1015 if (!native_mode_info || !mirror_mode_info ||
1016 native_mode_info->height == 0 || mirror_mode_info->height == 0 ||
1017 native_mode_info->width == 0 || mirror_mode_info->width == 0)
1020 float native_mode_ar = static_cast<float>(native_mode_info->width) /
1021 static_cast<float>(native_mode_info->height);
1022 float mirror_mode_ar = static_cast<float>(mirror_mode_info->width) /
1023 static_cast<float>(mirror_mode_info->height);
1025 if (mirror_mode_ar > native_mode_ar) { // Letterboxing
1028 ctm.y_scale = mirror_mode_ar / native_mode_ar;
1029 ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5;
1032 if (native_mode_ar > mirror_mode_ar) { // Pillarboxing
1035 ctm.x_scale = native_mode_ar / mirror_mode_ar;
1036 ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5;
1040 return ctm; // Same aspect ratio - return identity
1043 float OutputConfigurator::GetMirroredDisplayAreaRatio(
1044 const OutputConfigurator::OutputSnapshot& output) {
1045 float area_ratio = 1.0f;
1046 const ModeInfo* native_mode_info = GetModeInfo(output, output.native_mode);
1047 const ModeInfo* mirror_mode_info = GetModeInfo(output, output.mirror_mode);
1049 if (!native_mode_info || !mirror_mode_info ||
1050 native_mode_info->height == 0 || mirror_mode_info->height == 0 ||
1051 native_mode_info->width == 0 || mirror_mode_info->width == 0)
1054 float width_ratio = static_cast<float>(mirror_mode_info->width) /
1055 static_cast<float>(native_mode_info->width);
1056 float height_ratio = static_cast<float>(mirror_mode_info->height) /
1057 static_cast<float>(native_mode_info->height);
1059 area_ratio = width_ratio * height_ratio;
1063 } // namespace chromeos