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