Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / ui / display / chromeos / display_configurator.cc
blob68c026ea0c6f8d9d41cca7660f75f6e96b117d80
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 display_control_changing_(false),
477 displays_suspended_(false),
478 layout_manager_(new DisplayLayoutManagerImpl(this)),
479 weak_ptr_factory_(this) {
482 DisplayConfigurator::~DisplayConfigurator() {
483 if (native_display_delegate_)
484 native_display_delegate_->RemoveObserver(this);
486 CallAndClearInProgressCallbacks(false);
487 CallAndClearQueuedCallbacks(false);
489 while (!query_protection_callbacks_.empty()) {
490 query_protection_callbacks_.front().Run(QueryProtectionResponse());
491 query_protection_callbacks_.pop();
494 while (!enable_protection_callbacks_.empty()) {
495 enable_protection_callbacks_.front().Run(false);
496 enable_protection_callbacks_.pop();
500 void DisplayConfigurator::SetDelegateForTesting(
501 scoped_ptr<NativeDisplayDelegate> display_delegate) {
502 DCHECK(!native_display_delegate_);
504 native_display_delegate_ = display_delegate.Pass();
505 configure_display_ = true;
508 void DisplayConfigurator::SetInitialDisplayPower(
509 chromeos::DisplayPowerState power_state) {
510 DCHECK_EQ(current_display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
511 requested_power_state_ = current_power_state_ = power_state;
514 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) {
515 is_panel_fitting_enabled_ = is_panel_fitting_enabled;
516 if (!configure_display_ || display_externally_controlled_)
517 return;
519 // If the delegate is already initialized don't update it (For example, tests
520 // set their own delegates).
521 if (!native_display_delegate_) {
522 native_display_delegate_ = CreatePlatformNativeDisplayDelegate();
523 native_display_delegate_->AddObserver(this);
527 void DisplayConfigurator::TakeControl(const DisplayControlCallback& callback) {
528 if (display_control_changing_) {
529 callback.Run(false);
530 return;
533 if (!display_externally_controlled_) {
534 callback.Run(true);
535 return;
538 display_control_changing_ = true;
539 native_display_delegate_->TakeDisplayControl(
540 base::Bind(&DisplayConfigurator::OnDisplayControlTaken,
541 weak_ptr_factory_.GetWeakPtr(), callback));
544 void DisplayConfigurator::OnDisplayControlTaken(
545 const DisplayControlCallback& callback,
546 bool success) {
547 display_control_changing_ = false;
548 display_externally_controlled_ = !success;
549 if (success) {
550 force_configure_ = true;
551 RunPendingConfiguration();
554 callback.Run(success);
557 void DisplayConfigurator::RelinquishControl(
558 const DisplayControlCallback& callback) {
559 if (display_control_changing_) {
560 callback.Run(false);
561 return;
564 if (display_externally_controlled_) {
565 callback.Run(true);
566 return;
569 // For simplicity, just fail if in the middle of a display configuration.
570 if (configuration_task_) {
571 callback.Run(false);
572 return;
575 // Set the flag early such that an incoming configuration event won't start
576 // while we're releasing control of the displays.
577 display_control_changing_ = true;
578 display_externally_controlled_ = true;
579 native_display_delegate_->RelinquishDisplayControl(
580 base::Bind(&DisplayConfigurator::OnDisplayControlRelinquished,
581 weak_ptr_factory_.GetWeakPtr(), callback));
584 void DisplayConfigurator::OnDisplayControlRelinquished(
585 const DisplayControlCallback& callback,
586 bool success) {
587 display_control_changing_ = false;
588 display_externally_controlled_ = success;
589 if (!success) {
590 force_configure_ = true;
591 RunPendingConfiguration();
594 callback.Run(success);
597 void DisplayConfigurator::ForceInitialConfigure(
598 uint32_t background_color_argb) {
599 if (!configure_display_ || display_externally_controlled_)
600 return;
602 native_display_delegate_->Initialize();
604 // ForceInitialConfigure should be the first configuration so there shouldn't
605 // be anything scheduled.
606 DCHECK(!configuration_task_);
608 configuration_task_.reset(new UpdateDisplayConfigurationTask(
609 native_display_delegate_.get(), layout_manager_.get(),
610 requested_display_state_, requested_power_state_,
611 kSetDisplayPowerForceProbe, background_color_argb, true,
612 base::Bind(&DisplayConfigurator::OnConfigured,
613 weak_ptr_factory_.GetWeakPtr())));
614 configuration_task_->Run();
617 DisplayConfigurator::ContentProtectionClientId
618 DisplayConfigurator::RegisterContentProtectionClient() {
619 if (!configure_display_ || display_externally_controlled_)
620 return kInvalidClientId;
622 return next_display_protection_client_id_++;
625 void DisplayConfigurator::UnregisterContentProtectionClient(
626 ContentProtectionClientId client_id) {
627 client_protection_requests_.erase(client_id);
629 ContentProtections protections;
630 for (const auto& requests_pair : client_protection_requests_) {
631 for (const auto& protections_pair : requests_pair.second) {
632 protections[protections_pair.first] |= protections_pair.second;
636 enable_protection_callbacks_.push(base::Bind(&DoNothing));
637 ApplyContentProtectionTask* task = new ApplyContentProtectionTask(
638 layout_manager_.get(), native_display_delegate_.get(), protections,
639 base::Bind(&DisplayConfigurator::OnContentProtectionClientUnregistered,
640 weak_ptr_factory_.GetWeakPtr()));
641 content_protection_tasks_.push(
642 base::Bind(&ApplyContentProtectionTask::Run, base::Owned(task)));
644 if (content_protection_tasks_.size() == 1)
645 content_protection_tasks_.front().Run();
648 void DisplayConfigurator::OnContentProtectionClientUnregistered(bool success) {
649 DCHECK(!content_protection_tasks_.empty());
650 content_protection_tasks_.pop();
652 DCHECK(!enable_protection_callbacks_.empty());
653 EnableProtectionCallback callback = enable_protection_callbacks_.front();
654 enable_protection_callbacks_.pop();
656 if (!content_protection_tasks_.empty())
657 content_protection_tasks_.front().Run();
660 void DisplayConfigurator::QueryContentProtectionStatus(
661 ContentProtectionClientId client_id,
662 int64_t display_id,
663 const QueryProtectionCallback& callback) {
664 if (!configure_display_ || display_externally_controlled_) {
665 callback.Run(QueryProtectionResponse());
666 return;
669 query_protection_callbacks_.push(callback);
670 QueryContentProtectionTask* task = new QueryContentProtectionTask(
671 layout_manager_.get(), native_display_delegate_.get(), display_id,
672 base::Bind(&DisplayConfigurator::OnContentProtectionQueried,
673 weak_ptr_factory_.GetWeakPtr(), client_id, display_id));
674 content_protection_tasks_.push(
675 base::Bind(&QueryContentProtectionTask::Run, base::Owned(task)));
676 if (content_protection_tasks_.size() == 1)
677 content_protection_tasks_.front().Run();
680 void DisplayConfigurator::OnContentProtectionQueried(
681 ContentProtectionClientId client_id,
682 int64_t display_id,
683 QueryContentProtectionTask::Response task_response) {
684 QueryProtectionResponse response;
685 response.success = task_response.success;
686 response.link_mask = task_response.link_mask;
688 // Don't reveal protections requested by other clients.
689 ProtectionRequests::iterator it = client_protection_requests_.find(client_id);
690 if (response.success && it != client_protection_requests_.end()) {
691 uint32_t requested_mask = 0;
692 if (it->second.find(display_id) != it->second.end())
693 requested_mask = it->second[display_id];
694 response.protection_mask =
695 task_response.enabled & ~task_response.unfulfilled & requested_mask;
698 DCHECK(!content_protection_tasks_.empty());
699 content_protection_tasks_.pop();
701 DCHECK(!query_protection_callbacks_.empty());
702 QueryProtectionCallback callback = query_protection_callbacks_.front();
703 query_protection_callbacks_.pop();
704 callback.Run(response);
706 if (!content_protection_tasks_.empty())
707 content_protection_tasks_.front().Run();
710 void DisplayConfigurator::EnableContentProtection(
711 ContentProtectionClientId client_id,
712 int64_t display_id,
713 uint32_t desired_method_mask,
714 const EnableProtectionCallback& callback) {
715 if (!configure_display_ || display_externally_controlled_) {
716 callback.Run(false);
717 return;
720 ContentProtections protections;
721 for (const auto& requests_pair : client_protection_requests_) {
722 for (const auto& protections_pair : requests_pair.second) {
723 if (requests_pair.first == client_id &&
724 protections_pair.first == display_id)
725 continue;
727 protections[protections_pair.first] |= protections_pair.second;
730 protections[display_id] |= desired_method_mask;
732 enable_protection_callbacks_.push(callback);
733 ApplyContentProtectionTask* task = new ApplyContentProtectionTask(
734 layout_manager_.get(), native_display_delegate_.get(), protections,
735 base::Bind(&DisplayConfigurator::OnContentProtectionEnabled,
736 weak_ptr_factory_.GetWeakPtr(), client_id, display_id,
737 desired_method_mask));
738 content_protection_tasks_.push(
739 base::Bind(&ApplyContentProtectionTask::Run, base::Owned(task)));
740 if (content_protection_tasks_.size() == 1)
741 content_protection_tasks_.front().Run();
744 void DisplayConfigurator::OnContentProtectionEnabled(
745 ContentProtectionClientId client_id,
746 int64_t display_id,
747 uint32_t desired_method_mask,
748 bool success) {
749 DCHECK(!content_protection_tasks_.empty());
750 content_protection_tasks_.pop();
752 DCHECK(!enable_protection_callbacks_.empty());
753 EnableProtectionCallback callback = enable_protection_callbacks_.front();
754 enable_protection_callbacks_.pop();
756 if (!success) {
757 callback.Run(false);
758 return;
761 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) {
762 if (client_protection_requests_.find(client_id) !=
763 client_protection_requests_.end()) {
764 client_protection_requests_[client_id].erase(display_id);
765 if (client_protection_requests_[client_id].size() == 0)
766 client_protection_requests_.erase(client_id);
768 } else {
769 client_protection_requests_[client_id][display_id] = desired_method_mask;
772 callback.Run(true);
773 if (!content_protection_tasks_.empty())
774 content_protection_tasks_.front().Run();
777 std::vector<ui::ColorCalibrationProfile>
778 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) {
779 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
780 switches::kDisableDisplayColorCalibration)) {
781 for (const DisplaySnapshot* display : cached_displays_) {
782 if (display->display_id() == display_id) {
783 return native_display_delegate_->GetAvailableColorCalibrationProfiles(
784 *display);
789 return std::vector<ui::ColorCalibrationProfile>();
792 bool DisplayConfigurator::SetColorCalibrationProfile(
793 int64_t display_id,
794 ui::ColorCalibrationProfile new_profile) {
795 for (const DisplaySnapshot* display : cached_displays_) {
796 if (display->display_id() == display_id) {
797 return native_display_delegate_->SetColorCalibrationProfile(*display,
798 new_profile);
802 return false;
805 bool DisplayConfigurator::SetGammaRamp(
806 int64_t display_id,
807 const std::vector<GammaRampRGBEntry>& lut) {
808 for (const DisplaySnapshot* display : cached_displays_) {
809 if (display->display_id() == display_id)
810 return native_display_delegate_->SetGammaRamp(*display, lut);
813 return false;
816 void DisplayConfigurator::PrepareForExit() {
817 configure_display_ = false;
820 void DisplayConfigurator::SetDisplayPower(
821 chromeos::DisplayPowerState power_state,
822 int flags,
823 const ConfigurationCallback& callback) {
824 if (!configure_display_ || display_externally_controlled_) {
825 callback.Run(false);
826 return;
829 VLOG(1) << "SetDisplayPower: power_state="
830 << DisplayPowerStateToString(power_state) << " flags=" << flags
831 << ", configure timer="
832 << (configure_timer_.IsRunning() ? "Running" : "Stopped");
833 if (power_state == requested_power_state_ &&
834 !(flags & kSetDisplayPowerForceProbe)) {
835 callback.Run(true);
836 return;
839 requested_power_state_ = power_state;
840 requested_power_state_change_ = true;
841 requested_power_flags_ = flags;
842 queued_configuration_callbacks_.push_back(callback);
844 RunPendingConfiguration();
847 void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
848 if (!configure_display_ || display_externally_controlled_)
849 return;
851 VLOG(1) << "SetDisplayMode: state="
852 << MultipleDisplayStateToString(new_state);
853 if (current_display_state_ == new_state) {
854 // Cancel software mirroring if the state is moving from
855 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
856 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
857 if (mirroring_controller_ &&
858 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
859 mirroring_controller_->SetSoftwareMirroring(false);
860 NotifyObservers(true, new_state);
861 return;
864 requested_display_state_ = new_state;
866 RunPendingConfiguration();
869 void DisplayConfigurator::OnConfigurationChanged() {
870 // Don't do anything if the displays are currently suspended. Instead we will
871 // probe and reconfigure the displays if necessary in ResumeDisplays().
872 if (displays_suspended_) {
873 VLOG(1) << "Displays are currently suspended. Not attempting to "
874 << "reconfigure them.";
875 return;
878 // Configure displays with |kConfigureDelayMs| delay,
879 // so that time-consuming ConfigureDisplays() won't be called multiple times.
880 if (configure_timer_.IsRunning()) {
881 // Note: when the timer is running it is possible that a different task
882 // (RestoreRequestedPowerStateAfterResume()) is scheduled. In these cases,
883 // prefer the already scheduled task to ConfigureDisplays() since
884 // ConfigureDisplays() performs only basic configuration while
885 // RestoreRequestedPowerStateAfterResume() will perform additional
886 // operations.
887 configure_timer_.Reset();
888 } else {
889 configure_timer_.Start(
890 FROM_HERE,
891 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
892 this,
893 &DisplayConfigurator::ConfigureDisplays);
897 void DisplayConfigurator::AddObserver(Observer* observer) {
898 observers_.AddObserver(observer);
901 void DisplayConfigurator::RemoveObserver(Observer* observer) {
902 observers_.RemoveObserver(observer);
905 void DisplayConfigurator::SuspendDisplays(
906 const ConfigurationCallback& callback) {
907 // If the display is off due to user inactivity and there's only a single
908 // internal display connected, switch to the all-on state before
909 // suspending. This shouldn't be very noticeable to the user since the
910 // backlight is off at this point, and doing this lets us resume directly
911 // into the "on" state, which greatly reduces resume times.
912 if (requested_power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) {
913 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
914 kSetDisplayPowerOnlyIfSingleInternalDisplay, callback);
916 // We need to make sure that the monitor configuration we just did actually
917 // completes before we return, because otherwise the X message could be
918 // racing with the HandleSuspendReadiness message.
919 native_display_delegate_->SyncWithServer();
920 } else {
921 callback.Run(true);
924 displays_suspended_ = true;
926 // Stop |configure_timer_| because we will force probe and configure all the
927 // displays at resume time anyway.
928 configure_timer_.Stop();
931 void DisplayConfigurator::ResumeDisplays() {
932 displays_suspended_ = false;
934 configure_timer_.Start(
935 FROM_HERE,
936 base::TimeDelta::FromMilliseconds(kResumeDelayMs),
937 base::Bind(&DisplayConfigurator::RestoreRequestedPowerStateAfterResume,
938 base::Unretained(this)));
941 void DisplayConfigurator::ConfigureDisplays() {
942 if (!configure_display_ || display_externally_controlled_)
943 return;
945 force_configure_ = true;
946 RunPendingConfiguration();
949 void DisplayConfigurator::RunPendingConfiguration() {
950 // Configuration task is currently running. Do not start a second
951 // configuration.
952 if (configuration_task_)
953 return;
955 if (!ShouldRunConfigurationTask()) {
956 LOG(ERROR) << "Called RunPendingConfiguration without any changes"
957 " requested";
958 CallAndClearQueuedCallbacks(true);
959 return;
962 configuration_task_.reset(new UpdateDisplayConfigurationTask(
963 native_display_delegate_.get(), layout_manager_.get(),
964 requested_display_state_, requested_power_state_, requested_power_flags_,
965 0, force_configure_, base::Bind(&DisplayConfigurator::OnConfigured,
966 weak_ptr_factory_.GetWeakPtr())));
968 // Reset the flags before running the task; otherwise it may end up scheduling
969 // another configuration.
970 force_configure_ = false;
971 requested_power_flags_ = kSetDisplayPowerNoFlags;
972 requested_power_state_change_ = false;
973 requested_display_state_ = MULTIPLE_DISPLAY_STATE_INVALID;
975 DCHECK(in_progress_configuration_callbacks_.empty());
976 in_progress_configuration_callbacks_.swap(queued_configuration_callbacks_);
978 configuration_task_->Run();
981 void DisplayConfigurator::OnConfigured(
982 bool success,
983 const std::vector<DisplaySnapshot*>& displays,
984 const gfx::Size& framebuffer_size,
985 MultipleDisplayState new_display_state,
986 chromeos::DisplayPowerState new_power_state) {
987 VLOG(1) << "OnConfigured: success=" << success << " new_display_state="
988 << MultipleDisplayStateToString(new_display_state)
989 << " new_power_state=" << DisplayPowerStateToString(new_power_state);
991 cached_displays_ = displays;
992 if (success) {
993 current_display_state_ = new_display_state;
994 current_power_state_ = new_power_state;
996 // |framebuffer_size| is empty in software mirroring mode, headless mode,
997 // or all displays are off.
998 DCHECK(!framebuffer_size.IsEmpty() ||
999 (mirroring_controller_ &&
1000 mirroring_controller_->SoftwareMirroringEnabled()) ||
1001 new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS ||
1002 new_power_state == chromeos::DISPLAY_POWER_ALL_OFF);
1004 if (!framebuffer_size.IsEmpty())
1005 framebuffer_size_ = framebuffer_size;
1007 // If the requested power state hasn't changed then make sure that value
1008 // gets updated as well since the last requested value may have been
1009 // dependent on certain conditions (ie: if only the internal monitor was
1010 // present).
1011 if (!requested_power_state_change_)
1012 requested_power_state_ = new_power_state;
1015 configuration_task_.reset();
1016 NotifyObservers(success, new_display_state);
1017 CallAndClearInProgressCallbacks(success);
1019 if (success && !configure_timer_.IsRunning() &&
1020 ShouldRunConfigurationTask()) {
1021 configure_timer_.Start(FROM_HERE,
1022 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
1023 this, &DisplayConfigurator::RunPendingConfiguration);
1024 } else {
1025 // If a new configuration task isn't scheduled respond to all queued
1026 // callbacks (for example if requested state is current state).
1027 if (!configure_timer_.IsRunning())
1028 CallAndClearQueuedCallbacks(success);
1032 bool DisplayConfigurator::ShouldRunConfigurationTask() const {
1033 if (force_configure_)
1034 return true;
1036 // Schedule if there is a request to change the display state.
1037 if (requested_display_state_ != current_display_state_ &&
1038 requested_display_state_ != MULTIPLE_DISPLAY_STATE_INVALID)
1039 return true;
1041 // Schedule if there is a request to change the power state.
1042 if (requested_power_state_change_)
1043 return true;
1045 return false;
1048 void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success) {
1049 for (const auto& callback : in_progress_configuration_callbacks_)
1050 callback.Run(success);
1052 in_progress_configuration_callbacks_.clear();
1055 void DisplayConfigurator::CallAndClearQueuedCallbacks(bool success) {
1056 for (const auto& callback : queued_configuration_callbacks_)
1057 callback.Run(success);
1059 queued_configuration_callbacks_.clear();
1062 void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() {
1063 // Force probing to ensure that we pick up any changes that were made while
1064 // the system was suspended.
1065 SetDisplayPower(requested_power_state_, kSetDisplayPowerForceProbe,
1066 base::Bind(&DoNothing));
1069 void DisplayConfigurator::NotifyObservers(
1070 bool success,
1071 MultipleDisplayState attempted_state) {
1072 if (success) {
1073 FOR_EACH_OBSERVER(
1074 Observer, observers_, OnDisplayModeChanged(cached_displays_));
1075 } else {
1076 FOR_EACH_OBSERVER(
1077 Observer, observers_, OnDisplayModeChangeFailed(cached_displays_,
1078 attempted_state));
1082 } // namespace ui