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 output
->transform
= GetExtendedModeCTM(*output
, width
, height
);
942 // Finally, apply the desired changes.
943 DCHECK_EQ(cached_outputs_
.size(), updated_outputs
.size());
944 bool all_succeeded
= true;
945 if (!updated_outputs
.empty()) {
946 delegate_
->CreateFrameBuffer(width
, height
, updated_outputs
);
947 for (size_t i
= 0; i
< updated_outputs
.size(); ++i
) {
948 const OutputSnapshot
& output
= updated_outputs
[i
];
949 bool configure_succeeded
=false;
952 if (delegate_
->ConfigureCrtc(output
.crtc
, output
.current_mode
,
953 output
.output
, output
.x
, output
.y
)) {
954 configure_succeeded
= true;
958 LOG(WARNING
) << "Unable to configure CRTC " << output
.crtc
<< ":"
959 << " mode=" << output
.current_mode
960 << " output=" << output
.output
962 << " y=" << output
.y
;
964 const ModeInfo
* mode_info
= GetModeInfo(output
, output
.current_mode
);
968 // Find the mode with the next-best resolution and see if that can
970 int best_mode_pixels
= 0;
972 int current_mode_pixels
= mode_info
->width
* mode_info
->height
;
973 for (ModeInfoMap::const_iterator it
= output
.mode_infos
.begin();
974 it
!= output
.mode_infos
.end(); it
++) {
975 int pixel_count
= it
->second
.width
* it
->second
.height
;
976 if ((pixel_count
< current_mode_pixels
) &&
977 (pixel_count
> best_mode_pixels
)) {
978 updated_outputs
[i
].current_mode
= it
->first
;
979 best_mode_pixels
= pixel_count
;
983 if (best_mode_pixels
== 0)
987 if (configure_succeeded
) {
988 if (output
.touch_device_id
)
989 delegate_
->ConfigureCTM(output
.touch_device_id
, output
.transform
);
990 cached_outputs_
[i
] = updated_outputs
[i
];
992 all_succeeded
= false;
995 // If we are trying to set mirror mode and one of the modesets fails,
996 // then the two monitors will be mis-matched. In this case, return
997 // false to let the observers be aware.
998 if (output_state
== STATE_DUAL_MIRROR
&&
1000 output
.current_mode
!= output
.mirror_mode
)
1001 all_succeeded
= false;
1006 if (all_succeeded
) {
1007 output_state_
= output_state
;
1008 power_state_
= power_state
;
1010 return all_succeeded
;
1013 OutputState
OutputConfigurator::ChooseOutputState(
1014 DisplayPowerState power_state
) const {
1015 int num_on_outputs
= GetOutputPower(cached_outputs_
, power_state
, NULL
);
1016 switch (cached_outputs_
.size()) {
1018 return STATE_HEADLESS
;
1020 return STATE_SINGLE
;
1022 if (num_on_outputs
== 1) {
1023 // If only one output is currently turned on, return the "single"
1024 // state so that its native mode will be used.
1025 return STATE_SINGLE
;
1027 // With either both outputs on or both outputs off, use one of the
1029 std::vector
<int64
> display_ids
;
1030 for (size_t i
= 0; i
< cached_outputs_
.size(); ++i
) {
1031 // If display id isn't available, switch to extended mode.
1032 if (!cached_outputs_
[i
].has_display_id
)
1033 return STATE_DUAL_EXTENDED
;
1034 display_ids
.push_back(cached_outputs_
[i
].display_id
);
1036 return state_controller_
->GetStateForDisplayIds(display_ids
);
1042 return STATE_INVALID
;
1045 OutputConfigurator::CoordinateTransformation
1046 OutputConfigurator::GetMirrorModeCTM(
1047 const OutputConfigurator::OutputSnapshot
& output
) {
1048 CoordinateTransformation ctm
; // Default to identity
1049 const ModeInfo
* native_mode_info
= GetModeInfo(output
, output
.native_mode
);
1050 const ModeInfo
* mirror_mode_info
= GetModeInfo(output
, output
.mirror_mode
);
1052 if (!native_mode_info
|| !mirror_mode_info
||
1053 native_mode_info
->height
== 0 || mirror_mode_info
->height
== 0 ||
1054 native_mode_info
->width
== 0 || mirror_mode_info
->width
== 0)
1057 float native_mode_ar
= static_cast<float>(native_mode_info
->width
) /
1058 static_cast<float>(native_mode_info
->height
);
1059 float mirror_mode_ar
= static_cast<float>(mirror_mode_info
->width
) /
1060 static_cast<float>(mirror_mode_info
->height
);
1062 if (mirror_mode_ar
> native_mode_ar
) { // Letterboxing
1065 ctm
.y_scale
= mirror_mode_ar
/ native_mode_ar
;
1066 ctm
.y_offset
= (native_mode_ar
/ mirror_mode_ar
- 1.0) * 0.5;
1069 if (native_mode_ar
> mirror_mode_ar
) { // Pillarboxing
1072 ctm
.x_scale
= native_mode_ar
/ mirror_mode_ar
;
1073 ctm
.x_offset
= (mirror_mode_ar
/ native_mode_ar
- 1.0) * 0.5;
1077 return ctm
; // Same aspect ratio - return identity
1080 OutputConfigurator::CoordinateTransformation
1081 OutputConfigurator::GetExtendedModeCTM(
1082 const OutputConfigurator::OutputSnapshot
& output
,
1083 int framebuffer_width
,
1084 int framebuffer_height
) {
1085 CoordinateTransformation ctm
; // Default to identity
1086 const ModeInfo
* mode_info
= GetModeInfo(output
, output
.selected_mode
);
1090 // An example of how to calculate the CTM.
1091 // Suppose we have 2 monitors, the first one has size 1366 x 768.
1092 // The second one has size 2560 x 1600
1093 // The total size of framebuffer is 2560 x 2428
1094 // where 2428 = 768 + 60 (hidden gap) + 1600
1095 // and the sceond monitor is translated to Point (0, 828) in the
1097 // X will first map input event location to [0, 2560) x [0, 2428),
1098 // then apply CTM on it.
1099 // So to compute CTM, for monitor1, we have
1100 // x_scale = (1366 - 1) / (2560 - 1)
1101 // x_offset = 0 / (2560 - 1)
1102 // y_scale = (768 - 1) / (2428 - 1)
1103 // y_offset = 0 / (2428 -1)
1104 // For Monitor 2, we have
1105 // x_scale = (2560 - 1) / (2560 - 1)
1106 // x_offset = 0 / (2560 - 1)
1107 // y_scale = (1600 - 1) / (2428 - 1)
1108 // y_offset = 828 / (2428 -1)
1109 // See the unittest OutputConfiguratorTest.CTMForMultiScreens.
1111 static_cast<float>(mode_info
->width
- 1) / (framebuffer_width
- 1);
1112 ctm
.x_offset
= static_cast<float>(output
.x
) / (framebuffer_width
- 1);
1114 static_cast<float>(mode_info
->height
- 1) / (framebuffer_height
- 1);
1115 ctm
.y_offset
= static_cast<float>(output
.y
) / (framebuffer_height
- 1);
1119 float OutputConfigurator::GetMirroredDisplayAreaRatio(
1120 const OutputConfigurator::OutputSnapshot
& output
) {
1121 float area_ratio
= 1.0f
;
1122 const ModeInfo
* native_mode_info
= GetModeInfo(output
, output
.native_mode
);
1123 const ModeInfo
* mirror_mode_info
= GetModeInfo(output
, output
.mirror_mode
);
1125 if (!native_mode_info
|| !mirror_mode_info
||
1126 native_mode_info
->height
== 0 || mirror_mode_info
->height
== 0 ||
1127 native_mode_info
->width
== 0 || mirror_mode_info
->width
== 0)
1130 float width_ratio
= static_cast<float>(mirror_mode_info
->width
) /
1131 static_cast<float>(native_mode_info
->width
);
1132 float height_ratio
= static_cast<float>(mirror_mode_info
->height
) /
1133 static_cast<float>(native_mode_info
->height
);
1135 area_ratio
= width_ratio
* height_ratio
;
1139 } // namespace chromeos