• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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