1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/display/chromeos/display_configurator.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/sys_info.h"
13 #include "base/time/time.h"
14 #include "ui/display/display_switches.h"
15 #include "ui/display/types/chromeos/display_mode.h"
16 #include "ui/display/types/chromeos/display_snapshot.h"
17 #include "ui/display/types/chromeos/native_display_delegate.h"
23 typedef std::vector
<const DisplayMode
*> DisplayModeList
;
25 // The delay to perform configuration after RRNotify. See the comment
27 const int kConfigureDelayMs
= 500;
29 // Returns a string describing |state|.
30 std::string
DisplayPowerStateToString(chromeos::DisplayPowerState state
) {
32 case chromeos::DISPLAY_POWER_ALL_ON
:
34 case chromeos::DISPLAY_POWER_ALL_OFF
:
36 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
:
37 return "INTERNAL_OFF_EXTERNAL_ON";
38 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
:
39 return "INTERNAL_ON_EXTERNAL_OFF";
41 return "unknown (" + base::IntToString(state
) + ")";
45 // Returns a string describing |state|.
46 std::string
DisplayStateToString(MultipleDisplayState state
) {
48 case MULTIPLE_DISPLAY_STATE_INVALID
:
50 case MULTIPLE_DISPLAY_STATE_HEADLESS
:
52 case MULTIPLE_DISPLAY_STATE_SINGLE
:
54 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
:
56 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
:
57 return "DUAL_EXTENDED";
59 NOTREACHED() << "Unknown state " << state
;
63 // Returns the number of displays in |displays| that should be turned on, per
64 // |state|. If |display_power| is non-NULL, it is updated to contain the
65 // on/off state of each corresponding entry in |displays|.
67 const std::vector
<DisplayConfigurator::DisplayState
>& display_states
,
68 chromeos::DisplayPowerState state
,
69 std::vector
<bool>* display_power
) {
70 int num_on_displays
= 0;
72 display_power
->resize(display_states
.size());
74 for (size_t i
= 0; i
< display_states
.size(); ++i
) {
76 display_states
[i
].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
78 state
== chromeos::DISPLAY_POWER_ALL_ON
||
79 (state
== chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
&&
81 (state
== chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
&& internal
);
83 (*display_power
)[i
] = on
;
87 return num_on_displays
;
92 DisplayConfigurator::DisplayState::DisplayState()
98 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
99 if (configurator_
->configure_timer_
.get() &&
100 configurator_
->configure_timer_
->IsRunning()) {
101 configurator_
->configure_timer_
.reset();
102 configurator_
->ConfigureDisplays();
110 const DisplayMode
* DisplayConfigurator::FindDisplayModeMatchingSize(
111 const DisplaySnapshot
& display
,
112 const gfx::Size
& size
) {
113 const DisplayMode
* best_mode
= NULL
;
114 for (DisplayModeList::const_iterator it
= display
.modes().begin();
115 it
!= display
.modes().end();
117 const DisplayMode
* mode
= *it
;
119 if (mode
->size() != size
)
127 if (mode
->is_interlaced()) {
128 if (!best_mode
->is_interlaced())
131 // Reset the best rate if the non interlaced is
132 // found the first time.
133 if (best_mode
->is_interlaced()) {
138 if (mode
->refresh_rate() < best_mode
->refresh_rate())
147 DisplayConfigurator::DisplayConfigurator()
148 : state_controller_(NULL
),
149 mirroring_controller_(NULL
),
150 is_panel_fitting_enabled_(false),
151 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
152 display_state_(MULTIPLE_DISPLAY_STATE_INVALID
),
153 power_state_(chromeos::DISPLAY_POWER_ALL_ON
),
154 next_display_protection_client_id_(1) {}
156 DisplayConfigurator::~DisplayConfigurator() {
157 if (native_display_delegate_
)
158 native_display_delegate_
->RemoveObserver(this);
161 void DisplayConfigurator::SetDelegatesForTesting(
162 scoped_ptr
<NativeDisplayDelegate
> display_delegate
,
163 scoped_ptr
<TouchscreenDelegate
> touchscreen_delegate
) {
164 DCHECK(!native_display_delegate_
);
165 DCHECK(!touchscreen_delegate_
);
167 InitializeDelegates(display_delegate
.Pass(), touchscreen_delegate
.Pass());
168 configure_display_
= true;
171 void DisplayConfigurator::SetInitialDisplayPower(
172 chromeos::DisplayPowerState power_state
) {
173 DCHECK_EQ(display_state_
, MULTIPLE_DISPLAY_STATE_INVALID
);
174 power_state_
= power_state
;
177 void DisplayConfigurator::Init(bool is_panel_fitting_enabled
) {
178 is_panel_fitting_enabled_
= is_panel_fitting_enabled
;
179 if (!configure_display_
)
182 PlatformInitialize();
185 void DisplayConfigurator::InitializeDelegates(
186 scoped_ptr
<NativeDisplayDelegate
> display_delegate
,
187 scoped_ptr
<TouchscreenDelegate
> touchscreen_delegate
) {
188 if (!native_display_delegate_
&& !touchscreen_delegate_
) {
189 native_display_delegate_
= display_delegate
.Pass();
190 touchscreen_delegate_
= touchscreen_delegate
.Pass();
192 native_display_delegate_
->AddObserver(this);
196 void DisplayConfigurator::ForceInitialConfigure(
197 uint32_t background_color_argb
) {
198 if (!configure_display_
)
201 native_display_delegate_
->GrabServer();
202 native_display_delegate_
->Initialize();
204 UpdateCachedDisplays();
205 if (cached_displays_
.size() > 1 && background_color_argb
)
206 native_display_delegate_
->SetBackgroundColor(background_color_argb
);
207 const MultipleDisplayState new_state
= ChooseDisplayState(power_state_
);
209 EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state_
);
211 // Force the DPMS on chrome startup as the driver doesn't always detect
212 // that all displays are on when signing out.
213 native_display_delegate_
->ForceDPMSOn();
214 native_display_delegate_
->UngrabServer();
215 NotifyObservers(success
, new_state
);
218 bool DisplayConfigurator::IsMirroring() const {
219 return display_state_
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
||
220 (mirroring_controller_
&&
221 mirroring_controller_
->SoftwareMirroringEnabled());
224 bool DisplayConfigurator::ApplyProtections(const ContentProtections
& requests
) {
225 for (DisplayStateList::const_iterator it
= cached_displays_
.begin();
226 it
!= cached_displays_
.end();
228 uint32_t all_desired
= 0;
230 // In mirror mode, protection request of all displays need to be fulfilled.
231 // In non-mirror mode, only request of client's display needs to be
233 ContentProtections::const_iterator request_it
;
235 for (request_it
= requests
.begin();
236 request_it
!= requests
.end();
238 all_desired
|= request_it
->second
;
240 request_it
= requests
.find(it
->display
->display_id());
241 if (request_it
!= requests
.end())
242 all_desired
= request_it
->second
;
245 switch (it
->display
->type()) {
246 case DISPLAY_CONNECTION_TYPE_UNKNOWN
:
248 // DisplayPort, DVI, and HDMI all support HDCP.
249 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT
:
250 case DISPLAY_CONNECTION_TYPE_DVI
:
251 case DISPLAY_CONNECTION_TYPE_HDMI
: {
252 HDCPState new_desired_state
=
253 (all_desired
& CONTENT_PROTECTION_METHOD_HDCP
) ?
254 HDCP_STATE_DESIRED
: HDCP_STATE_UNDESIRED
;
255 if (!native_display_delegate_
->SetHDCPState(*it
->display
,
260 case DISPLAY_CONNECTION_TYPE_INTERNAL
:
261 case DISPLAY_CONNECTION_TYPE_VGA
:
262 case DISPLAY_CONNECTION_TYPE_NETWORK
:
263 // No protections for these types. Do nothing.
265 case DISPLAY_CONNECTION_TYPE_NONE
:
274 DisplayConfigurator::ContentProtectionClientId
275 DisplayConfigurator::RegisterContentProtectionClient() {
276 if (!configure_display_
)
277 return kInvalidClientId
;
279 return next_display_protection_client_id_
++;
282 void DisplayConfigurator::UnregisterContentProtectionClient(
283 ContentProtectionClientId client_id
) {
284 client_protection_requests_
.erase(client_id
);
286 ContentProtections protections
;
287 for (ProtectionRequests::const_iterator it
=
288 client_protection_requests_
.begin();
289 it
!= client_protection_requests_
.end();
291 for (ContentProtections::const_iterator it2
= it
->second
.begin();
292 it2
!= it
->second
.end();
294 protections
[it2
->first
] |= it2
->second
;
298 ApplyProtections(protections
);
301 bool DisplayConfigurator::QueryContentProtectionStatus(
302 ContentProtectionClientId client_id
,
305 uint32_t* protection_mask
) {
306 if (!configure_display_
)
309 uint32_t enabled
= 0;
310 uint32_t unfulfilled
= 0;
312 for (DisplayStateList::const_iterator it
= cached_displays_
.begin();
313 it
!= cached_displays_
.end();
315 // Query display if it is in mirror mode or client on the same display.
316 if (!IsMirroring() && it
->display
->display_id() != display_id
)
319 *link_mask
|= it
->display
->type();
320 switch (it
->display
->type()) {
321 case DISPLAY_CONNECTION_TYPE_UNKNOWN
:
323 // DisplayPort, DVI, and HDMI all support HDCP.
324 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT
:
325 case DISPLAY_CONNECTION_TYPE_DVI
:
326 case DISPLAY_CONNECTION_TYPE_HDMI
: {
328 if (!native_display_delegate_
->GetHDCPState(*it
->display
, &state
))
330 if (state
== HDCP_STATE_ENABLED
)
331 enabled
|= CONTENT_PROTECTION_METHOD_HDCP
;
333 unfulfilled
|= CONTENT_PROTECTION_METHOD_HDCP
;
336 case DISPLAY_CONNECTION_TYPE_INTERNAL
:
337 case DISPLAY_CONNECTION_TYPE_VGA
:
338 case DISPLAY_CONNECTION_TYPE_NETWORK
:
339 // No protections for these types. Do nothing.
341 case DISPLAY_CONNECTION_TYPE_NONE
:
347 // Don't reveal protections requested by other clients.
348 ProtectionRequests::iterator it
= client_protection_requests_
.find(client_id
);
349 if (it
!= client_protection_requests_
.end()) {
350 uint32_t requested_mask
= 0;
351 if (it
->second
.find(display_id
) != it
->second
.end())
352 requested_mask
= it
->second
[display_id
];
353 *protection_mask
= enabled
& ~unfulfilled
& requested_mask
;
355 *protection_mask
= 0;
360 bool DisplayConfigurator::EnableContentProtection(
361 ContentProtectionClientId client_id
,
363 uint32_t desired_method_mask
) {
364 if (!configure_display_
)
367 ContentProtections protections
;
368 for (ProtectionRequests::const_iterator it
=
369 client_protection_requests_
.begin();
370 it
!= client_protection_requests_
.end();
372 for (ContentProtections::const_iterator it2
= it
->second
.begin();
373 it2
!= it
->second
.end();
375 if (it
->first
== client_id
&& it2
->first
== display_id
)
377 protections
[it2
->first
] |= it2
->second
;
380 protections
[display_id
] |= desired_method_mask
;
382 if (!ApplyProtections(protections
))
385 if (desired_method_mask
== CONTENT_PROTECTION_METHOD_NONE
) {
386 if (client_protection_requests_
.find(client_id
) !=
387 client_protection_requests_
.end()) {
388 client_protection_requests_
[client_id
].erase(display_id
);
389 if (client_protection_requests_
[client_id
].size() == 0)
390 client_protection_requests_
.erase(client_id
);
393 client_protection_requests_
[client_id
][display_id
] = desired_method_mask
;
399 std::vector
<ui::ColorCalibrationProfile
>
400 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id
) {
401 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
402 switches::kDisableDisplayColorCalibration
)) {
403 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
404 if (cached_displays_
[i
].display
&&
405 cached_displays_
[i
].display
->display_id() == display_id
) {
406 return native_display_delegate_
->GetAvailableColorCalibrationProfiles(
407 *cached_displays_
[i
].display
);
412 return std::vector
<ui::ColorCalibrationProfile
>();
415 bool DisplayConfigurator::SetColorCalibrationProfile(
417 ui::ColorCalibrationProfile new_profile
) {
418 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
419 if (cached_displays_
[i
].display
&&
420 cached_displays_
[i
].display
->display_id() == display_id
) {
421 return native_display_delegate_
->SetColorCalibrationProfile(
422 *cached_displays_
[i
].display
, new_profile
);
429 void DisplayConfigurator::PrepareForExit() {
430 configure_display_
= false;
433 bool DisplayConfigurator::SetDisplayPower(
434 chromeos::DisplayPowerState power_state
,
436 if (!configure_display_
)
439 VLOG(1) << "SetDisplayPower: power_state="
440 << DisplayPowerStateToString(power_state
) << " flags=" << flags
441 << ", configure timer="
442 << ((configure_timer_
.get() && configure_timer_
->IsRunning()) ?
443 "Running" : "Stopped");
444 if (power_state
== power_state_
&& !(flags
& kSetDisplayPowerForceProbe
))
447 native_display_delegate_
->GrabServer();
448 UpdateCachedDisplays();
450 const MultipleDisplayState new_state
= ChooseDisplayState(power_state
);
451 bool attempted_change
= false;
452 bool success
= false;
454 bool only_if_single_internal_display
=
455 flags
& kSetDisplayPowerOnlyIfSingleInternalDisplay
;
456 bool single_internal_display
=
457 cached_displays_
.size() == 1 &&
458 cached_displays_
[0].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
459 if (single_internal_display
|| !only_if_single_internal_display
) {
460 success
= EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state
);
461 attempted_change
= true;
463 // Force the DPMS on since the driver doesn't always detect that it
464 // should turn on. This is needed when coming back from idle suspend.
465 if (success
&& power_state
!= chromeos::DISPLAY_POWER_ALL_OFF
)
466 native_display_delegate_
->ForceDPMSOn();
469 native_display_delegate_
->UngrabServer();
470 if (attempted_change
)
471 NotifyObservers(success
, new_state
);
475 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state
) {
476 if (!configure_display_
)
479 VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state
);
480 if (display_state_
== new_state
) {
481 // Cancel software mirroring if the state is moving from
482 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
483 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
484 if (mirroring_controller_
&&
485 new_state
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
)
486 mirroring_controller_
->SetSoftwareMirroring(false);
487 NotifyObservers(true, new_state
);
491 native_display_delegate_
->GrabServer();
492 UpdateCachedDisplays();
494 EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state_
);
495 native_display_delegate_
->UngrabServer();
497 NotifyObservers(success
, new_state
);
501 void DisplayConfigurator::OnConfigurationChanged() {
502 // Configure displays with |kConfigureDelayMs| delay,
503 // so that time-consuming ConfigureDisplays() won't be called multiple times.
504 if (configure_timer_
.get()) {
505 configure_timer_
->Reset();
507 configure_timer_
.reset(new base::OneShotTimer
<DisplayConfigurator
>());
508 configure_timer_
->Start(
510 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
512 &DisplayConfigurator::ConfigureDisplays
);
516 void DisplayConfigurator::AddObserver(Observer
* observer
) {
517 observers_
.AddObserver(observer
);
520 void DisplayConfigurator::RemoveObserver(Observer
* observer
) {
521 observers_
.RemoveObserver(observer
);
524 void DisplayConfigurator::SuspendDisplays() {
525 // If the display is off due to user inactivity and there's only a single
526 // internal display connected, switch to the all-on state before
527 // suspending. This shouldn't be very noticeable to the user since the
528 // backlight is off at this point, and doing this lets us resume directly
529 // into the "on" state, which greatly reduces resume times.
530 if (power_state_
== chromeos::DISPLAY_POWER_ALL_OFF
) {
531 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON
,
532 kSetDisplayPowerOnlyIfSingleInternalDisplay
);
534 // We need to make sure that the monitor configuration we just did actually
535 // completes before we return, because otherwise the X message could be
536 // racing with the HandleSuspendReadiness message.
537 native_display_delegate_
->SyncWithServer();
541 void DisplayConfigurator::ResumeDisplays() {
542 // Force probing to ensure that we pick up any changes that were made
543 // while the system was suspended.
544 SetDisplayPower(power_state_
, kSetDisplayPowerForceProbe
);
547 void DisplayConfigurator::UpdateCachedDisplays() {
548 std::vector
<DisplaySnapshot
*> snapshots
=
549 native_display_delegate_
->GetDisplays();
551 cached_displays_
.clear();
552 for (size_t i
= 0; i
< snapshots
.size(); ++i
) {
553 DisplayState display_state
;
554 display_state
.display
= snapshots
[i
];
555 cached_displays_
.push_back(display_state
);
558 touchscreen_delegate_
->AssociateTouchscreens(&cached_displays_
);
560 // Set |selected_mode| fields.
561 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
562 DisplayState
* display_state
= &cached_displays_
[i
];
563 if (display_state
->display
->has_proper_display_id()) {
565 if (state_controller_
&&
566 state_controller_
->GetResolutionForDisplayId(
567 display_state
->display
->display_id(), &size
)) {
568 display_state
->selected_mode
=
569 FindDisplayModeMatchingSize(*display_state
->display
, size
);
572 // Fall back to native mode.
573 if (!display_state
->selected_mode
)
574 display_state
->selected_mode
= display_state
->display
->native_mode();
577 // Set |mirror_mode| fields.
578 if (cached_displays_
.size() == 2) {
579 bool one_is_internal
=
580 cached_displays_
[0].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
581 bool two_is_internal
=
582 cached_displays_
[1].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
583 int internal_displays
=
584 (one_is_internal
? 1 : 0) + (two_is_internal
? 1 : 0);
585 DCHECK_LT(internal_displays
, 2);
586 LOG_IF(WARNING
, internal_displays
== 2)
587 << "Two internal displays detected.";
589 bool can_mirror
= false;
590 for (int attempt
= 0; !can_mirror
&& attempt
< 2; ++attempt
) {
591 // Try preserving external display's aspect ratio on the first attempt.
592 // If that fails, fall back to the highest matching resolution.
593 bool preserve_aspect
= attempt
== 0;
595 if (internal_displays
== 1) {
596 if (one_is_internal
) {
597 can_mirror
= FindMirrorMode(&cached_displays_
[0],
598 &cached_displays_
[1],
599 is_panel_fitting_enabled_
,
602 DCHECK(two_is_internal
);
603 can_mirror
= FindMirrorMode(&cached_displays_
[1],
604 &cached_displays_
[0],
605 is_panel_fitting_enabled_
,
608 } else { // if (internal_displays == 0)
609 // No panel fitting for external displays, so fall back to exact match.
610 can_mirror
= FindMirrorMode(
611 &cached_displays_
[0], &cached_displays_
[1], false, preserve_aspect
);
612 if (!can_mirror
&& preserve_aspect
) {
613 // FindMirrorMode() will try to preserve aspect ratio of what it
614 // thinks is external display, so if it didn't succeed with one, maybe
615 // it will succeed with the other. This way we will have the correct
616 // aspect ratio on at least one of them.
617 can_mirror
= FindMirrorMode(&cached_displays_
[1],
618 &cached_displays_
[0],
627 bool DisplayConfigurator::FindMirrorMode(DisplayState
* internal_display
,
628 DisplayState
* external_display
,
629 bool try_panel_fitting
,
630 bool preserve_aspect
) {
631 const DisplayMode
* internal_native_info
=
632 internal_display
->display
->native_mode();
633 const DisplayMode
* external_native_info
=
634 external_display
->display
->native_mode();
635 if (!internal_native_info
|| !external_native_info
)
638 // Check if some external display resolution can be mirrored on internal.
639 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
640 // this is the order in which they look better on the monitor.
641 for (DisplayModeList::const_iterator external_it
=
642 external_display
->display
->modes().begin();
643 external_it
!= external_display
->display
->modes().end();
645 const DisplayMode
& external_info
= **external_it
;
646 bool is_native_aspect_ratio
=
647 external_native_info
->size().width() * external_info
.size().height() ==
648 external_native_info
->size().height() * external_info
.size().width();
649 if (preserve_aspect
&& !is_native_aspect_ratio
)
650 continue; // Allow only aspect ratio preserving modes for mirroring.
652 // Try finding an exact match.
653 for (DisplayModeList::const_iterator internal_it
=
654 internal_display
->display
->modes().begin();
655 internal_it
!= internal_display
->display
->modes().end();
657 const DisplayMode
& internal_info
= **internal_it
;
658 if (internal_info
.size().width() == external_info
.size().width() &&
659 internal_info
.size().height() == external_info
.size().height() &&
660 internal_info
.is_interlaced() == external_info
.is_interlaced()) {
661 internal_display
->mirror_mode
= *internal_it
;
662 external_display
->mirror_mode
= *external_it
;
663 return true; // Mirror mode found.
667 // Try to create a matching internal display mode by panel fitting.
668 if (try_panel_fitting
) {
669 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
670 // ugly, so, can fit == can upscale. Also, internal panels don't support
671 // fitting interlaced modes.
672 bool can_fit
= internal_native_info
->size().width() >=
673 external_info
.size().width() &&
674 internal_native_info
->size().height() >=
675 external_info
.size().height() &&
676 !external_info
.is_interlaced();
678 native_display_delegate_
->AddMode(*internal_display
->display
,
680 internal_display
->display
->add_mode(*external_it
);
681 internal_display
->mirror_mode
= *external_it
;
682 external_display
->mirror_mode
= *external_it
;
683 return true; // Mirror mode created.
691 void DisplayConfigurator::ConfigureDisplays() {
692 configure_timer_
.reset();
694 if (!configure_display_
)
697 native_display_delegate_
->GrabServer();
698 UpdateCachedDisplays();
699 const MultipleDisplayState new_state
= ChooseDisplayState(power_state_
);
701 EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state_
);
702 native_display_delegate_
->UngrabServer();
704 NotifyObservers(success
, new_state
);
707 void DisplayConfigurator::NotifyObservers(
709 MultipleDisplayState attempted_state
) {
712 Observer
, observers_
, OnDisplayModeChanged(cached_displays_
));
715 Observer
, observers_
, OnDisplayModeChangeFailed(attempted_state
));
719 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
720 MultipleDisplayState display_state
,
721 chromeos::DisplayPowerState power_state
) {
722 bool success
= EnterState(display_state
, power_state
);
723 if (mirroring_controller_
) {
724 bool enable_software_mirroring
= false;
725 if (!success
&& display_state
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
) {
726 if (display_state_
!= MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
||
727 power_state_
!= power_state
)
728 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
, power_state
);
729 enable_software_mirroring
= success
=
730 display_state_
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
;
732 mirroring_controller_
->SetSoftwareMirroring(enable_software_mirroring
);
737 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state
,
738 chromeos::DisplayPowerState power_state
) {
739 std::vector
<bool> display_power
;
740 int num_on_displays
=
741 GetDisplayPower(cached_displays_
, power_state
, &display_power
);
742 VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state
)
743 << " power=" << DisplayPowerStateToString(power_state
);
745 // Framebuffer dimensions.
748 std::vector
<gfx::Point
> new_origins(cached_displays_
.size(), gfx::Point());
749 std::vector
<const DisplayMode
*> new_mode
;
750 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
)
751 new_mode
.push_back(cached_displays_
[i
].display
->current_mode());
753 switch (display_state
) {
754 case MULTIPLE_DISPLAY_STATE_INVALID
:
755 NOTREACHED() << "Ignoring request to enter invalid state with "
756 << cached_displays_
.size() << " connected display(s)";
758 case MULTIPLE_DISPLAY_STATE_HEADLESS
:
759 if (cached_displays_
.size() != 0) {
760 LOG(WARNING
) << "Ignoring request to enter headless mode with "
761 << cached_displays_
.size() << " connected display(s)";
765 case MULTIPLE_DISPLAY_STATE_SINGLE
: {
766 // If there are multiple displays connected, only one should be turned on.
767 if (cached_displays_
.size() != 1 && num_on_displays
!= 1) {
768 LOG(WARNING
) << "Ignoring request to enter single mode with "
769 << cached_displays_
.size() << " connected displays and "
770 << num_on_displays
<< " turned on";
774 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
775 DisplayState
* state
= &cached_displays_
[i
];
776 new_mode
[i
] = display_power
[i
] ? state
->selected_mode
: NULL
;
778 if (display_power
[i
] || cached_displays_
.size() == 1) {
779 const DisplayMode
* mode_info
= state
->selected_mode
;
782 if (mode_info
->size() == gfx::Size(1024, 768)) {
783 VLOG(1) << "Potentially misdetecting display(1024x768):"
784 << " displays size=" << cached_displays_
.size()
785 << ", num_on_displays=" << num_on_displays
786 << ", current size:" << size
.width() << "x" << size
.height()
787 << ", i=" << i
<< ", display=" << state
->display
->ToString()
788 << ", display_mode=" << mode_info
->ToString();
790 size
= mode_info
->size();
795 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
: {
796 if (cached_displays_
.size() != 2 ||
797 (num_on_displays
!= 0 && num_on_displays
!= 2)) {
798 LOG(WARNING
) << "Ignoring request to enter mirrored mode with "
799 << cached_displays_
.size() << " connected display(s) and "
800 << num_on_displays
<< " turned on";
804 const DisplayMode
* mode_info
= cached_displays_
[0].mirror_mode
;
807 size
= mode_info
->size();
809 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
810 DisplayState
* state
= &cached_displays_
[i
];
811 new_mode
[i
] = display_power
[i
] ? state
->mirror_mode
: NULL
;
812 if (state
->touch_device_id
) {
813 if (state
->mirror_mode
!= state
->display
->native_mode() &&
814 state
->display
->is_aspect_preserving_scaling()) {
815 mirrored_display_area_ratio_map_
[state
->touch_device_id
] =
816 GetMirroredDisplayAreaRatio(*state
);
822 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
: {
823 if (cached_displays_
.size() != 2 ||
824 (num_on_displays
!= 0 && num_on_displays
!= 2)) {
825 LOG(WARNING
) << "Ignoring request to enter extended mode with "
826 << cached_displays_
.size() << " connected display(s) and "
827 << num_on_displays
<< " turned on";
831 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
832 DisplayState
* state
= &cached_displays_
[i
];
833 new_origins
[i
].set_y(size
.height() ? size
.height() + kVerticalGap
: 0);
834 new_mode
[i
] = display_power
[i
] ? state
->selected_mode
: NULL
;
836 // Retain the full screen size even if all displays are off so the
837 // same desktop configuration can be restored when the displays are
839 const DisplayMode
* mode_info
= cached_displays_
[i
].selected_mode
;
843 size
.set_width(std::max
<int>(size
.width(), mode_info
->size().width()));
844 size
.set_height(size
.height() + (size
.height() ? kVerticalGap
: 0) +
845 mode_info
->size().height());
851 // Finally, apply the desired changes.
852 bool all_succeeded
= true;
853 if (!cached_displays_
.empty()) {
854 native_display_delegate_
->CreateFrameBuffer(size
);
855 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
856 const DisplayState
& state
= cached_displays_
[i
];
857 bool configure_succeeded
= false;
860 if (native_display_delegate_
->Configure(
861 *state
.display
, new_mode
[i
], new_origins
[i
])) {
862 state
.display
->set_current_mode(new_mode
[i
]);
863 state
.display
->set_origin(new_origins
[i
]);
865 configure_succeeded
= true;
869 const DisplayMode
* mode_info
= new_mode
[i
];
873 // Find the mode with the next-best resolution and see if that can
875 int best_mode_pixels
= 0;
877 int current_mode_pixels
= mode_info
->size().GetArea();
878 for (DisplayModeList::const_iterator it
=
879 state
.display
->modes().begin();
880 it
!= state
.display
->modes().end();
882 int pixel_count
= (*it
)->size().GetArea();
883 if ((pixel_count
< current_mode_pixels
) &&
884 (pixel_count
> best_mode_pixels
)) {
886 best_mode_pixels
= pixel_count
;
890 if (best_mode_pixels
== 0)
894 if (!configure_succeeded
)
895 all_succeeded
= false;
897 // If we are trying to set mirror mode and one of the modesets fails,
898 // then the two monitors will be mis-matched. In this case, return
899 // false to let the observers be aware.
900 if (display_state
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
&&
902 state
.display
->current_mode() != state
.mirror_mode
)
903 all_succeeded
= false;
908 display_state_
= display_state
;
909 power_state_
= power_state
;
910 framebuffer_size_
= size
;
912 return all_succeeded
;
915 MultipleDisplayState
DisplayConfigurator::ChooseDisplayState(
916 chromeos::DisplayPowerState power_state
) const {
917 int num_on_displays
= GetDisplayPower(cached_displays_
, power_state
, NULL
);
918 switch (cached_displays_
.size()) {
920 return MULTIPLE_DISPLAY_STATE_HEADLESS
;
922 return MULTIPLE_DISPLAY_STATE_SINGLE
;
924 if (num_on_displays
== 1) {
925 // If only one display is currently turned on, return the "single"
926 // state so that its native mode will be used.
927 return MULTIPLE_DISPLAY_STATE_SINGLE
;
929 if (!state_controller_
)
930 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
;
931 // With either both displays on or both displays off, use one of the
933 std::vector
<int64_t> display_ids
;
934 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
935 // If display id isn't available, switch to extended mode.
936 if (!cached_displays_
[i
].display
->has_proper_display_id())
937 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
;
938 display_ids
.push_back(cached_displays_
[i
].display
->display_id());
940 return state_controller_
->GetStateForDisplayIds(display_ids
);
946 return MULTIPLE_DISPLAY_STATE_INVALID
;
949 float DisplayConfigurator::GetMirroredDisplayAreaRatio(
950 const DisplayState
& display_state
) {
951 float area_ratio
= 1.0f
;
952 const DisplayMode
* native_mode_info
= display_state
.display
->native_mode();
953 const DisplayMode
* mirror_mode_info
= display_state
.mirror_mode
;
955 if (!native_mode_info
|| !mirror_mode_info
||
956 native_mode_info
->size().height() == 0 ||
957 mirror_mode_info
->size().height() == 0 ||
958 native_mode_info
->size().width() == 0 ||
959 mirror_mode_info
->size().width() == 0)
962 float width_ratio
= static_cast<float>(mirror_mode_info
->size().width()) /
963 static_cast<float>(native_mode_info
->size().width());
964 float height_ratio
= static_cast<float>(mirror_mode_info
->size().height()) /
965 static_cast<float>(native_mode_info
->size().height());
967 area_ratio
= width_ratio
* height_ratio
;