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/sys_info.h"
11 #include "base/time/time.h"
12 #include "ui/display/chromeos/display_util.h"
13 #include "ui/display/chromeos/update_display_configuration_task.h"
14 #include "ui/display/display_switches.h"
15 #include "ui/display/types/display_mode.h"
16 #include "ui/display/types/display_snapshot.h"
17 #include "ui/display/types/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;
36 DisplaySnapshot
* display
= nullptr; // Not owned.
38 // User-selected mode for the display.
39 const DisplayMode
* selected_mode
= nullptr;
41 // Mode used when displaying the same desktop on multiple displays.
42 const DisplayMode
* mirror_mode
= nullptr;
45 void DoNothing(bool status
) {
50 const int DisplayConfigurator::kSetDisplayPowerNoFlags
= 0;
51 const int DisplayConfigurator::kSetDisplayPowerForceProbe
= 1 << 0;
53 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay
= 1 << 1;
55 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
56 if (configurator_
->configure_timer_
.IsRunning()) {
57 configurator_
->configure_timer_
.user_task().Run();
58 configurator_
->configure_timer_
.Stop();
65 ////////////////////////////////////////////////////////////////////////////////
66 // DisplayConfigurator::DisplayLayoutManagerImpl implementation
68 class DisplayConfigurator::DisplayLayoutManagerImpl
69 : public DisplayLayoutManager
{
71 DisplayLayoutManagerImpl(DisplayConfigurator
* configurator
);
72 ~DisplayLayoutManagerImpl() override
;
74 // DisplayConfigurator::DisplayLayoutManager:
75 SoftwareMirroringController
* GetSoftwareMirroringController() const override
;
76 StateController
* GetStateController() const override
;
77 MultipleDisplayState
GetDisplayState() const override
;
78 chromeos::DisplayPowerState
GetPowerState() const override
;
79 bool GetDisplayLayout(const std::vector
<DisplaySnapshot
*>& displays
,
80 MultipleDisplayState new_display_state
,
81 chromeos::DisplayPowerState new_power_state
,
82 std::vector
<DisplayConfigureRequest
>* requests
,
83 gfx::Size
* framebuffer_size
) const override
;
86 // Parses the |displays| into a list of DisplayStates. This effectively adds
87 // |mirror_mode| and |selected_mode| to the returned results.
88 // TODO(dnicoara): Break this into GetSelectedMode() and GetMirrorMode() and
89 // remove DisplayState.
90 std::vector
<DisplayState
> ParseDisplays(
91 const std::vector
<DisplaySnapshot
*>& displays
) const;
93 const DisplayMode
* GetUserSelectedMode(const DisplaySnapshot
& display
) const;
95 // Helper method for ParseDisplays() that initializes the passed-in
96 // displays' |mirror_mode| fields by looking for a mode in |internal_display|
97 // and |external_display| having the same resolution. Returns false if a
98 // shared mode wasn't found or created.
100 // |try_panel_fitting| allows creating a panel-fitting mode for
101 // |internal_display| instead of only searching for a matching mode (note that
102 // it may lead to a crash if |internal_display| is not capable of panel
105 // |preserve_aspect| limits the search/creation only to the modes having the
106 // native aspect ratio of |external_display|.
107 bool FindMirrorMode(DisplayState
* internal_display
,
108 DisplayState
* external_display
,
109 bool try_panel_fitting
,
110 bool preserve_aspect
) const;
112 DisplayConfigurator
* configurator_
; // Not owned.
114 DISALLOW_COPY_AND_ASSIGN(DisplayLayoutManagerImpl
);
117 DisplayConfigurator::DisplayLayoutManagerImpl::DisplayLayoutManagerImpl(
118 DisplayConfigurator
* configurator
)
119 : configurator_(configurator
) {
122 DisplayConfigurator::DisplayLayoutManagerImpl::~DisplayLayoutManagerImpl() {
125 DisplayConfigurator::SoftwareMirroringController
*
126 DisplayConfigurator::DisplayLayoutManagerImpl::GetSoftwareMirroringController()
128 return configurator_
->mirroring_controller_
;
131 DisplayConfigurator::StateController
*
132 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const {
133 return configurator_
->state_controller_
;
137 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayState() const {
138 return configurator_
->current_display_state_
;
141 chromeos::DisplayPowerState
142 DisplayConfigurator::DisplayLayoutManagerImpl::GetPowerState() const {
143 return configurator_
->current_power_state_
;
146 std::vector
<DisplayState
>
147 DisplayConfigurator::DisplayLayoutManagerImpl::ParseDisplays(
148 const std::vector
<DisplaySnapshot
*>& snapshots
) const {
149 std::vector
<DisplayState
> cached_displays
;
150 for (auto snapshot
: snapshots
) {
151 DisplayState display_state
;
152 display_state
.display
= snapshot
;
153 display_state
.selected_mode
= GetUserSelectedMode(*snapshot
);
154 cached_displays
.push_back(display_state
);
157 // Set |mirror_mode| fields.
158 if (cached_displays
.size() == 2) {
159 bool one_is_internal
=
160 cached_displays
[0].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
161 bool two_is_internal
=
162 cached_displays
[1].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
163 int internal_displays
=
164 (one_is_internal
? 1 : 0) + (two_is_internal
? 1 : 0);
165 DCHECK_LT(internal_displays
, 2);
166 LOG_IF(WARNING
, internal_displays
>= 2)
167 << "At least two internal displays detected.";
169 bool can_mirror
= false;
170 for (int attempt
= 0; !can_mirror
&& attempt
< 2; ++attempt
) {
171 // Try preserving external display's aspect ratio on the first attempt.
172 // If that fails, fall back to the highest matching resolution.
173 bool preserve_aspect
= attempt
== 0;
175 if (internal_displays
== 1) {
176 can_mirror
= FindMirrorMode(&cached_displays
[one_is_internal
? 0 : 1],
177 &cached_displays
[one_is_internal
? 1 : 0],
178 configurator_
->is_panel_fitting_enabled_
,
180 } else { // if (internal_displays == 0)
181 // No panel fitting for external displays, so fall back to exact match.
182 can_mirror
= FindMirrorMode(&cached_displays
[0], &cached_displays
[1],
183 false, preserve_aspect
);
184 if (!can_mirror
&& preserve_aspect
) {
185 // FindMirrorMode() will try to preserve aspect ratio of what it
186 // thinks is external display, so if it didn't succeed with one, maybe
187 // it will succeed with the other. This way we will have the correct
188 // aspect ratio on at least one of them.
189 can_mirror
= FindMirrorMode(&cached_displays
[1], &cached_displays
[0],
190 false, preserve_aspect
);
196 return cached_displays
;
199 bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout(
200 const std::vector
<DisplaySnapshot
*>& displays
,
201 MultipleDisplayState new_display_state
,
202 chromeos::DisplayPowerState new_power_state
,
203 std::vector
<DisplayConfigureRequest
>* requests
,
204 gfx::Size
* framebuffer_size
) const {
205 std::vector
<DisplayState
> states
= ParseDisplays(displays
);
206 std::vector
<bool> display_power
;
207 int num_on_displays
=
208 GetDisplayPower(displays
, new_power_state
, &display_power
);
209 VLOG(1) << "EnterState: display="
210 << MultipleDisplayStateToString(new_display_state
)
211 << " power=" << DisplayPowerStateToString(new_power_state
);
213 // Framebuffer dimensions.
216 for (size_t i
= 0; i
< displays
.size(); ++i
) {
217 requests
->push_back(DisplayConfigureRequest(
218 displays
[i
], displays
[i
]->current_mode(), gfx::Point()));
221 switch (new_display_state
) {
222 case MULTIPLE_DISPLAY_STATE_INVALID
:
223 NOTREACHED() << "Ignoring request to enter invalid state with "
224 << displays
.size() << " connected display(s)";
226 case MULTIPLE_DISPLAY_STATE_HEADLESS
:
227 if (displays
.size() != 0) {
228 LOG(WARNING
) << "Ignoring request to enter headless mode with "
229 << displays
.size() << " connected display(s)";
233 case MULTIPLE_DISPLAY_STATE_SINGLE
: {
234 // If there are multiple displays connected, only one should be turned on.
235 if (displays
.size() != 1 && num_on_displays
!= 1) {
236 LOG(WARNING
) << "Ignoring request to enter single mode with "
237 << displays
.size() << " connected displays and "
238 << num_on_displays
<< " turned on";
242 for (size_t i
= 0; i
< states
.size(); ++i
) {
243 const DisplayState
* state
= &states
[i
];
244 (*requests
)[i
].mode
= display_power
[i
] ? state
->selected_mode
: NULL
;
246 if (display_power
[i
] || states
.size() == 1) {
247 const DisplayMode
* mode_info
= state
->selected_mode
;
249 LOG(WARNING
) << "No selected mode when configuring display: "
250 << state
->display
->ToString();
253 if (mode_info
->size() == gfx::Size(1024, 768)) {
254 VLOG(1) << "Potentially misdetecting display(1024x768):"
255 << " displays size=" << states
.size()
256 << ", num_on_displays=" << num_on_displays
257 << ", current size:" << size
.width() << "x" << size
.height()
258 << ", i=" << i
<< ", display=" << state
->display
->ToString()
259 << ", display_mode=" << mode_info
->ToString();
261 size
= mode_info
->size();
266 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
: {
267 if (states
.size() != 2 ||
268 (num_on_displays
!= 0 && num_on_displays
!= 2)) {
269 LOG(WARNING
) << "Ignoring request to enter mirrored mode with "
270 << states
.size() << " connected display(s) and "
271 << num_on_displays
<< " turned on";
275 const DisplayMode
* mode_info
= states
[0].mirror_mode
;
277 LOG(WARNING
) << "No mirror mode when configuring display: "
278 << states
[0].display
->ToString();
281 size
= mode_info
->size();
283 for (size_t i
= 0; i
< states
.size(); ++i
) {
284 const DisplayState
* state
= &states
[i
];
285 (*requests
)[i
].mode
= display_power
[i
] ? state
->mirror_mode
: NULL
;
289 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
:
290 case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED
: {
291 if ((new_display_state
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
&&
292 states
.size() != 2) ||
293 (new_display_state
== MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED
&&
294 states
.size() <= 2) ||
295 (num_on_displays
!= 0 &&
296 num_on_displays
!= static_cast<int>(displays
.size()))) {
297 LOG(WARNING
) << "Ignoring request to enter extended mode with "
298 << states
.size() << " connected display(s) and "
299 << num_on_displays
<< " turned on";
303 for (size_t i
= 0; i
< states
.size(); ++i
) {
304 const DisplayState
* state
= &states
[i
];
305 (*requests
)[i
].origin
.set_y(size
.height() ? size
.height() + kVerticalGap
307 (*requests
)[i
].mode
= display_power
[i
] ? state
->selected_mode
: NULL
;
309 // Retain the full screen size even if all displays are off so the
310 // same desktop configuration can be restored when the displays are
312 const DisplayMode
* mode_info
= states
[i
].selected_mode
;
314 LOG(WARNING
) << "No selected mode when configuring display: "
315 << state
->display
->ToString();
319 size
.set_width(std::max
<int>(size
.width(), mode_info
->size().width()));
320 size
.set_height(size
.height() + (size
.height() ? kVerticalGap
: 0) +
321 mode_info
->size().height());
326 DCHECK(new_display_state
== MULTIPLE_DISPLAY_STATE_HEADLESS
||
328 *framebuffer_size
= size
;
333 DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode(
334 const DisplaySnapshot
& display
) const {
336 const DisplayMode
* selected_mode
= nullptr;
337 if (GetStateController() &&
338 GetStateController()->GetResolutionForDisplayId(display
.display_id(),
340 selected_mode
= FindDisplayModeMatchingSize(display
, size
);
343 // Fall back to native mode.
344 return selected_mode
? selected_mode
: display
.native_mode();
347 bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode(
348 DisplayState
* internal_display
,
349 DisplayState
* external_display
,
350 bool try_panel_fitting
,
351 bool preserve_aspect
) const {
352 const DisplayMode
* internal_native_info
=
353 internal_display
->display
->native_mode();
354 const DisplayMode
* external_native_info
=
355 external_display
->display
->native_mode();
356 if (!internal_native_info
|| !external_native_info
)
359 // Check if some external display resolution can be mirrored on internal.
360 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
361 // this is the order in which they look better on the monitor.
362 for (const auto* external_mode
: external_display
->display
->modes()) {
363 bool is_native_aspect_ratio
=
364 external_native_info
->size().width() * external_mode
->size().height() ==
365 external_native_info
->size().height() * external_mode
->size().width();
366 if (preserve_aspect
&& !is_native_aspect_ratio
)
367 continue; // Allow only aspect ratio preserving modes for mirroring.
369 // Try finding an exact match.
370 for (const auto* internal_mode
: internal_display
->display
->modes()) {
371 if (internal_mode
->size() == external_mode
->size() &&
372 internal_mode
->is_interlaced() == external_mode
->is_interlaced()) {
373 internal_display
->mirror_mode
= internal_mode
;
374 external_display
->mirror_mode
= external_mode
;
375 return true; // Mirror mode found.
379 // Try to create a matching internal display mode by panel fitting.
380 if (try_panel_fitting
) {
381 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
382 // ugly, so, can fit == can upscale. Also, internal panels don't support
383 // fitting interlaced modes.
384 bool can_fit
= internal_native_info
->size().width() >=
385 external_mode
->size().width() &&
386 internal_native_info
->size().height() >=
387 external_mode
->size().height() &&
388 !external_mode
->is_interlaced();
390 configurator_
->native_display_delegate_
->AddMode(
391 *internal_display
->display
, external_mode
);
392 internal_display
->display
->add_mode(external_mode
);
393 internal_display
->mirror_mode
= external_mode
;
394 external_display
->mirror_mode
= external_mode
;
395 return true; // Mirror mode created.
403 ////////////////////////////////////////////////////////////////////////////////
404 // DisplayConfigurator implementation
407 const DisplayMode
* DisplayConfigurator::FindDisplayModeMatchingSize(
408 const DisplaySnapshot
& display
,
409 const gfx::Size
& size
) {
410 const DisplayMode
* best_mode
= NULL
;
411 for (const DisplayMode
* mode
: display
.modes()) {
412 if (mode
->size() != size
)
415 if (mode
== display
.native_mode()) {
425 if (mode
->is_interlaced()) {
426 if (!best_mode
->is_interlaced())
429 // Reset the best rate if the non interlaced is
430 // found the first time.
431 if (best_mode
->is_interlaced()) {
436 if (mode
->refresh_rate() < best_mode
->refresh_rate())
445 DisplayConfigurator::DisplayConfigurator()
446 : state_controller_(NULL
),
447 mirroring_controller_(NULL
),
448 is_panel_fitting_enabled_(false),
449 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
450 current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID
),
451 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON
),
452 requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID
),
453 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON
),
454 requested_power_state_change_(false),
455 requested_power_flags_(kSetDisplayPowerNoFlags
),
456 force_configure_(false),
457 next_display_protection_client_id_(1),
458 display_externally_controlled_(false),
459 displays_suspended_(false),
460 layout_manager_(new DisplayLayoutManagerImpl(this)),
461 weak_ptr_factory_(this) {
464 DisplayConfigurator::~DisplayConfigurator() {
465 if (native_display_delegate_
)
466 native_display_delegate_
->RemoveObserver(this);
468 CallAndClearInProgressCallbacks(false);
469 CallAndClearQueuedCallbacks(false);
472 void DisplayConfigurator::SetDelegateForTesting(
473 scoped_ptr
<NativeDisplayDelegate
> display_delegate
) {
474 DCHECK(!native_display_delegate_
);
476 native_display_delegate_
= display_delegate
.Pass();
477 configure_display_
= true;
480 void DisplayConfigurator::SetInitialDisplayPower(
481 chromeos::DisplayPowerState power_state
) {
482 DCHECK_EQ(current_display_state_
, MULTIPLE_DISPLAY_STATE_INVALID
);
483 requested_power_state_
= current_power_state_
= power_state
;
486 void DisplayConfigurator::Init(bool is_panel_fitting_enabled
) {
487 is_panel_fitting_enabled_
= is_panel_fitting_enabled
;
488 if (!configure_display_
|| display_externally_controlled_
)
491 // If the delegate is already initialized don't update it (For example, tests
492 // set their own delegates).
493 if (!native_display_delegate_
) {
494 native_display_delegate_
= CreatePlatformNativeDisplayDelegate();
495 native_display_delegate_
->AddObserver(this);
499 void DisplayConfigurator::TakeControl() {
500 if (cached_displays_
.empty())
503 if (!display_externally_controlled_
)
506 if (!native_display_delegate_
->TakeDisplayControl())
509 display_externally_controlled_
= false;
510 force_configure_
= true;
511 RunPendingConfiguration();
514 void DisplayConfigurator::RelinquishControl() {
515 if (display_externally_controlled_
)
518 display_externally_controlled_
= true;
519 native_display_delegate_
->RelinquishDisplayControl();
522 void DisplayConfigurator::ForceInitialConfigure(
523 uint32_t background_color_argb
) {
524 if (!configure_display_
|| display_externally_controlled_
)
527 native_display_delegate_
->Initialize();
529 // ForceInitialConfigure should be the first configuration so there shouldn't
530 // be anything scheduled.
531 DCHECK(!configuration_task_
);
533 configuration_task_
.reset(new UpdateDisplayConfigurationTask(
534 native_display_delegate_
.get(), layout_manager_
.get(),
535 requested_display_state_
, requested_power_state_
,
536 kSetDisplayPowerForceProbe
, background_color_argb
, true,
537 base::Bind(&DisplayConfigurator::OnConfigured
,
538 weak_ptr_factory_
.GetWeakPtr())));
539 configuration_task_
->Run();
542 bool DisplayConfigurator::IsMirroring() const {
543 return current_display_state_
== MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
||
544 (mirroring_controller_
&&
545 mirroring_controller_
->SoftwareMirroringEnabled());
548 bool DisplayConfigurator::ApplyProtections(const ContentProtections
& requests
) {
549 for (const DisplaySnapshot
* display
: cached_displays_
) {
550 uint32_t all_desired
= 0;
552 // In mirror mode, protection request of all displays need to be fulfilled.
553 // In non-mirror mode, only request of client's display needs to be
556 for (const auto& protections_pair
: requests
)
557 all_desired
|= protections_pair
.second
;
559 ContentProtections::const_iterator request_it
=
560 requests
.find(display
->display_id());
561 if (request_it
!= requests
.end())
562 all_desired
= request_it
->second
;
565 switch (display
->type()) {
566 case DISPLAY_CONNECTION_TYPE_UNKNOWN
:
568 // DisplayPort, DVI, and HDMI all support HDCP.
569 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT
:
570 case DISPLAY_CONNECTION_TYPE_DVI
:
571 case DISPLAY_CONNECTION_TYPE_HDMI
: {
572 HDCPState current_state
;
573 // Need to poll the driver for updates since other applications may
574 // have updated the state.
575 if (!native_display_delegate_
->GetHDCPState(*display
, ¤t_state
))
577 bool current_desired
= (current_state
!= HDCP_STATE_UNDESIRED
);
578 bool new_desired
= (all_desired
& CONTENT_PROTECTION_METHOD_HDCP
);
579 // Don't enable again if HDCP is already active. Some buggy drivers
580 // may disable and enable if setting "desired" in active state.
581 if (current_desired
!= new_desired
) {
582 HDCPState new_state
=
583 new_desired
? HDCP_STATE_DESIRED
: HDCP_STATE_UNDESIRED
;
584 if (!native_display_delegate_
->SetHDCPState(*display
, new_state
))
589 case DISPLAY_CONNECTION_TYPE_INTERNAL
:
590 case DISPLAY_CONNECTION_TYPE_VGA
:
591 case DISPLAY_CONNECTION_TYPE_NETWORK
:
592 // No protections for these types. Do nothing.
594 case DISPLAY_CONNECTION_TYPE_NONE
:
603 DisplayConfigurator::ContentProtectionClientId
604 DisplayConfigurator::RegisterContentProtectionClient() {
605 if (!configure_display_
|| display_externally_controlled_
)
606 return kInvalidClientId
;
608 return next_display_protection_client_id_
++;
611 void DisplayConfigurator::UnregisterContentProtectionClient(
612 ContentProtectionClientId client_id
) {
613 client_protection_requests_
.erase(client_id
);
615 ContentProtections protections
;
616 for (const auto& requests_pair
: client_protection_requests_
) {
617 for (const auto& protections_pair
: requests_pair
.second
) {
618 protections
[protections_pair
.first
] |= protections_pair
.second
;
622 ApplyProtections(protections
);
625 bool DisplayConfigurator::QueryContentProtectionStatus(
626 ContentProtectionClientId client_id
,
629 uint32_t* protection_mask
) {
630 if (!configure_display_
|| display_externally_controlled_
)
633 uint32_t enabled
= 0;
634 uint32_t unfulfilled
= 0;
636 for (const DisplaySnapshot
* display
: cached_displays_
) {
637 // Query display if it is in mirror mode or client on the same display.
638 if (!IsMirroring() && display
->display_id() != display_id
)
641 *link_mask
|= display
->type();
642 switch (display
->type()) {
643 case DISPLAY_CONNECTION_TYPE_UNKNOWN
:
645 // DisplayPort, DVI, and HDMI all support HDCP.
646 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT
:
647 case DISPLAY_CONNECTION_TYPE_DVI
:
648 case DISPLAY_CONNECTION_TYPE_HDMI
: {
650 if (!native_display_delegate_
->GetHDCPState(*display
, &state
))
652 if (state
== HDCP_STATE_ENABLED
)
653 enabled
|= CONTENT_PROTECTION_METHOD_HDCP
;
655 unfulfilled
|= CONTENT_PROTECTION_METHOD_HDCP
;
658 case DISPLAY_CONNECTION_TYPE_INTERNAL
:
659 case DISPLAY_CONNECTION_TYPE_VGA
:
660 case DISPLAY_CONNECTION_TYPE_NETWORK
:
661 // No protections for these types. Do nothing.
663 case DISPLAY_CONNECTION_TYPE_NONE
:
669 // Don't reveal protections requested by other clients.
670 ProtectionRequests::iterator it
= client_protection_requests_
.find(client_id
);
671 if (it
!= client_protection_requests_
.end()) {
672 uint32_t requested_mask
= 0;
673 if (it
->second
.find(display_id
) != it
->second
.end())
674 requested_mask
= it
->second
[display_id
];
675 *protection_mask
= enabled
& ~unfulfilled
& requested_mask
;
677 *protection_mask
= 0;
682 bool DisplayConfigurator::EnableContentProtection(
683 ContentProtectionClientId client_id
,
685 uint32_t desired_method_mask
) {
686 if (!configure_display_
|| display_externally_controlled_
)
689 ContentProtections protections
;
690 for (const auto& requests_pair
: client_protection_requests_
) {
691 for (const auto& protections_pair
: requests_pair
.second
) {
692 if (requests_pair
.first
== client_id
&&
693 protections_pair
.first
== display_id
)
696 protections
[protections_pair
.first
] |= protections_pair
.second
;
699 protections
[display_id
] |= desired_method_mask
;
701 if (!ApplyProtections(protections
))
704 if (desired_method_mask
== CONTENT_PROTECTION_METHOD_NONE
) {
705 if (client_protection_requests_
.find(client_id
) !=
706 client_protection_requests_
.end()) {
707 client_protection_requests_
[client_id
].erase(display_id
);
708 if (client_protection_requests_
[client_id
].size() == 0)
709 client_protection_requests_
.erase(client_id
);
712 client_protection_requests_
[client_id
][display_id
] = desired_method_mask
;
718 std::vector
<ui::ColorCalibrationProfile
>
719 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id
) {
720 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
721 switches::kDisableDisplayColorCalibration
)) {
722 for (const DisplaySnapshot
* display
: cached_displays_
) {
723 if (display
->display_id() == display_id
) {
724 return native_display_delegate_
->GetAvailableColorCalibrationProfiles(
730 return std::vector
<ui::ColorCalibrationProfile
>();
733 bool DisplayConfigurator::SetColorCalibrationProfile(
735 ui::ColorCalibrationProfile new_profile
) {
736 for (const DisplaySnapshot
* display
: cached_displays_
) {
737 if (display
->display_id() == display_id
) {
738 return native_display_delegate_
->SetColorCalibrationProfile(*display
,
746 void DisplayConfigurator::PrepareForExit() {
747 configure_display_
= false;
750 void DisplayConfigurator::SetDisplayPower(
751 chromeos::DisplayPowerState power_state
,
753 const ConfigurationCallback
& callback
) {
754 if (!configure_display_
|| display_externally_controlled_
) {
759 VLOG(1) << "SetDisplayPower: power_state="
760 << DisplayPowerStateToString(power_state
) << " flags=" << flags
761 << ", configure timer="
762 << (configure_timer_
.IsRunning() ? "Running" : "Stopped");
763 if (power_state
== requested_power_state_
&&
764 !(flags
& kSetDisplayPowerForceProbe
)) {
769 requested_power_state_
= power_state
;
770 requested_power_state_change_
= true;
771 requested_power_flags_
= flags
;
772 queued_configuration_callbacks_
.push_back(callback
);
774 RunPendingConfiguration();
777 void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state
) {
778 if (!configure_display_
|| display_externally_controlled_
)
781 VLOG(1) << "SetDisplayMode: state="
782 << MultipleDisplayStateToString(new_state
);
783 if (current_display_state_
== new_state
) {
784 // Cancel software mirroring if the state is moving from
785 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
786 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
787 if (mirroring_controller_
&&
788 new_state
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
)
789 mirroring_controller_
->SetSoftwareMirroring(false);
790 NotifyObservers(true, new_state
);
794 requested_display_state_
= new_state
;
796 RunPendingConfiguration();
799 void DisplayConfigurator::OnConfigurationChanged() {
800 // Don't do anything if the displays are currently suspended. Instead we will
801 // probe and reconfigure the displays if necessary in ResumeDisplays().
802 if (displays_suspended_
) {
803 VLOG(1) << "Displays are currently suspended. Not attempting to "
804 << "reconfigure them.";
808 // Configure displays with |kConfigureDelayMs| delay,
809 // so that time-consuming ConfigureDisplays() won't be called multiple times.
810 if (configure_timer_
.IsRunning()) {
811 // Note: when the timer is running it is possible that a different task
812 // (RestoreRequestedPowerStateAfterResume()) is scheduled. In these cases,
813 // prefer the already scheduled task to ConfigureDisplays() since
814 // ConfigureDisplays() performs only basic configuration while
815 // RestoreRequestedPowerStateAfterResume() will perform additional
817 configure_timer_
.Reset();
819 configure_timer_
.Start(
821 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
823 &DisplayConfigurator::ConfigureDisplays
);
827 void DisplayConfigurator::AddObserver(Observer
* observer
) {
828 observers_
.AddObserver(observer
);
831 void DisplayConfigurator::RemoveObserver(Observer
* observer
) {
832 observers_
.RemoveObserver(observer
);
835 void DisplayConfigurator::SuspendDisplays() {
836 // If the display is off due to user inactivity and there's only a single
837 // internal display connected, switch to the all-on state before
838 // suspending. This shouldn't be very noticeable to the user since the
839 // backlight is off at this point, and doing this lets us resume directly
840 // into the "on" state, which greatly reduces resume times.
841 if (requested_power_state_
== chromeos::DISPLAY_POWER_ALL_OFF
) {
842 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON
,
843 kSetDisplayPowerOnlyIfSingleInternalDisplay
,
844 base::Bind(&DoNothing
));
846 // We need to make sure that the monitor configuration we just did actually
847 // completes before we return, because otherwise the X message could be
848 // racing with the HandleSuspendReadiness message.
849 native_display_delegate_
->SyncWithServer();
852 displays_suspended_
= true;
854 // Stop |configure_timer_| because we will force probe and configure all the
855 // displays at resume time anyway.
856 configure_timer_
.Stop();
859 void DisplayConfigurator::ResumeDisplays() {
860 displays_suspended_
= false;
862 configure_timer_
.Start(
864 base::TimeDelta::FromMilliseconds(kResumeDelayMs
),
865 base::Bind(&DisplayConfigurator::RestoreRequestedPowerStateAfterResume
,
866 base::Unretained(this)));
869 void DisplayConfigurator::ConfigureDisplays() {
870 if (!configure_display_
|| display_externally_controlled_
)
873 force_configure_
= true;
874 RunPendingConfiguration();
877 void DisplayConfigurator::RunPendingConfiguration() {
878 // Configuration task is currently running. Do not start a second
880 if (configuration_task_
)
883 if (!ShouldRunConfigurationTask()) {
884 LOG(ERROR
) << "Called RunPendingConfiguration without any changes"
886 CallAndClearQueuedCallbacks(true);
890 configuration_task_
.reset(new UpdateDisplayConfigurationTask(
891 native_display_delegate_
.get(), layout_manager_
.get(),
892 requested_display_state_
, requested_power_state_
, requested_power_flags_
,
893 0, force_configure_
, base::Bind(&DisplayConfigurator::OnConfigured
,
894 weak_ptr_factory_
.GetWeakPtr())));
896 // Reset the flags before running the task; otherwise it may end up scheduling
897 // another configuration.
898 force_configure_
= false;
899 requested_power_flags_
= kSetDisplayPowerNoFlags
;
900 requested_power_state_change_
= false;
901 requested_display_state_
= MULTIPLE_DISPLAY_STATE_INVALID
;
903 DCHECK(in_progress_configuration_callbacks_
.empty());
904 in_progress_configuration_callbacks_
.swap(queued_configuration_callbacks_
);
906 configuration_task_
->Run();
909 void DisplayConfigurator::OnConfigured(
911 const std::vector
<DisplaySnapshot
*>& displays
,
912 const gfx::Size
& framebuffer_size
,
913 MultipleDisplayState new_display_state
,
914 chromeos::DisplayPowerState new_power_state
) {
915 VLOG(1) << "OnConfigured: success=" << success
<< " new_display_state="
916 << MultipleDisplayStateToString(new_display_state
)
917 << " new_power_state=" << DisplayPowerStateToString(new_power_state
);
919 cached_displays_
= displays
;
921 current_display_state_
= new_display_state
;
922 current_power_state_
= new_power_state
;
924 // |framebuffer_size| is empty in software mirroring mode, headless mode,
925 // or all displays are off.
926 DCHECK(!framebuffer_size
.IsEmpty() ||
927 (mirroring_controller_
&&
928 mirroring_controller_
->SoftwareMirroringEnabled()) ||
929 new_display_state
== MULTIPLE_DISPLAY_STATE_HEADLESS
||
930 new_power_state
== chromeos::DISPLAY_POWER_ALL_OFF
);
932 if (!framebuffer_size
.IsEmpty())
933 framebuffer_size_
= framebuffer_size
;
935 // If the requested power state hasn't changed then make sure that value
936 // gets updated as well since the last requested value may have been
937 // dependent on certain conditions (ie: if only the internal monitor was
939 if (!requested_power_state_change_
)
940 requested_power_state_
= new_power_state
;
943 configuration_task_
.reset();
944 NotifyObservers(success
, new_display_state
);
945 CallAndClearInProgressCallbacks(success
);
947 if (success
&& !configure_timer_
.IsRunning() &&
948 ShouldRunConfigurationTask()) {
949 configure_timer_
.Start(FROM_HERE
,
950 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
951 this, &DisplayConfigurator::RunPendingConfiguration
);
953 // If a new configuration task isn't scheduled respond to all queued
954 // callbacks (for example if requested state is current state).
955 if (!configure_timer_
.IsRunning())
956 CallAndClearQueuedCallbacks(success
);
960 bool DisplayConfigurator::ShouldRunConfigurationTask() const {
961 if (force_configure_
)
964 // Schedule if there is a request to change the display state.
965 if (requested_display_state_
!= current_display_state_
&&
966 requested_display_state_
!= MULTIPLE_DISPLAY_STATE_INVALID
)
969 // Schedule if there is a request to change the power state.
970 if (requested_power_state_change_
)
976 void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success
) {
977 for (const auto& callback
: in_progress_configuration_callbacks_
)
978 callback
.Run(success
);
980 in_progress_configuration_callbacks_
.clear();
983 void DisplayConfigurator::CallAndClearQueuedCallbacks(bool success
) {
984 for (const auto& callback
: queued_configuration_callbacks_
)
985 callback
.Run(success
);
987 queued_configuration_callbacks_
.clear();
990 void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() {
991 // Force probing to ensure that we pick up any changes that were made while
992 // the system was suspended.
993 SetDisplayPower(requested_power_state_
, kSetDisplayPowerForceProbe
,
994 base::Bind(&DoNothing
));
997 void DisplayConfigurator::NotifyObservers(
999 MultipleDisplayState attempted_state
) {
1002 Observer
, observers_
, OnDisplayModeChanged(cached_displays_
));
1005 Observer
, observers_
, OnDisplayModeChangeFailed(cached_displays_
,