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/apply_content_protection_task.h"
13 #include "ui/display/chromeos/display_layout_manager.h"
14 #include "ui/display/chromeos/display_util.h"
15 #include "ui/display/chromeos/update_display_configuration_task.h"
16 #include "ui/display/display_switches.h"
17 #include "ui/display/types/display_mode.h"
18 #include "ui/display/types/display_snapshot.h"
19 #include "ui/display/types/native_display_delegate.h"
25 typedef std::vector
<const DisplayMode
*> DisplayModeList
;
27 // The delay to perform configuration after RRNotify. See the comment for
28 // |configure_timer_|.
29 const int kConfigureDelayMs
= 500;
31 // The delay spent before reading the display configuration after coming out of
32 // suspend. While coming out of suspend the display state may be updating. This
33 // is used to wait until the hardware had a chance to update the display state
34 // such that we read an up to date state.
35 const int kResumeDelayMs
= 500;
38 DisplaySnapshot
* display
= nullptr; // Not owned.
40 // User-selected mode for the display.
41 const DisplayMode
* selected_mode
= nullptr;
43 // Mode used when displaying the same desktop on multiple displays.
44 const DisplayMode
* mirror_mode
= nullptr;
47 void DoNothing(bool status
) {
52 const int DisplayConfigurator::kSetDisplayPowerNoFlags
= 0;
53 const int DisplayConfigurator::kSetDisplayPowerForceProbe
= 1 << 0;
55 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay
= 1 << 1;
57 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
58 if (configurator_
->configure_timer_
.IsRunning()) {
59 configurator_
->configure_timer_
.user_task().Run();
60 configurator_
->configure_timer_
.Stop();
67 ////////////////////////////////////////////////////////////////////////////////
68 // DisplayConfigurator::DisplayLayoutManagerImpl implementation
70 class DisplayConfigurator::DisplayLayoutManagerImpl
71 : public DisplayLayoutManager
{
73 DisplayLayoutManagerImpl(DisplayConfigurator
* configurator
);
74 ~DisplayLayoutManagerImpl() override
;
76 // DisplayConfigurator::DisplayLayoutManager:
77 SoftwareMirroringController
* GetSoftwareMirroringController() const override
;
78 StateController
* GetStateController() const override
;
79 MultipleDisplayState
GetDisplayState() const override
;
80 chromeos::DisplayPowerState
GetPowerState() const override
;
81 bool GetDisplayLayout(const std::vector
<DisplaySnapshot
*>& displays
,
82 MultipleDisplayState new_display_state
,
83 chromeos::DisplayPowerState new_power_state
,
84 std::vector
<DisplayConfigureRequest
>* requests
,
85 gfx::Size
* framebuffer_size
) const override
;
86 DisplayStateList
GetDisplayStates() const override
;
87 bool IsMirroring() const override
;
90 // Parses the |displays| into a list of DisplayStates. This effectively adds
91 // |mirror_mode| and |selected_mode| to the returned results.
92 // TODO(dnicoara): Break this into GetSelectedMode() and GetMirrorMode() and
93 // remove DisplayState.
94 std::vector
<DisplayState
> ParseDisplays(
95 const std::vector
<DisplaySnapshot
*>& displays
) const;
97 const DisplayMode
* GetUserSelectedMode(const DisplaySnapshot
& display
) const;
99 // Helper method for ParseDisplays() that initializes the passed-in
100 // displays' |mirror_mode| fields by looking for a mode in |internal_display|
101 // and |external_display| having the same resolution. Returns false if a
102 // shared mode wasn't found or created.
104 // |try_panel_fitting| allows creating a panel-fitting mode for
105 // |internal_display| instead of only searching for a matching mode (note that
106 // it may lead to a crash if |internal_display| is not capable of panel
109 // |preserve_aspect| limits the search/creation only to the modes having the
110 // native aspect ratio of |external_display|.
111 bool FindMirrorMode(DisplayState
* internal_display
,
112 DisplayState
* external_display
,
113 bool try_panel_fitting
,
114 bool preserve_aspect
) const;
116 DisplayConfigurator
* configurator_
; // Not owned.
118 DISALLOW_COPY_AND_ASSIGN(DisplayLayoutManagerImpl
);
121 DisplayConfigurator::DisplayLayoutManagerImpl::DisplayLayoutManagerImpl(
122 DisplayConfigurator
* configurator
)
123 : configurator_(configurator
) {
126 DisplayConfigurator::DisplayLayoutManagerImpl::~DisplayLayoutManagerImpl() {
129 DisplayConfigurator::SoftwareMirroringController
*
130 DisplayConfigurator::DisplayLayoutManagerImpl::GetSoftwareMirroringController()
132 return configurator_
->mirroring_controller_
;
135 DisplayConfigurator::StateController
*
136 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const {
137 return configurator_
->state_controller_
;
141 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayState() const {
142 return configurator_
->current_display_state_
;
145 chromeos::DisplayPowerState
146 DisplayConfigurator::DisplayLayoutManagerImpl::GetPowerState() const {
147 return configurator_
->current_power_state_
;
150 std::vector
<DisplayState
>
151 DisplayConfigurator::DisplayLayoutManagerImpl::ParseDisplays(
152 const std::vector
<DisplaySnapshot
*>& snapshots
) const {
153 std::vector
<DisplayState
> cached_displays
;
154 for (auto snapshot
: snapshots
) {
155 DisplayState display_state
;
156 display_state
.display
= snapshot
;
157 display_state
.selected_mode
= GetUserSelectedMode(*snapshot
);
158 cached_displays
.push_back(display_state
);
161 // Set |mirror_mode| fields.
162 if (cached_displays
.size() == 2) {
163 bool one_is_internal
=
164 cached_displays
[0].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
165 bool two_is_internal
=
166 cached_displays
[1].display
->type() == DISPLAY_CONNECTION_TYPE_INTERNAL
;
167 int internal_displays
=
168 (one_is_internal
? 1 : 0) + (two_is_internal
? 1 : 0);
169 DCHECK_LT(internal_displays
, 2);
170 LOG_IF(WARNING
, internal_displays
>= 2)
171 << "At least two internal displays detected.";
173 bool can_mirror
= false;
174 for (int attempt
= 0; !can_mirror
&& attempt
< 2; ++attempt
) {
175 // Try preserving external display's aspect ratio on the first attempt.
176 // If that fails, fall back to the highest matching resolution.
177 bool preserve_aspect
= attempt
== 0;
179 if (internal_displays
== 1) {
180 can_mirror
= FindMirrorMode(&cached_displays
[one_is_internal
? 0 : 1],
181 &cached_displays
[one_is_internal
? 1 : 0],
182 configurator_
->is_panel_fitting_enabled_
,
184 } else { // if (internal_displays == 0)
185 // No panel fitting for external displays, so fall back to exact match.
186 can_mirror
= FindMirrorMode(&cached_displays
[0], &cached_displays
[1],
187 false, preserve_aspect
);
188 if (!can_mirror
&& preserve_aspect
) {
189 // FindMirrorMode() will try to preserve aspect ratio of what it
190 // thinks is external display, so if it didn't succeed with one, maybe
191 // it will succeed with the other. This way we will have the correct
192 // aspect ratio on at least one of them.
193 can_mirror
= FindMirrorMode(&cached_displays
[1], &cached_displays
[0],
194 false, preserve_aspect
);
200 return cached_displays
;
203 bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout(
204 const std::vector
<DisplaySnapshot
*>& displays
,
205 MultipleDisplayState new_display_state
,
206 chromeos::DisplayPowerState new_power_state
,
207 std::vector
<DisplayConfigureRequest
>* requests
,
208 gfx::Size
* framebuffer_size
) const {
209 std::vector
<DisplayState
> states
= ParseDisplays(displays
);
210 std::vector
<bool> display_power
;
211 int num_on_displays
=
212 GetDisplayPower(displays
, new_power_state
, &display_power
);
213 VLOG(1) << "EnterState: display="
214 << MultipleDisplayStateToString(new_display_state
)
215 << " power=" << DisplayPowerStateToString(new_power_state
);
217 // Framebuffer dimensions.
220 for (size_t i
= 0; i
< displays
.size(); ++i
) {
221 requests
->push_back(DisplayConfigureRequest(
222 displays
[i
], displays
[i
]->current_mode(), gfx::Point()));
225 switch (new_display_state
) {
226 case MULTIPLE_DISPLAY_STATE_INVALID
:
227 NOTREACHED() << "Ignoring request to enter invalid state with "
228 << displays
.size() << " connected display(s)";
230 case MULTIPLE_DISPLAY_STATE_HEADLESS
:
231 if (displays
.size() != 0) {
232 LOG(WARNING
) << "Ignoring request to enter headless mode with "
233 << displays
.size() << " connected display(s)";
237 case MULTIPLE_DISPLAY_STATE_SINGLE
: {
238 // If there are multiple displays connected, only one should be turned on.
239 if (displays
.size() != 1 && num_on_displays
!= 1) {
240 LOG(WARNING
) << "Ignoring request to enter single mode with "
241 << displays
.size() << " connected displays and "
242 << num_on_displays
<< " turned on";
246 for (size_t i
= 0; i
< states
.size(); ++i
) {
247 const DisplayState
* state
= &states
[i
];
248 (*requests
)[i
].mode
= display_power
[i
] ? state
->selected_mode
: NULL
;
250 if (display_power
[i
] || states
.size() == 1) {
251 const DisplayMode
* mode_info
= state
->selected_mode
;
253 LOG(WARNING
) << "No selected mode when configuring display: "
254 << state
->display
->ToString();
257 if (mode_info
->size() == gfx::Size(1024, 768)) {
258 VLOG(1) << "Potentially misdetecting display(1024x768):"
259 << " displays size=" << states
.size()
260 << ", num_on_displays=" << num_on_displays
261 << ", current size:" << size
.width() << "x" << size
.height()
262 << ", i=" << i
<< ", display=" << state
->display
->ToString()
263 << ", display_mode=" << mode_info
->ToString();
265 size
= mode_info
->size();
270 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
: {
271 if (states
.size() != 2 ||
272 (num_on_displays
!= 0 && num_on_displays
!= 2)) {
273 LOG(WARNING
) << "Ignoring request to enter mirrored mode with "
274 << states
.size() << " connected display(s) and "
275 << num_on_displays
<< " turned on";
279 const DisplayMode
* mode_info
= states
[0].mirror_mode
;
281 LOG(WARNING
) << "No mirror mode when configuring display: "
282 << states
[0].display
->ToString();
285 size
= mode_info
->size();
287 for (size_t i
= 0; i
< states
.size(); ++i
) {
288 const DisplayState
* state
= &states
[i
];
289 (*requests
)[i
].mode
= display_power
[i
] ? state
->mirror_mode
: NULL
;
293 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
:
294 case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED
: {
295 if ((new_display_state
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
&&
296 states
.size() != 2) ||
297 (new_display_state
== MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED
&&
298 states
.size() <= 2) ||
299 (num_on_displays
!= 0 &&
300 num_on_displays
!= static_cast<int>(displays
.size()))) {
301 LOG(WARNING
) << "Ignoring request to enter extended mode with "
302 << states
.size() << " connected display(s) and "
303 << num_on_displays
<< " turned on";
307 for (size_t i
= 0; i
< states
.size(); ++i
) {
308 const DisplayState
* state
= &states
[i
];
309 (*requests
)[i
].origin
.set_y(size
.height() ? size
.height() + kVerticalGap
311 (*requests
)[i
].mode
= display_power
[i
] ? state
->selected_mode
: NULL
;
313 // Retain the full screen size even if all displays are off so the
314 // same desktop configuration can be restored when the displays are
316 const DisplayMode
* mode_info
= states
[i
].selected_mode
;
318 LOG(WARNING
) << "No selected mode when configuring display: "
319 << state
->display
->ToString();
323 size
.set_width(std::max
<int>(size
.width(), mode_info
->size().width()));
324 size
.set_height(size
.height() + (size
.height() ? kVerticalGap
: 0) +
325 mode_info
->size().height());
330 DCHECK(new_display_state
== MULTIPLE_DISPLAY_STATE_HEADLESS
||
332 *framebuffer_size
= size
;
336 DisplayConfigurator::DisplayStateList
337 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayStates() const {
338 return configurator_
->cached_displays();
341 bool DisplayConfigurator::DisplayLayoutManagerImpl::IsMirroring() const {
342 if (GetDisplayState() == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR
)
345 return GetSoftwareMirroringController() &&
346 GetSoftwareMirroringController()->SoftwareMirroringEnabled();
350 DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode(
351 const DisplaySnapshot
& display
) const {
353 const DisplayMode
* selected_mode
= nullptr;
354 if (GetStateController() &&
355 GetStateController()->GetResolutionForDisplayId(display
.display_id(),
357 selected_mode
= FindDisplayModeMatchingSize(display
, size
);
360 // Fall back to native mode.
361 return selected_mode
? selected_mode
: display
.native_mode();
364 bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode(
365 DisplayState
* internal_display
,
366 DisplayState
* external_display
,
367 bool try_panel_fitting
,
368 bool preserve_aspect
) const {
369 const DisplayMode
* internal_native_info
=
370 internal_display
->display
->native_mode();
371 const DisplayMode
* external_native_info
=
372 external_display
->display
->native_mode();
373 if (!internal_native_info
|| !external_native_info
)
376 // Check if some external display resolution can be mirrored on internal.
377 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
378 // this is the order in which they look better on the monitor.
379 for (const auto* external_mode
: external_display
->display
->modes()) {
380 bool is_native_aspect_ratio
=
381 external_native_info
->size().width() * external_mode
->size().height() ==
382 external_native_info
->size().height() * external_mode
->size().width();
383 if (preserve_aspect
&& !is_native_aspect_ratio
)
384 continue; // Allow only aspect ratio preserving modes for mirroring.
386 // Try finding an exact match.
387 for (const auto* internal_mode
: internal_display
->display
->modes()) {
388 if (internal_mode
->size() == external_mode
->size() &&
389 internal_mode
->is_interlaced() == external_mode
->is_interlaced()) {
390 internal_display
->mirror_mode
= internal_mode
;
391 external_display
->mirror_mode
= external_mode
;
392 return true; // Mirror mode found.
396 // Try to create a matching internal display mode by panel fitting.
397 if (try_panel_fitting
) {
398 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
399 // ugly, so, can fit == can upscale. Also, internal panels don't support
400 // fitting interlaced modes.
401 bool can_fit
= internal_native_info
->size().width() >=
402 external_mode
->size().width() &&
403 internal_native_info
->size().height() >=
404 external_mode
->size().height() &&
405 !external_mode
->is_interlaced();
407 configurator_
->native_display_delegate_
->AddMode(
408 *internal_display
->display
, external_mode
);
409 internal_display
->display
->add_mode(external_mode
);
410 internal_display
->mirror_mode
= external_mode
;
411 external_display
->mirror_mode
= external_mode
;
412 return true; // Mirror mode created.
420 ////////////////////////////////////////////////////////////////////////////////
421 // DisplayConfigurator implementation
424 const DisplayMode
* DisplayConfigurator::FindDisplayModeMatchingSize(
425 const DisplaySnapshot
& display
,
426 const gfx::Size
& size
) {
427 const DisplayMode
* best_mode
= NULL
;
428 for (const DisplayMode
* mode
: display
.modes()) {
429 if (mode
->size() != size
)
432 if (mode
== display
.native_mode()) {
442 if (mode
->is_interlaced()) {
443 if (!best_mode
->is_interlaced())
446 // Reset the best rate if the non interlaced is
447 // found the first time.
448 if (best_mode
->is_interlaced()) {
453 if (mode
->refresh_rate() < best_mode
->refresh_rate())
462 DisplayConfigurator::DisplayConfigurator()
463 : state_controller_(NULL
),
464 mirroring_controller_(NULL
),
465 is_panel_fitting_enabled_(false),
466 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
467 current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID
),
468 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON
),
469 requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID
),
470 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON
),
471 requested_power_state_change_(false),
472 requested_power_flags_(kSetDisplayPowerNoFlags
),
473 force_configure_(false),
474 next_display_protection_client_id_(1),
475 display_externally_controlled_(false),
476 displays_suspended_(false),
477 layout_manager_(new DisplayLayoutManagerImpl(this)),
478 weak_ptr_factory_(this) {
481 DisplayConfigurator::~DisplayConfigurator() {
482 if (native_display_delegate_
)
483 native_display_delegate_
->RemoveObserver(this);
485 CallAndClearInProgressCallbacks(false);
486 CallAndClearQueuedCallbacks(false);
488 while (!query_protection_callbacks_
.empty()) {
489 query_protection_callbacks_
.front().Run(QueryProtectionResponse());
490 query_protection_callbacks_
.pop();
493 while (!enable_protection_callbacks_
.empty()) {
494 enable_protection_callbacks_
.front().Run(false);
495 enable_protection_callbacks_
.pop();
499 void DisplayConfigurator::SetDelegateForTesting(
500 scoped_ptr
<NativeDisplayDelegate
> display_delegate
) {
501 DCHECK(!native_display_delegate_
);
503 native_display_delegate_
= display_delegate
.Pass();
504 configure_display_
= true;
507 void DisplayConfigurator::SetInitialDisplayPower(
508 chromeos::DisplayPowerState power_state
) {
509 DCHECK_EQ(current_display_state_
, MULTIPLE_DISPLAY_STATE_INVALID
);
510 requested_power_state_
= current_power_state_
= power_state
;
513 void DisplayConfigurator::Init(bool is_panel_fitting_enabled
) {
514 is_panel_fitting_enabled_
= is_panel_fitting_enabled
;
515 if (!configure_display_
|| display_externally_controlled_
)
518 // If the delegate is already initialized don't update it (For example, tests
519 // set their own delegates).
520 if (!native_display_delegate_
) {
521 native_display_delegate_
= CreatePlatformNativeDisplayDelegate();
522 native_display_delegate_
->AddObserver(this);
526 void DisplayConfigurator::TakeControl() {
527 if (cached_displays_
.empty())
530 if (!display_externally_controlled_
)
533 if (!native_display_delegate_
->TakeDisplayControl())
536 display_externally_controlled_
= false;
537 force_configure_
= true;
538 RunPendingConfiguration();
541 void DisplayConfigurator::RelinquishControl() {
542 if (display_externally_controlled_
)
545 display_externally_controlled_
= true;
546 native_display_delegate_
->RelinquishDisplayControl();
549 void DisplayConfigurator::ForceInitialConfigure(
550 uint32_t background_color_argb
) {
551 if (!configure_display_
|| display_externally_controlled_
)
554 native_display_delegate_
->Initialize();
556 // ForceInitialConfigure should be the first configuration so there shouldn't
557 // be anything scheduled.
558 DCHECK(!configuration_task_
);
560 configuration_task_
.reset(new UpdateDisplayConfigurationTask(
561 native_display_delegate_
.get(), layout_manager_
.get(),
562 requested_display_state_
, requested_power_state_
,
563 kSetDisplayPowerForceProbe
, background_color_argb
, true,
564 base::Bind(&DisplayConfigurator::OnConfigured
,
565 weak_ptr_factory_
.GetWeakPtr())));
566 configuration_task_
->Run();
569 DisplayConfigurator::ContentProtectionClientId
570 DisplayConfigurator::RegisterContentProtectionClient() {
571 if (!configure_display_
|| display_externally_controlled_
)
572 return kInvalidClientId
;
574 return next_display_protection_client_id_
++;
577 void DisplayConfigurator::UnregisterContentProtectionClient(
578 ContentProtectionClientId client_id
) {
579 client_protection_requests_
.erase(client_id
);
581 ContentProtections protections
;
582 for (const auto& requests_pair
: client_protection_requests_
) {
583 for (const auto& protections_pair
: requests_pair
.second
) {
584 protections
[protections_pair
.first
] |= protections_pair
.second
;
588 enable_protection_callbacks_
.push(base::Bind(&DoNothing
));
589 ApplyContentProtectionTask
* task
= new ApplyContentProtectionTask(
590 layout_manager_
.get(), native_display_delegate_
.get(), protections
,
591 base::Bind(&DisplayConfigurator::OnContentProtectionClientUnregistered
,
592 weak_ptr_factory_
.GetWeakPtr()));
593 content_protection_tasks_
.push(
594 base::Bind(&ApplyContentProtectionTask::Run
, base::Owned(task
)));
596 if (content_protection_tasks_
.size() == 1)
597 content_protection_tasks_
.front().Run();
600 void DisplayConfigurator::OnContentProtectionClientUnregistered(bool success
) {
601 DCHECK(!content_protection_tasks_
.empty());
602 content_protection_tasks_
.pop();
604 DCHECK(!enable_protection_callbacks_
.empty());
605 EnableProtectionCallback callback
= enable_protection_callbacks_
.front();
606 enable_protection_callbacks_
.pop();
608 if (!content_protection_tasks_
.empty())
609 content_protection_tasks_
.front().Run();
612 void DisplayConfigurator::QueryContentProtectionStatus(
613 ContentProtectionClientId client_id
,
615 const QueryProtectionCallback
& callback
) {
616 if (!configure_display_
|| display_externally_controlled_
) {
617 callback
.Run(QueryProtectionResponse());
621 query_protection_callbacks_
.push(callback
);
622 QueryContentProtectionTask
* task
= new QueryContentProtectionTask(
623 layout_manager_
.get(), native_display_delegate_
.get(), display_id
,
624 base::Bind(&DisplayConfigurator::OnContentProtectionQueried
,
625 weak_ptr_factory_
.GetWeakPtr(), client_id
, display_id
));
626 content_protection_tasks_
.push(
627 base::Bind(&QueryContentProtectionTask::Run
, base::Owned(task
)));
628 if (content_protection_tasks_
.size() == 1)
629 content_protection_tasks_
.front().Run();
632 void DisplayConfigurator::OnContentProtectionQueried(
633 ContentProtectionClientId client_id
,
635 QueryContentProtectionTask::Response task_response
) {
636 QueryProtectionResponse response
;
637 response
.success
= task_response
.success
;
638 response
.link_mask
= task_response
.link_mask
;
640 // Don't reveal protections requested by other clients.
641 ProtectionRequests::iterator it
= client_protection_requests_
.find(client_id
);
642 if (response
.success
&& it
!= client_protection_requests_
.end()) {
643 uint32_t requested_mask
= 0;
644 if (it
->second
.find(display_id
) != it
->second
.end())
645 requested_mask
= it
->second
[display_id
];
646 response
.protection_mask
=
647 task_response
.enabled
& ~task_response
.unfulfilled
& requested_mask
;
650 DCHECK(!content_protection_tasks_
.empty());
651 content_protection_tasks_
.pop();
653 DCHECK(!query_protection_callbacks_
.empty());
654 QueryProtectionCallback callback
= query_protection_callbacks_
.front();
655 query_protection_callbacks_
.pop();
656 callback
.Run(response
);
658 if (!content_protection_tasks_
.empty())
659 content_protection_tasks_
.front().Run();
662 void DisplayConfigurator::EnableContentProtection(
663 ContentProtectionClientId client_id
,
665 uint32_t desired_method_mask
,
666 const EnableProtectionCallback
& callback
) {
667 if (!configure_display_
|| display_externally_controlled_
) {
672 ContentProtections protections
;
673 for (const auto& requests_pair
: client_protection_requests_
) {
674 for (const auto& protections_pair
: requests_pair
.second
) {
675 if (requests_pair
.first
== client_id
&&
676 protections_pair
.first
== display_id
)
679 protections
[protections_pair
.first
] |= protections_pair
.second
;
682 protections
[display_id
] |= desired_method_mask
;
684 enable_protection_callbacks_
.push(callback
);
685 ApplyContentProtectionTask
* task
= new ApplyContentProtectionTask(
686 layout_manager_
.get(), native_display_delegate_
.get(), protections
,
687 base::Bind(&DisplayConfigurator::OnContentProtectionEnabled
,
688 weak_ptr_factory_
.GetWeakPtr(), client_id
, display_id
,
689 desired_method_mask
));
690 content_protection_tasks_
.push(
691 base::Bind(&ApplyContentProtectionTask::Run
, base::Owned(task
)));
692 if (content_protection_tasks_
.size() == 1)
693 content_protection_tasks_
.front().Run();
696 void DisplayConfigurator::OnContentProtectionEnabled(
697 ContentProtectionClientId client_id
,
699 uint32_t desired_method_mask
,
701 DCHECK(!content_protection_tasks_
.empty());
702 content_protection_tasks_
.pop();
704 DCHECK(!enable_protection_callbacks_
.empty());
705 EnableProtectionCallback callback
= enable_protection_callbacks_
.front();
706 enable_protection_callbacks_
.pop();
713 if (desired_method_mask
== CONTENT_PROTECTION_METHOD_NONE
) {
714 if (client_protection_requests_
.find(client_id
) !=
715 client_protection_requests_
.end()) {
716 client_protection_requests_
[client_id
].erase(display_id
);
717 if (client_protection_requests_
[client_id
].size() == 0)
718 client_protection_requests_
.erase(client_id
);
721 client_protection_requests_
[client_id
][display_id
] = desired_method_mask
;
725 if (!content_protection_tasks_
.empty())
726 content_protection_tasks_
.front().Run();
729 std::vector
<ui::ColorCalibrationProfile
>
730 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id
) {
731 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
732 switches::kDisableDisplayColorCalibration
)) {
733 for (const DisplaySnapshot
* display
: cached_displays_
) {
734 if (display
->display_id() == display_id
) {
735 return native_display_delegate_
->GetAvailableColorCalibrationProfiles(
741 return std::vector
<ui::ColorCalibrationProfile
>();
744 bool DisplayConfigurator::SetColorCalibrationProfile(
746 ui::ColorCalibrationProfile new_profile
) {
747 for (const DisplaySnapshot
* display
: cached_displays_
) {
748 if (display
->display_id() == display_id
) {
749 return native_display_delegate_
->SetColorCalibrationProfile(*display
,
757 bool DisplayConfigurator::SetGammaRamp(
759 const std::vector
<GammaRampRGBEntry
>& lut
) {
760 for (const DisplaySnapshot
* display
: cached_displays_
) {
761 if (display
->display_id() == display_id
)
762 return native_display_delegate_
->SetGammaRamp(*display
, lut
);
768 void DisplayConfigurator::PrepareForExit() {
769 configure_display_
= false;
772 void DisplayConfigurator::SetDisplayPower(
773 chromeos::DisplayPowerState power_state
,
775 const ConfigurationCallback
& callback
) {
776 if (!configure_display_
|| display_externally_controlled_
) {
781 VLOG(1) << "SetDisplayPower: power_state="
782 << DisplayPowerStateToString(power_state
) << " flags=" << flags
783 << ", configure timer="
784 << (configure_timer_
.IsRunning() ? "Running" : "Stopped");
785 if (power_state
== requested_power_state_
&&
786 !(flags
& kSetDisplayPowerForceProbe
)) {
791 requested_power_state_
= power_state
;
792 requested_power_state_change_
= true;
793 requested_power_flags_
= flags
;
794 queued_configuration_callbacks_
.push_back(callback
);
796 RunPendingConfiguration();
799 void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state
) {
800 if (!configure_display_
|| display_externally_controlled_
)
803 VLOG(1) << "SetDisplayMode: state="
804 << MultipleDisplayStateToString(new_state
);
805 if (current_display_state_
== new_state
) {
806 // Cancel software mirroring if the state is moving from
807 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
808 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
809 if (mirroring_controller_
&&
810 new_state
== MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
)
811 mirroring_controller_
->SetSoftwareMirroring(false);
812 NotifyObservers(true, new_state
);
816 requested_display_state_
= new_state
;
818 RunPendingConfiguration();
821 void DisplayConfigurator::OnConfigurationChanged() {
822 // Don't do anything if the displays are currently suspended. Instead we will
823 // probe and reconfigure the displays if necessary in ResumeDisplays().
824 if (displays_suspended_
) {
825 VLOG(1) << "Displays are currently suspended. Not attempting to "
826 << "reconfigure them.";
830 // Configure displays with |kConfigureDelayMs| delay,
831 // so that time-consuming ConfigureDisplays() won't be called multiple times.
832 if (configure_timer_
.IsRunning()) {
833 // Note: when the timer is running it is possible that a different task
834 // (RestoreRequestedPowerStateAfterResume()) is scheduled. In these cases,
835 // prefer the already scheduled task to ConfigureDisplays() since
836 // ConfigureDisplays() performs only basic configuration while
837 // RestoreRequestedPowerStateAfterResume() will perform additional
839 configure_timer_
.Reset();
841 configure_timer_
.Start(
843 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
845 &DisplayConfigurator::ConfigureDisplays
);
849 void DisplayConfigurator::AddObserver(Observer
* observer
) {
850 observers_
.AddObserver(observer
);
853 void DisplayConfigurator::RemoveObserver(Observer
* observer
) {
854 observers_
.RemoveObserver(observer
);
857 void DisplayConfigurator::SuspendDisplays(
858 const ConfigurationCallback
& callback
) {
859 // If the display is off due to user inactivity and there's only a single
860 // internal display connected, switch to the all-on state before
861 // suspending. This shouldn't be very noticeable to the user since the
862 // backlight is off at this point, and doing this lets us resume directly
863 // into the "on" state, which greatly reduces resume times.
864 if (requested_power_state_
== chromeos::DISPLAY_POWER_ALL_OFF
) {
865 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON
,
866 kSetDisplayPowerOnlyIfSingleInternalDisplay
, callback
);
868 // We need to make sure that the monitor configuration we just did actually
869 // completes before we return, because otherwise the X message could be
870 // racing with the HandleSuspendReadiness message.
871 native_display_delegate_
->SyncWithServer();
876 displays_suspended_
= true;
878 // Stop |configure_timer_| because we will force probe and configure all the
879 // displays at resume time anyway.
880 configure_timer_
.Stop();
883 void DisplayConfigurator::ResumeDisplays() {
884 displays_suspended_
= false;
886 configure_timer_
.Start(
888 base::TimeDelta::FromMilliseconds(kResumeDelayMs
),
889 base::Bind(&DisplayConfigurator::RestoreRequestedPowerStateAfterResume
,
890 base::Unretained(this)));
893 void DisplayConfigurator::ConfigureDisplays() {
894 if (!configure_display_
|| display_externally_controlled_
)
897 force_configure_
= true;
898 RunPendingConfiguration();
901 void DisplayConfigurator::RunPendingConfiguration() {
902 // Configuration task is currently running. Do not start a second
904 if (configuration_task_
)
907 if (!ShouldRunConfigurationTask()) {
908 LOG(ERROR
) << "Called RunPendingConfiguration without any changes"
910 CallAndClearQueuedCallbacks(true);
914 configuration_task_
.reset(new UpdateDisplayConfigurationTask(
915 native_display_delegate_
.get(), layout_manager_
.get(),
916 requested_display_state_
, requested_power_state_
, requested_power_flags_
,
917 0, force_configure_
, base::Bind(&DisplayConfigurator::OnConfigured
,
918 weak_ptr_factory_
.GetWeakPtr())));
920 // Reset the flags before running the task; otherwise it may end up scheduling
921 // another configuration.
922 force_configure_
= false;
923 requested_power_flags_
= kSetDisplayPowerNoFlags
;
924 requested_power_state_change_
= false;
925 requested_display_state_
= MULTIPLE_DISPLAY_STATE_INVALID
;
927 DCHECK(in_progress_configuration_callbacks_
.empty());
928 in_progress_configuration_callbacks_
.swap(queued_configuration_callbacks_
);
930 configuration_task_
->Run();
933 void DisplayConfigurator::OnConfigured(
935 const std::vector
<DisplaySnapshot
*>& displays
,
936 const gfx::Size
& framebuffer_size
,
937 MultipleDisplayState new_display_state
,
938 chromeos::DisplayPowerState new_power_state
) {
939 VLOG(1) << "OnConfigured: success=" << success
<< " new_display_state="
940 << MultipleDisplayStateToString(new_display_state
)
941 << " new_power_state=" << DisplayPowerStateToString(new_power_state
);
943 cached_displays_
= displays
;
945 current_display_state_
= new_display_state
;
946 current_power_state_
= new_power_state
;
948 // |framebuffer_size| is empty in software mirroring mode, headless mode,
949 // or all displays are off.
950 DCHECK(!framebuffer_size
.IsEmpty() ||
951 (mirroring_controller_
&&
952 mirroring_controller_
->SoftwareMirroringEnabled()) ||
953 new_display_state
== MULTIPLE_DISPLAY_STATE_HEADLESS
||
954 new_power_state
== chromeos::DISPLAY_POWER_ALL_OFF
);
956 if (!framebuffer_size
.IsEmpty())
957 framebuffer_size_
= framebuffer_size
;
959 // If the requested power state hasn't changed then make sure that value
960 // gets updated as well since the last requested value may have been
961 // dependent on certain conditions (ie: if only the internal monitor was
963 if (!requested_power_state_change_
)
964 requested_power_state_
= new_power_state
;
967 configuration_task_
.reset();
968 NotifyObservers(success
, new_display_state
);
969 CallAndClearInProgressCallbacks(success
);
971 if (success
&& !configure_timer_
.IsRunning() &&
972 ShouldRunConfigurationTask()) {
973 configure_timer_
.Start(FROM_HERE
,
974 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
975 this, &DisplayConfigurator::RunPendingConfiguration
);
977 // If a new configuration task isn't scheduled respond to all queued
978 // callbacks (for example if requested state is current state).
979 if (!configure_timer_
.IsRunning())
980 CallAndClearQueuedCallbacks(success
);
984 bool DisplayConfigurator::ShouldRunConfigurationTask() const {
985 if (force_configure_
)
988 // Schedule if there is a request to change the display state.
989 if (requested_display_state_
!= current_display_state_
&&
990 requested_display_state_
!= MULTIPLE_DISPLAY_STATE_INVALID
)
993 // Schedule if there is a request to change the power state.
994 if (requested_power_state_change_
)
1000 void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success
) {
1001 for (const auto& callback
: in_progress_configuration_callbacks_
)
1002 callback
.Run(success
);
1004 in_progress_configuration_callbacks_
.clear();
1007 void DisplayConfigurator::CallAndClearQueuedCallbacks(bool success
) {
1008 for (const auto& callback
: queued_configuration_callbacks_
)
1009 callback
.Run(success
);
1011 queued_configuration_callbacks_
.clear();
1014 void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() {
1015 // Force probing to ensure that we pick up any changes that were made while
1016 // the system was suspended.
1017 SetDisplayPower(requested_power_state_
, kSetDisplayPowerForceProbe
,
1018 base::Bind(&DoNothing
));
1021 void DisplayConfigurator::NotifyObservers(
1023 MultipleDisplayState attempted_state
) {
1026 Observer
, observers_
, OnDisplayModeChanged(cached_displays_
));
1029 Observer
, observers_
, OnDisplayModeChangeFailed(cached_displays_
,