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

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

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

Powered by Google App Engine
This is Rietveld 408576698