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) { |
| 410 case STATE_INVALID: |
| 411 NOTREACHED() << "Ignoring request to enter invalid state with " |
| 412 << outputs.size() << " connected output(s)"; |
| 413 return false; |
422 case STATE_HEADLESS: | 414 case STATE_HEADLESS: |
423 if (outputs.size() != 0) { | 415 if (outputs.size() != 0) { |
424 LOG(WARNING) << "Ignoring request to enter headless mode with " | 416 LOG(WARNING) << "Ignoring request to enter headless mode with " |
425 << outputs.size() << " connected output(s)"; | 417 << outputs.size() << " connected output(s)"; |
426 return false; | 418 return false; |
427 } | 419 } |
428 break; | 420 break; |
429 case STATE_SINGLE: { | 421 case STATE_SINGLE: { |
430 // If there are multiple outputs connected, only one should be turned on. | 422 // If there are multiple outputs connected, only one should be turned on. |
431 if (outputs.size() != 1 && num_on_outputs != 1) { | 423 if (outputs.size() != 1 && num_on_outputs != 1) { |
432 LOG(WARNING) << "Ignoring request to enter single mode with " | 424 LOG(WARNING) << "Ignoring request to enter single mode with " |
433 << outputs.size() << " connected outputs and " | 425 << outputs.size() << " connected outputs and " |
434 << num_on_outputs << " turned on"; | 426 << num_on_outputs << " turned on"; |
435 return false; | 427 return false; |
436 } | 428 } |
437 | 429 |
438 // Determine which output to use. | 430 for (size_t i = 0; i < updated_outputs.size(); ++i) { |
439 const OutputSnapshot& output = outputs.size() == 1 ? outputs[0] : | 431 OutputSnapshot* output = &updated_outputs[i]; |
440 (output_power[0] ? outputs[0] : outputs[1]); | 432 output->x = 0; |
441 int width = 0, height = 0; | 433 output->y = 0; |
442 if (!delegate_->GetModeDetails( | 434 output->current_mode = output_power[i] ? output->selected_mode : None; |
443 output.selected_mode, &width, &height, NULL)) | |
444 return false; | |
445 | 435 |
446 std::vector<CrtcConfig> configs(outputs.size()); | 436 if (output_power[i] || outputs.size() == 1) { |
447 for (size_t i = 0; i < outputs.size(); ++i) { | 437 if (!delegate_->GetModeDetails( |
448 configs[i] = CrtcConfig( | 438 output->selected_mode, &width, &height, NULL)) |
449 outputs[i].crtc, 0, 0, | 439 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 } | 440 } |
461 } | 441 } |
462 break; | 442 break; |
463 } | 443 } |
464 case STATE_DUAL_MIRROR: { | 444 case STATE_DUAL_MIRROR: { |
465 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { | 445 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { |
466 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | 446 LOG(WARNING) << "Ignoring request to enter mirrored mode with " |
467 << outputs.size() << " connected output(s) and " | 447 << outputs.size() << " connected output(s) and " |
468 << num_on_outputs << " turned on"; | 448 << num_on_outputs << " turned on"; |
469 return false; | 449 return false; |
470 } | 450 } |
471 | 451 |
472 int width = 0, height = 0; | |
473 if (!delegate_->GetModeDetails( | 452 if (!delegate_->GetModeDetails( |
474 outputs[0].mirror_mode, &width, &height, NULL)) { | 453 outputs[0].mirror_mode, &width, &height, NULL)) |
475 return false; | 454 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 | 455 |
487 for (size_t i = 0; i < outputs.size(); ++i) { | 456 for (size_t i = 0; i < outputs.size(); ++i) { |
488 delegate_->ConfigureCrtc(&configs[i]); | 457 OutputSnapshot* output = &updated_outputs[i]; |
489 if (outputs[i].touch_device_id) { | 458 output->x = 0; |
490 CoordinateTransformation ctm; | 459 output->y = 0; |
| 460 output->current_mode = output_power[i] ? output->mirror_mode : None; |
| 461 if (output->touch_device_id) { |
491 // CTM needs to be calculated if aspect preserving scaling is used. | 462 // CTM needs to be calculated if aspect preserving scaling is used. |
492 // Otherwise, assume it is full screen, and use identity CTM. | 463 // Otherwise, assume it is full screen, and use identity CTM. |
493 if (outputs[i].mirror_mode != outputs[i].native_mode && | 464 if (output->mirror_mode != output->native_mode && |
494 outputs[i].is_aspect_preserving_scaling) { | 465 output->is_aspect_preserving_scaling) { |
495 ctm = GetMirrorModeCTM(&outputs[i]); | 466 output->transform = GetMirrorModeCTM(output); |
496 mirrored_display_area_ratio_map_[outputs[i].touch_device_id] = | 467 mirrored_display_area_ratio_map_[output->touch_device_id] = |
497 GetMirroredDisplayAreaRatio(&outputs[i]); | 468 GetMirroredDisplayAreaRatio(output); |
498 } | 469 } |
499 delegate_->ConfigureCTM(outputs[i].touch_device_id, ctm); | |
500 } | 470 } |
501 } | 471 } |
502 break; | 472 break; |
503 } | 473 } |
504 case STATE_DUAL_EXTENDED: { | 474 case STATE_DUAL_EXTENDED: { |
505 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { | 475 if (outputs.size() != 2 || (num_on_outputs != 0 && num_on_outputs != 2)) { |
506 LOG(WARNING) << "Ignoring request to enter extended mode with " | 476 LOG(WARNING) << "Ignoring request to enter extended mode with " |
507 << outputs.size() << " connected output(s) and " | 477 << outputs.size() << " connected output(s) and " |
508 << num_on_outputs << " turned on"; | 478 << num_on_outputs << " turned on"; |
509 return false; | 479 return false; |
510 } | 480 } |
511 | 481 |
512 // Pairs are [width, height] corresponding to the given output's mode. | 482 // Pairs are [width, height] corresponding to the given output's mode. |
513 std::vector<std::pair<int, int> > mode_sizes(outputs.size()); | 483 std::vector<std::pair<int, int> > mode_sizes(outputs.size()); |
514 std::vector<CrtcConfig> configs(outputs.size()); | |
515 int width = 0, height = 0; | |
516 | 484 |
517 for (size_t i = 0; i < outputs.size(); ++i) { | 485 for (size_t i = 0; i < outputs.size(); ++i) { |
518 if (!delegate_->GetModeDetails(outputs[i].selected_mode, | 486 if (!delegate_->GetModeDetails(outputs[i].selected_mode, |
519 &(mode_sizes[i].first), &(mode_sizes[i].second), NULL)) { | 487 &(mode_sizes[i].first), &(mode_sizes[i].second), NULL)) { |
520 return false; | 488 return false; |
521 } | 489 } |
522 | 490 |
523 configs[i] = CrtcConfig( | 491 OutputSnapshot* output = &updated_outputs[i]; |
524 outputs[i].crtc, 0, (height ? height + kVerticalGap : 0), | 492 output->x = 0; |
525 output_power[i] ? outputs[i].selected_mode : None, | 493 output->y = height ? height + kVerticalGap : 0; |
526 outputs[i].output); | 494 output->current_mode = output_power[i] ? output->selected_mode : None; |
527 | 495 |
528 // Retain the full screen size even if all outputs are off so the | 496 // Retain the full screen size even if all outputs are off so the |
529 // same desktop configuration can be restored when the outputs are | 497 // same desktop configuration can be restored when the outputs are |
530 // turned back on. | 498 // turned back on. |
531 width = std::max<int>(width, mode_sizes[i].first); | 499 width = std::max<int>(width, mode_sizes[i].first); |
532 height += (height ? kVerticalGap : 0) + mode_sizes[i].second; | 500 height += (height ? kVerticalGap : 0) + mode_sizes[i].second; |
533 } | 501 } |
534 | 502 |
535 delegate_->CreateFrameBuffer(width, height, configs); | |
536 | |
537 for (size_t i = 0; i < outputs.size(); ++i) { | 503 for (size_t i = 0; i < outputs.size(); ++i) { |
538 delegate_->ConfigureCrtc(&configs[i]); | 504 OutputSnapshot* output = &updated_outputs[i]; |
539 if (outputs[i].touch_device_id) { | 505 if (output->touch_device_id) { |
540 CoordinateTransformation ctm; | 506 CoordinateTransformation* ctm = &(output->transform); |
541 ctm.x_scale = static_cast<float>(mode_sizes[i].first) / width; | 507 ctm->x_scale = static_cast<float>(mode_sizes[i].first) / width; |
542 ctm.x_offset = static_cast<float>(configs[i].x) / width; | 508 ctm->x_offset = static_cast<float>(output->x) / width; |
543 ctm.y_scale = static_cast<float>(mode_sizes[i].second) / height; | 509 ctm->y_scale = static_cast<float>(mode_sizes[i].second) / height; |
544 ctm.y_offset = static_cast<float>(configs[i].y) / height; | 510 ctm->y_offset = static_cast<float>(output->y) / height; |
545 delegate_->ConfigureCTM(outputs[i].touch_device_id, ctm); | |
546 } | 511 } |
547 } | 512 } |
548 break; | 513 break; |
549 } | 514 } |
550 default: | 515 } |
551 NOTREACHED() << "Got request to enter output state " << output_state | 516 |
552 << " with " << outputs.size() << " output(s)"; | 517 // Finally, apply the desired changes. |
553 return false; | 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 } |
554 } | 536 } |
555 | 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 |