Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / ui / display / chromeos / display_configurator.cc
blob151b8f1f331c617e3a5fb0b4f2ea2e2f174c9564
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/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"
21 namespace ui {
23 namespace {
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;
37 struct DisplayState {
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) {
50 } // namespace
52 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0;
53 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0;
54 const int
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();
61 return true;
62 } else {
63 return false;
67 ////////////////////////////////////////////////////////////////////////////////
68 // DisplayConfigurator::DisplayLayoutManagerImpl implementation
70 class DisplayConfigurator::DisplayLayoutManagerImpl
71 : public DisplayLayoutManager {
72 public:
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;
89 private:
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
107 // fitting).
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()
131 const {
132 return configurator_->mirroring_controller_;
135 DisplayConfigurator::StateController*
136 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const {
137 return configurator_->state_controller_;
140 MultipleDisplayState
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_,
183 preserve_aspect);
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.
218 gfx::Size size;
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)";
229 return false;
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)";
234 return false;
236 break;
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";
243 return false;
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;
252 if (!mode_info) {
253 LOG(WARNING) << "No selected mode when configuring display: "
254 << state->display->ToString();
255 return false;
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();
268 break;
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";
276 return false;
279 const DisplayMode* mode_info = states[0].mirror_mode;
280 if (!mode_info) {
281 LOG(WARNING) << "No mirror mode when configuring display: "
282 << states[0].display->ToString();
283 return false;
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;
291 break;
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";
304 return false;
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
310 : 0);
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
315 // turned back on.
316 const DisplayMode* mode_info = states[i].selected_mode;
317 if (!mode_info) {
318 LOG(WARNING) << "No selected mode when configuring display: "
319 << state->display->ToString();
320 return false;
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());
327 break;
330 DCHECK(new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS ||
331 !size.IsEmpty());
332 *framebuffer_size = size;
333 return true;
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)
343 return true;
345 return GetSoftwareMirroringController() &&
346 GetSoftwareMirroringController()->SoftwareMirroringEnabled();
349 const DisplayMode*
350 DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode(
351 const DisplaySnapshot& display) const {
352 gfx::Size size;
353 const DisplayMode* selected_mode = nullptr;
354 if (GetStateController() &&
355 GetStateController()->GetResolutionForDisplayId(display.display_id(),
356 &size)) {
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)
374 return false;
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();
406 if (can_fit) {
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.
417 return false;
420 ////////////////////////////////////////////////////////////////////////////////
421 // DisplayConfigurator implementation
423 // static
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)
430 continue;
432 if (mode == display.native_mode()) {
433 best_mode = mode;
434 break;
437 if (!best_mode) {
438 best_mode = mode;
439 continue;
442 if (mode->is_interlaced()) {
443 if (!best_mode->is_interlaced())
444 continue;
445 } else {
446 // Reset the best rate if the non interlaced is
447 // found the first time.
448 if (best_mode->is_interlaced()) {
449 best_mode = mode;
450 continue;
453 if (mode->refresh_rate() < best_mode->refresh_rate())
454 continue;
456 best_mode = mode;
459 return best_mode;
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_)
516 return;
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())
528 return;
530 if (!display_externally_controlled_)
531 return;
533 if (!native_display_delegate_->TakeDisplayControl())
534 return;
536 display_externally_controlled_ = false;
537 force_configure_ = true;
538 RunPendingConfiguration();
541 void DisplayConfigurator::RelinquishControl() {
542 if (display_externally_controlled_)
543 return;
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_)
552 return;
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,
614 int64_t display_id,
615 const QueryProtectionCallback& callback) {
616 if (!configure_display_ || display_externally_controlled_) {
617 callback.Run(QueryProtectionResponse());
618 return;
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,
634 int64_t display_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,
664 int64_t display_id,
665 uint32_t desired_method_mask,
666 const EnableProtectionCallback& callback) {
667 if (!configure_display_ || display_externally_controlled_) {
668 callback.Run(false);
669 return;
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)
677 continue;
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,
698 int64_t display_id,
699 uint32_t desired_method_mask,
700 bool success) {
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();
708 if (!success) {
709 callback.Run(false);
710 return;
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);
720 } else {
721 client_protection_requests_[client_id][display_id] = desired_method_mask;
724 callback.Run(true);
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(
736 *display);
741 return std::vector<ui::ColorCalibrationProfile>();
744 bool DisplayConfigurator::SetColorCalibrationProfile(
745 int64_t display_id,
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,
750 new_profile);
754 return false;
757 bool DisplayConfigurator::SetGammaRamp(
758 int64_t display_id,
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);
765 return false;
768 void DisplayConfigurator::PrepareForExit() {
769 configure_display_ = false;
772 void DisplayConfigurator::SetDisplayPower(
773 chromeos::DisplayPowerState power_state,
774 int flags,
775 const ConfigurationCallback& callback) {
776 if (!configure_display_ || display_externally_controlled_) {
777 callback.Run(false);
778 return;
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)) {
787 callback.Run(true);
788 return;
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_)
801 return;
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);
813 return;
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.";
827 return;
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
838 // operations.
839 configure_timer_.Reset();
840 } else {
841 configure_timer_.Start(
842 FROM_HERE,
843 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
844 this,
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();
872 } else {
873 callback.Run(true);
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(
887 FROM_HERE,
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_)
895 return;
897 force_configure_ = true;
898 RunPendingConfiguration();
901 void DisplayConfigurator::RunPendingConfiguration() {
902 // Configuration task is currently running. Do not start a second
903 // configuration.
904 if (configuration_task_)
905 return;
907 if (!ShouldRunConfigurationTask()) {
908 LOG(ERROR) << "Called RunPendingConfiguration without any changes"
909 " requested";
910 CallAndClearQueuedCallbacks(true);
911 return;
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(
934 bool success,
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;
944 if (success) {
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
962 // present).
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);
976 } else {
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_)
986 return true;
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)
991 return true;
993 // Schedule if there is a request to change the power state.
994 if (requested_power_state_change_)
995 return true;
997 return false;
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(
1022 bool success,
1023 MultipleDisplayState attempted_state) {
1024 if (success) {
1025 FOR_EACH_OBSERVER(
1026 Observer, observers_, OnDisplayModeChanged(cached_displays_));
1027 } else {
1028 FOR_EACH_OBSERVER(
1029 Observer, observers_, OnDisplayModeChangeFailed(cached_displays_,
1030 attempted_state));
1034 } // namespace ui