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 = FindMirrorMode(&outputs[0], &outputs[1], |
| 498 is_panel_fitting_enabled_, preserve_aspect); |
| 499 } else { |
| 500 DCHECK(two_is_internal); |
| 501 can_mirror = FindMirrorMode(&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 = FindMirrorMode(&outputs[0], &outputs[1], false, |
| 507 preserve_aspect); |
| 508 if (!can_mirror && preserve_aspect) { |
| 509 // FindMirrorMode() will try to preserve aspect ratio of what it |
| 510 // thinks is external display, so if it didn't succeed with one, maybe |
| 511 // it will succeed with the other. This way we will have the correct |
| 512 // aspect ratio on at least one of them. |
| 513 can_mirror = FindMirrorMode(&outputs[1], &outputs[0], false, |
| 514 preserve_aspect); |
| 515 } |
| 516 } |
| 517 } |
| 518 } |
| 519 |
| 520 return outputs; |
| 521 } |
| 522 |
| 523 bool OutputConfigurator::FindMirrorMode(OutputSnapshot* internal_output, |
| 524 OutputSnapshot* external_output, |
| 525 bool try_panel_fitting, |
| 526 bool preserve_aspect) { |
| 527 const ModeInfo* internal_native_info = |
| 528 GetModeInfo(*internal_output, internal_output->native_mode); |
| 529 const ModeInfo* external_native_info = |
| 530 GetModeInfo(*external_output, external_output->native_mode); |
| 531 if (!internal_native_info || !external_native_info) |
| 532 return false; |
| 533 |
| 534 // Check if some external output resolution can be mirrored on internal. |
| 535 // Prefer the modes in the order that X sorts them, assuming this is the order |
| 536 // in which they look better on the monitor. |
| 537 for (ModeInfoMap::const_iterator external_it = |
| 538 external_output->mode_infos.begin(); |
| 539 external_it != external_output->mode_infos.end(); ++external_it) { |
| 540 const ModeInfo& external_info = external_it->second; |
| 541 bool is_native_aspect_ratio = |
| 542 external_native_info->width * external_info.height == |
| 543 external_native_info->height * external_info.width; |
| 544 if (preserve_aspect && !is_native_aspect_ratio) |
| 545 continue; // Allow only aspect ratio preserving modes for mirroring. |
| 546 |
| 547 // Try finding an exact match. |
| 548 for (ModeInfoMap::const_iterator internal_it = |
| 549 internal_output->mode_infos.begin(); |
| 550 internal_it != internal_output->mode_infos.end(); ++internal_it) { |
| 551 const ModeInfo& internal_info = internal_it->second; |
| 552 if (internal_info.width == external_info.width && |
| 553 internal_info.height == external_info.height && |
| 554 internal_info.interlaced == external_info.interlaced) { |
| 555 internal_output->mirror_mode = internal_it->first; |
| 556 external_output->mirror_mode = external_it->first; |
| 557 return true; // Mirror mode found. |
| 558 } |
| 559 } |
| 560 |
| 561 // Try to create a matching internal output mode by panel fitting. |
| 562 if (try_panel_fitting) { |
| 563 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks |
| 564 // ugly, so, can fit == can upscale. Also, internal panels don't support |
| 565 // fitting interlaced modes. |
| 566 bool can_fit = |
| 567 internal_native_info->width >= external_info.width && |
| 568 internal_native_info->height >= external_info.height && |
| 569 !external_info.interlaced; |
| 570 if (can_fit) { |
| 571 RRMode mode = external_it->first; |
| 572 delegate_->AddOutputMode(internal_output->output, mode); |
| 573 internal_output->mode_infos.insert(std::make_pair(mode, external_info)); |
| 574 internal_output->mirror_mode = mode; |
| 575 external_output->mirror_mode = mode; |
| 576 return true; // Mirror mode created. |
| 577 } |
| 578 } |
| 579 } |
| 580 |
| 581 return false; |
| 582 } |
| 583 |
421 void OutputConfigurator::ConfigureOutputs() { | 584 void OutputConfigurator::ConfigureOutputs() { |
422 configure_timer_.reset(); | 585 configure_timer_.reset(); |
423 | 586 |
424 delegate_->GrabServer(); | 587 delegate_->GrabServer(); |
425 std::vector<OutputSnapshot> outputs = | 588 std::vector<OutputSnapshot> outputs = GetOutputs(); |
426 delegate_->GetOutputs(state_controller_); | |
427 const OutputState new_state = GetOutputState(outputs, power_state_); | 589 const OutputState new_state = GetOutputState(outputs, power_state_); |
428 const bool success = EnterStateOrFallBackToSoftwareMirroring( | 590 const bool success = EnterStateOrFallBackToSoftwareMirroring( |
429 new_state, power_state_, outputs); | 591 new_state, power_state_, outputs); |
430 delegate_->UngrabServer(); | 592 delegate_->UngrabServer(); |
431 | 593 |
432 NotifyObservers(success, new_state); | 594 NotifyObservers(success, new_state); |
433 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); | 595 delegate_->SendProjectingStateToPowerManager(IsProjecting(outputs)); |
434 } | 596 } |
435 | 597 |
436 void OutputConfigurator::NotifyObservers(bool success, | 598 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) / | 861 float width_ratio = static_cast<float>(mirror_mode_info->width) / |
700 static_cast<float>(native_mode_info->width); | 862 static_cast<float>(native_mode_info->width); |
701 float height_ratio = static_cast<float>(mirror_mode_info->height) / | 863 float height_ratio = static_cast<float>(mirror_mode_info->height) / |
702 static_cast<float>(native_mode_info->height); | 864 static_cast<float>(native_mode_info->height); |
703 | 865 |
704 area_ratio = width_ratio * height_ratio; | 866 area_ratio = width_ratio * height_ratio; |
705 return area_ratio; | 867 return area_ratio; |
706 } | 868 } |
707 | 869 |
708 } // namespace chromeos | 870 } // namespace chromeos |
OLD | NEW |