OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/display/output_configurator.h" | 5 #include "chromeos/display/output_configurator.h" |
6 | 6 |
7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
8 #include <X11/extensions/Xrandr.h> | 8 #include <X11/extensions/Xrandr.h> |
9 #include <X11/extensions/XInput2.h> | 9 #include <X11/extensions/XInput2.h> |
10 | 10 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
94 // "Projecting" is defined as having more than 1 output connected while at | 94 // "Projecting" is defined as having more than 1 output connected while at |
95 // least one of them is an internal output. | 95 // least one of them is an internal output. |
96 return has_internal_output && (connected_output_count > 1); | 96 return has_internal_output && (connected_output_count > 1); |
97 } | 97 } |
98 | 98 |
99 } // namespace | 99 } // namespace |
100 | 100 |
101 OutputConfigurator::ModeInfo::ModeInfo() | 101 OutputConfigurator::ModeInfo::ModeInfo() |
102 : width(0), | 102 : width(0), |
103 height(0), | 103 height(0), |
104 interlaced(false) {} | 104 interlaced(false), |
105 refresh_rate(0.0) {} | |
105 | 106 |
106 OutputConfigurator::ModeInfo::ModeInfo(int width, int height, bool interlaced) | 107 OutputConfigurator::ModeInfo::ModeInfo(int width, |
108 int height, | |
109 bool interlaced, | |
110 float refresh_rate) | |
107 : width(width), | 111 : width(width), |
108 height(height), | 112 height(height), |
109 interlaced(interlaced) {} | 113 interlaced(interlaced), |
114 refresh_rate(refresh_rate) {} | |
110 | 115 |
111 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() | 116 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() |
112 : x_scale(1.0), | 117 : x_scale(1.0), |
113 x_offset(0.0), | 118 x_offset(0.0), |
114 y_scale(1.0), | 119 y_scale(1.0), |
115 y_offset(0.0) {} | 120 y_offset(0.0) {} |
116 | 121 |
117 OutputConfigurator::OutputSnapshot::OutputSnapshot() | 122 OutputConfigurator::OutputSnapshot::OutputSnapshot() |
118 : output(None), | 123 : output(None), |
119 crtc(None), | 124 crtc(None), |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
165 } | 170 } |
166 } | 171 } |
167 | 172 |
168 // static | 173 // static |
169 const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo( | 174 const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo( |
170 const OutputSnapshot& output, | 175 const OutputSnapshot& output, |
171 RRMode mode) { | 176 RRMode mode) { |
172 if (mode == None) | 177 if (mode == None) |
173 return NULL; | 178 return NULL; |
174 | 179 |
175 std::map<RRMode, ModeInfo>::const_iterator it = output.mode_infos.find(mode); | 180 ModeInfoMap::const_iterator it = output.mode_infos.find(mode); |
176 if (it == output.mode_infos.end()) { | 181 if (it == output.mode_infos.end()) { |
177 LOG(WARNING) << "Unable to find info about mode " << mode | 182 LOG(WARNING) << "Unable to find info about mode " << mode |
178 << " for output " << output.output; | 183 << " for output " << output.output; |
179 return NULL; | 184 return NULL; |
180 } | 185 } |
181 return &it->second; | 186 return &it->second; |
182 } | 187 } |
183 | 188 |
189 // static | |
190 RRMode OutputConfigurator::FindOutputModeMatchingSize( | |
191 const OutputSnapshot& output, | |
192 int width, | |
193 int height) { | |
194 RRMode found = None; | |
195 float best_rate = 0; | |
196 bool non_interlaced_found = false; | |
197 for (ModeInfoMap::const_iterator it = output.mode_infos.begin(); | |
198 it != output.mode_infos.end(); ++it) { | |
199 RRMode mode = it->first; | |
200 const ModeInfo& info = it->second; | |
201 | |
202 if (info.width == width && info.height == height) { | |
203 if (info.interlaced) { | |
204 if (non_interlaced_found) | |
205 continue; | |
206 } else { | |
207 // Reset the best rate if the non interlaced is | |
208 // found the first time. | |
209 if (!non_interlaced_found) | |
210 best_rate = info.refresh_rate; | |
211 non_interlaced_found = true; | |
212 } | |
213 if (info.refresh_rate < best_rate) | |
214 continue; | |
215 | |
216 found = mode; | |
217 best_rate = info.refresh_rate; | |
218 } | |
219 } | |
220 return found; | |
221 } | |
222 | |
184 OutputConfigurator::OutputConfigurator() | 223 OutputConfigurator::OutputConfigurator() |
185 : state_controller_(NULL), | 224 : state_controller_(NULL), |
186 mirroring_controller_(NULL), | 225 mirroring_controller_(NULL), |
226 is_panel_fitting_enabled_(false), | |
187 configure_display_(base::chromeos::IsRunningOnChromeOS()), | 227 configure_display_(base::chromeos::IsRunningOnChromeOS()), |
188 xrandr_event_base_(0), | 228 xrandr_event_base_(0), |
189 output_state_(STATE_INVALID), | 229 output_state_(STATE_INVALID), |
190 power_state_(DISPLAY_POWER_ALL_ON) { | 230 power_state_(DISPLAY_POWER_ALL_ON) { |
191 } | 231 } |
192 | 232 |
193 OutputConfigurator::~OutputConfigurator() {} | 233 OutputConfigurator::~OutputConfigurator() {} |
194 | 234 |
195 void OutputConfigurator::SetDelegateForTesting(scoped_ptr<Delegate> delegate) { | 235 void OutputConfigurator::SetDelegateForTesting(scoped_ptr<Delegate> delegate) { |
196 delegate_ = delegate.Pass(); | 236 delegate_ = delegate.Pass(); |
197 configure_display_ = true; | 237 configure_display_ = true; |
198 } | 238 } |
199 | 239 |
200 void OutputConfigurator::SetInitialDisplayPower(DisplayPowerState power_state) { | 240 void OutputConfigurator::SetInitialDisplayPower(DisplayPowerState power_state) { |
201 DCHECK_EQ(output_state_, STATE_INVALID); | 241 DCHECK_EQ(output_state_, STATE_INVALID); |
202 power_state_ = power_state; | 242 power_state_ = power_state; |
203 } | 243 } |
204 | 244 |
205 void OutputConfigurator::Init(bool is_panel_fitting_enabled) { | 245 void OutputConfigurator::Init(bool is_panel_fitting_enabled) { |
246 is_panel_fitting_enabled_ = is_panel_fitting_enabled; | |
206 if (!configure_display_) | 247 if (!configure_display_) |
207 return; | 248 return; |
208 | 249 |
209 if (!delegate_) | 250 if (!delegate_) |
210 delegate_.reset(new RealOutputConfiguratorDelegate()); | 251 delegate_.reset(new RealOutputConfiguratorDelegate()); |
211 delegate_->SetPanelFittingEnabled(is_panel_fitting_enabled); | |
212 } | 252 } |
213 | 253 |
214 void OutputConfigurator::Start(uint32 background_color_argb) { | 254 void OutputConfigurator::Start(uint32 background_color_argb) { |
215 if (!configure_display_) | 255 if (!configure_display_) |
216 return; | 256 return; |
217 | 257 |
218 delegate_->GrabServer(); | 258 delegate_->GrabServer(); |
219 delegate_->InitXRandRExtension(&xrandr_event_base_); | 259 delegate_->InitXRandRExtension(&xrandr_event_base_); |
220 | 260 |
221 std::vector<OutputSnapshot> outputs = | 261 std::vector<OutputSnapshot> outputs = GetOutputs(); |
222 delegate_->GetOutputs(state_controller_); | |
223 if (outputs.size() > 1 && background_color_argb) | 262 if (outputs.size() > 1 && background_color_argb) |
224 delegate_->SetBackgroundColor(background_color_argb); | 263 delegate_->SetBackgroundColor(background_color_argb); |
225 const OutputState new_state = GetOutputState(outputs, power_state_); | 264 const OutputState new_state = GetOutputState(outputs, power_state_); |
226 const bool success = EnterStateOrFallBackToSoftwareMirroring( | 265 const bool success = EnterStateOrFallBackToSoftwareMirroring( |
227 new_state, power_state_, outputs); | 266 new_state, power_state_, outputs); |
228 | 267 |
229 // Force the DPMS on chrome startup as the driver doesn't always detect | 268 // Force the DPMS on chrome startup as the driver doesn't always detect |
230 // that all displays are on when signing out. | 269 // that all displays are on when signing out. |
231 delegate_->ForceDPMSOn(); | 270 delegate_->ForceDPMSOn(); |
232 delegate_->UngrabServer(); | 271 delegate_->UngrabServer(); |
233 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); | 272 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); |
234 NotifyObservers(success, new_state); | 273 NotifyObservers(success, new_state); |
235 } | 274 } |
236 | 275 |
237 void OutputConfigurator::Stop() { | 276 void OutputConfigurator::Stop() { |
238 configure_display_ = false; | 277 configure_display_ = false; |
239 } | 278 } |
240 | 279 |
241 bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, | 280 bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, |
242 int flags) { | 281 int flags) { |
243 if (!configure_display_) | 282 if (!configure_display_) |
244 return false; | 283 return false; |
245 | 284 |
246 VLOG(1) << "SetDisplayPower: power_state=" | 285 VLOG(1) << "SetDisplayPower: power_state=" |
247 << DisplayPowerStateToString(power_state) << " flags=" << flags; | 286 << DisplayPowerStateToString(power_state) << " flags=" << flags; |
248 if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe)) | 287 if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe)) |
249 return true; | 288 return true; |
250 | 289 |
251 delegate_->GrabServer(); | 290 delegate_->GrabServer(); |
252 std::vector<OutputSnapshot> outputs = | 291 std::vector<OutputSnapshot> outputs = GetOutputs(); |
253 delegate_->GetOutputs(state_controller_); | |
254 | 292 |
255 const OutputState new_state = GetOutputState(outputs, power_state); | 293 const OutputState new_state = GetOutputState(outputs, power_state); |
256 bool attempted_change = false; | 294 bool attempted_change = false; |
257 bool success = false; | 295 bool success = false; |
258 | 296 |
259 bool only_if_single_internal_display = | 297 bool only_if_single_internal_display = |
260 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; | 298 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; |
261 bool single_internal_display = outputs.size() == 1 && outputs[0].is_internal; | 299 bool single_internal_display = outputs.size() == 1 && outputs[0].is_internal; |
262 if (single_internal_display || !only_if_single_internal_display) { | 300 if (single_internal_display || !only_if_single_internal_display) { |
263 success = EnterStateOrFallBackToSoftwareMirroring( | 301 success = EnterStateOrFallBackToSoftwareMirroring( |
(...skipping 20 matching lines...) Expand all Loading... | |
284 if (output_state_ == new_state) { | 322 if (output_state_ == new_state) { |
285 // Cancel software mirroring if the state is moving from | 323 // Cancel software mirroring if the state is moving from |
286 // STATE_DUAL_EXTENDED to STATE_DUAL_EXTENDED. | 324 // STATE_DUAL_EXTENDED to STATE_DUAL_EXTENDED. |
287 if (mirroring_controller_ && new_state == STATE_DUAL_EXTENDED) | 325 if (mirroring_controller_ && new_state == STATE_DUAL_EXTENDED) |
288 mirroring_controller_->SetSoftwareMirroring(false); | 326 mirroring_controller_->SetSoftwareMirroring(false); |
289 NotifyObservers(true, new_state); | 327 NotifyObservers(true, new_state); |
290 return true; | 328 return true; |
291 } | 329 } |
292 | 330 |
293 delegate_->GrabServer(); | 331 delegate_->GrabServer(); |
294 std::vector<OutputSnapshot> outputs = | 332 std::vector<OutputSnapshot> outputs = GetOutputs(); |
295 delegate_->GetOutputs(state_controller_); | |
296 const bool success = EnterStateOrFallBackToSoftwareMirroring( | 333 const bool success = EnterStateOrFallBackToSoftwareMirroring( |
297 new_state, power_state_, outputs); | 334 new_state, power_state_, outputs); |
298 delegate_->UngrabServer(); | 335 delegate_->UngrabServer(); |
299 | 336 |
300 NotifyObservers(success, new_state); | 337 NotifyObservers(success, new_state); |
301 return success; | 338 return success; |
302 } | 339 } |
303 | 340 |
304 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) { | 341 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) { |
305 if (!configure_display_) | 342 if (!configure_display_) |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
411 } else { | 448 } else { |
412 configure_timer_.reset(new base::OneShotTimer<OutputConfigurator>()); | 449 configure_timer_.reset(new base::OneShotTimer<OutputConfigurator>()); |
413 configure_timer_->Start( | 450 configure_timer_->Start( |
414 FROM_HERE, | 451 FROM_HERE, |
415 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), | 452 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), |
416 this, | 453 this, |
417 &OutputConfigurator::ConfigureOutputs); | 454 &OutputConfigurator::ConfigureOutputs); |
418 } | 455 } |
419 } | 456 } |
420 | 457 |
458 std::vector<OutputConfigurator::OutputSnapshot> | |
459 OutputConfigurator::GetOutputs() { | |
460 std::vector<OutputSnapshot> outputs = delegate_->GetOutputs(); | |
461 | |
462 // Set |selected_mode| fields. | |
463 for (size_t i = 0; i < outputs.size(); ++i) { | |
464 OutputSnapshot* output = &outputs[i]; | |
465 if (output->has_display_id) { | |
466 int width = 0, height = 0; | |
467 if (state_controller_ && | |
468 state_controller_->GetResolutionForDisplayId( | |
469 output->display_id, &width, &height)) { | |
470 output->selected_mode = | |
471 FindOutputModeMatchingSize(*output, width, height); | |
472 } | |
473 } | |
474 // Fall back to native mode. | |
475 if (output->selected_mode == None) | |
476 output->selected_mode = output->native_mode; | |
477 } | |
478 | |
479 // Set |mirror_mode| fields. | |
480 if (outputs.size() == 2) { | |
481 bool one_is_internal = outputs[0].is_internal; | |
482 bool two_is_internal = outputs[1].is_internal; | |
483 int internal_outputs = (one_is_internal ? 1 : 0) + | |
484 (two_is_internal ? 1 : 0); | |
485 DCHECK_LT(internal_outputs, 2); | |
486 LOG_IF(WARNING, internal_outputs == 2) | |
487 << "Two internal outputs detected."; | |
488 | |
489 bool can_mirror = false; | |
490 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { | |
491 // Try preserving external output's aspect ratio on the first attempt. | |
492 // If that fails, fall back to the highest matching resolution. | |
493 bool preserve_aspect = attempt == 0; | |
494 | |
495 if (internal_outputs == 1) { | |
496 if (one_is_internal) { | |
497 can_mirror = FindOrCreateMirrorMode(&outputs[0], &outputs[1], | |
498 is_panel_fitting_enabled_, preserve_aspect); | |
499 } else { | |
500 DCHECK(two_is_internal); | |
501 can_mirror = FindOrCreateMirrorMode(&outputs[1], &outputs[0], | |
502 is_panel_fitting_enabled_, preserve_aspect); | |
503 } | |
504 } else { // if (internal_outputs == 0) | |
505 // No panel fitting for external outputs, so fall back to exact match. | |
506 can_mirror = FindOrCreateMirrorMode(&outputs[0], &outputs[1], false, | |
507 preserve_aspect); | |
508 if (!can_mirror && preserve_aspect) { | |
509 // FindOrCreateMirrorMode will try to preserve aspect ratio of | |
510 // what it thinks is external display, so if it didn't succeed | |
511 // with one, maybe it will succeed with the other. This way we | |
512 // will have the correct aspect ratio on at least one of them. | |
513 can_mirror = FindOrCreateMirrorMode(&outputs[1], &outputs[0], | |
514 false, preserve_aspect); | |
515 } | |
516 } | |
517 } | |
518 } | |
519 | |
520 return outputs; | |
521 } | |
522 | |
523 bool OutputConfigurator::FindOrCreateMirrorMode( | |
524 OutputSnapshot* internal_output, | |
525 OutputSnapshot* external_output, | |
526 bool try_creating, | |
527 bool preserve_aspect) { | |
528 const ModeInfo* internal_native_info = | |
529 GetModeInfo(*internal_output, internal_output->native_mode); | |
530 const ModeInfo* external_native_info = | |
531 GetModeInfo(*external_output, external_output->native_mode); | |
532 if (!internal_native_info || !external_native_info) | |
533 return false; | |
534 | |
535 // Check if some external output resolution can be mirrored on internal. | |
536 // Prefer the modes in the order that X sorts them, assuming this is the order | |
537 // in which they look better on the monitor. | |
538 for (ModeInfoMap::const_iterator external_it = | |
539 external_output->mode_infos.begin(); | |
540 external_it != external_output->mode_infos.end(); ++external_it) { | |
541 const ModeInfo& external_info = external_it->second; | |
542 bool is_native_aspect_ratio = | |
543 external_native_info->width * external_info.height == | |
544 external_native_info->height * external_info.width; | |
545 if (preserve_aspect && !is_native_aspect_ratio) | |
546 continue; // Allow only aspect ratio preserving modes for mirroring. | |
547 | |
548 // Try finding an exact match. | |
549 for (ModeInfoMap::const_iterator internal_it = | |
550 internal_output->mode_infos.begin(); | |
551 internal_it != internal_output->mode_infos.end(); ++internal_it) { | |
552 const ModeInfo& internal_info = internal_it->second; | |
553 if (internal_info.width == external_info.width && | |
554 internal_info.height == external_info.height && | |
555 internal_info.interlaced == external_info.interlaced) { | |
556 internal_output->mirror_mode = internal_it->first; | |
557 external_output->mirror_mode = external_it->first; | |
558 return true; // Mirror mode found. | |
559 } | |
560 } | |
561 | |
562 // Try to create a matching internal output mode by panel fitting. | |
563 if (try_creating) { | |
564 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks | |
565 // ugly, so, can fit == can upscale. Also, internal panels don't support | |
566 // fitting interlaced modes. | |
567 bool can_fit = | |
568 internal_native_info->width >= external_info.width && | |
569 internal_native_info->height >= external_info.height && | |
570 !external_info.interlaced; | |
571 if (can_fit) { | |
572 RRMode mode = external_it->first; | |
573 delegate_->AddOutputMode(internal_output->output, mode); | |
574 internal_output->mode_infos.insert(std::make_pair(mode, external_info)); | |
Daniel Erat
2013/09/17 15:53:27
(Side note: Apart from this line, which is the act
| |
575 internal_output->mirror_mode = mode; | |
576 external_output->mirror_mode = mode; | |
577 return true; // Mirror mode created. | |
578 } | |
579 } | |
580 } | |
581 | |
582 return false; | |
583 } | |
584 | |
421 void OutputConfigurator::ConfigureOutputs() { | 585 void OutputConfigurator::ConfigureOutputs() { |
422 configure_timer_.reset(); | 586 configure_timer_.reset(); |
423 | 587 |
424 delegate_->GrabServer(); | 588 delegate_->GrabServer(); |
425 std::vector<OutputSnapshot> outputs = | 589 std::vector<OutputSnapshot> outputs = GetOutputs(); |
426 delegate_->GetOutputs(state_controller_); | |
427 const OutputState new_state = GetOutputState(outputs, power_state_); | 590 const OutputState new_state = GetOutputState(outputs, power_state_); |
428 const bool success = EnterStateOrFallBackToSoftwareMirroring( | 591 const bool success = EnterStateOrFallBackToSoftwareMirroring( |
429 new_state, power_state_, outputs); | 592 new_state, power_state_, outputs); |
430 delegate_->UngrabServer(); | 593 delegate_->UngrabServer(); |
431 | 594 |
432 NotifyObservers(success, new_state); | 595 NotifyObservers(success, new_state); |
433 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); | 596 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); |
434 } | 597 } |
435 | 598 |
436 void OutputConfigurator::NotifyObservers(bool success, | 599 void OutputConfigurator::NotifyObservers(bool success, |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
699 float width_ratio = static_cast<float>(mirror_mode_info->width) / | 862 float width_ratio = static_cast<float>(mirror_mode_info->width) / |
700 static_cast<float>(native_mode_info->width); | 863 static_cast<float>(native_mode_info->width); |
701 float height_ratio = static_cast<float>(mirror_mode_info->height) / | 864 float height_ratio = static_cast<float>(mirror_mode_info->height) / |
702 static_cast<float>(native_mode_info->height); | 865 static_cast<float>(native_mode_info->height); |
703 | 866 |
704 area_ratio = width_ratio * height_ratio; | 867 area_ratio = width_ratio * height_ratio; |
705 return area_ratio; | 868 return area_ratio; |
706 } | 869 } |
707 | 870 |
708 } // namespace chromeos | 871 } // namespace chromeos |
OLD | NEW |