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 "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/sys_info.h"
13 #include "base/time/time.h"
14 #include "ui/display/display_switches.h"
15 #include "ui/display/types/chromeos/display_mode.h"
16 #include "ui/display/types/chromeos/display_snapshot.h"
17 #include "ui/display/types/chromeos/native_display_delegate.h"
18
19 namespace ui {
20
21 namespace {
22
23 typedef std::vector<const DisplayMode*> DisplayModeList;
24
25 // The delay to perform configuration after RRNotify. See the comment
26 // in |Dispatch()|.
27 const int kConfigureDelayMs = 500;
28
29 // Returns a string describing |state|.
DisplayPowerStateToString(chromeos::DisplayPowerState state)30 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) {
31 switch (state) {
32 case chromeos::DISPLAY_POWER_ALL_ON:
33 return "ALL_ON";
34 case chromeos::DISPLAY_POWER_ALL_OFF:
35 return "ALL_OFF";
36 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON:
37 return "INTERNAL_OFF_EXTERNAL_ON";
38 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF:
39 return "INTERNAL_ON_EXTERNAL_OFF";
40 default:
41 return "unknown (" + base::IntToString(state) + ")";
42 }
43 }
44
45 // Returns a string describing |state|.
DisplayStateToString(MultipleDisplayState state)46 std::string DisplayStateToString(MultipleDisplayState state) {
47 switch (state) {
48 case MULTIPLE_DISPLAY_STATE_INVALID:
49 return "INVALID";
50 case MULTIPLE_DISPLAY_STATE_HEADLESS:
51 return "HEADLESS";
52 case MULTIPLE_DISPLAY_STATE_SINGLE:
53 return "SINGLE";
54 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR:
55 return "DUAL_MIRROR";
56 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED:
57 return "DUAL_EXTENDED";
58 }
59 NOTREACHED() << "Unknown state " << state;
60 return "INVALID";
61 }
62
63 // Returns the number of displays in |displays| that should be turned on, per
64 // |state|. If |display_power| is non-NULL, it is updated to contain the
65 // on/off state of each corresponding entry in |displays|.
GetDisplayPower(const std::vector<DisplayConfigurator::DisplayState> & display_states,chromeos::DisplayPowerState state,std::vector<bool> * display_power)66 int GetDisplayPower(
67 const std::vector<DisplayConfigurator::DisplayState>& display_states,
68 chromeos::DisplayPowerState state,
69 std::vector<bool>* display_power) {
70 int num_on_displays = 0;
71 if (display_power)
72 display_power->resize(display_states.size());
73
74 for (size_t i = 0; i < display_states.size(); ++i) {
75 bool internal =
76 display_states[i].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
77 bool on =
78 state == chromeos::DISPLAY_POWER_ALL_ON ||
79 (state == chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON &&
80 !internal) ||
81 (state == chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && internal);
82 if (display_power)
83 (*display_power)[i] = on;
84 if (on)
85 num_on_displays++;
86 }
87 return num_on_displays;
88 }
89
90 } // namespace
91
DisplayState()92 DisplayConfigurator::DisplayState::DisplayState()
93 : display(NULL),
94 touch_device_id(0),
95 selected_mode(NULL),
96 mirror_mode(NULL) {}
97
TriggerConfigureTimeout()98 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
99 if (configurator_->configure_timer_.get() &&
100 configurator_->configure_timer_->IsRunning()) {
101 configurator_->configure_timer_.reset();
102 configurator_->ConfigureDisplays();
103 return true;
104 } else {
105 return false;
106 }
107 }
108
109 // static
FindDisplayModeMatchingSize(const DisplaySnapshot & display,const gfx::Size & size)110 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize(
111 const DisplaySnapshot& display,
112 const gfx::Size& size) {
113 const DisplayMode* best_mode = NULL;
114 for (DisplayModeList::const_iterator it = display.modes().begin();
115 it != display.modes().end();
116 ++it) {
117 const DisplayMode* mode = *it;
118
119 if (mode->size() != size)
120 continue;
121
122 if (!best_mode) {
123 best_mode = mode;
124 continue;
125 }
126
127 if (mode->is_interlaced()) {
128 if (!best_mode->is_interlaced())
129 continue;
130 } else {
131 // Reset the best rate if the non interlaced is
132 // found the first time.
133 if (best_mode->is_interlaced()) {
134 best_mode = mode;
135 continue;
136 }
137 }
138 if (mode->refresh_rate() < best_mode->refresh_rate())
139 continue;
140
141 best_mode = mode;
142 }
143
144 return best_mode;
145 }
146
DisplayConfigurator()147 DisplayConfigurator::DisplayConfigurator()
148 : state_controller_(NULL),
149 mirroring_controller_(NULL),
150 is_panel_fitting_enabled_(false),
151 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
152 display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
153 power_state_(chromeos::DISPLAY_POWER_ALL_ON),
154 next_display_protection_client_id_(1) {}
155
~DisplayConfigurator()156 DisplayConfigurator::~DisplayConfigurator() {
157 if (native_display_delegate_)
158 native_display_delegate_->RemoveObserver(this);
159 }
160
SetDelegatesForTesting(scoped_ptr<NativeDisplayDelegate> display_delegate,scoped_ptr<TouchscreenDelegate> touchscreen_delegate)161 void DisplayConfigurator::SetDelegatesForTesting(
162 scoped_ptr<NativeDisplayDelegate> display_delegate,
163 scoped_ptr<TouchscreenDelegate> touchscreen_delegate) {
164 DCHECK(!native_display_delegate_);
165 DCHECK(!touchscreen_delegate_);
166
167 InitializeDelegates(display_delegate.Pass(), touchscreen_delegate.Pass());
168 configure_display_ = true;
169 }
170
SetInitialDisplayPower(chromeos::DisplayPowerState power_state)171 void DisplayConfigurator::SetInitialDisplayPower(
172 chromeos::DisplayPowerState power_state) {
173 DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
174 power_state_ = power_state;
175 }
176
Init(bool is_panel_fitting_enabled)177 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) {
178 is_panel_fitting_enabled_ = is_panel_fitting_enabled;
179 if (!configure_display_)
180 return;
181
182 PlatformInitialize();
183 }
184
InitializeDelegates(scoped_ptr<NativeDisplayDelegate> display_delegate,scoped_ptr<TouchscreenDelegate> touchscreen_delegate)185 void DisplayConfigurator::InitializeDelegates(
186 scoped_ptr<NativeDisplayDelegate> display_delegate,
187 scoped_ptr<TouchscreenDelegate> touchscreen_delegate) {
188 if (!native_display_delegate_ && !touchscreen_delegate_) {
189 native_display_delegate_ = display_delegate.Pass();
190 touchscreen_delegate_ = touchscreen_delegate.Pass();
191
192 native_display_delegate_->AddObserver(this);
193 }
194 }
195
ForceInitialConfigure(uint32_t background_color_argb)196 void DisplayConfigurator::ForceInitialConfigure(
197 uint32_t background_color_argb) {
198 if (!configure_display_)
199 return;
200
201 native_display_delegate_->GrabServer();
202 native_display_delegate_->Initialize();
203
204 UpdateCachedDisplays();
205 if (cached_displays_.size() > 1 && background_color_argb)
206 native_display_delegate_->SetBackgroundColor(background_color_argb);
207 const MultipleDisplayState new_state = ChooseDisplayState(power_state_);
208 const bool success =
209 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_);
210
211 // Force the DPMS on chrome startup as the driver doesn't always detect
212 // that all displays are on when signing out.
213 native_display_delegate_->ForceDPMSOn();
214 native_display_delegate_->UngrabServer();
215 NotifyObservers(success, new_state);
216 }
217
IsMirroring() const218 bool DisplayConfigurator::IsMirroring() const {
219 return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
220 (mirroring_controller_ &&
221 mirroring_controller_->SoftwareMirroringEnabled());
222 }
223
ApplyProtections(const ContentProtections & requests)224 bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) {
225 for (DisplayStateList::const_iterator it = cached_displays_.begin();
226 it != cached_displays_.end();
227 ++it) {
228 uint32_t all_desired = 0;
229
230 // In mirror mode, protection request of all displays need to be fulfilled.
231 // In non-mirror mode, only request of client's display needs to be
232 // fulfilled.
233 ContentProtections::const_iterator request_it;
234 if (IsMirroring()) {
235 for (request_it = requests.begin();
236 request_it != requests.end();
237 ++request_it)
238 all_desired |= request_it->second;
239 } else {
240 request_it = requests.find(it->display->display_id());
241 if (request_it != requests.end())
242 all_desired = request_it->second;
243 }
244
245 switch (it->display->type()) {
246 case DISPLAY_CONNECTION_TYPE_UNKNOWN:
247 return false;
248 // DisplayPort, DVI, and HDMI all support HDCP.
249 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
250 case DISPLAY_CONNECTION_TYPE_DVI:
251 case DISPLAY_CONNECTION_TYPE_HDMI: {
252 HDCPState new_desired_state =
253 (all_desired & CONTENT_PROTECTION_METHOD_HDCP) ?
254 HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED;
255 if (!native_display_delegate_->SetHDCPState(*it->display,
256 new_desired_state))
257 return false;
258 break;
259 }
260 case DISPLAY_CONNECTION_TYPE_INTERNAL:
261 case DISPLAY_CONNECTION_TYPE_VGA:
262 case DISPLAY_CONNECTION_TYPE_NETWORK:
263 // No protections for these types. Do nothing.
264 break;
265 case DISPLAY_CONNECTION_TYPE_NONE:
266 NOTREACHED();
267 break;
268 }
269 }
270
271 return true;
272 }
273
274 DisplayConfigurator::ContentProtectionClientId
RegisterContentProtectionClient()275 DisplayConfigurator::RegisterContentProtectionClient() {
276 if (!configure_display_)
277 return kInvalidClientId;
278
279 return next_display_protection_client_id_++;
280 }
281
UnregisterContentProtectionClient(ContentProtectionClientId client_id)282 void DisplayConfigurator::UnregisterContentProtectionClient(
283 ContentProtectionClientId client_id) {
284 client_protection_requests_.erase(client_id);
285
286 ContentProtections protections;
287 for (ProtectionRequests::const_iterator it =
288 client_protection_requests_.begin();
289 it != client_protection_requests_.end();
290 ++it) {
291 for (ContentProtections::const_iterator it2 = it->second.begin();
292 it2 != it->second.end();
293 ++it2) {
294 protections[it2->first] |= it2->second;
295 }
296 }
297
298 ApplyProtections(protections);
299 }
300
QueryContentProtectionStatus(ContentProtectionClientId client_id,int64_t display_id,uint32_t * link_mask,uint32_t * protection_mask)301 bool DisplayConfigurator::QueryContentProtectionStatus(
302 ContentProtectionClientId client_id,
303 int64_t display_id,
304 uint32_t* link_mask,
305 uint32_t* protection_mask) {
306 if (!configure_display_)
307 return false;
308
309 uint32_t enabled = 0;
310 uint32_t unfulfilled = 0;
311 *link_mask = 0;
312 for (DisplayStateList::const_iterator it = cached_displays_.begin();
313 it != cached_displays_.end();
314 ++it) {
315 // Query display if it is in mirror mode or client on the same display.
316 if (!IsMirroring() && it->display->display_id() != display_id)
317 continue;
318
319 *link_mask |= it->display->type();
320 switch (it->display->type()) {
321 case DISPLAY_CONNECTION_TYPE_UNKNOWN:
322 return false;
323 // DisplayPort, DVI, and HDMI all support HDCP.
324 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
325 case DISPLAY_CONNECTION_TYPE_DVI:
326 case DISPLAY_CONNECTION_TYPE_HDMI: {
327 HDCPState state;
328 if (!native_display_delegate_->GetHDCPState(*it->display, &state))
329 return false;
330 if (state == HDCP_STATE_ENABLED)
331 enabled |= CONTENT_PROTECTION_METHOD_HDCP;
332 else
333 unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP;
334 break;
335 }
336 case DISPLAY_CONNECTION_TYPE_INTERNAL:
337 case DISPLAY_CONNECTION_TYPE_VGA:
338 case DISPLAY_CONNECTION_TYPE_NETWORK:
339 // No protections for these types. Do nothing.
340 break;
341 case DISPLAY_CONNECTION_TYPE_NONE:
342 NOTREACHED();
343 break;
344 }
345 }
346
347 // Don't reveal protections requested by other clients.
348 ProtectionRequests::iterator it = client_protection_requests_.find(client_id);
349 if (it != client_protection_requests_.end()) {
350 uint32_t requested_mask = 0;
351 if (it->second.find(display_id) != it->second.end())
352 requested_mask = it->second[display_id];
353 *protection_mask = enabled & ~unfulfilled & requested_mask;
354 } else {
355 *protection_mask = 0;
356 }
357 return true;
358 }
359
EnableContentProtection(ContentProtectionClientId client_id,int64_t display_id,uint32_t desired_method_mask)360 bool DisplayConfigurator::EnableContentProtection(
361 ContentProtectionClientId client_id,
362 int64_t display_id,
363 uint32_t desired_method_mask) {
364 if (!configure_display_)
365 return false;
366
367 ContentProtections protections;
368 for (ProtectionRequests::const_iterator it =
369 client_protection_requests_.begin();
370 it != client_protection_requests_.end();
371 ++it) {
372 for (ContentProtections::const_iterator it2 = it->second.begin();
373 it2 != it->second.end();
374 ++it2) {
375 if (it->first == client_id && it2->first == display_id)
376 continue;
377 protections[it2->first] |= it2->second;
378 }
379 }
380 protections[display_id] |= desired_method_mask;
381
382 if (!ApplyProtections(protections))
383 return false;
384
385 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) {
386 if (client_protection_requests_.find(client_id) !=
387 client_protection_requests_.end()) {
388 client_protection_requests_[client_id].erase(display_id);
389 if (client_protection_requests_[client_id].size() == 0)
390 client_protection_requests_.erase(client_id);
391 }
392 } else {
393 client_protection_requests_[client_id][display_id] = desired_method_mask;
394 }
395
396 return true;
397 }
398
399 std::vector<ui::ColorCalibrationProfile>
GetAvailableColorCalibrationProfiles(int64_t display_id)400 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) {
401 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
402 switches::kDisableDisplayColorCalibration)) {
403 for (size_t i = 0; i < cached_displays_.size(); ++i) {
404 if (cached_displays_[i].display &&
405 cached_displays_[i].display->display_id() == display_id) {
406 return native_display_delegate_->GetAvailableColorCalibrationProfiles(
407 *cached_displays_[i].display);
408 }
409 }
410 }
411
412 return std::vector<ui::ColorCalibrationProfile>();
413 }
414
SetColorCalibrationProfile(int64_t display_id,ui::ColorCalibrationProfile new_profile)415 bool DisplayConfigurator::SetColorCalibrationProfile(
416 int64_t display_id,
417 ui::ColorCalibrationProfile new_profile) {
418 for (size_t i = 0; i < cached_displays_.size(); ++i) {
419 if (cached_displays_[i].display &&
420 cached_displays_[i].display->display_id() == display_id) {
421 return native_display_delegate_->SetColorCalibrationProfile(
422 *cached_displays_[i].display, new_profile);
423 }
424 }
425
426 return false;
427 }
428
PrepareForExit()429 void DisplayConfigurator::PrepareForExit() {
430 configure_display_ = false;
431 }
432
SetDisplayPower(chromeos::DisplayPowerState power_state,int flags)433 bool DisplayConfigurator::SetDisplayPower(
434 chromeos::DisplayPowerState power_state,
435 int flags) {
436 if (!configure_display_)
437 return false;
438
439 VLOG(1) << "SetDisplayPower: power_state="
440 << DisplayPowerStateToString(power_state) << " flags=" << flags
441 << ", configure timer="
442 << ((configure_timer_.get() && configure_timer_->IsRunning()) ?
443 "Running" : "Stopped");
444 if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe))
445 return true;
446
447 native_display_delegate_->GrabServer();
448 UpdateCachedDisplays();
449
450 const MultipleDisplayState new_state = ChooseDisplayState(power_state);
451 bool attempted_change = false;
452 bool success = false;
453
454 bool only_if_single_internal_display =
455 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay;
456 bool single_internal_display =
457 cached_displays_.size() == 1 &&
458 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
459 if (single_internal_display || !only_if_single_internal_display) {
460 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state);
461 attempted_change = true;
462
463 // Force the DPMS on since the driver doesn't always detect that it
464 // should turn on. This is needed when coming back from idle suspend.
465 if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF)
466 native_display_delegate_->ForceDPMSOn();
467 }
468
469 native_display_delegate_->UngrabServer();
470 if (attempted_change)
471 NotifyObservers(success, new_state);
472 return true;
473 }
474
SetDisplayMode(MultipleDisplayState new_state)475 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
476 if (!configure_display_)
477 return false;
478
479 VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state);
480 if (display_state_ == new_state) {
481 // Cancel software mirroring if the state is moving from
482 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
483 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
484 if (mirroring_controller_ &&
485 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
486 mirroring_controller_->SetSoftwareMirroring(false);
487 NotifyObservers(true, new_state);
488 return true;
489 }
490
491 native_display_delegate_->GrabServer();
492 UpdateCachedDisplays();
493 const bool success =
494 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_);
495 native_display_delegate_->UngrabServer();
496
497 NotifyObservers(success, new_state);
498 return success;
499 }
500
OnConfigurationChanged()501 void DisplayConfigurator::OnConfigurationChanged() {
502 // Configure displays with |kConfigureDelayMs| delay,
503 // so that time-consuming ConfigureDisplays() won't be called multiple times.
504 if (configure_timer_.get()) {
505 configure_timer_->Reset();
506 } else {
507 configure_timer_.reset(new base::OneShotTimer<DisplayConfigurator>());
508 configure_timer_->Start(
509 FROM_HERE,
510 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
511 this,
512 &DisplayConfigurator::ConfigureDisplays);
513 }
514 }
515
AddObserver(Observer * observer)516 void DisplayConfigurator::AddObserver(Observer* observer) {
517 observers_.AddObserver(observer);
518 }
519
RemoveObserver(Observer * observer)520 void DisplayConfigurator::RemoveObserver(Observer* observer) {
521 observers_.RemoveObserver(observer);
522 }
523
SuspendDisplays()524 void DisplayConfigurator::SuspendDisplays() {
525 // If the display is off due to user inactivity and there's only a single
526 // internal display connected, switch to the all-on state before
527 // suspending. This shouldn't be very noticeable to the user since the
528 // backlight is off at this point, and doing this lets us resume directly
529 // into the "on" state, which greatly reduces resume times.
530 if (power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) {
531 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
532 kSetDisplayPowerOnlyIfSingleInternalDisplay);
533
534 // We need to make sure that the monitor configuration we just did actually
535 // completes before we return, because otherwise the X message could be
536 // racing with the HandleSuspendReadiness message.
537 native_display_delegate_->SyncWithServer();
538 }
539 }
540
ResumeDisplays()541 void DisplayConfigurator::ResumeDisplays() {
542 // Force probing to ensure that we pick up any changes that were made
543 // while the system was suspended.
544 SetDisplayPower(power_state_, kSetDisplayPowerForceProbe);
545 }
546
UpdateCachedDisplays()547 void DisplayConfigurator::UpdateCachedDisplays() {
548 std::vector<DisplaySnapshot*> snapshots =
549 native_display_delegate_->GetDisplays();
550
551 cached_displays_.clear();
552 for (size_t i = 0; i < snapshots.size(); ++i) {
553 DisplayState display_state;
554 display_state.display = snapshots[i];
555 cached_displays_.push_back(display_state);
556 }
557
558 touchscreen_delegate_->AssociateTouchscreens(&cached_displays_);
559
560 // Set |selected_mode| fields.
561 for (size_t i = 0; i < cached_displays_.size(); ++i) {
562 DisplayState* display_state = &cached_displays_[i];
563 if (display_state->display->has_proper_display_id()) {
564 gfx::Size size;
565 if (state_controller_ &&
566 state_controller_->GetResolutionForDisplayId(
567 display_state->display->display_id(), &size)) {
568 display_state->selected_mode =
569 FindDisplayModeMatchingSize(*display_state->display, size);
570 }
571 }
572 // Fall back to native mode.
573 if (!display_state->selected_mode)
574 display_state->selected_mode = display_state->display->native_mode();
575 }
576
577 // Set |mirror_mode| fields.
578 if (cached_displays_.size() == 2) {
579 bool one_is_internal =
580 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
581 bool two_is_internal =
582 cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
583 int internal_displays =
584 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
585 DCHECK_LT(internal_displays, 2);
586 LOG_IF(WARNING, internal_displays == 2)
587 << "Two internal displays detected.";
588
589 bool can_mirror = false;
590 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
591 // Try preserving external display's aspect ratio on the first attempt.
592 // If that fails, fall back to the highest matching resolution.
593 bool preserve_aspect = attempt == 0;
594
595 if (internal_displays == 1) {
596 if (one_is_internal) {
597 can_mirror = FindMirrorMode(&cached_displays_[0],
598 &cached_displays_[1],
599 is_panel_fitting_enabled_,
600 preserve_aspect);
601 } else {
602 DCHECK(two_is_internal);
603 can_mirror = FindMirrorMode(&cached_displays_[1],
604 &cached_displays_[0],
605 is_panel_fitting_enabled_,
606 preserve_aspect);
607 }
608 } else { // if (internal_displays == 0)
609 // No panel fitting for external displays, so fall back to exact match.
610 can_mirror = FindMirrorMode(
611 &cached_displays_[0], &cached_displays_[1], false, preserve_aspect);
612 if (!can_mirror && preserve_aspect) {
613 // FindMirrorMode() will try to preserve aspect ratio of what it
614 // thinks is external display, so if it didn't succeed with one, maybe
615 // it will succeed with the other. This way we will have the correct
616 // aspect ratio on at least one of them.
617 can_mirror = FindMirrorMode(&cached_displays_[1],
618 &cached_displays_[0],
619 false,
620 preserve_aspect);
621 }
622 }
623 }
624 }
625 }
626
FindMirrorMode(DisplayState * internal_display,DisplayState * external_display,bool try_panel_fitting,bool preserve_aspect)627 bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display,
628 DisplayState* external_display,
629 bool try_panel_fitting,
630 bool preserve_aspect) {
631 const DisplayMode* internal_native_info =
632 internal_display->display->native_mode();
633 const DisplayMode* external_native_info =
634 external_display->display->native_mode();
635 if (!internal_native_info || !external_native_info)
636 return false;
637
638 // Check if some external display resolution can be mirrored on internal.
639 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
640 // this is the order in which they look better on the monitor.
641 for (DisplayModeList::const_iterator external_it =
642 external_display->display->modes().begin();
643 external_it != external_display->display->modes().end();
644 ++external_it) {
645 const DisplayMode& external_info = **external_it;
646 bool is_native_aspect_ratio =
647 external_native_info->size().width() * external_info.size().height() ==
648 external_native_info->size().height() * external_info.size().width();
649 if (preserve_aspect && !is_native_aspect_ratio)
650 continue; // Allow only aspect ratio preserving modes for mirroring.
651
652 // Try finding an exact match.
653 for (DisplayModeList::const_iterator internal_it =
654 internal_display->display->modes().begin();
655 internal_it != internal_display->display->modes().end();
656 ++internal_it) {
657 const DisplayMode& internal_info = **internal_it;
658 if (internal_info.size().width() == external_info.size().width() &&
659 internal_info.size().height() == external_info.size().height() &&
660 internal_info.is_interlaced() == external_info.is_interlaced()) {
661 internal_display->mirror_mode = *internal_it;
662 external_display->mirror_mode = *external_it;
663 return true; // Mirror mode found.
664 }
665 }
666
667 // Try to create a matching internal display mode by panel fitting.
668 if (try_panel_fitting) {
669 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
670 // ugly, so, can fit == can upscale. Also, internal panels don't support
671 // fitting interlaced modes.
672 bool can_fit = internal_native_info->size().width() >=
673 external_info.size().width() &&
674 internal_native_info->size().height() >=
675 external_info.size().height() &&
676 !external_info.is_interlaced();
677 if (can_fit) {
678 native_display_delegate_->AddMode(*internal_display->display,
679 *external_it);
680 internal_display->display->add_mode(*external_it);
681 internal_display->mirror_mode = *external_it;
682 external_display->mirror_mode = *external_it;
683 return true; // Mirror mode created.
684 }
685 }
686 }
687
688 return false;
689 }
690
ConfigureDisplays()691 void DisplayConfigurator::ConfigureDisplays() {
692 configure_timer_.reset();
693
694 if (!configure_display_)
695 return;
696
697 native_display_delegate_->GrabServer();
698 UpdateCachedDisplays();
699 const MultipleDisplayState new_state = ChooseDisplayState(power_state_);
700 const bool success =
701 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_);
702 native_display_delegate_->UngrabServer();
703
704 NotifyObservers(success, new_state);
705 }
706
NotifyObservers(bool success,MultipleDisplayState attempted_state)707 void DisplayConfigurator::NotifyObservers(
708 bool success,
709 MultipleDisplayState attempted_state) {
710 if (success) {
711 FOR_EACH_OBSERVER(
712 Observer, observers_, OnDisplayModeChanged(cached_displays_));
713 } else {
714 FOR_EACH_OBSERVER(
715 Observer, observers_, OnDisplayModeChangeFailed(attempted_state));
716 }
717 }
718
EnterStateOrFallBackToSoftwareMirroring(MultipleDisplayState display_state,chromeos::DisplayPowerState power_state)719 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
720 MultipleDisplayState display_state,
721 chromeos::DisplayPowerState power_state) {
722 bool success = EnterState(display_state, power_state);
723 if (mirroring_controller_) {
724 bool enable_software_mirroring = false;
725 if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
726 if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED ||
727 power_state_ != power_state)
728 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state);
729 enable_software_mirroring = success =
730 display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
731 }
732 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
733 }
734 return success;
735 }
736
EnterState(MultipleDisplayState display_state,chromeos::DisplayPowerState power_state)737 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state,
738 chromeos::DisplayPowerState power_state) {
739 std::vector<bool> display_power;
740 int num_on_displays =
741 GetDisplayPower(cached_displays_, power_state, &display_power);
742 VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state)
743 << " power=" << DisplayPowerStateToString(power_state);
744
745 // Framebuffer dimensions.
746 gfx::Size size;
747
748 std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point());
749 std::vector<const DisplayMode*> new_mode;
750 for (size_t i = 0; i < cached_displays_.size(); ++i)
751 new_mode.push_back(cached_displays_[i].display->current_mode());
752
753 switch (display_state) {
754 case MULTIPLE_DISPLAY_STATE_INVALID:
755 NOTREACHED() << "Ignoring request to enter invalid state with "
756 << cached_displays_.size() << " connected display(s)";
757 return false;
758 case MULTIPLE_DISPLAY_STATE_HEADLESS:
759 if (cached_displays_.size() != 0) {
760 LOG(WARNING) << "Ignoring request to enter headless mode with "
761 << cached_displays_.size() << " connected display(s)";
762 return false;
763 }
764 break;
765 case MULTIPLE_DISPLAY_STATE_SINGLE: {
766 // If there are multiple displays connected, only one should be turned on.
767 if (cached_displays_.size() != 1 && num_on_displays != 1) {
768 LOG(WARNING) << "Ignoring request to enter single mode with "
769 << cached_displays_.size() << " connected displays and "
770 << num_on_displays << " turned on";
771 return false;
772 }
773
774 for (size_t i = 0; i < cached_displays_.size(); ++i) {
775 DisplayState* state = &cached_displays_[i];
776 new_mode[i] = display_power[i] ? state->selected_mode : NULL;
777
778 if (display_power[i] || cached_displays_.size() == 1) {
779 const DisplayMode* mode_info = state->selected_mode;
780 if (!mode_info)
781 return false;
782 if (mode_info->size() == gfx::Size(1024, 768)) {
783 VLOG(1) << "Potentially misdetecting display(1024x768):"
784 << " displays size=" << cached_displays_.size()
785 << ", num_on_displays=" << num_on_displays
786 << ", current size:" << size.width() << "x" << size.height()
787 << ", i=" << i << ", display=" << state->display->ToString()
788 << ", display_mode=" << mode_info->ToString();
789 }
790 size = mode_info->size();
791 }
792 }
793 break;
794 }
795 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
796 if (cached_displays_.size() != 2 ||
797 (num_on_displays != 0 && num_on_displays != 2)) {
798 LOG(WARNING) << "Ignoring request to enter mirrored mode with "
799 << cached_displays_.size() << " connected display(s) and "
800 << num_on_displays << " turned on";
801 return false;
802 }
803
804 const DisplayMode* mode_info = cached_displays_[0].mirror_mode;
805 if (!mode_info)
806 return false;
807 size = mode_info->size();
808
809 for (size_t i = 0; i < cached_displays_.size(); ++i) {
810 DisplayState* state = &cached_displays_[i];
811 new_mode[i] = display_power[i] ? state->mirror_mode : NULL;
812 if (state->touch_device_id) {
813 if (state->mirror_mode != state->display->native_mode() &&
814 state->display->is_aspect_preserving_scaling()) {
815 mirrored_display_area_ratio_map_[state->touch_device_id] =
816 GetMirroredDisplayAreaRatio(*state);
817 }
818 }
819 }
820 break;
821 }
822 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: {
823 if (cached_displays_.size() != 2 ||
824 (num_on_displays != 0 && num_on_displays != 2)) {
825 LOG(WARNING) << "Ignoring request to enter extended mode with "
826 << cached_displays_.size() << " connected display(s) and "
827 << num_on_displays << " turned on";
828 return false;
829 }
830
831 for (size_t i = 0; i < cached_displays_.size(); ++i) {
832 DisplayState* state = &cached_displays_[i];
833 new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0);
834 new_mode[i] = display_power[i] ? state->selected_mode : NULL;
835
836 // Retain the full screen size even if all displays are off so the
837 // same desktop configuration can be restored when the displays are
838 // turned back on.
839 const DisplayMode* mode_info = cached_displays_[i].selected_mode;
840 if (!mode_info)
841 return false;
842
843 size.set_width(std::max<int>(size.width(), mode_info->size().width()));
844 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
845 mode_info->size().height());
846 }
847 break;
848 }
849 }
850
851 // Finally, apply the desired changes.
852 bool all_succeeded = true;
853 if (!cached_displays_.empty()) {
854 native_display_delegate_->CreateFrameBuffer(size);
855 for (size_t i = 0; i < cached_displays_.size(); ++i) {
856 const DisplayState& state = cached_displays_[i];
857 bool configure_succeeded = false;
858
859 while (true) {
860 if (native_display_delegate_->Configure(
861 *state.display, new_mode[i], new_origins[i])) {
862 state.display->set_current_mode(new_mode[i]);
863 state.display->set_origin(new_origins[i]);
864
865 configure_succeeded = true;
866 break;
867 }
868
869 const DisplayMode* mode_info = new_mode[i];
870 if (!mode_info)
871 break;
872
873 // Find the mode with the next-best resolution and see if that can
874 // be set.
875 int best_mode_pixels = 0;
876
877 int current_mode_pixels = mode_info->size().GetArea();
878 for (DisplayModeList::const_iterator it =
879 state.display->modes().begin();
880 it != state.display->modes().end();
881 it++) {
882 int pixel_count = (*it)->size().GetArea();
883 if ((pixel_count < current_mode_pixels) &&
884 (pixel_count > best_mode_pixels)) {
885 new_mode[i] = *it;
886 best_mode_pixels = pixel_count;
887 }
888 }
889
890 if (best_mode_pixels == 0)
891 break;
892 }
893
894 if (!configure_succeeded)
895 all_succeeded = false;
896
897 // If we are trying to set mirror mode and one of the modesets fails,
898 // then the two monitors will be mis-matched. In this case, return
899 // false to let the observers be aware.
900 if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR &&
901 display_power[i] &&
902 state.display->current_mode() != state.mirror_mode)
903 all_succeeded = false;
904 }
905 }
906
907 if (all_succeeded) {
908 display_state_ = display_state;
909 power_state_ = power_state;
910 framebuffer_size_ = size;
911 }
912 return all_succeeded;
913 }
914
ChooseDisplayState(chromeos::DisplayPowerState power_state) const915 MultipleDisplayState DisplayConfigurator::ChooseDisplayState(
916 chromeos::DisplayPowerState power_state) const {
917 int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL);
918 switch (cached_displays_.size()) {
919 case 0:
920 return MULTIPLE_DISPLAY_STATE_HEADLESS;
921 case 1:
922 return MULTIPLE_DISPLAY_STATE_SINGLE;
923 case 2: {
924 if (num_on_displays == 1) {
925 // If only one display is currently turned on, return the "single"
926 // state so that its native mode will be used.
927 return MULTIPLE_DISPLAY_STATE_SINGLE;
928 } else {
929 if (!state_controller_)
930 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
931 // With either both displays on or both displays off, use one of the
932 // dual modes.
933 std::vector<int64_t> display_ids;
934 for (size_t i = 0; i < cached_displays_.size(); ++i) {
935 // If display id isn't available, switch to extended mode.
936 if (!cached_displays_[i].display->has_proper_display_id())
937 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
938 display_ids.push_back(cached_displays_[i].display->display_id());
939 }
940 return state_controller_->GetStateForDisplayIds(display_ids);
941 }
942 }
943 default:
944 NOTREACHED();
945 }
946 return MULTIPLE_DISPLAY_STATE_INVALID;
947 }
948
GetMirroredDisplayAreaRatio(const DisplayState & display_state)949 float DisplayConfigurator::GetMirroredDisplayAreaRatio(
950 const DisplayState& display_state) {
951 float area_ratio = 1.0f;
952 const DisplayMode* native_mode_info = display_state.display->native_mode();
953 const DisplayMode* mirror_mode_info = display_state.mirror_mode;
954
955 if (!native_mode_info || !mirror_mode_info ||
956 native_mode_info->size().height() == 0 ||
957 mirror_mode_info->size().height() == 0 ||
958 native_mode_info->size().width() == 0 ||
959 mirror_mode_info->size().width() == 0)
960 return area_ratio;
961
962 float width_ratio = static_cast<float>(mirror_mode_info->size().width()) /
963 static_cast<float>(native_mode_info->size().width());
964 float height_ratio = static_cast<float>(mirror_mode_info->size().height()) /
965 static_cast<float>(native_mode_info->size().height());
966
967 area_ratio = width_ratio * height_ratio;
968 return area_ratio;
969 }
970
971 } // namespace ui
972