Remove unused parameter.
[chromium-blink-merge.git] / ui / display / chromeos / display_configurator.cc
blob8b1f1f7c22dcb089d4bc5e6d91f2c7b42543ed35
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"
7 #include "base/bind.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"
19 namespace ui {
21 namespace {
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 struct DisplayState {
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) {
48 } // namespace
50 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0;
51 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0;
52 const int
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();
59 return true;
60 } else {
61 return false;
65 ////////////////////////////////////////////////////////////////////////////////
66 // DisplayConfigurator::DisplayLayoutManagerImpl implementation
68 class DisplayConfigurator::DisplayLayoutManagerImpl
69 : public DisplayLayoutManager {
70 public:
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;
85 private:
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
103 // fitting).
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()
127 const {
128 return configurator_->mirroring_controller_;
131 DisplayConfigurator::StateController*
132 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const {
133 return configurator_->state_controller_;
136 MultipleDisplayState
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_,
179 preserve_aspect);
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.
214 gfx::Size size;
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)";
225 return false;
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)";
230 return false;
232 break;
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";
239 return false;
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;
248 if (!mode_info) {
249 LOG(WARNING) << "No selected mode when configuring display: "
250 << state->display->ToString();
251 return false;
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();
264 break;
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";
272 return false;
275 const DisplayMode* mode_info = states[0].mirror_mode;
276 if (!mode_info) {
277 LOG(WARNING) << "No mirror mode when configuring display: "
278 << states[0].display->ToString();
279 return false;
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;
287 break;
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";
300 return false;
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
306 : 0);
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
311 // turned back on.
312 const DisplayMode* mode_info = states[i].selected_mode;
313 if (!mode_info) {
314 LOG(WARNING) << "No selected mode when configuring display: "
315 << state->display->ToString();
316 return false;
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());
323 break;
326 DCHECK(new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS ||
327 !size.IsEmpty());
328 *framebuffer_size = size;
329 return true;
332 const DisplayMode*
333 DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode(
334 const DisplaySnapshot& display) const {
335 gfx::Size size;
336 const DisplayMode* selected_mode = nullptr;
337 if (GetStateController() &&
338 GetStateController()->GetResolutionForDisplayId(display.display_id(),
339 &size)) {
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)
357 return false;
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();
389 if (can_fit) {
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.
400 return false;
403 ////////////////////////////////////////////////////////////////////////////////
404 // DisplayConfigurator implementation
406 // static
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)
413 continue;
415 if (mode == display.native_mode()) {
416 best_mode = mode;
417 break;
420 if (!best_mode) {
421 best_mode = mode;
422 continue;
425 if (mode->is_interlaced()) {
426 if (!best_mode->is_interlaced())
427 continue;
428 } else {
429 // Reset the best rate if the non interlaced is
430 // found the first time.
431 if (best_mode->is_interlaced()) {
432 best_mode = mode;
433 continue;
436 if (mode->refresh_rate() < best_mode->refresh_rate())
437 continue;
439 best_mode = mode;
442 return best_mode;
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_)
489 return;
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())
501 return;
503 if (!display_externally_controlled_)
504 return;
506 if (!native_display_delegate_->TakeDisplayControl())
507 return;
509 display_externally_controlled_ = false;
510 force_configure_ = true;
511 RunPendingConfiguration();
514 void DisplayConfigurator::RelinquishControl() {
515 if (display_externally_controlled_)
516 return;
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_)
525 return;
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
554 // fulfilled.
555 if (IsMirroring()) {
556 for (const auto& protections_pair : requests)
557 all_desired |= protections_pair.second;
558 } else {
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:
567 return false;
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, &current_state))
576 return false;
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))
585 return false;
587 break;
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.
593 break;
594 case DISPLAY_CONNECTION_TYPE_NONE:
595 NOTREACHED();
596 break;
600 return true;
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,
627 int64_t display_id,
628 uint32_t* link_mask,
629 uint32_t* protection_mask) {
630 if (!configure_display_ || display_externally_controlled_)
631 return false;
633 uint32_t enabled = 0;
634 uint32_t unfulfilled = 0;
635 *link_mask = 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)
639 continue;
641 *link_mask |= display->type();
642 switch (display->type()) {
643 case DISPLAY_CONNECTION_TYPE_UNKNOWN:
644 return false;
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: {
649 HDCPState state;
650 if (!native_display_delegate_->GetHDCPState(*display, &state))
651 return false;
652 if (state == HDCP_STATE_ENABLED)
653 enabled |= CONTENT_PROTECTION_METHOD_HDCP;
654 else
655 unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP;
656 break;
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.
662 break;
663 case DISPLAY_CONNECTION_TYPE_NONE:
664 NOTREACHED();
665 break;
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;
676 } else {
677 *protection_mask = 0;
679 return true;
682 bool DisplayConfigurator::EnableContentProtection(
683 ContentProtectionClientId client_id,
684 int64_t display_id,
685 uint32_t desired_method_mask) {
686 if (!configure_display_ || display_externally_controlled_)
687 return false;
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)
694 continue;
696 protections[protections_pair.first] |= protections_pair.second;
699 protections[display_id] |= desired_method_mask;
701 if (!ApplyProtections(protections))
702 return false;
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);
711 } else {
712 client_protection_requests_[client_id][display_id] = desired_method_mask;
715 return true;
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(
725 *display);
730 return std::vector<ui::ColorCalibrationProfile>();
733 bool DisplayConfigurator::SetColorCalibrationProfile(
734 int64_t display_id,
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,
739 new_profile);
743 return false;
746 void DisplayConfigurator::PrepareForExit() {
747 configure_display_ = false;
750 void DisplayConfigurator::SetDisplayPower(
751 chromeos::DisplayPowerState power_state,
752 int flags,
753 const ConfigurationCallback& callback) {
754 if (!configure_display_ || display_externally_controlled_) {
755 callback.Run(false);
756 return;
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)) {
765 callback.Run(true);
766 return;
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_)
779 return;
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);
791 return;
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.";
805 return;
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
816 // operations.
817 configure_timer_.Reset();
818 } else {
819 configure_timer_.Start(
820 FROM_HERE,
821 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
822 this,
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(
863 FROM_HERE,
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_)
871 return;
873 force_configure_ = true;
874 RunPendingConfiguration();
877 void DisplayConfigurator::RunPendingConfiguration() {
878 // Configuration task is currently running. Do not start a second
879 // configuration.
880 if (configuration_task_)
881 return;
883 if (!ShouldRunConfigurationTask()) {
884 LOG(ERROR) << "Called RunPendingConfiguration without any changes"
885 " requested";
886 CallAndClearQueuedCallbacks(true);
887 return;
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(
910 bool success,
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;
920 if (success) {
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
938 // present).
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);
952 } else {
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_)
962 return true;
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)
967 return true;
969 // Schedule if there is a request to change the power state.
970 if (requested_power_state_change_)
971 return true;
973 return false;
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(
998 bool success,
999 MultipleDisplayState attempted_state) {
1000 if (success) {
1001 FOR_EACH_OBSERVER(
1002 Observer, observers_, OnDisplayModeChanged(cached_displays_));
1003 } else {
1004 FOR_EACH_OBSERVER(
1005 Observer, observers_, OnDisplayModeChangeFailed(cached_displays_,
1006 attempted_state));
1010 } // namespace ui