Chromium Code Reviews| 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 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 for (size_t i = 0; i < outputs.size(); ++i) | 91 for (size_t i = 0; i < outputs.size(); ++i) |
| 92 has_internal_output |= outputs[i].is_internal; | 92 has_internal_output |= outputs[i].is_internal; |
| 93 | 93 |
| 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::CoordinateTransformation::CoordinateTransformation() | |
| 102 : x_scale(1.0), | |
| 103 x_offset(0.0), | |
| 104 y_scale(1.0), | |
| 105 y_offset(0.0) {} | |
| 106 | |
| 101 OutputConfigurator::OutputSnapshot::OutputSnapshot() | 107 OutputConfigurator::OutputSnapshot::OutputSnapshot() |
| 102 : output(None), | 108 : output(None), |
| 103 crtc(None), | 109 crtc(None), |
| 104 current_mode(None), | 110 current_mode(None), |
| 105 native_mode(None), | 111 native_mode(None), |
| 106 mirror_mode(None), | 112 mirror_mode(None), |
| 107 selected_mode(None), | 113 selected_mode(None), |
| 114 x(0), | |
| 108 y(0), | 115 y(0), |
| 109 height(0), | |
| 110 is_internal(false), | 116 is_internal(false), |
| 111 is_aspect_preserving_scaling(false), | 117 is_aspect_preserving_scaling(false), |
| 112 touch_device_id(0), | 118 touch_device_id(0), |
| 113 display_id(0), | 119 display_id(0), |
| 114 has_display_id(false) {} | 120 has_display_id(false) {} |
| 115 | 121 |
| 116 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() | |
| 117 : x_scale(1.0), | |
| 118 x_offset(0.0), | |
| 119 y_scale(1.0), | |
| 120 y_offset(0.0) {} | |
| 121 | |
| 122 OutputConfigurator::CrtcConfig::CrtcConfig() | |
| 123 : crtc(None), | |
| 124 x(0), | |
| 125 y(0), | |
| 126 mode(None), | |
| 127 output(None) {} | |
| 128 | |
| 129 OutputConfigurator::CrtcConfig::CrtcConfig(RRCrtc crtc, | |
| 130 int x, int y, | |
| 131 RRMode mode, | |
| 132 RROutput output) | |
| 133 : crtc(crtc), | |
| 134 x(x), | |
| 135 y(y), | |
| 136 mode(mode), | |
| 137 output(output) {} | |
| 138 | |
| 139 bool OutputConfigurator::TestApi::SendOutputChangeEvents(bool connected) { | 122 bool OutputConfigurator::TestApi::SendOutputChangeEvents(bool connected) { |
| 140 XRRScreenChangeNotifyEvent screen_event; | 123 XRRScreenChangeNotifyEvent screen_event; |
| 141 memset(&screen_event, 0, sizeof(screen_event)); | 124 memset(&screen_event, 0, sizeof(screen_event)); |
| 142 screen_event.type = xrandr_event_base_ + RRScreenChangeNotify; | 125 screen_event.type = xrandr_event_base_ + RRScreenChangeNotify; |
| 143 configurator_->Dispatch( | 126 configurator_->Dispatch( |
| 144 reinterpret_cast<const base::NativeEvent>(&screen_event)); | 127 reinterpret_cast<const base::NativeEvent>(&screen_event)); |
| 145 | 128 |
| 146 XRROutputChangeNotifyEvent notify_event; | 129 XRROutputChangeNotifyEvent notify_event; |
| 147 memset(¬ify_event, 0, sizeof(notify_event)); | 130 memset(¬ify_event, 0, sizeof(notify_event)); |
| 148 notify_event.type = xrandr_event_base_ + RRNotify; | 131 notify_event.type = xrandr_event_base_ + RRNotify; |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 411 } | 394 } |
| 412 | 395 |
| 413 bool OutputConfigurator::EnterState( | 396 bool OutputConfigurator::EnterState( |
| 414 OutputState output_state, | 397 OutputState output_state, |
| 415 DisplayPowerState power_state, | 398 DisplayPowerState power_state, |
| 416 const std::vector<OutputSnapshot>& outputs) { | 399 const std::vector<OutputSnapshot>& outputs) { |
| 417 std::vector<bool> output_power; | 400 std::vector<bool> output_power; |
| 418 int num_on_outputs = GetOutputPower(outputs, power_state, &output_power); | 401 int num_on_outputs = GetOutputPower(outputs, power_state, &output_power); |
| 419 VLOG(1) << "EnterState: output=" << OutputStateToString(output_state) | 402 VLOG(1) << "EnterState: output=" << OutputStateToString(output_state) |
| 420 << " power=" << DisplayPowerStateToString(power_state); | 403 << " power=" << DisplayPowerStateToString(power_state); |
| 404 | |
| 405 // Framebuffer dimensions. | |
| 406 int width = 0, height = 0; | |
| 407 std::vector<OutputSnapshot> updated_outputs = outputs; | |
| 408 | |
| 421 switch (output_state) { | 409 switch (output_state) { |
| 422 case STATE_HEADLESS: | 410 case STATE_HEADLESS: |
|
oshima
2013/08/08 01:19:49
do you think we can nuke STATE_HEADLESS?
Daniel Erat
2013/08/08 16:07:37
What's the alternate proposal? Do you think we sho
oshima
2013/08/08 16:52:09
Exit early if there is no output and just leave th
| |
| 423 if (outputs.size() != 0) { | 411 if (outputs.size() != 0) { |
| 424 LOG(WARNING) << "Ignoring request to enter headless mode with " | 412 LOG(WARNING) << "Ignoring request to enter headless mode with " |
| 425 << outputs.size() << " connected output(s)"; | 413 << outputs.size() << " connected output(s)"; |
| 426 return false; | 414 return false; |
| 427 } | 415 } |
| 428 break; | 416 break; |
| 429 case STATE_SINGLE: { | 417 case STATE_SINGLE: { |
| 430 // If there are multiple outputs connected, only one should be turned on. | 418 // If there are multiple outputs connected, only one should be turned on. |
| 431 if (outputs.size() != 1 && num_on_outputs != 1) { | 419 if (outputs.size() != 1 && num_on_outputs != 1) { |
| 432 LOG(WARNING) << "Ignoring request to enter single mode with " | 420 LOG(WARNING) << "Ignoring request to enter single mode with " |
| 433 << outputs.size() << " connected outputs and " | 421 << outputs.size() << " connected outputs and " |
| 434 << num_on_outputs << " turned on"; | 422 << num_on_outputs << " turned on"; |
| 435 return false; | 423 return false; |
| 436 } | 424 } |
| 437 | 425 |
| 438 // Determine which output to use. | 426 for (size_t i = 0; i < updated_outputs.size(); ++i) { |
| 439 const OutputSnapshot& output = outputs.size() == 1 ? outputs[0] : | 427 OutputSnapshot* output = &updated_outputs[i]; |
| 440 (output_power[0] ? outputs[0] : outputs[1]); | 428 output->x = 0; |
| 441 int width = 0, height = 0; | 429 output->y = 0; |
| 442 if (!delegate_->GetModeDetails( | 430 output->current_mode = output_power[i] ? output->selected_mode : None; |
| 443 output.selected_mode, &width, &height, NULL)) | |
| 444 return false; | |
| 445 | 431 |
| 446 std::vector<CrtcConfig> configs(outputs.size()); | 432 if (output_power[i] || outputs.size() == 1) { |
| 447 for (size_t i = 0; i < outputs.size(); ++i) { | 433 if (!delegate_->GetModeDetails( |
| 448 configs[i] = CrtcConfig( | 434 output->selected_mode, &width, &height, NULL)) |
| 449 outputs[i].crtc, 0, 0, | 435 return false; |
| 450 output_power[i] ? outputs[i].selected_mode : None, | |
| 451 outputs[i].output); | |
| 452 } | |
| 453 delegate_->CreateFrameBuffer(width, height, configs); | |
| 454 | |
| 455 for (size_t i = 0; i < outputs.size(); ++i) { | |
| 456 delegate_->ConfigureCrtc(&configs[i]); | |
| 457 if (outputs[i].touch_device_id) { | |
| 458 delegate_->ConfigureCTM(outputs[i].touch_device_id, | |
| 459 CoordinateTransformation()); | |
| 460 } | 436 } |
| 461 } | 437 } |
| 462 break; | 438 break; |
| 463 } | 439 } |
| 464 case STATE_DUAL_MIRROR: { | 440 case STATE_DUAL_MIRROR: { |
| 465 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { | 441 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { |
| 466 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | 442 LOG(WARNING) << "Ignoring request to enter mirrored mode with " |
| 467 << outputs.size() << " connected output(s) and " | 443 << outputs.size() << " connected output(s) and " |
| 468 << num_on_outputs << " turned on"; | 444 << num_on_outputs << " turned on"; |
| 469 return false; | 445 return false; |
| 470 } | 446 } |
| 471 | 447 |
| 472 int width = 0, height = 0; | |
| 473 if (!delegate_->GetModeDetails( | 448 if (!delegate_->GetModeDetails( |
| 474 outputs[0].mirror_mode, &width, &height, NULL)) { | 449 outputs[0].mirror_mode, &width, &height, NULL)) |
| 475 return false; | 450 return false; |
| 476 } | |
| 477 | |
| 478 std::vector<CrtcConfig> configs(outputs.size()); | |
| 479 for (size_t i = 0; i < outputs.size(); ++i) { | |
| 480 configs[i] = CrtcConfig( | |
| 481 outputs[i].crtc, 0, 0, | |
| 482 output_power[i] ? outputs[i].mirror_mode : None, | |
| 483 outputs[i].output); | |
| 484 } | |
| 485 delegate_->CreateFrameBuffer(width, height, configs); | |
| 486 | 451 |
| 487 for (size_t i = 0; i < outputs.size(); ++i) { | 452 for (size_t i = 0; i < outputs.size(); ++i) { |
| 488 delegate_->ConfigureCrtc(&configs[i]); | 453 OutputSnapshot* output = &updated_outputs[i]; |
| 489 if (outputs[i].touch_device_id) { | 454 output->x = 0; |
| 490 CoordinateTransformation ctm; | 455 output->y = 0; |
| 456 output->current_mode = output_power[i] ? output->mirror_mode : None; | |
| 457 if (output->touch_device_id) { | |
| 491 // CTM needs to be calculated if aspect preserving scaling is used. | 458 // CTM needs to be calculated if aspect preserving scaling is used. |
| 492 // Otherwise, assume it is full screen, and use identity CTM. | 459 // Otherwise, assume it is full screen, and use identity CTM. |
| 493 if (outputs[i].mirror_mode != outputs[i].native_mode && | 460 if (output->mirror_mode != output->native_mode && |
| 494 outputs[i].is_aspect_preserving_scaling) { | 461 output->is_aspect_preserving_scaling) { |
| 495 ctm = GetMirrorModeCTM(&outputs[i]); | 462 output->transform = GetMirrorModeCTM(output); |
| 496 mirrored_display_area_ratio_map_[outputs[i].touch_device_id] = | 463 mirrored_display_area_ratio_map_[output->touch_device_id] = |
| 497 GetMirroredDisplayAreaRatio(&outputs[i]); | 464 GetMirroredDisplayAreaRatio(output); |
| 498 } | 465 } |
| 499 delegate_->ConfigureCTM(outputs[i].touch_device_id, ctm); | |
| 500 } | 466 } |
| 501 } | 467 } |
| 502 break; | 468 break; |
| 503 } | 469 } |
| 504 case STATE_DUAL_EXTENDED: { | 470 case STATE_DUAL_EXTENDED: { |
| 505 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { | 471 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { |
| 506 LOG(WARNING) << "Ignoring request to enter extended mode with " | 472 LOG(WARNING) << "Ignoring request to enter extended mode with " |
| 507 << outputs.size() << " connected output(s) and " | 473 << outputs.size() << " connected output(s) and " |
| 508 << num_on_outputs << " turned on"; | 474 << num_on_outputs << " turned on"; |
| 509 return false; | 475 return false; |
| 510 } | 476 } |
| 511 | 477 |
| 512 // Pairs are [width, height] corresponding to the given output's mode. | 478 // Pairs are [width, height] corresponding to the given output's mode. |
| 513 std::vector<std::pair<int, int> > mode_sizes(outputs.size()); | 479 std::vector<std::pair<int, int> > mode_sizes(outputs.size()); |
| 514 std::vector<CrtcConfig> configs(outputs.size()); | |
| 515 int width = 0, height = 0; | |
| 516 | 480 |
| 517 for (size_t i = 0; i < outputs.size(); ++i) { | 481 for (size_t i = 0; i < outputs.size(); ++i) { |
| 518 if (!delegate_->GetModeDetails(outputs[i].selected_mode, | 482 if (!delegate_->GetModeDetails(outputs[i].selected_mode, |
| 519 &(mode_sizes[i].first), &(mode_sizes[i].second), NULL)) { | 483 &(mode_sizes[i].first), &(mode_sizes[i].second), NULL)) { |
| 520 return false; | 484 return false; |
| 521 } | 485 } |
| 522 | 486 |
| 523 configs[i] = CrtcConfig( | 487 OutputSnapshot* output = &updated_outputs[i]; |
| 524 outputs[i].crtc, 0, (height ? height + kVerticalGap : 0), | 488 output->x = 0; |
| 525 output_power[i] ? outputs[i].selected_mode : None, | 489 output->y = height ? height + kVerticalGap : 0; |
| 526 outputs[i].output); | 490 output->current_mode = output_power[i] ? output->selected_mode : None; |
| 527 | 491 |
| 528 // Retain the full screen size even if all outputs are off so the | 492 // Retain the full screen size even if all outputs are off so the |
| 529 // same desktop configuration can be restored when the outputs are | 493 // same desktop configuration can be restored when the outputs are |
| 530 // turned back on. | 494 // turned back on. |
| 531 width = std::max<int>(width, mode_sizes[i].first); | 495 width = std::max<int>(width, mode_sizes[i].first); |
| 532 height += (height ? kVerticalGap : 0) + mode_sizes[i].second; | 496 height += (height ? kVerticalGap : 0) + mode_sizes[i].second; |
| 533 } | 497 } |
| 534 | 498 |
| 535 delegate_->CreateFrameBuffer(width, height, configs); | |
| 536 | |
| 537 for (size_t i = 0; i < outputs.size(); ++i) { | 499 for (size_t i = 0; i < outputs.size(); ++i) { |
| 538 delegate_->ConfigureCrtc(&configs[i]); | 500 OutputSnapshot* output = &updated_outputs[i]; |
| 539 if (outputs[i].touch_device_id) { | 501 if (output->touch_device_id) { |
| 540 CoordinateTransformation ctm; | 502 CoordinateTransformation* ctm = &(output->transform); |
| 541 ctm.x_scale = static_cast<float>(mode_sizes[i].first) / width; | 503 ctm->x_scale = static_cast<float>(mode_sizes[i].first) / width; |
| 542 ctm.x_offset = static_cast<float>(configs[i].x) / width; | 504 ctm->x_offset = static_cast<float>(output->x) / width; |
| 543 ctm.y_scale = static_cast<float>(mode_sizes[i].second) / height; | 505 ctm->y_scale = static_cast<float>(mode_sizes[i].second) / height; |
| 544 ctm.y_offset = static_cast<float>(configs[i].y) / height; | 506 ctm->y_offset = static_cast<float>(output->y) / height; |
| 545 delegate_->ConfigureCTM(outputs[i].touch_device_id, ctm); | |
| 546 } | 507 } |
| 547 } | 508 } |
| 548 break; | 509 break; |
| 549 } | 510 } |
| 550 default: | 511 default: |
| 551 NOTREACHED() << "Got request to enter output state " << output_state | 512 NOTREACHED() << "Got request to enter output state " << output_state |
| 552 << " with " << outputs.size() << " output(s)"; | 513 << " with " << outputs.size() << " output(s)"; |
| 553 return false; | 514 return false; |
|
oshima
2013/08/08 01:19:49
let's remove default because output_state is enum.
Daniel Erat
2013/08/08 16:07:37
It was covering STATE_INVALID. I've added a separa
| |
| 554 } | 515 } |
| 555 | 516 |
| 517 // Finally, apply the desired changes. | |
| 518 DCHECK_EQ(outputs.size(), updated_outputs.size()); | |
| 519 if (!outputs.empty()) { | |
| 520 delegate_->CreateFrameBuffer(width, height, updated_outputs); | |
| 521 for (size_t i = 0; i < outputs.size(); ++i) { | |
| 522 const OutputSnapshot& output = updated_outputs[i]; | |
| 523 if (delegate_->ConfigureCrtc(output.crtc, output.current_mode, | |
| 524 output.output, output.x, output.y)) { | |
| 525 if (output.touch_device_id) | |
| 526 delegate_->ConfigureCTM(output.touch_device_id, output.transform); | |
| 527 } else { | |
| 528 LOG(WARNING) << "Unable to configure CRTC " << output.crtc << ":" | |
| 529 << " mode=" << output.current_mode | |
| 530 << " output=" << output.output | |
| 531 << " x=" << output.x | |
| 532 << " y=" << output.y; | |
| 533 updated_outputs[i] = outputs[i]; | |
| 534 } | |
| 535 } | |
| 536 } | |
| 537 | |
| 556 output_state_ = output_state; | 538 output_state_ = output_state; |
| 557 power_state_ = power_state; | 539 power_state_ = power_state; |
| 540 cached_outputs_ = updated_outputs; | |
| 558 return true; | 541 return true; |
| 559 } | 542 } |
| 560 | 543 |
| 561 OutputState OutputConfigurator::GetOutputState( | 544 OutputState OutputConfigurator::GetOutputState( |
| 562 const std::vector<OutputSnapshot>& outputs, | 545 const std::vector<OutputSnapshot>& outputs, |
| 563 DisplayPowerState power_state) const { | 546 DisplayPowerState power_state) const { |
| 564 int num_on_outputs = GetOutputPower(outputs, power_state, NULL); | 547 int num_on_outputs = GetOutputPower(outputs, power_state, NULL); |
| 565 switch (outputs.size()) { | 548 switch (outputs.size()) { |
| 566 case 0: | 549 case 0: |
| 567 return STATE_HEADLESS; | 550 return STATE_HEADLESS; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 648 float width_ratio = static_cast<float>(mirror_mode_width) / | 631 float width_ratio = static_cast<float>(mirror_mode_width) / |
| 649 static_cast<float>(native_mode_width); | 632 static_cast<float>(native_mode_width); |
| 650 float height_ratio = static_cast<float>(mirror_mode_height) / | 633 float height_ratio = static_cast<float>(mirror_mode_height) / |
| 651 static_cast<float>(native_mode_height); | 634 static_cast<float>(native_mode_height); |
| 652 | 635 |
| 653 area_ratio = width_ratio * height_ratio; | 636 area_ratio = width_ratio * height_ratio; |
| 654 return area_ratio; | 637 return area_ratio; |
| 655 } | 638 } |
| 656 | 639 |
| 657 } // namespace chromeos | 640 } // namespace chromeos |
| OLD | NEW |