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 for
26 // |configure_timer_|.
27 const int kConfigureDelayMs
= 500;
29 // The delay spent before reading the display configuration after coming out of
30 // suspend. While coming out of suspend the display state may be updating. This
31 // is used to wait until the hardware had a chance to update the display state
32 // such that we read an up to date state.
33 const int kResumeDelayMs
= 500;
35 // Returns a string describing |state|.
36 std::string
DisplayPowerStateToString(chromeos::DisplayPowerState state
) {
38 case chromeos::DISPLAY_POWER_ALL_ON
:
40 case chromeos::DISPLAY_POWER_ALL_OFF
:
42 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
:
43 return "INTERNAL_OFF_EXTERNAL_ON";
44 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
:
45 return "INTERNAL_ON_EXTERNAL_OFF";
47 return "unknown (" + base::IntToString(state
) + ")";
51 // Returns a string describing |state|.
52 std::string
DisplayStateToString(MultipleDisplayState state
) {
54 case MULTIPLE_DISPLAY_STATE_INVALID
:
56 case MULTIPLE_DISPLAY_STATE_HEADLESS
:
58 case MULTIPLE_DISPLAY_STATE_SINGLE
:
60 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
:
62 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
:
63 return "DUAL_EXTENDED";
65 NOTREACHED() << "Unknown state " << state
;
69 // Returns the number of displays in |displays| that should be turned on, per
70 // |state|. If |display_power| is non-NULL, it is updated to contain the
71 // on/off state of each corresponding entry in |displays|.
73 const std::vector
<DisplayConfigurator::DisplayState
>& display_states
,
74 chromeos::DisplayPowerState state
,
75 std::vector
<bool>* display_power
) {
76 int num_on_displays
= 0;
78 display_power
->resize(display_states
.size());
80 for (size_t i
= 0; i
< display_states
.size(); ++i
) {
82 display_states
[i
].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
84 state
== chromeos::DISPLAY_POWER_ALL_ON
||
85 (state
== chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON
&&
87 (state
== chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF
&& internal
);
89 (*display_power
)[i
] = on
;
93 return num_on_displays
;
99 const int DisplayConfigurator::kSetDisplayPowerNoFlags
= 0;
100 const int DisplayConfigurator::kSetDisplayPowerForceProbe
= 1 << 0;
102 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay
= 1 << 1;
104 DisplayConfigurator::DisplayState::DisplayState()
110 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
111 if (configurator_
->configure_timer_
.IsRunning()) {
112 configurator_
->configure_timer_
.user_task().Run();
113 configurator_
->configure_timer_
.Stop();
121 const DisplayMode
* DisplayConfigurator::FindDisplayModeMatchingSize(
122 const DisplaySnapshot
& display
,
123 const gfx::Size
& size
) {
124 const DisplayMode
* best_mode
= NULL
;
125 for (DisplayModeList::const_iterator it
= display
.modes().begin();
126 it
!= display
.modes().end();
128 const DisplayMode
* mode
= *it
;
130 if (mode
->size() != size
)
138 if (mode
->is_interlaced()) {
139 if (!best_mode
->is_interlaced())
142 // Reset the best rate if the non interlaced is
143 // found the first time.
144 if (best_mode
->is_interlaced()) {
149 if (mode
->refresh_rate() < best_mode
->refresh_rate())
158 DisplayConfigurator::DisplayConfigurator()
159 : state_controller_(NULL
),
160 mirroring_controller_(NULL
),
161 is_panel_fitting_enabled_(false),
162 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
163 display_state_(MULTIPLE_DISPLAY_STATE_INVALID
),
164 power_state_(chromeos::DISPLAY_POWER_ALL_ON
),
165 next_display_protection_client_id_(1) {}
167 DisplayConfigurator::~DisplayConfigurator() {
168 if (native_display_delegate_
)
169 native_display_delegate_
->RemoveObserver(this);
172 void DisplayConfigurator::SetDelegatesForTesting(
173 scoped_ptr
<NativeDisplayDelegate
> display_delegate
,
174 scoped_ptr
<TouchscreenDelegate
> touchscreen_delegate
) {
175 DCHECK(!native_display_delegate_
);
176 DCHECK(!touchscreen_delegate_
);
178 native_display_delegate_
= display_delegate
.Pass();
179 touchscreen_delegate_
= touchscreen_delegate
.Pass();
180 configure_display_
= true;
183 void DisplayConfigurator::SetInitialDisplayPower(
184 chromeos::DisplayPowerState power_state
) {
185 DCHECK_EQ(display_state_
, MULTIPLE_DISPLAY_STATE_INVALID
);
186 power_state_
= power_state
;
189 void DisplayConfigurator::Init(bool is_panel_fitting_enabled
) {
190 is_panel_fitting_enabled_
= is_panel_fitting_enabled
;
191 if (!configure_display_
)
194 // If the delegates are already initialized don't update them (For example,
195 // tests set their own delegates).
196 if (!native_display_delegate_
) {
197 native_display_delegate_
= CreatePlatformNativeDisplayDelegate();
198 native_display_delegate_
->AddObserver(this);
201 if (!touchscreen_delegate_
)
202 touchscreen_delegate_
= CreatePlatformTouchscreenDelegate();
205 void DisplayConfigurator::ForceInitialConfigure(
206 uint32_t background_color_argb
) {
207 if (!configure_display_
)
210 native_display_delegate_
->GrabServer();
211 native_display_delegate_
->Initialize();
213 UpdateCachedDisplays();
214 if (cached_displays_
.size() > 1 && background_color_argb
)
215 native_display_delegate_
->SetBackgroundColor(background_color_argb
);
216 const MultipleDisplayState new_state
= ChooseDisplayState(power_state_
);
218 EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state_
);
220 // Force the DPMS on chrome startup as the driver doesn't always detect
221 // that all displays are on when signing out.
222 native_display_delegate_
->ForceDPMSOn();
223 native_display_delegate_
->UngrabServer();
224 NotifyObservers(success
, new_state
);
227 bool DisplayConfigurator::IsMirroring() const {
228 return display_state_
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
||
229 (mirroring_controller_
&&
230 mirroring_controller_
->SoftwareMirroringEnabled());
233 bool DisplayConfigurator::ApplyProtections(const ContentProtections
& requests
) {
234 for (DisplayStateList::const_iterator it
= cached_displays_
.begin();
235 it
!= cached_displays_
.end();
237 uint32_t all_desired
= 0;
239 // In mirror mode, protection request of all displays need to be fulfilled.
240 // In non-mirror mode, only request of client's display needs to be
242 ContentProtections::const_iterator request_it
;
244 for (request_it
= requests
.begin();
245 request_it
!= requests
.end();
247 all_desired
|= request_it
->second
;
249 request_it
= requests
.find(it
->display
->display_id());
250 if (request_it
!= requests
.end())
251 all_desired
= request_it
->second
;
254 switch (it
->display
->type()) {
255 case DISPLAY_CONNECTION_TYPE_UNKNOWN
:
257 // DisplayPort, DVI, and HDMI all support HDCP.
258 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT
:
259 case DISPLAY_CONNECTION_TYPE_DVI
:
260 case DISPLAY_CONNECTION_TYPE_HDMI
: {
261 HDCPState new_desired_state
=
262 (all_desired
& CONTENT_PROTECTION_METHOD_HDCP
) ?
263 HDCP_STATE_DESIRED
: HDCP_STATE_UNDESIRED
;
264 if (!native_display_delegate_
->SetHDCPState(*it
->display
,
269 case DISPLAY_CONNECTION_TYPE_INTERNAL
:
270 case DISPLAY_CONNECTION_TYPE_VGA
:
271 case DISPLAY_CONNECTION_TYPE_NETWORK
:
272 // No protections for these types. Do nothing.
274 case DISPLAY_CONNECTION_TYPE_NONE
:
283 DisplayConfigurator::ContentProtectionClientId
284 DisplayConfigurator::RegisterContentProtectionClient() {
285 if (!configure_display_
)
286 return kInvalidClientId
;
288 return next_display_protection_client_id_
++;
291 void DisplayConfigurator::UnregisterContentProtectionClient(
292 ContentProtectionClientId client_id
) {
293 client_protection_requests_
.erase(client_id
);
295 ContentProtections protections
;
296 for (ProtectionRequests::const_iterator it
=
297 client_protection_requests_
.begin();
298 it
!= client_protection_requests_
.end();
300 for (ContentProtections::const_iterator it2
= it
->second
.begin();
301 it2
!= it
->second
.end();
303 protections
[it2
->first
] |= it2
->second
;
307 ApplyProtections(protections
);
310 bool DisplayConfigurator::QueryContentProtectionStatus(
311 ContentProtectionClientId client_id
,
314 uint32_t* protection_mask
) {
315 if (!configure_display_
)
318 uint32_t enabled
= 0;
319 uint32_t unfulfilled
= 0;
321 for (DisplayStateList::const_iterator it
= cached_displays_
.begin();
322 it
!= cached_displays_
.end();
324 // Query display if it is in mirror mode or client on the same display.
325 if (!IsMirroring() && it
->display
->display_id() != display_id
)
328 *link_mask
|= it
->display
->type();
329 switch (it
->display
->type()) {
330 case DISPLAY_CONNECTION_TYPE_UNKNOWN
:
332 // DisplayPort, DVI, and HDMI all support HDCP.
333 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT
:
334 case DISPLAY_CONNECTION_TYPE_DVI
:
335 case DISPLAY_CONNECTION_TYPE_HDMI
: {
337 if (!native_display_delegate_
->GetHDCPState(*it
->display
, &state
))
339 if (state
== HDCP_STATE_ENABLED
)
340 enabled
|= CONTENT_PROTECTION_METHOD_HDCP
;
342 unfulfilled
|= CONTENT_PROTECTION_METHOD_HDCP
;
345 case DISPLAY_CONNECTION_TYPE_INTERNAL
:
346 case DISPLAY_CONNECTION_TYPE_VGA
:
347 case DISPLAY_CONNECTION_TYPE_NETWORK
:
348 // No protections for these types. Do nothing.
350 case DISPLAY_CONNECTION_TYPE_NONE
:
356 // Don't reveal protections requested by other clients.
357 ProtectionRequests::iterator it
= client_protection_requests_
.find(client_id
);
358 if (it
!= client_protection_requests_
.end()) {
359 uint32_t requested_mask
= 0;
360 if (it
->second
.find(display_id
) != it
->second
.end())
361 requested_mask
= it
->second
[display_id
];
362 *protection_mask
= enabled
& ~unfulfilled
& requested_mask
;
364 *protection_mask
= 0;
369 bool DisplayConfigurator::EnableContentProtection(
370 ContentProtectionClientId client_id
,
372 uint32_t desired_method_mask
) {
373 if (!configure_display_
)
376 ContentProtections protections
;
377 for (ProtectionRequests::const_iterator it
=
378 client_protection_requests_
.begin();
379 it
!= client_protection_requests_
.end();
381 for (ContentProtections::const_iterator it2
= it
->second
.begin();
382 it2
!= it
->second
.end();
384 if (it
->first
== client_id
&& it2
->first
== display_id
)
386 protections
[it2
->first
] |= it2
->second
;
389 protections
[display_id
] |= desired_method_mask
;
391 if (!ApplyProtections(protections
))
394 if (desired_method_mask
== CONTENT_PROTECTION_METHOD_NONE
) {
395 if (client_protection_requests_
.find(client_id
) !=
396 client_protection_requests_
.end()) {
397 client_protection_requests_
[client_id
].erase(display_id
);
398 if (client_protection_requests_
[client_id
].size() == 0)
399 client_protection_requests_
.erase(client_id
);
402 client_protection_requests_
[client_id
][display_id
] = desired_method_mask
;
408 std::vector
<ui::ColorCalibrationProfile
>
409 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id
) {
410 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
411 switches::kDisableDisplayColorCalibration
)) {
412 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
413 if (cached_displays_
[i
].display
&&
414 cached_displays_
[i
].display
->display_id() == display_id
) {
415 return native_display_delegate_
->GetAvailableColorCalibrationProfiles(
416 *cached_displays_
[i
].display
);
421 return std::vector
<ui::ColorCalibrationProfile
>();
424 bool DisplayConfigurator::SetColorCalibrationProfile(
426 ui::ColorCalibrationProfile new_profile
) {
427 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
428 if (cached_displays_
[i
].display
&&
429 cached_displays_
[i
].display
->display_id() == display_id
) {
430 return native_display_delegate_
->SetColorCalibrationProfile(
431 *cached_displays_
[i
].display
, new_profile
);
438 void DisplayConfigurator::PrepareForExit() {
439 configure_display_
= false;
442 bool DisplayConfigurator::SetDisplayPower(
443 chromeos::DisplayPowerState power_state
,
445 if (!configure_display_
)
448 VLOG(1) << "SetDisplayPower: power_state="
449 << DisplayPowerStateToString(power_state
) << " flags=" << flags
450 << ", configure timer="
451 << (configure_timer_
.IsRunning() ? "Running" : "Stopped");
452 if (power_state
== power_state_
&& !(flags
& kSetDisplayPowerForceProbe
))
455 native_display_delegate_
->GrabServer();
456 UpdateCachedDisplays();
458 const MultipleDisplayState new_state
= ChooseDisplayState(power_state
);
459 bool attempted_change
= false;
460 bool success
= false;
462 bool only_if_single_internal_display
=
463 flags
& kSetDisplayPowerOnlyIfSingleInternalDisplay
;
464 bool single_internal_display
=
465 cached_displays_
.size() == 1 &&
466 cached_displays_
[0].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
467 if (single_internal_display
|| !only_if_single_internal_display
) {
468 success
= EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state
);
469 attempted_change
= true;
471 // Force the DPMS on since the driver doesn't always detect that it
472 // should turn on. This is needed when coming back from idle suspend.
473 if (success
&& power_state
!= chromeos::DISPLAY_POWER_ALL_OFF
)
474 native_display_delegate_
->ForceDPMSOn();
477 native_display_delegate_
->UngrabServer();
478 if (attempted_change
)
479 NotifyObservers(success
, new_state
);
483 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state
) {
484 if (!configure_display_
)
487 VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state
);
488 if (display_state_
== new_state
) {
489 // Cancel software mirroring if the state is moving from
490 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
491 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
492 if (mirroring_controller_
&&
493 new_state
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
)
494 mirroring_controller_
->SetSoftwareMirroring(false);
495 NotifyObservers(true, new_state
);
499 native_display_delegate_
->GrabServer();
500 UpdateCachedDisplays();
502 EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state_
);
503 native_display_delegate_
->UngrabServer();
505 NotifyObservers(success
, new_state
);
509 void DisplayConfigurator::OnConfigurationChanged() {
510 // Configure displays with |kConfigureDelayMs| delay,
511 // so that time-consuming ConfigureDisplays() won't be called multiple times.
512 if (configure_timer_
.IsRunning()) {
513 // Note: when the timer is running it is possible that a different task
514 // (SetDisplayPower()) is scheduled. In these cases, prefer the already
515 // scheduled task to ConfigureDisplays() since ConfigureDisplays() performs
516 // only basic configuration while SetDisplayPower() will perform additional
518 configure_timer_
.Reset();
520 configure_timer_
.Start(
522 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
524 &DisplayConfigurator::ConfigureDisplays
);
528 void DisplayConfigurator::AddObserver(Observer
* observer
) {
529 observers_
.AddObserver(observer
);
532 void DisplayConfigurator::RemoveObserver(Observer
* observer
) {
533 observers_
.RemoveObserver(observer
);
536 void DisplayConfigurator::SuspendDisplays() {
537 // If the display is off due to user inactivity and there's only a single
538 // internal display connected, switch to the all-on state before
539 // suspending. This shouldn't be very noticeable to the user since the
540 // backlight is off at this point, and doing this lets us resume directly
541 // into the "on" state, which greatly reduces resume times.
542 if (power_state_
== chromeos::DISPLAY_POWER_ALL_OFF
) {
543 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON
,
544 kSetDisplayPowerOnlyIfSingleInternalDisplay
);
546 // We need to make sure that the monitor configuration we just did actually
547 // completes before we return, because otherwise the X message could be
548 // racing with the HandleSuspendReadiness message.
549 native_display_delegate_
->SyncWithServer();
553 void DisplayConfigurator::ResumeDisplays() {
554 // Force probing to ensure that we pick up any changes that were made
555 // while the system was suspended.
556 configure_timer_
.Start(
558 base::TimeDelta::FromMilliseconds(kResumeDelayMs
),
559 base::Bind(base::IgnoreResult(&DisplayConfigurator::SetDisplayPower
),
560 base::Unretained(this),
562 kSetDisplayPowerForceProbe
));
565 void DisplayConfigurator::UpdateCachedDisplays() {
566 std::vector
<DisplaySnapshot
*> snapshots
=
567 native_display_delegate_
->GetDisplays();
569 cached_displays_
.clear();
570 for (size_t i
= 0; i
< snapshots
.size(); ++i
) {
571 DisplayState display_state
;
572 display_state
.display
= snapshots
[i
];
573 cached_displays_
.push_back(display_state
);
576 touchscreen_delegate_
->AssociateTouchscreens(&cached_displays_
);
578 // Set |selected_mode| fields.
579 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
580 DisplayState
* display_state
= &cached_displays_
[i
];
581 if (display_state
->display
->has_proper_display_id()) {
583 if (state_controller_
&&
584 state_controller_
->GetResolutionForDisplayId(
585 display_state
->display
->display_id(), &size
)) {
586 display_state
->selected_mode
=
587 FindDisplayModeMatchingSize(*display_state
->display
, size
);
590 // Fall back to native mode.
591 if (!display_state
->selected_mode
)
592 display_state
->selected_mode
= display_state
->display
->native_mode();
595 // Set |mirror_mode| fields.
596 if (cached_displays_
.size() == 2) {
597 bool one_is_internal
=
598 cached_displays_
[0].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
599 bool two_is_internal
=
600 cached_displays_
[1].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
601 int internal_displays
=
602 (one_is_internal
? 1 : 0) + (two_is_internal
? 1 : 0);
603 DCHECK_LT(internal_displays
, 2);
604 LOG_IF(WARNING
, internal_displays
== 2)
605 << "Two internal displays detected.";
607 bool can_mirror
= false;
608 for (int attempt
= 0; !can_mirror
&& attempt
< 2; ++attempt
) {
609 // Try preserving external display's aspect ratio on the first attempt.
610 // If that fails, fall back to the highest matching resolution.
611 bool preserve_aspect
= attempt
== 0;
613 if (internal_displays
== 1) {
614 if (one_is_internal
) {
615 can_mirror
= FindMirrorMode(&cached_displays_
[0],
616 &cached_displays_
[1],
617 is_panel_fitting_enabled_
,
620 DCHECK(two_is_internal
);
621 can_mirror
= FindMirrorMode(&cached_displays_
[1],
622 &cached_displays_
[0],
623 is_panel_fitting_enabled_
,
626 } else { // if (internal_displays == 0)
627 // No panel fitting for external displays, so fall back to exact match.
628 can_mirror
= FindMirrorMode(
629 &cached_displays_
[0], &cached_displays_
[1], false, preserve_aspect
);
630 if (!can_mirror
&& preserve_aspect
) {
631 // FindMirrorMode() will try to preserve aspect ratio of what it
632 // thinks is external display, so if it didn't succeed with one, maybe
633 // it will succeed with the other. This way we will have the correct
634 // aspect ratio on at least one of them.
635 can_mirror
= FindMirrorMode(&cached_displays_
[1],
636 &cached_displays_
[0],
645 bool DisplayConfigurator::FindMirrorMode(DisplayState
* internal_display
,
646 DisplayState
* external_display
,
647 bool try_panel_fitting
,
648 bool preserve_aspect
) {
649 const DisplayMode
* internal_native_info
=
650 internal_display
->display
->native_mode();
651 const DisplayMode
* external_native_info
=
652 external_display
->display
->native_mode();
653 if (!internal_native_info
|| !external_native_info
)
656 // Check if some external display resolution can be mirrored on internal.
657 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
658 // this is the order in which they look better on the monitor.
659 for (DisplayModeList::const_iterator external_it
=
660 external_display
->display
->modes().begin();
661 external_it
!= external_display
->display
->modes().end();
663 const DisplayMode
& external_info
= **external_it
;
664 bool is_native_aspect_ratio
=
665 external_native_info
->size().width() * external_info
.size().height() ==
666 external_native_info
->size().height() * external_info
.size().width();
667 if (preserve_aspect
&& !is_native_aspect_ratio
)
668 continue; // Allow only aspect ratio preserving modes for mirroring.
670 // Try finding an exact match.
671 for (DisplayModeList::const_iterator internal_it
=
672 internal_display
->display
->modes().begin();
673 internal_it
!= internal_display
->display
->modes().end();
675 const DisplayMode
& internal_info
= **internal_it
;
676 if (internal_info
.size().width() == external_info
.size().width() &&
677 internal_info
.size().height() == external_info
.size().height() &&
678 internal_info
.is_interlaced() == external_info
.is_interlaced()) {
679 internal_display
->mirror_mode
= *internal_it
;
680 external_display
->mirror_mode
= *external_it
;
681 return true; // Mirror mode found.
685 // Try to create a matching internal display mode by panel fitting.
686 if (try_panel_fitting
) {
687 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
688 // ugly, so, can fit == can upscale. Also, internal panels don't support
689 // fitting interlaced modes.
690 bool can_fit
= internal_native_info
->size().width() >=
691 external_info
.size().width() &&
692 internal_native_info
->size().height() >=
693 external_info
.size().height() &&
694 !external_info
.is_interlaced();
696 native_display_delegate_
->AddMode(*internal_display
->display
,
698 internal_display
->display
->add_mode(*external_it
);
699 internal_display
->mirror_mode
= *external_it
;
700 external_display
->mirror_mode
= *external_it
;
701 return true; // Mirror mode created.
709 void DisplayConfigurator::ConfigureDisplays() {
710 if (!configure_display_
)
713 native_display_delegate_
->GrabServer();
714 UpdateCachedDisplays();
715 const MultipleDisplayState new_state
= ChooseDisplayState(power_state_
);
717 EnterStateOrFallBackToSoftwareMirroring(new_state
, power_state_
);
718 native_display_delegate_
->UngrabServer();
720 NotifyObservers(success
, new_state
);
723 void DisplayConfigurator::NotifyObservers(
725 MultipleDisplayState attempted_state
) {
728 Observer
, observers_
, OnDisplayModeChanged(cached_displays_
));
731 Observer
, observers_
, OnDisplayModeChangeFailed(attempted_state
));
735 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
736 MultipleDisplayState display_state
,
737 chromeos::DisplayPowerState power_state
) {
738 bool success
= EnterState(display_state
, power_state
);
739 if (mirroring_controller_
) {
740 bool enable_software_mirroring
= false;
741 if (!success
&& display_state
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
) {
742 if (display_state_
!= MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
||
743 power_state_
!= power_state
)
744 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
, power_state
);
745 enable_software_mirroring
= success
=
746 display_state_
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
;
748 mirroring_controller_
->SetSoftwareMirroring(enable_software_mirroring
);
753 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state
,
754 chromeos::DisplayPowerState power_state
) {
755 std::vector
<bool> display_power
;
756 int num_on_displays
=
757 GetDisplayPower(cached_displays_
, power_state
, &display_power
);
758 VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state
)
759 << " power=" << DisplayPowerStateToString(power_state
);
761 // Framebuffer dimensions.
764 std::vector
<gfx::Point
> new_origins(cached_displays_
.size(), gfx::Point());
765 std::vector
<const DisplayMode
*> new_mode
;
766 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
)
767 new_mode
.push_back(cached_displays_
[i
].display
->current_mode());
769 switch (display_state
) {
770 case MULTIPLE_DISPLAY_STATE_INVALID
:
771 NOTREACHED() << "Ignoring request to enter invalid state with "
772 << cached_displays_
.size() << " connected display(s)";
774 case MULTIPLE_DISPLAY_STATE_HEADLESS
:
775 if (cached_displays_
.size() != 0) {
776 LOG(WARNING
) << "Ignoring request to enter headless mode with "
777 << cached_displays_
.size() << " connected display(s)";
781 case MULTIPLE_DISPLAY_STATE_SINGLE
: {
782 // If there are multiple displays connected, only one should be turned on.
783 if (cached_displays_
.size() != 1 && num_on_displays
!= 1) {
784 LOG(WARNING
) << "Ignoring request to enter single mode with "
785 << cached_displays_
.size() << " connected displays and "
786 << num_on_displays
<< " turned on";
790 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
791 DisplayState
* state
= &cached_displays_
[i
];
792 new_mode
[i
] = display_power
[i
] ? state
->selected_mode
: NULL
;
794 if (display_power
[i
] || cached_displays_
.size() == 1) {
795 const DisplayMode
* mode_info
= state
->selected_mode
;
797 LOG(WARNING
) << "No selected mode when configuring display: "
798 << state
->display
->ToString();
801 if (mode_info
->size() == gfx::Size(1024, 768)) {
802 VLOG(1) << "Potentially misdetecting display(1024x768):"
803 << " displays size=" << cached_displays_
.size()
804 << ", num_on_displays=" << num_on_displays
805 << ", current size:" << size
.width() << "x" << size
.height()
806 << ", i=" << i
<< ", display=" << state
->display
->ToString()
807 << ", display_mode=" << mode_info
->ToString();
809 size
= mode_info
->size();
814 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
: {
815 if (cached_displays_
.size() != 2 ||
816 (num_on_displays
!= 0 && num_on_displays
!= 2)) {
817 LOG(WARNING
) << "Ignoring request to enter mirrored mode with "
818 << cached_displays_
.size() << " connected display(s) and "
819 << num_on_displays
<< " turned on";
823 const DisplayMode
* mode_info
= cached_displays_
[0].mirror_mode
;
825 LOG(WARNING
) << "No mirror mode when configuring display: "
826 << cached_displays_
[0].display
->ToString();
829 size
= mode_info
->size();
831 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
832 DisplayState
* state
= &cached_displays_
[i
];
833 new_mode
[i
] = display_power
[i
] ? state
->mirror_mode
: NULL
;
837 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
: {
838 if (cached_displays_
.size() != 2 ||
839 (num_on_displays
!= 0 && num_on_displays
!= 2)) {
840 LOG(WARNING
) << "Ignoring request to enter extended mode with "
841 << cached_displays_
.size() << " connected display(s) and "
842 << num_on_displays
<< " turned on";
846 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
847 DisplayState
* state
= &cached_displays_
[i
];
848 new_origins
[i
].set_y(size
.height() ? size
.height() + kVerticalGap
: 0);
849 new_mode
[i
] = display_power
[i
] ? state
->selected_mode
: NULL
;
851 // Retain the full screen size even if all displays are off so the
852 // same desktop configuration can be restored when the displays are
854 const DisplayMode
* mode_info
= cached_displays_
[i
].selected_mode
;
856 LOG(WARNING
) << "No selected mode when configuring display: "
857 << state
->display
->ToString();
861 size
.set_width(std::max
<int>(size
.width(), mode_info
->size().width()));
862 size
.set_height(size
.height() + (size
.height() ? kVerticalGap
: 0) +
863 mode_info
->size().height());
869 // Finally, apply the desired changes.
870 bool all_succeeded
= true;
871 if (!cached_displays_
.empty()) {
872 native_display_delegate_
->CreateFrameBuffer(size
);
873 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
874 const DisplayState
& state
= cached_displays_
[i
];
875 bool configure_succeeded
= false;
878 if (native_display_delegate_
->Configure(
879 *state
.display
, new_mode
[i
], new_origins
[i
])) {
880 state
.display
->set_current_mode(new_mode
[i
]);
881 state
.display
->set_origin(new_origins
[i
]);
883 configure_succeeded
= true;
887 const DisplayMode
* mode_info
= new_mode
[i
];
891 // Find the mode with the next-best resolution and see if that can
893 int best_mode_pixels
= 0;
895 int current_mode_pixels
= mode_info
->size().GetArea();
896 for (DisplayModeList::const_iterator it
=
897 state
.display
->modes().begin();
898 it
!= state
.display
->modes().end();
900 int pixel_count
= (*it
)->size().GetArea();
901 if ((pixel_count
< current_mode_pixels
) &&
902 (pixel_count
> best_mode_pixels
)) {
904 best_mode_pixels
= pixel_count
;
908 if (best_mode_pixels
== 0)
912 if (!configure_succeeded
)
913 all_succeeded
= false;
915 // If we are trying to set mirror mode and one of the modesets fails,
916 // then the two monitors will be mis-matched. In this case, return
917 // false to let the observers be aware.
918 if (display_state
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
&&
920 state
.display
->current_mode() != state
.mirror_mode
)
921 all_succeeded
= false;
926 display_state_
= display_state
;
927 power_state_
= power_state
;
928 framebuffer_size_
= size
;
930 return all_succeeded
;
933 MultipleDisplayState
DisplayConfigurator::ChooseDisplayState(
934 chromeos::DisplayPowerState power_state
) const {
935 int num_on_displays
= GetDisplayPower(cached_displays_
, power_state
, NULL
);
936 switch (cached_displays_
.size()) {
938 return MULTIPLE_DISPLAY_STATE_HEADLESS
;
940 return MULTIPLE_DISPLAY_STATE_SINGLE
;
942 if (num_on_displays
== 1) {
943 // If only one display is currently turned on, return the "single"
944 // state so that its native mode will be used.
945 return MULTIPLE_DISPLAY_STATE_SINGLE
;
947 if (!state_controller_
)
948 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
;
949 // With either both displays on or both displays off, use one of the
951 std::vector
<int64_t> display_ids
;
952 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
) {
953 // If display id isn't available, switch to extended mode.
954 if (!cached_displays_
[i
].display
->has_proper_display_id())
955 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
;
956 display_ids
.push_back(cached_displays_
[i
].display
->display_id());
958 return state_controller_
->GetStateForDisplayIds(display_ids
);
964 return MULTIPLE_DISPLAY_STATE_INVALID
;