Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: ui/display/chromeos/display_configurator.cc

Issue 2540313002: Split //ui/display and create //ui/display/manager. (Closed)
Patch Set: Cleanup export header. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "ui/display/chromeos/display_configurator.h"
6
7 #include <stddef.h>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/sys_info.h"
15 #include "base/time/time.h"
16 #include "ui/display/chromeos/apply_content_protection_task.h"
17 #include "ui/display/chromeos/display_layout_manager.h"
18 #include "ui/display/chromeos/display_snapshot_virtual.h"
19 #include "ui/display/chromeos/display_util.h"
20 #include "ui/display/chromeos/update_display_configuration_task.h"
21 #include "ui/display/display.h"
22 #include "ui/display/display_switches.h"
23 #include "ui/display/types/display_mode.h"
24 #include "ui/display/types/display_snapshot.h"
25 #include "ui/display/types/native_display_delegate.h"
26 #include "ui/display/util/display_util.h"
27
28 namespace ui {
29
30 namespace {
31
32 typedef std::vector<const DisplayMode*> DisplayModeList;
33
34 // The EDID specification marks the top bit of the manufacturer id as reserved.
35 const int16_t kReservedManufacturerID = static_cast<int16_t>(1 << 15);
36
37 struct DisplayState {
38 DisplaySnapshot* display = nullptr; // Not owned.
39
40 // User-selected mode for the display.
41 const DisplayMode* selected_mode = nullptr;
42
43 // Mode used when displaying the same desktop on multiple displays.
44 const DisplayMode* mirror_mode = nullptr;
45 };
46
47 void DoNothing(bool status) {
48 }
49
50 } // namespace
51
52 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0;
53 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0;
54 const int
55 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1;
56
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;
64 }
65 }
66
67 base::TimeDelta DisplayConfigurator::TestApi::GetConfigureDelay() const {
68 return configurator_->configure_timer_.IsRunning()
69 ? configurator_->configure_timer_.GetCurrentDelay()
70 : base::TimeDelta();
71 }
72
73 ////////////////////////////////////////////////////////////////////////////////
74 // DisplayConfigurator::DisplayLayoutManagerImpl implementation
75
76 class DisplayConfigurator::DisplayLayoutManagerImpl
77 : public DisplayLayoutManager {
78 public:
79 DisplayLayoutManagerImpl(DisplayConfigurator* configurator);
80 ~DisplayLayoutManagerImpl() override;
81
82 // DisplayConfigurator::DisplayLayoutManager:
83 SoftwareMirroringController* GetSoftwareMirroringController() const override;
84 StateController* GetStateController() const override;
85 MultipleDisplayState GetDisplayState() const override;
86 chromeos::DisplayPowerState GetPowerState() const override;
87 bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays,
88 MultipleDisplayState new_display_state,
89 chromeos::DisplayPowerState new_power_state,
90 std::vector<DisplayConfigureRequest>* requests,
91 gfx::Size* framebuffer_size) const override;
92 DisplayStateList GetDisplayStates() const override;
93 bool IsMirroring() const override;
94
95 private:
96 // Parses the |displays| into a list of DisplayStates. This effectively adds
97 // |mirror_mode| and |selected_mode| to the returned results.
98 // TODO(dnicoara): Break this into GetSelectedMode() and GetMirrorMode() and
99 // remove DisplayState.
100 std::vector<DisplayState> ParseDisplays(
101 const std::vector<DisplaySnapshot*>& displays) const;
102
103 const DisplayMode* GetUserSelectedMode(const DisplaySnapshot& display) const;
104
105 // Helper method for ParseDisplays() that initializes the passed-in
106 // displays' |mirror_mode| fields by looking for a mode in |internal_display|
107 // and |external_display| having the same resolution. Returns false if a
108 // shared mode wasn't found or created.
109 //
110 // |try_panel_fitting| allows creating a panel-fitting mode for
111 // |internal_display| instead of only searching for a matching mode (note that
112 // it may lead to a crash if |internal_display| is not capable of panel
113 // fitting).
114 //
115 // |preserve_aspect| limits the search/creation only to the modes having the
116 // native aspect ratio of |external_display|.
117 bool FindMirrorMode(DisplayState* internal_display,
118 DisplayState* external_display,
119 bool try_panel_fitting,
120 bool preserve_aspect) const;
121
122 DisplayConfigurator* configurator_; // Not owned.
123
124 DISALLOW_COPY_AND_ASSIGN(DisplayLayoutManagerImpl);
125 };
126
127 DisplayConfigurator::DisplayLayoutManagerImpl::DisplayLayoutManagerImpl(
128 DisplayConfigurator* configurator)
129 : configurator_(configurator) {
130 }
131
132 DisplayConfigurator::DisplayLayoutManagerImpl::~DisplayLayoutManagerImpl() {
133 }
134
135 DisplayConfigurator::SoftwareMirroringController*
136 DisplayConfigurator::DisplayLayoutManagerImpl::GetSoftwareMirroringController()
137 const {
138 return configurator_->mirroring_controller_;
139 }
140
141 DisplayConfigurator::StateController*
142 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const {
143 return configurator_->state_controller_;
144 }
145
146 MultipleDisplayState
147 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayState() const {
148 return configurator_->current_display_state_;
149 }
150
151 chromeos::DisplayPowerState
152 DisplayConfigurator::DisplayLayoutManagerImpl::GetPowerState() const {
153 return configurator_->current_power_state_;
154 }
155
156 std::vector<DisplayState>
157 DisplayConfigurator::DisplayLayoutManagerImpl::ParseDisplays(
158 const std::vector<DisplaySnapshot*>& snapshots) const {
159 std::vector<DisplayState> cached_displays;
160 for (auto* snapshot : snapshots) {
161 DisplayState display_state;
162 display_state.display = snapshot;
163 display_state.selected_mode = GetUserSelectedMode(*snapshot);
164 cached_displays.push_back(display_state);
165 }
166
167 // Set |mirror_mode| fields.
168 if (cached_displays.size() == 2) {
169 bool one_is_internal =
170 cached_displays[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
171 bool two_is_internal =
172 cached_displays[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
173 int internal_displays =
174 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
175 DCHECK_LT(internal_displays, 2);
176 LOG_IF(WARNING, internal_displays >= 2)
177 << "At least two internal displays detected.";
178
179 bool can_mirror = false;
180 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
181 // Try preserving external display's aspect ratio on the first attempt.
182 // If that fails, fall back to the highest matching resolution.
183 bool preserve_aspect = attempt == 0;
184
185 if (internal_displays == 1) {
186 can_mirror = FindMirrorMode(&cached_displays[one_is_internal ? 0 : 1],
187 &cached_displays[one_is_internal ? 1 : 0],
188 configurator_->is_panel_fitting_enabled_,
189 preserve_aspect);
190 } else { // if (internal_displays == 0)
191 // No panel fitting for external displays, so fall back to exact match.
192 can_mirror = FindMirrorMode(&cached_displays[0], &cached_displays[1],
193 false, preserve_aspect);
194 if (!can_mirror && preserve_aspect) {
195 // FindMirrorMode() will try to preserve aspect ratio of what it
196 // thinks is external display, so if it didn't succeed with one, maybe
197 // it will succeed with the other. This way we will have the correct
198 // aspect ratio on at least one of them.
199 can_mirror = FindMirrorMode(&cached_displays[1], &cached_displays[0],
200 false, preserve_aspect);
201 }
202 }
203 }
204 }
205
206 return cached_displays;
207 }
208
209 bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout(
210 const std::vector<DisplaySnapshot*>& displays,
211 MultipleDisplayState new_display_state,
212 chromeos::DisplayPowerState new_power_state,
213 std::vector<DisplayConfigureRequest>* requests,
214 gfx::Size* framebuffer_size) const {
215 std::vector<DisplayState> states = ParseDisplays(displays);
216 std::vector<bool> display_power;
217 int num_on_displays =
218 GetDisplayPower(displays, new_power_state, &display_power);
219 VLOG(1) << "EnterState: display="
220 << MultipleDisplayStateToString(new_display_state)
221 << " power=" << DisplayPowerStateToString(new_power_state);
222
223 // Framebuffer dimensions.
224 gfx::Size size;
225
226 for (size_t i = 0; i < displays.size(); ++i) {
227 requests->push_back(DisplayConfigureRequest(
228 displays[i], displays[i]->current_mode(), gfx::Point()));
229 }
230
231 switch (new_display_state) {
232 case MULTIPLE_DISPLAY_STATE_INVALID:
233 NOTREACHED() << "Ignoring request to enter invalid state with "
234 << displays.size() << " connected display(s)";
235 return false;
236 case MULTIPLE_DISPLAY_STATE_HEADLESS:
237 if (displays.size() != 0) {
238 LOG(WARNING) << "Ignoring request to enter headless mode with "
239 << displays.size() << " connected display(s)";
240 return false;
241 }
242 break;
243 case MULTIPLE_DISPLAY_STATE_SINGLE: {
244 // If there are multiple displays connected, only one should be turned on.
245 if (displays.size() != 1 && num_on_displays != 1) {
246 LOG(WARNING) << "Ignoring request to enter single mode with "
247 << displays.size() << " connected displays and "
248 << num_on_displays << " turned on";
249 return false;
250 }
251
252 for (size_t i = 0; i < states.size(); ++i) {
253 const DisplayState* state = &states[i];
254 (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL;
255
256 if (display_power[i] || states.size() == 1) {
257 const DisplayMode* mode_info = state->selected_mode;
258 if (!mode_info) {
259 LOG(WARNING) << "No selected mode when configuring display: "
260 << state->display->ToString();
261 return false;
262 }
263 if (mode_info->size() == gfx::Size(1024, 768)) {
264 VLOG(1) << "Potentially misdetecting display(1024x768):"
265 << " displays size=" << states.size()
266 << ", num_on_displays=" << num_on_displays
267 << ", current size:" << size.width() << "x" << size.height()
268 << ", i=" << i << ", display=" << state->display->ToString()
269 << ", display_mode=" << mode_info->ToString();
270 }
271 size = mode_info->size();
272 }
273 }
274 break;
275 }
276 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
277 if (states.size() != 2 ||
278 (num_on_displays != 0 && num_on_displays != 2)) {
279 LOG(WARNING) << "Ignoring request to enter mirrored mode with "
280 << states.size() << " connected display(s) and "
281 << num_on_displays << " turned on";
282 return false;
283 }
284
285 const DisplayMode* mode_info = states[0].mirror_mode;
286 if (!mode_info) {
287 LOG(WARNING) << "No mirror mode when configuring display: "
288 << states[0].display->ToString();
289 return false;
290 }
291 size = mode_info->size();
292
293 for (size_t i = 0; i < states.size(); ++i) {
294 const DisplayState* state = &states[i];
295 (*requests)[i].mode = display_power[i] ? state->mirror_mode : NULL;
296 }
297 break;
298 }
299 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED:
300 case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED: {
301 // In docked mode (with internal display + 2 external displays) the state
302 // will be DUAL_EXTENDED with internal display turned off and the 2
303 // external displays turned on.
304 if (new_display_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED &&
305 states.size() != 2 && num_on_displays != 2) {
306 LOG(WARNING) << "Ignoring request to enter extended mode with "
307 << states.size() << " connected display(s) and "
308 << num_on_displays << " turned on";
309 return false;
310 }
311
312 for (size_t i = 0; i < states.size(); ++i) {
313 const DisplayState* state = &states[i];
314 (*requests)[i].origin.set_y(size.height() ? size.height() + kVerticalGap
315 : 0);
316 (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL;
317
318 // Retain the full screen size even if all displays are off so the
319 // same desktop configuration can be restored when the displays are
320 // turned back on.
321 const DisplayMode* mode_info = states[i].selected_mode;
322 if (!mode_info) {
323 LOG(WARNING) << "No selected mode when configuring display: "
324 << state->display->ToString();
325 return false;
326 }
327
328 size.set_width(std::max<int>(size.width(), mode_info->size().width()));
329 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
330 mode_info->size().height());
331 }
332 break;
333 }
334 }
335 DCHECK(new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS ||
336 !size.IsEmpty());
337 *framebuffer_size = size;
338 return true;
339 }
340
341 DisplayConfigurator::DisplayStateList
342 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayStates() const {
343 return configurator_->cached_displays();
344 }
345
346 bool DisplayConfigurator::DisplayLayoutManagerImpl::IsMirroring() const {
347 if (GetDisplayState() == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR)
348 return true;
349
350 return GetSoftwareMirroringController() &&
351 GetSoftwareMirroringController()->SoftwareMirroringEnabled();
352 }
353
354 const DisplayMode*
355 DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode(
356 const DisplaySnapshot& display) const {
357 gfx::Size size;
358 const DisplayMode* selected_mode = nullptr;
359 if (GetStateController() &&
360 GetStateController()->GetResolutionForDisplayId(display.display_id(),
361 &size)) {
362 selected_mode = FindDisplayModeMatchingSize(display, size);
363 }
364
365 // Fall back to native mode.
366 return selected_mode ? selected_mode : display.native_mode();
367 }
368
369 bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode(
370 DisplayState* internal_display,
371 DisplayState* external_display,
372 bool try_panel_fitting,
373 bool preserve_aspect) const {
374 const DisplayMode* internal_native_info =
375 internal_display->display->native_mode();
376 const DisplayMode* external_native_info =
377 external_display->display->native_mode();
378 if (!internal_native_info || !external_native_info)
379 return false;
380
381 // Check if some external display resolution can be mirrored on internal.
382 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
383 // this is the order in which they look better on the monitor.
384 for (const auto& external_mode : external_display->display->modes()) {
385 bool is_native_aspect_ratio =
386 external_native_info->size().width() * external_mode->size().height() ==
387 external_native_info->size().height() * external_mode->size().width();
388 if (preserve_aspect && !is_native_aspect_ratio)
389 continue; // Allow only aspect ratio preserving modes for mirroring.
390
391 // Try finding an exact match.
392 for (const auto& internal_mode : internal_display->display->modes()) {
393 if (internal_mode->size() == external_mode->size() &&
394 internal_mode->is_interlaced() == external_mode->is_interlaced()) {
395 internal_display->mirror_mode = internal_mode.get();
396 external_display->mirror_mode = external_mode.get();
397 return true; // Mirror mode found.
398 }
399 }
400
401 // Try to create a matching internal display mode by panel fitting.
402 if (try_panel_fitting) {
403 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
404 // ugly, so, can fit == can upscale. Also, internal panels don't support
405 // fitting interlaced modes.
406 bool can_fit = internal_native_info->size().width() >=
407 external_mode->size().width() &&
408 internal_native_info->size().height() >=
409 external_mode->size().height() &&
410 !external_mode->is_interlaced();
411 if (can_fit) {
412 configurator_->native_display_delegate_->AddMode(
413 *internal_display->display, external_mode.get());
414 internal_display->display->add_mode(external_mode.get());
415 internal_display->mirror_mode =
416 internal_display->display->modes().back().get();
417 external_display->mirror_mode = external_mode.get();
418 return true; // Mirror mode created.
419 }
420 }
421 }
422
423 return false;
424 }
425
426 ////////////////////////////////////////////////////////////////////////////////
427 // DisplayConfigurator implementation
428
429 // static
430 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize(
431 const DisplaySnapshot& display,
432 const gfx::Size& size) {
433 const DisplayMode* best_mode = NULL;
434 for (const std::unique_ptr<const DisplayMode>& mode : display.modes()) {
435 if (mode->size() != size)
436 continue;
437
438 if (mode.get() == display.native_mode()) {
439 best_mode = mode.get();
440 break;
441 }
442
443 if (!best_mode) {
444 best_mode = mode.get();
445 continue;
446 }
447
448 if (mode->is_interlaced()) {
449 if (!best_mode->is_interlaced())
450 continue;
451 } else {
452 // Reset the best rate if the non interlaced is
453 // found the first time.
454 if (best_mode->is_interlaced()) {
455 best_mode = mode.get();
456 continue;
457 }
458 }
459 if (mode->refresh_rate() < best_mode->refresh_rate())
460 continue;
461
462 best_mode = mode.get();
463 }
464
465 return best_mode;
466 }
467
468 DisplayConfigurator::DisplayConfigurator()
469 : state_controller_(NULL),
470 mirroring_controller_(NULL),
471 is_panel_fitting_enabled_(false),
472 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
473 current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
474 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
475 requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
476 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
477 pending_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
478 has_pending_power_state_(false),
479 pending_power_flags_(kSetDisplayPowerNoFlags),
480 force_configure_(false),
481 next_display_protection_client_id_(1),
482 display_externally_controlled_(false),
483 display_control_changing_(false),
484 displays_suspended_(false),
485 layout_manager_(new DisplayLayoutManagerImpl(this)),
486 weak_ptr_factory_(this) {
487 }
488
489 DisplayConfigurator::~DisplayConfigurator() {
490 if (native_display_delegate_)
491 native_display_delegate_->RemoveObserver(this);
492
493 CallAndClearInProgressCallbacks(false);
494 CallAndClearQueuedCallbacks(false);
495
496 while (!query_protection_callbacks_.empty()) {
497 query_protection_callbacks_.front().Run(QueryProtectionResponse());
498 query_protection_callbacks_.pop();
499 }
500
501 while (!enable_protection_callbacks_.empty()) {
502 enable_protection_callbacks_.front().Run(false);
503 enable_protection_callbacks_.pop();
504 }
505 }
506
507 void DisplayConfigurator::SetDelegateForTesting(
508 std::unique_ptr<NativeDisplayDelegate> display_delegate) {
509 DCHECK(!native_display_delegate_);
510
511 native_display_delegate_ = std::move(display_delegate);
512 configure_display_ = true;
513 }
514
515 void DisplayConfigurator::SetInitialDisplayPower(
516 chromeos::DisplayPowerState power_state) {
517 DCHECK_EQ(current_display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
518 requested_power_state_ = current_power_state_ = power_state;
519 NotifyPowerStateObservers();
520 }
521
522 void DisplayConfigurator::Init(
523 std::unique_ptr<NativeDisplayDelegate> display_delegate,
524 bool is_panel_fitting_enabled) {
525 is_panel_fitting_enabled_ = is_panel_fitting_enabled;
526 if (!configure_display_ || display_externally_controlled_)
527 return;
528
529 // If the delegate is already initialized don't update it (For example, tests
530 // set their own delegates).
531 if (!native_display_delegate_) {
532 native_display_delegate_ = std::move(display_delegate);
533 native_display_delegate_->AddObserver(this);
534 }
535 }
536
537 void DisplayConfigurator::TakeControl(const DisplayControlCallback& callback) {
538 if (display_control_changing_) {
539 callback.Run(false);
540 return;
541 }
542
543 if (!display_externally_controlled_) {
544 callback.Run(true);
545 return;
546 }
547
548 display_control_changing_ = true;
549 native_display_delegate_->TakeDisplayControl(
550 base::Bind(&DisplayConfigurator::OnDisplayControlTaken,
551 weak_ptr_factory_.GetWeakPtr(), callback));
552 }
553
554 void DisplayConfigurator::OnDisplayControlTaken(
555 const DisplayControlCallback& callback,
556 bool success) {
557 display_control_changing_ = false;
558 display_externally_controlled_ = !success;
559 if (success) {
560 // Force a configuration since the display configuration may have changed.
561 force_configure_ = true;
562 // Restore the last power state used before releasing control.
563 SetDisplayPower(requested_power_state_, kSetDisplayPowerNoFlags,
564 base::Bind(&DoNothing));
565 }
566
567 callback.Run(success);
568 }
569
570 void DisplayConfigurator::RelinquishControl(
571 const DisplayControlCallback& callback) {
572 if (display_control_changing_) {
573 callback.Run(false);
574 return;
575 }
576
577 if (display_externally_controlled_) {
578 callback.Run(true);
579 return;
580 }
581
582 // For simplicity, just fail if in the middle of a display configuration.
583 if (configuration_task_) {
584 callback.Run(false);
585 return;
586 }
587
588 display_control_changing_ = true;
589
590 // Turn off the displays before releasing control since we're no longer using
591 // them for output.
592 SetDisplayPowerInternal(
593 chromeos::DISPLAY_POWER_ALL_OFF, kSetDisplayPowerNoFlags,
594 base::Bind(&DisplayConfigurator::SendRelinquishDisplayControl,
595 weak_ptr_factory_.GetWeakPtr(), callback));
596 }
597
598 void DisplayConfigurator::SendRelinquishDisplayControl(
599 const DisplayControlCallback& callback, bool success) {
600 if (success) {
601 // Set the flag early such that an incoming configuration event won't start
602 // while we're releasing control of the displays.
603 display_externally_controlled_ = true;
604 native_display_delegate_->RelinquishDisplayControl(
605 base::Bind(&DisplayConfigurator::OnDisplayControlRelinquished,
606 weak_ptr_factory_.GetWeakPtr(), callback));
607 } else {
608 display_control_changing_ = false;
609 callback.Run(false);
610 }
611 }
612
613 void DisplayConfigurator::OnDisplayControlRelinquished(
614 const DisplayControlCallback& callback,
615 bool success) {
616 display_control_changing_ = false;
617 display_externally_controlled_ = success;
618 if (!success) {
619 force_configure_ = true;
620 RunPendingConfiguration();
621 }
622
623 callback.Run(success);
624 }
625
626 void DisplayConfigurator::ForceInitialConfigure(
627 uint32_t background_color_argb) {
628 if (!configure_display_ || display_externally_controlled_)
629 return;
630
631 DCHECK(native_display_delegate_);
632 native_display_delegate_->Initialize();
633
634 // ForceInitialConfigure should be the first configuration so there shouldn't
635 // be anything scheduled.
636 DCHECK(!configuration_task_);
637
638 configuration_task_.reset(new UpdateDisplayConfigurationTask(
639 native_display_delegate_.get(), layout_manager_.get(),
640 requested_display_state_, requested_power_state_,
641 kSetDisplayPowerForceProbe, background_color_argb, true,
642 base::Bind(&DisplayConfigurator::OnConfigured,
643 weak_ptr_factory_.GetWeakPtr())));
644 configuration_task_->Run();
645 }
646
647 DisplayConfigurator::ContentProtectionClientId
648 DisplayConfigurator::RegisterContentProtectionClient() {
649 if (!configure_display_ || display_externally_controlled_)
650 return kInvalidClientId;
651
652 return next_display_protection_client_id_++;
653 }
654
655 void DisplayConfigurator::UnregisterContentProtectionClient(
656 ContentProtectionClientId client_id) {
657 client_protection_requests_.erase(client_id);
658
659 ContentProtections protections;
660 for (const auto& requests_pair : client_protection_requests_) {
661 for (const auto& protections_pair : requests_pair.second) {
662 protections[protections_pair.first] |= protections_pair.second;
663 }
664 }
665
666 enable_protection_callbacks_.push(base::Bind(&DoNothing));
667 ApplyContentProtectionTask* task = new ApplyContentProtectionTask(
668 layout_manager_.get(), native_display_delegate_.get(), protections,
669 base::Bind(&DisplayConfigurator::OnContentProtectionClientUnregistered,
670 weak_ptr_factory_.GetWeakPtr()));
671 content_protection_tasks_.push(
672 base::Bind(&ApplyContentProtectionTask::Run, base::Owned(task)));
673
674 if (content_protection_tasks_.size() == 1)
675 content_protection_tasks_.front().Run();
676 }
677
678 void DisplayConfigurator::OnContentProtectionClientUnregistered(bool success) {
679 DCHECK(!content_protection_tasks_.empty());
680 content_protection_tasks_.pop();
681
682 DCHECK(!enable_protection_callbacks_.empty());
683 EnableProtectionCallback callback = enable_protection_callbacks_.front();
684 enable_protection_callbacks_.pop();
685
686 if (!content_protection_tasks_.empty())
687 content_protection_tasks_.front().Run();
688 }
689
690 void DisplayConfigurator::QueryContentProtectionStatus(
691 ContentProtectionClientId client_id,
692 int64_t display_id,
693 const QueryProtectionCallback& callback) {
694 // Exclude virtual displays so that protected content will not be recaptured
695 // through the cast stream.
696 for (const DisplaySnapshot* display : cached_displays_) {
697 if (display->display_id() == display_id &&
698 !IsPhysicalDisplayType(display->type())) {
699 callback.Run(QueryProtectionResponse());
700 return;
701 }
702 }
703
704 if (!configure_display_ || display_externally_controlled_) {
705 callback.Run(QueryProtectionResponse());
706 return;
707 }
708
709 query_protection_callbacks_.push(callback);
710 QueryContentProtectionTask* task = new QueryContentProtectionTask(
711 layout_manager_.get(), native_display_delegate_.get(), display_id,
712 base::Bind(&DisplayConfigurator::OnContentProtectionQueried,
713 weak_ptr_factory_.GetWeakPtr(), client_id, display_id));
714 content_protection_tasks_.push(
715 base::Bind(&QueryContentProtectionTask::Run, base::Owned(task)));
716 if (content_protection_tasks_.size() == 1)
717 content_protection_tasks_.front().Run();
718 }
719
720 void DisplayConfigurator::OnContentProtectionQueried(
721 ContentProtectionClientId client_id,
722 int64_t display_id,
723 QueryContentProtectionTask::Response task_response) {
724 QueryProtectionResponse response;
725 response.success = task_response.success;
726 response.link_mask = task_response.link_mask;
727
728 // Don't reveal protections requested by other clients.
729 ProtectionRequests::iterator it = client_protection_requests_.find(client_id);
730 if (response.success && it != client_protection_requests_.end()) {
731 uint32_t requested_mask = 0;
732 if (it->second.find(display_id) != it->second.end())
733 requested_mask = it->second[display_id];
734 response.protection_mask =
735 task_response.enabled & ~task_response.unfulfilled & requested_mask;
736 }
737
738 DCHECK(!content_protection_tasks_.empty());
739 content_protection_tasks_.pop();
740
741 DCHECK(!query_protection_callbacks_.empty());
742 QueryProtectionCallback callback = query_protection_callbacks_.front();
743 query_protection_callbacks_.pop();
744 callback.Run(response);
745
746 if (!content_protection_tasks_.empty())
747 content_protection_tasks_.front().Run();
748 }
749
750 void DisplayConfigurator::EnableContentProtection(
751 ContentProtectionClientId client_id,
752 int64_t display_id,
753 uint32_t desired_method_mask,
754 const EnableProtectionCallback& callback) {
755 if (!configure_display_ || display_externally_controlled_) {
756 callback.Run(false);
757 return;
758 }
759
760 ContentProtections protections;
761 for (const auto& requests_pair : client_protection_requests_) {
762 for (const auto& protections_pair : requests_pair.second) {
763 if (requests_pair.first == client_id &&
764 protections_pair.first == display_id)
765 continue;
766
767 protections[protections_pair.first] |= protections_pair.second;
768 }
769 }
770 protections[display_id] |= desired_method_mask;
771
772 enable_protection_callbacks_.push(callback);
773 ApplyContentProtectionTask* task = new ApplyContentProtectionTask(
774 layout_manager_.get(), native_display_delegate_.get(), protections,
775 base::Bind(&DisplayConfigurator::OnContentProtectionEnabled,
776 weak_ptr_factory_.GetWeakPtr(), client_id, display_id,
777 desired_method_mask));
778 content_protection_tasks_.push(
779 base::Bind(&ApplyContentProtectionTask::Run, base::Owned(task)));
780 if (content_protection_tasks_.size() == 1)
781 content_protection_tasks_.front().Run();
782 }
783
784 void DisplayConfigurator::OnContentProtectionEnabled(
785 ContentProtectionClientId client_id,
786 int64_t display_id,
787 uint32_t desired_method_mask,
788 bool success) {
789 DCHECK(!content_protection_tasks_.empty());
790 content_protection_tasks_.pop();
791
792 DCHECK(!enable_protection_callbacks_.empty());
793 EnableProtectionCallback callback = enable_protection_callbacks_.front();
794 enable_protection_callbacks_.pop();
795
796 if (!success) {
797 callback.Run(false);
798 return;
799 }
800
801 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) {
802 if (client_protection_requests_.find(client_id) !=
803 client_protection_requests_.end()) {
804 client_protection_requests_[client_id].erase(display_id);
805 if (client_protection_requests_[client_id].size() == 0)
806 client_protection_requests_.erase(client_id);
807 }
808 } else {
809 client_protection_requests_[client_id][display_id] = desired_method_mask;
810 }
811
812 callback.Run(true);
813 if (!content_protection_tasks_.empty())
814 content_protection_tasks_.front().Run();
815 }
816
817 std::vector<ui::ColorCalibrationProfile>
818 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) {
819 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
820 switches::kDisableDisplayColorCalibration)) {
821 for (const DisplaySnapshot* display : cached_displays_) {
822 if (display->display_id() == display_id &&
823 IsPhysicalDisplayType(display->type())) {
824 return native_display_delegate_->GetAvailableColorCalibrationProfiles(
825 *display);
826 }
827 }
828 }
829
830 return std::vector<ui::ColorCalibrationProfile>();
831 }
832
833 bool DisplayConfigurator::SetColorCalibrationProfile(
834 int64_t display_id,
835 ui::ColorCalibrationProfile new_profile) {
836 for (const DisplaySnapshot* display : cached_displays_) {
837 if (display->display_id() == display_id &&
838 IsPhysicalDisplayType(display->type())) {
839 return native_display_delegate_->SetColorCalibrationProfile(*display,
840 new_profile);
841 }
842 }
843
844 return false;
845 }
846
847 bool DisplayConfigurator::SetColorCorrection(
848 int64_t display_id,
849 const std::vector<GammaRampRGBEntry>& degamma_lut,
850 const std::vector<GammaRampRGBEntry>& gamma_lut,
851 const std::vector<float>& correction_matrix) {
852 for (const DisplaySnapshot* display : cached_displays_) {
853 if (display->display_id() == display_id)
854 return native_display_delegate_->SetColorCorrection(
855 *display, degamma_lut, gamma_lut, correction_matrix);
856 }
857
858 return false;
859 }
860
861 void DisplayConfigurator::PrepareForExit() {
862 configure_display_ = false;
863 }
864
865 void DisplayConfigurator::SetDisplayPowerInternal(
866 chromeos::DisplayPowerState power_state,
867 int flags,
868 const ConfigurationCallback& callback) {
869 // Only skip if the current power state is the same and the latest requested
870 // power state is the same. If |pending_power_state_ != current_power_state_|
871 // then there is a current task pending or the last configuration failed. In
872 // either case request a new configuration to make sure the state is
873 // consistent with the expectations.
874 if (power_state == current_power_state_ &&
875 power_state == pending_power_state_ &&
876 !(flags & kSetDisplayPowerForceProbe)) {
877 callback.Run(true);
878 return;
879 }
880
881 pending_power_state_ = power_state;
882 has_pending_power_state_ = true;
883 pending_power_flags_ = flags;
884 queued_configuration_callbacks_.push_back(callback);
885
886 if (configure_timer_.IsRunning()) {
887 // If there is a configuration task scheduled, avoid performing
888 // configuration immediately. Instead reset the timer to wait for things to
889 // settle.
890 configure_timer_.Reset();
891 return;
892 }
893
894 RunPendingConfiguration();
895 }
896
897 void DisplayConfigurator::SetDisplayPower(
898 chromeos::DisplayPowerState power_state,
899 int flags,
900 const ConfigurationCallback& callback) {
901 if (!configure_display_ || display_externally_controlled_) {
902 callback.Run(false);
903 return;
904 }
905
906 VLOG(1) << "SetDisplayPower: power_state="
907 << DisplayPowerStateToString(power_state) << " flags=" << flags
908 << ", configure timer="
909 << (configure_timer_.IsRunning() ? "Running" : "Stopped");
910
911 requested_power_state_ = power_state;
912 SetDisplayPowerInternal(requested_power_state_, flags, callback);
913 }
914
915 void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
916 if (!configure_display_ || display_externally_controlled_)
917 return;
918
919 VLOG(1) << "SetDisplayMode: state="
920 << MultipleDisplayStateToString(new_state);
921 if (current_display_state_ == new_state) {
922 // Cancel software mirroring if the state is moving from
923 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
924 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
925 if (mirroring_controller_ &&
926 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
927 mirroring_controller_->SetSoftwareMirroring(false);
928 NotifyDisplayStateObservers(true, new_state);
929 return;
930 }
931
932 requested_display_state_ = new_state;
933
934 RunPendingConfiguration();
935 }
936
937 void DisplayConfigurator::OnConfigurationChanged() {
938 // Don't do anything if the displays are currently suspended. Instead we will
939 // probe and reconfigure the displays if necessary in ResumeDisplays().
940 if (displays_suspended_) {
941 VLOG(1) << "Displays are currently suspended. Not attempting to "
942 << "reconfigure them.";
943 return;
944 }
945
946 // Configure displays with |kConfigureDelayMs| delay,
947 // so that time-consuming ConfigureDisplays() won't be called multiple times.
948 configure_timer_.Start(FROM_HERE,
949 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
950 this, &DisplayConfigurator::ConfigureDisplays);
951 }
952
953 void DisplayConfigurator::AddObserver(Observer* observer) {
954 observers_.AddObserver(observer);
955 }
956
957 void DisplayConfigurator::RemoveObserver(Observer* observer) {
958 observers_.RemoveObserver(observer);
959 }
960
961 void DisplayConfigurator::SuspendDisplays(
962 const ConfigurationCallback& callback) {
963 if (!configure_display_ || display_externally_controlled_) {
964 callback.Run(false);
965 return;
966 }
967
968 displays_suspended_ = true;
969
970 // Stop |configure_timer_| because we will force probe and configure all the
971 // displays at resume time anyway.
972 configure_timer_.Stop();
973
974 // Turn off the displays for suspend. This way, if we wake up for lucid sleep,
975 // the displays will not turn on (all displays should be off for lucid sleep
976 // unless explicitly requested by lucid sleep code). Use
977 // SetDisplayPowerInternal so requested_power_state_ is maintained.
978 SetDisplayPowerInternal(chromeos::DISPLAY_POWER_ALL_OFF,
979 kSetDisplayPowerNoFlags, callback);
980
981 // We need to make sure that the monitor configuration we just did actually
982 // completes before we return.
983 native_display_delegate_->SyncWithServer();
984 }
985
986 void DisplayConfigurator::ResumeDisplays() {
987 if (!configure_display_ || display_externally_controlled_)
988 return;
989
990 displays_suspended_ = false;
991
992 if (current_display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
993 current_display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED ||
994 current_display_state_ == MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED) {
995 // When waking up from suspend while being in a multi display mode, we
996 // schedule a delayed forced configuration, which will make
997 // SetDisplayPowerInternal() avoid performing the configuration immediately.
998 // This gives a chance to wait for all displays to be added and detected
999 // before configuration is performed, so we won't immediately resize the
1000 // desktops and the windows on it to fit on a single display.
1001 configure_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
1002 kResumeConfigureMultiDisplayDelayMs),
1003 this, &DisplayConfigurator::ConfigureDisplays);
1004 }
1005
1006 // If requested_power_state_ is ALL_OFF due to idle suspend, powerd will turn
1007 // the display power on when it enables the backlight.
1008 SetDisplayPower(requested_power_state_, kSetDisplayPowerNoFlags,
1009 base::Bind(&DoNothing));
1010 }
1011
1012 void DisplayConfigurator::ConfigureDisplays() {
1013 if (!configure_display_ || display_externally_controlled_)
1014 return;
1015
1016 force_configure_ = true;
1017 RunPendingConfiguration();
1018 }
1019
1020 void DisplayConfigurator::RunPendingConfiguration() {
1021 // Configuration task is currently running. Do not start a second
1022 // configuration.
1023 if (configuration_task_)
1024 return;
1025
1026 if (!ShouldRunConfigurationTask()) {
1027 LOG(ERROR) << "Called RunPendingConfiguration without any changes"
1028 " requested";
1029 CallAndClearQueuedCallbacks(true);
1030 return;
1031 }
1032
1033 configuration_task_.reset(new UpdateDisplayConfigurationTask(
1034 native_display_delegate_.get(), layout_manager_.get(),
1035 requested_display_state_, pending_power_state_, pending_power_flags_,
1036 0, force_configure_, base::Bind(&DisplayConfigurator::OnConfigured,
1037 weak_ptr_factory_.GetWeakPtr())));
1038 configuration_task_->set_virtual_display_snapshots(
1039 virtual_display_snapshots_.get());
1040
1041 // Reset the flags before running the task; otherwise it may end up scheduling
1042 // another configuration.
1043 force_configure_ = false;
1044 pending_power_flags_ = kSetDisplayPowerNoFlags;
1045 has_pending_power_state_ = false;
1046 requested_display_state_ = MULTIPLE_DISPLAY_STATE_INVALID;
1047
1048 DCHECK(in_progress_configuration_callbacks_.empty());
1049 in_progress_configuration_callbacks_.swap(queued_configuration_callbacks_);
1050
1051 configuration_task_->Run();
1052 }
1053
1054 void DisplayConfigurator::OnConfigured(
1055 bool success,
1056 const std::vector<DisplaySnapshot*>& displays,
1057 const gfx::Size& framebuffer_size,
1058 MultipleDisplayState new_display_state,
1059 chromeos::DisplayPowerState new_power_state) {
1060 VLOG(1) << "OnConfigured: success=" << success << " new_display_state="
1061 << MultipleDisplayStateToString(new_display_state)
1062 << " new_power_state=" << DisplayPowerStateToString(new_power_state);
1063
1064 cached_displays_ = displays;
1065 if (success) {
1066 chromeos::DisplayPowerState old_power_state = current_power_state_;
1067 current_display_state_ = new_display_state;
1068 current_power_state_ = new_power_state;
1069
1070 // |framebuffer_size| is empty in software mirroring mode, headless mode,
1071 // or all displays are off.
1072 DCHECK(!framebuffer_size.IsEmpty() ||
1073 (mirroring_controller_ &&
1074 mirroring_controller_->SoftwareMirroringEnabled()) ||
1075 new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS ||
1076 new_power_state == chromeos::DISPLAY_POWER_ALL_OFF);
1077
1078 if (!framebuffer_size.IsEmpty())
1079 framebuffer_size_ = framebuffer_size;
1080
1081 // If the pending power state hasn't changed then make sure that value
1082 // gets updated as well since the last requested value may have been
1083 // dependent on certain conditions (ie: if only the internal monitor was
1084 // present).
1085 if (!has_pending_power_state_)
1086 pending_power_state_ = new_power_state;
1087
1088 if (old_power_state != current_power_state_)
1089 NotifyPowerStateObservers();
1090 }
1091
1092 configuration_task_.reset();
1093 NotifyDisplayStateObservers(success, new_display_state);
1094 CallAndClearInProgressCallbacks(success);
1095
1096 if (success && !configure_timer_.IsRunning() &&
1097 ShouldRunConfigurationTask()) {
1098 configure_timer_.Start(FROM_HERE,
1099 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
1100 this, &DisplayConfigurator::RunPendingConfiguration);
1101 } else {
1102 // If a new configuration task isn't scheduled respond to all queued
1103 // callbacks (for example if requested state is current state).
1104 if (!configure_timer_.IsRunning())
1105 CallAndClearQueuedCallbacks(success);
1106 }
1107 }
1108
1109 bool DisplayConfigurator::ShouldRunConfigurationTask() const {
1110 if (force_configure_)
1111 return true;
1112
1113 // Schedule if there is a request to change the display state.
1114 if (requested_display_state_ != current_display_state_ &&
1115 requested_display_state_ != MULTIPLE_DISPLAY_STATE_INVALID)
1116 return true;
1117
1118 // Schedule if there is a request to change the power state.
1119 if (has_pending_power_state_)
1120 return true;
1121
1122 return false;
1123 }
1124
1125 void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success) {
1126 for (const auto& callback : in_progress_configuration_callbacks_)
1127 callback.Run(success);
1128
1129 in_progress_configuration_callbacks_.clear();
1130 }
1131
1132 void DisplayConfigurator::CallAndClearQueuedCallbacks(bool success) {
1133 for (const auto& callback : queued_configuration_callbacks_)
1134 callback.Run(success);
1135
1136 queued_configuration_callbacks_.clear();
1137 }
1138
1139 void DisplayConfigurator::NotifyDisplayStateObservers(
1140 bool success,
1141 MultipleDisplayState attempted_state) {
1142 if (success) {
1143 for (Observer& observer : observers_)
1144 observer.OnDisplayModeChanged(cached_displays_);
1145 } else {
1146 for (Observer& observer : observers_)
1147 observer.OnDisplayModeChangeFailed(cached_displays_, attempted_state);
1148 }
1149 }
1150
1151 void DisplayConfigurator::NotifyPowerStateObservers() {
1152 for (Observer& observer : observers_)
1153 observer.OnPowerStateChanged(current_power_state_);
1154 }
1155
1156 int64_t DisplayConfigurator::AddVirtualDisplay(const gfx::Size& display_size) {
1157 if (last_virtual_display_id_ == 0xff) {
1158 LOG(WARNING) << "Exceeded virtual display id limit";
1159 return display::kInvalidDisplayId;
1160 }
1161
1162 DisplaySnapshotVirtual* virtual_snapshot = new DisplaySnapshotVirtual(
1163 display::GenerateDisplayID(kReservedManufacturerID, 0x0,
1164 ++last_virtual_display_id_),
1165 display_size);
1166 virtual_display_snapshots_.push_back(virtual_snapshot);
1167 ConfigureDisplays();
1168
1169 return virtual_snapshot->display_id();
1170 }
1171
1172 bool DisplayConfigurator::RemoveVirtualDisplay(int64_t display_id) {
1173 bool display_found = false;
1174 for (auto it = virtual_display_snapshots_.begin();
1175 it != virtual_display_snapshots_.end(); ++it) {
1176 if ((*it)->display_id() == display_id) {
1177 virtual_display_snapshots_.erase(it);
1178 ConfigureDisplays();
1179 display_found = true;
1180 break;
1181 }
1182 }
1183
1184 if (!display_found)
1185 return false;
1186
1187 int64_t max_display_id = 0;
1188 for (auto* display : virtual_display_snapshots_)
1189 max_display_id = std::max(max_display_id, display->display_id());
1190 last_virtual_display_id_ = max_display_id & 0xff;
1191
1192 return true;
1193 }
1194
1195 bool DisplayConfigurator::IsDisplayOn() const {
1196 return current_power_state_ != chromeos::DISPLAY_POWER_ALL_OFF;
1197 }
1198
1199 } // namespace ui
OLDNEW
« no previous file with comments | « ui/display/chromeos/display_configurator.h ('k') | ui/display/chromeos/display_configurator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698