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 <cmath> | 7 #include <cmath> |
8 | 8 |
9 #include <X11/Xatom.h> | 9 #include <X11/Xatom.h> |
10 #include <X11/Xlib.h> | 10 #include <X11/Xlib.h> |
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
590 | 590 |
591 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> | 591 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> |
592 SetIsProjecting(is_projecting); | 592 SetIsProjecting(is_projecting); |
593 } | 593 } |
594 | 594 |
595 void OutputConfigurator::Stop() { | 595 void OutputConfigurator::Stop() { |
596 configure_display_ = false; | 596 configure_display_ = false; |
597 } | 597 } |
598 | 598 |
599 bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, | 599 bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, |
600 bool force_probe) { | 600 bool force_probe, |
601 bool only_if_single_internal_display) { | |
601 TRACE_EVENT0("chromeos", "OutputConfigurator::SetDisplayPower"); | 602 TRACE_EVENT0("chromeos", "OutputConfigurator::SetDisplayPower"); |
602 VLOG(1) << "OutputConfigurator::SetDisplayPower: power_state=" << power_state | 603 VLOG(1) << "OutputConfigurator::SetDisplayPower: power_state=" << power_state |
603 << " force_probe=" << force_probe; | 604 << " force_probe=" << force_probe |
605 << " only_if_single_internal_display=" | |
606 << only_if_single_internal_display; | |
604 | 607 |
605 if (!configure_display_) | 608 if (!configure_display_) |
606 return false; | 609 return false; |
607 if (power_state == power_state_ && !force_probe) | 610 if (power_state == power_state_ && !force_probe) |
608 return true; | 611 return true; |
609 | 612 |
610 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | 613 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
611 CHECK(display); | 614 CHECK(display); |
612 XGrabServer(display); | 615 XGrabServer(display); |
613 Window window = DefaultRootWindow(display); | 616 Window window = DefaultRootWindow(display); |
614 XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window); | 617 XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window); |
615 CHECK(screen); | 618 CHECK(screen); |
616 std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen); | 619 std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen); |
617 connected_output_count_ = outputs.size(); | 620 connected_output_count_ = outputs.size(); |
618 | 621 |
619 if (EnterState(display, screen, window, output_state_, power_state, | 622 bool single_internal_display = outputs.size() == 1 && outputs[0].is_internal; |
623 if ((single_internal_display || !only_if_single_internal_display) && | |
624 EnterState(display, screen, window, output_state_, power_state, | |
620 outputs)) { | 625 outputs)) { |
621 power_state_ = power_state; | 626 power_state_ = power_state; |
622 if (power_state != DISPLAY_POWER_ALL_OFF) { | 627 if (power_state != DISPLAY_POWER_ALL_OFF) { |
623 // Force the DPMS on since the driver doesn't always detect that it | 628 // Force the DPMS on since the driver doesn't always detect that it |
624 // should turn on. This is needed when coming back from idle suspend. | 629 // should turn on. This is needed when coming back from idle suspend. |
625 CHECK(DPMSEnable(display)); | 630 CHECK(DPMSEnable(display)); |
626 CHECK(DPMSForceLevel(display, DPMSModeOn)); | 631 CHECK(DPMSForceLevel(display, DPMSModeOn)); |
627 } | 632 } |
628 } | 633 } |
629 | 634 |
630 XRRFreeScreenResources(screen); | 635 XRRFreeScreenResources(screen); |
631 XUngrabServer(display); | 636 XUngrabServer(display); |
632 | |
633 return true; | 637 return true; |
634 } | 638 } |
635 | 639 |
636 bool OutputConfigurator::SetDisplayMode(OutputState new_state) { | 640 bool OutputConfigurator::SetDisplayMode(OutputState new_state) { |
637 TRACE_EVENT0("chromeos", "OutputConfigurator::SetDisplayMode"); | 641 TRACE_EVENT0("chromeos", "OutputConfigurator::SetDisplayMode"); |
638 if (output_state_ == STATE_INVALID || | 642 if (output_state_ == STATE_INVALID || |
639 output_state_ == STATE_HEADLESS || | 643 output_state_ == STATE_HEADLESS || |
640 output_state_ == STATE_SINGLE) | 644 output_state_ == STATE_SINGLE) |
641 return false; | 645 return false; |
642 | 646 |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
750 void OutputConfigurator::RemoveObserver(Observer* observer) { | 754 void OutputConfigurator::RemoveObserver(Observer* observer) { |
751 observers_.RemoveObserver(observer); | 755 observers_.RemoveObserver(observer); |
752 } | 756 } |
753 | 757 |
754 // static | 758 // static |
755 bool OutputConfigurator::IsInternalOutputName(const std::string& name) { | 759 bool OutputConfigurator::IsInternalOutputName(const std::string& name) { |
756 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0; | 760 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0; |
757 } | 761 } |
758 | 762 |
759 void OutputConfigurator::SuspendDisplays() { | 763 void OutputConfigurator::SuspendDisplays() { |
760 // Turn internal displays on before suspend. At this point, the backlight | 764 // If the display is off due to user inactivity and there's only a single |
761 // is off, so we turn on the internal display so that we can resume | 765 // internal display connected, switch to the all-on state before |
762 // directly into "on" state. This greatly reduces resume times. | 766 // suspending. This shouldn't be very noticeable to the user since the |
763 SetDisplayPower(DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF, false); | 767 // backlight is off at this point, and doing this lets us resume directly |
768 // into the "on" state, which greatly reduces resume times. | |
769 if (power_state_ == DISPLAY_POWER_ALL_OFF) { | |
770 SetDisplayPower(DISPLAY_POWER_ALL_ON, false, | |
771 true /* only_if_single_internal_display */); | |
Daniel Erat
2013/03/27 23:15:19
I think that we can only do this safely when there
| |
772 } | |
764 | 773 |
765 // We need to make sure that the monitor configuration we just did actually | 774 // We need to make sure that the monitor configuration we just did actually |
766 // completes before we return, because otherwise the X message could be | 775 // completes before we return, because otherwise the X message could be |
767 // racing with the HandleSuspendReadiness message. | 776 // racing with the HandleSuspendReadiness message. |
768 XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), 0); | 777 XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), 0); |
769 } | 778 } |
770 | 779 |
771 void OutputConfigurator::ResumeDisplays() { | 780 void OutputConfigurator::ResumeDisplays() { |
772 // Force probing to ensure that we pick up any changes that were made | 781 // Force probing to ensure that we pick up any changes that were made |
773 // while the system was suspended. | 782 // while the system was suspended. |
774 SetDisplayPower(DISPLAY_POWER_ALL_ON, true); | 783 SetDisplayPower(power_state_, true /* force_probe */, false); |
775 } | 784 } |
776 | 785 |
777 void OutputConfigurator::NotifyOnDisplayChanged() { | 786 void OutputConfigurator::NotifyOnDisplayChanged() { |
778 TRACE_EVENT0("chromeos", "OutputConfigurator::NotifyOnDisplayChanged"); | 787 TRACE_EVENT0("chromeos", "OutputConfigurator::NotifyOnDisplayChanged"); |
779 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); | 788 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); |
780 } | 789 } |
781 | 790 |
782 std::vector<OutputSnapshot> OutputConfigurator::GetDualOutputs( | 791 std::vector<OutputSnapshot> OutputConfigurator::GetDualOutputs( |
783 Display* display, | 792 Display* display, |
784 XRRScreenResources* screen) { | 793 XRRScreenResources* screen) { |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1095 } | 1104 } |
1096 | 1105 |
1097 bool OutputConfigurator::EnterState( | 1106 bool OutputConfigurator::EnterState( |
1098 Display* display, | 1107 Display* display, |
1099 XRRScreenResources* screen, | 1108 XRRScreenResources* screen, |
1100 Window window, | 1109 Window window, |
1101 OutputState output_state, | 1110 OutputState output_state, |
1102 DisplayPowerState power_state, | 1111 DisplayPowerState power_state, |
1103 const std::vector<OutputSnapshot>& outputs) { | 1112 const std::vector<OutputSnapshot>& outputs) { |
1104 TRACE_EVENT0("chromeos", "OutputConfigurator::EnterState"); | 1113 TRACE_EVENT0("chromeos", "OutputConfigurator::EnterState"); |
1114 | |
1115 std::vector<RRCrtc> crtcs(outputs.size()); | |
1116 std::vector<bool> output_power(outputs.size()); | |
1117 bool all_outputs_off = true; | |
1118 | |
1119 RRCrtc prev_crtc = None; | |
1120 for (size_t i = 0; i < outputs.size(); prev_crtc = crtcs[i], ++i) { | |
1121 crtcs[i] = GetNextCrtcAfter(display, screen, outputs[i].output, prev_crtc); | |
1122 output_power[i] = power_state == DISPLAY_POWER_ALL_ON || | |
1123 (power_state == DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON && | |
1124 !outputs[i].is_internal) || | |
1125 (power_state == DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && | |
1126 outputs[i].is_internal); | |
1127 if (output_power[i]) | |
1128 all_outputs_off = false; | |
1129 } | |
1130 | |
1105 switch (outputs.size()) { | 1131 switch (outputs.size()) { |
1106 case 0: | 1132 case 0: |
1107 // Do nothing as no 0-display states are supported. | 1133 // Do nothing as no 0-display states are supported. |
1108 break; | 1134 break; |
1109 case 1: { | 1135 case 1: { |
1110 // Re-allocate the framebuffer to fit. | 1136 // Re-allocate the framebuffer to fit. |
1111 XRRModeInfo* mode_info = ModeInfoForID(screen, outputs[0].native_mode); | 1137 XRRModeInfo* mode_info = ModeInfoForID(screen, outputs[0].native_mode); |
1112 if (mode_info == NULL) { | 1138 if (!mode_info) { |
1113 UMA_HISTOGRAM_COUNTS("Display.EnterState.single_failures", 1); | 1139 UMA_HISTOGRAM_COUNTS("Display.EnterState.single_failures", 1); |
1114 return false; | 1140 return false; |
1115 } | 1141 } |
1116 | 1142 |
1117 bool power_on = power_state == DISPLAY_POWER_ALL_ON || | 1143 CrtcConfig config(crtcs[0], 0, 0, |
1118 (power_state == DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON && | 1144 output_power[0] ? outputs[0].native_mode : None, |
1119 !outputs[0].is_internal) || | 1145 outputs[0].output); |
1120 (power_state == DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && | |
1121 outputs[0].is_internal); | |
1122 CrtcConfig config( | |
1123 GetNextCrtcAfter(display, screen, outputs[0].output, None), | |
1124 0, 0, power_on ? outputs[0].native_mode : None, outputs[0].output); | |
1125 | |
1126 CreateFrameBuffer(display, screen, window, mode_info->width, | 1146 CreateFrameBuffer(display, screen, window, mode_info->width, |
1127 mode_info->height, &config, NULL); | 1147 mode_info->height, &config, NULL); |
1128 | |
1129 ConfigureCrtc(display, screen, &config); | 1148 ConfigureCrtc(display, screen, &config); |
1130 | 1149 if (outputs[0].touch_device_id) { |
1131 // Restore identity transformation for single monitor in native mode. | 1150 // Restore identity transformation for single monitor in native mode. |
1132 if (outputs[0].touch_device_id != None) { | 1151 ConfigureCTM(display, outputs[0].touch_device_id, |
1133 CoordinateTransformation ctm; // Defaults to identity | 1152 CoordinateTransformation()); |
1134 ConfigureCTM(display, outputs[0].touch_device_id, ctm); | |
1135 } | 1153 } |
1136 break; | 1154 break; |
1137 } | 1155 } |
1138 case 2: { | 1156 case 2: { |
1139 RRCrtc primary_crtc = | |
1140 GetNextCrtcAfter(display, screen, outputs[0].output, None); | |
1141 RRCrtc secondary_crtc = | |
1142 GetNextCrtcAfter(display, screen, outputs[1].output, primary_crtc); | |
1143 | |
1144 // Workaround for crbug.com/148365: leave internal display on for | |
1145 // internal-off, external-on so user can move cursor (and hence | |
1146 // windows) onto internal display even when it's off. | |
1147 bool primary_power_on = power_state == DISPLAY_POWER_ALL_ON || | |
1148 (power_state == DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && | |
1149 outputs[0].is_internal); | |
1150 bool secondary_power_on = power_state == DISPLAY_POWER_ALL_ON || | |
1151 (power_state == DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && | |
1152 outputs[1].is_internal); | |
1153 | |
1154 if (output_state == STATE_DUAL_MIRROR) { | 1157 if (output_state == STATE_DUAL_MIRROR) { |
1155 XRRModeInfo* mode_info = ModeInfoForID(screen, outputs[0].mirror_mode); | 1158 XRRModeInfo* mode_info = ModeInfoForID(screen, outputs[0].mirror_mode); |
1156 if (mode_info == NULL) { | 1159 if (!mode_info) { |
1157 UMA_HISTOGRAM_COUNTS("Display.EnterState.mirror_failures", 1); | 1160 UMA_HISTOGRAM_COUNTS("Display.EnterState.mirror_failures", 1); |
1158 return false; | 1161 return false; |
1159 } | 1162 } |
1160 | 1163 |
1161 CrtcConfig config1(primary_crtc, 0, 0, | 1164 std::vector<CrtcConfig> configs(outputs.size()); |
1162 primary_power_on ? outputs[0].mirror_mode : None, | 1165 for (size_t i = 0; i < outputs.size(); ++i) { |
1163 outputs[0].output); | 1166 configs[i] = CrtcConfig( |
1164 CrtcConfig config2(secondary_crtc, 0, 0, | 1167 crtcs[i], 0, 0, |
1165 secondary_power_on ? outputs[1].mirror_mode : None, | 1168 output_power[i] ? outputs[i].mirror_mode : None, |
1166 outputs[1].output); | 1169 outputs[i].output); |
1170 } | |
1167 | 1171 |
1168 CreateFrameBuffer(display, screen, window, mode_info->width, | 1172 CreateFrameBuffer(display, screen, window, mode_info->width, |
1169 mode_info->height, &config1, &config2); | 1173 mode_info->height, &configs[0], &configs[1]); |
1170 | 1174 |
1171 ConfigureCrtc(display, screen, &config1); | 1175 for (size_t i = 0; i < outputs.size(); ++i) { |
1172 ConfigureCrtc(display, screen, &config2); | 1176 ConfigureCrtc(display, screen, &configs[i]); |
1177 if (outputs[i].touch_device_id) { | |
1178 CoordinateTransformation ctm; | |
1179 // CTM needs to be calculated if aspect preserving scaling is used. | |
1180 // Otherwise, assume it is full screen, and use identity CTM. | |
1181 if (outputs[i].mirror_mode != outputs[i].native_mode && | |
1182 outputs[i].is_aspect_preserving_scaling) { | |
1183 ctm = GetMirrorModeCTM(screen, &outputs[i]); | |
1184 } | |
1185 ConfigureCTM(display, outputs[i].touch_device_id, ctm); | |
1186 } | |
1187 } | |
1188 } else { // STATE_DUAL_EXTENDED | |
1189 std::vector<XRRModeInfo*> mode_infos(outputs.size()); | |
1190 std::vector<CrtcConfig> configs(outputs.size()); | |
1191 int width = 0, height = 0; | |
1173 | 1192 |
1174 for (size_t i = 0; i < outputs.size(); i++) { | 1193 for (size_t i = 0; i < outputs.size(); ++i) { |
1175 if (outputs[i].touch_device_id == None) | 1194 mode_infos[i] = ModeInfoForID(screen, outputs[i].native_mode); |
1176 continue; | 1195 if (!mode_infos[i]) { |
1196 UMA_HISTOGRAM_COUNTS("Display.EnterState.dual_failures", 1); | |
1197 return false; | |
1198 } | |
1177 | 1199 |
1178 CoordinateTransformation ctm; | 1200 configs[i] = CrtcConfig( |
1179 // CTM needs to be calculated if aspect preserving scaling is used. | 1201 crtcs[i], 0, height, |
1180 // Otherwise, assume it is full screen, and use identity CTM. | 1202 output_power[i] ? outputs[i].native_mode : None, |
1181 if (outputs[i].mirror_mode != outputs[i].native_mode && | 1203 outputs[i].output); |
1182 outputs[i].is_aspect_preserving_scaling) { | 1204 |
1183 ctm = GetMirrorModeCTM(screen, &outputs[i]); | 1205 // Retain the full screen size if all outputs are off so the same |
1206 // desktop configuration can be restored when the outputs are | |
1207 // turned back on. | |
1208 if (output_power[i] || all_outputs_off) { | |
1209 width = std::max<int>(width, mode_infos[i]->width); | |
1210 height += (height ? kVerticalGap : 0) + mode_infos[i]->height; | |
1184 } | 1211 } |
1185 ConfigureCTM(display, outputs[i].touch_device_id, ctm); | |
1186 } | |
1187 } else { | |
1188 XRRModeInfo* primary_mode_info = | |
1189 ModeInfoForID(screen, outputs[0].native_mode); | |
1190 XRRModeInfo* secondary_mode_info = | |
1191 ModeInfoForID(screen, outputs[1].native_mode); | |
1192 if (primary_mode_info == NULL || secondary_mode_info == NULL) { | |
1193 UMA_HISTOGRAM_COUNTS("Display.EnterState.dual_failures", 1); | |
1194 return false; | |
1195 } | 1212 } |
1196 | 1213 |
1197 int primary_height = primary_mode_info->height; | 1214 CreateFrameBuffer(display, screen, window, width, height, |
1198 int secondary_height = secondary_mode_info->height; | 1215 &configs[0], &configs[1]); |
1199 CrtcConfig config1(primary_crtc, 0, 0, | |
1200 primary_power_on ? outputs[0].native_mode : None, | |
1201 outputs[0].output); | |
1202 CrtcConfig config2(secondary_crtc, 0, 0, | |
1203 secondary_power_on ? outputs[1].native_mode : None, | |
1204 outputs[1].output); | |
1205 | 1216 |
1206 if (output_state == STATE_DUAL_EXTENDED) | 1217 for (size_t i = 0; i < outputs.size(); ++i) { |
1207 config2.y = primary_height + kVerticalGap; | 1218 ConfigureCrtc(display, screen, &configs[i]); |
1208 else | 1219 if (outputs[i].touch_device_id) { |
1209 config1.y = secondary_height + kVerticalGap; | 1220 CoordinateTransformation ctm; |
1210 | 1221 ctm.x_scale = static_cast<float>(mode_infos[i]->width) / width; |
1211 int width = std::max<int>( | 1222 ctm.x_offset = static_cast<float>(configs[i].x) / width; |
1212 primary_mode_info->width, secondary_mode_info->width); | 1223 ctm.y_scale = static_cast<float>(mode_infos[i]->height) / height; |
1213 int height = primary_height + secondary_height + kVerticalGap; | 1224 ctm.y_offset = static_cast<float>(configs[i].y) / height; |
1214 | 1225 ConfigureCTM(display, outputs[i].touch_device_id, ctm); |
1215 CreateFrameBuffer(display, screen, window, width, height, &config1, | 1226 } |
1216 &config2); | |
1217 | |
1218 ConfigureCrtc(display, screen, &config1); | |
1219 ConfigureCrtc(display, screen, &config2); | |
1220 | |
1221 if (outputs[0].touch_device_id != None) { | |
1222 CoordinateTransformation ctm; | |
1223 ctm.x_scale = static_cast<float>(primary_mode_info->width) / width; | |
1224 ctm.x_offset = static_cast<float>(config1.x) / width; | |
1225 ctm.y_scale = static_cast<float>(primary_height) / height; | |
1226 ctm.y_offset = static_cast<float>(config1.y) / height; | |
1227 ConfigureCTM(display, outputs[0].touch_device_id, ctm); | |
1228 } | |
1229 if (outputs[1].touch_device_id != None) { | |
1230 CoordinateTransformation ctm; | |
1231 ctm.x_scale = static_cast<float>(secondary_mode_info->width) / | |
1232 width; | |
1233 ctm.x_offset = static_cast<float>(config2.x) / width; | |
1234 ctm.y_scale = static_cast<float>(secondary_height) / height; | |
1235 ctm.y_offset = static_cast<float>(config2.y) / height; | |
1236 ConfigureCTM(display, outputs[1].touch_device_id, ctm); | |
1237 } | 1227 } |
1238 } | 1228 } |
1239 break; | 1229 break; |
1240 } | 1230 } |
1241 default: | 1231 default: |
1242 CHECK(false); | 1232 NOTREACHED() << "Got " << outputs.size() << " outputs"; |
1243 } | 1233 } |
1244 | 1234 |
1245 RecordPreviousStateUMA(); | 1235 RecordPreviousStateUMA(); |
1246 | |
1247 return true; | 1236 return true; |
1248 } | 1237 } |
1249 | 1238 |
1250 void OutputConfigurator::RecordPreviousStateUMA() { | 1239 void OutputConfigurator::RecordPreviousStateUMA() { |
1251 base::TimeDelta duration = base::TimeTicks::Now() - last_enter_state_time_; | 1240 base::TimeDelta duration = base::TimeTicks::Now() - last_enter_state_time_; |
1252 | 1241 |
1253 // |output_state_| can be used for the state being left, | 1242 // |output_state_| can be used for the state being left, |
1254 // since RecordPreviousStateUMA is called from EnterState, | 1243 // since RecordPreviousStateUMA is called from EnterState, |
1255 // and |output_state_| is always updated after EnterState is called. | 1244 // and |output_state_| is always updated after EnterState is called. |
1256 switch (output_state_) { | 1245 switch (output_state_) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1332 // static | 1321 // static |
1333 RRMode OutputConfigurator::GetOutputNativeMode( | 1322 RRMode OutputConfigurator::GetOutputNativeMode( |
1334 const XRROutputInfo* output_info) { | 1323 const XRROutputInfo* output_info) { |
1335 if (output_info->nmode <= 0) | 1324 if (output_info->nmode <= 0) |
1336 return None; | 1325 return None; |
1337 | 1326 |
1338 return output_info->modes[0]; | 1327 return output_info->modes[0]; |
1339 } | 1328 } |
1340 | 1329 |
1341 } // namespace chromeos | 1330 } // namespace chromeos |
OLD | NEW |