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

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

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

Powered by Google App Engine
This is Rietveld 408576698