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/dpms.h> | 8 #include <X11/extensions/dpms.h> |
9 #include <X11/extensions/Xrandr.h> | 9 #include <X11/extensions/Xrandr.h> |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include "base/logging.h" | 21 #include "base/logging.h" |
22 #include "base/message_pump_aurax11.h" | 22 #include "base/message_pump_aurax11.h" |
23 #include "base/metrics/histogram.h" | 23 #include "base/metrics/histogram.h" |
24 #include "base/perftimer.h" | 24 #include "base/perftimer.h" |
25 #include "base/time.h" | 25 #include "base/time.h" |
26 #include "chromeos/dbus/dbus_thread_manager.h" | 26 #include "chromeos/dbus/dbus_thread_manager.h" |
27 #include "chromeos/dbus/power_manager_client.h" | 27 #include "chromeos/dbus/power_manager_client.h" |
28 | 28 |
29 namespace chromeos { | 29 namespace chromeos { |
30 | 30 |
| 31 struct OutputSnapshot { |
| 32 RROutput output; |
| 33 RRCrtc crtc; |
| 34 RRMode current_mode; |
| 35 int height; |
| 36 int y; |
| 37 RRMode native_mode; |
| 38 RRMode mirror_mode; |
| 39 bool is_internal; |
| 40 }; |
| 41 |
31 namespace { | 42 namespace { |
32 // DPI measurements. | 43 // DPI measurements. |
33 const float kMmInInch = 25.4; | 44 const float kMmInInch = 25.4; |
34 const float kDpi96 = 96.0; | 45 const float kDpi96 = 96.0; |
35 const float kPixelsToMmScale = kMmInInch / kDpi96; | 46 const float kPixelsToMmScale = kMmInInch / kDpi96; |
36 | 47 |
37 // The DPI threshold to detech high density screen. | 48 // The DPI threshold to detech high density screen. |
38 // Higher DPI than this will use device_scale_factor=2 | 49 // Higher DPI than this will use device_scale_factor=2 |
39 // Should be kept in sync with display_change_observer_x11.cc | 50 // Should be kept in sync with display_change_observer_x11.cc |
40 const unsigned int kHighDensityDIPThreshold = 160; | 51 const unsigned int kHighDensityDIPThreshold = 160; |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
191 x, | 202 x, |
192 y, | 203 y, |
193 kMode, | 204 kMode, |
194 kOutput); | 205 kOutput); |
195 } | 206 } |
196 int mm_width = width * kPixelsToMmScale; | 207 int mm_width = width * kPixelsToMmScale; |
197 int mm_height = height * kPixelsToMmScale; | 208 int mm_height = height * kPixelsToMmScale; |
198 XRRSetScreenSize(display, window, width, height, mm_width, mm_height); | 209 XRRSetScreenSize(display, window, width, height, mm_width, mm_height); |
199 } | 210 } |
200 | 211 |
201 typedef struct OutputSnapshot { | |
202 RROutput output; | |
203 RRCrtc crtc; | |
204 RRMode current_mode; | |
205 int height; | |
206 int y; | |
207 RRMode native_mode; | |
208 RRMode mirror_mode; | |
209 bool is_internal; | |
210 } OutputSnapshot; | |
211 | |
212 static int GetDualOutputs(Display* display, | |
213 XRRScreenResources* screen, | |
214 OutputSnapshot* one, | |
215 OutputSnapshot* two) { | |
216 int found_count = 0; | |
217 XRROutputInfo* one_info = NULL; | |
218 XRROutputInfo* two_info = NULL; | |
219 | |
220 for (int i = 0; (i < screen->noutput) && (found_count < 2); ++i) { | |
221 RROutput this_id = screen->outputs[i]; | |
222 XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id); | |
223 bool is_connected = (RR_Connected == output_info->connection); | |
224 | |
225 if (is_connected) { | |
226 OutputSnapshot *to_populate = NULL; | |
227 | |
228 if (0 == found_count) { | |
229 to_populate = one; | |
230 one_info = output_info; | |
231 } else { | |
232 to_populate = two; | |
233 two_info = output_info; | |
234 } | |
235 | |
236 to_populate->output = this_id; | |
237 // Now, look up the corresponding CRTC and any related info. | |
238 to_populate->crtc = output_info->crtc; | |
239 if (None != to_populate->crtc) { | |
240 XRRCrtcInfo* crtc_info = | |
241 XRRGetCrtcInfo(display, screen, to_populate->crtc); | |
242 to_populate->current_mode = crtc_info->mode; | |
243 to_populate->height = crtc_info->height; | |
244 to_populate->y = crtc_info->y; | |
245 XRRFreeCrtcInfo(crtc_info); | |
246 } else { | |
247 to_populate->current_mode = 0; | |
248 to_populate->height = 0; | |
249 to_populate->y = 0; | |
250 } | |
251 // Find the native_mode and leave the mirror_mode for the pass after the | |
252 // loop. | |
253 if (output_info->nmode > 0) | |
254 to_populate->native_mode = output_info->modes[0]; | |
255 to_populate->mirror_mode = 0; | |
256 | |
257 // See if this output refers to an internal display. | |
258 to_populate->is_internal = | |
259 OutputConfigurator::IsInternalOutputName( | |
260 std::string(output_info->name)); | |
261 | |
262 VLOG(1) << "Found display #" << found_count | |
263 << " with output " << (int)to_populate->output | |
264 << " crtc " << (int)to_populate->crtc | |
265 << " current mode " << (int)to_populate->current_mode; | |
266 ++found_count; | |
267 } else { | |
268 XRRFreeOutputInfo(output_info); | |
269 } | |
270 } | |
271 | |
272 if (2 == found_count) { | |
273 // Find the mirror modes (if there are any). | |
274 bool can_mirror = FindMirrorModeForOutputs(display, | |
275 screen, | |
276 one->output, | |
277 two->output, | |
278 &one->mirror_mode, | |
279 &two->mirror_mode); | |
280 if (!can_mirror) { | |
281 // We can't mirror so set mirror_mode to 0. | |
282 one->mirror_mode = 0; | |
283 two->mirror_mode = 0; | |
284 } | |
285 } | |
286 | |
287 XRRFreeOutputInfo(one_info); | |
288 XRRFreeOutputInfo(two_info); | |
289 return found_count; | |
290 } | |
291 | |
292 static OutputState InferCurrentState(Display* display, | 212 static OutputState InferCurrentState(Display* display, |
293 XRRScreenResources* screen, | 213 XRRScreenResources* screen, |
294 const OutputSnapshot* outputs, | 214 const OutputSnapshot* outputs, |
295 int output_count) { | 215 int output_count) { |
296 OutputState state = STATE_INVALID; | 216 OutputState state = STATE_INVALID; |
297 switch (output_count) { | 217 switch (output_count) { |
298 case 0: | 218 case 0: |
299 state = STATE_HEADLESS; | 219 state = STATE_HEADLESS; |
300 break; | 220 break; |
301 case 1: | 221 case 1: |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 | 479 |
560 // "Projecting" is defined as having more than 1 output connected while at | 480 // "Projecting" is defined as having more than 1 output connected while at |
561 // least one of them is an internal output. | 481 // least one of them is an internal output. |
562 return has_internal_output && (connected_output_count > 1); | 482 return has_internal_output && (connected_output_count > 1); |
563 } | 483 } |
564 | 484 |
565 } // namespace | 485 } // namespace |
566 | 486 |
567 OutputConfigurator::OutputConfigurator() | 487 OutputConfigurator::OutputConfigurator() |
568 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()), | 488 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()), |
| 489 is_panel_fitting_enabled_(false), |
| 490 connected_output_count_(0), |
569 xrandr_event_base_(0), | 491 xrandr_event_base_(0), |
570 output_state_(STATE_INVALID) { | 492 output_state_(STATE_INVALID) { |
| 493 } |
| 494 |
| 495 void OutputConfigurator::Init(bool is_panel_fitting_enabled) { |
571 if (!is_running_on_chrome_os_) | 496 if (!is_running_on_chrome_os_) |
572 return; | 497 return; |
573 | 498 |
| 499 is_panel_fitting_enabled_ = is_panel_fitting_enabled; |
| 500 |
574 // Cache the initial output state. | 501 // Cache the initial output state. |
575 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | 502 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
576 CHECK(display != NULL); | 503 CHECK(display != NULL); |
577 XGrabServer(display); | 504 XGrabServer(display); |
578 Window window = DefaultRootWindow(display); | 505 Window window = DefaultRootWindow(display); |
579 XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window); | 506 XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window); |
580 CHECK(screen != NULL); | 507 CHECK(screen != NULL); |
581 | 508 |
582 // Detect our initial state. | 509 // Detect our initial state. |
583 OutputSnapshot outputs[2] = { {0}, {0} }; | 510 OutputSnapshot outputs[2] = { {0}, {0} }; |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
844 // static | 771 // static |
845 bool OutputConfigurator::IsInternalOutputName(const std::string& name) { | 772 bool OutputConfigurator::IsInternalOutputName(const std::string& name) { |
846 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0; | 773 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0; |
847 } | 774 } |
848 | 775 |
849 void OutputConfigurator::NotifyOnDisplayChanged() { | 776 void OutputConfigurator::NotifyOnDisplayChanged() { |
850 notification_timer_.reset(); | 777 notification_timer_.reset(); |
851 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); | 778 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); |
852 } | 779 } |
853 | 780 |
| 781 int OutputConfigurator::GetDualOutputs(Display* display, |
| 782 XRRScreenResources* screen, |
| 783 OutputSnapshot* one, |
| 784 OutputSnapshot* two) { |
| 785 int found_count = 0; |
| 786 XRROutputInfo* one_info = NULL; |
| 787 XRROutputInfo* two_info = NULL; |
| 788 |
| 789 for (int i = 0; (i < screen->noutput) && (found_count < 2); ++i) { |
| 790 RROutput this_id = screen->outputs[i]; |
| 791 XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id); |
| 792 bool is_connected = (RR_Connected == output_info->connection); |
| 793 |
| 794 if (is_connected) { |
| 795 OutputSnapshot *to_populate = NULL; |
| 796 |
| 797 if (0 == found_count) { |
| 798 to_populate = one; |
| 799 one_info = output_info; |
| 800 } else { |
| 801 to_populate = two; |
| 802 two_info = output_info; |
| 803 } |
| 804 |
| 805 to_populate->output = this_id; |
| 806 // Now, look up the corresponding CRTC and any related info. |
| 807 to_populate->crtc = output_info->crtc; |
| 808 if (None != to_populate->crtc) { |
| 809 XRRCrtcInfo* crtc_info = |
| 810 XRRGetCrtcInfo(display, screen, to_populate->crtc); |
| 811 to_populate->current_mode = crtc_info->mode; |
| 812 to_populate->height = crtc_info->height; |
| 813 to_populate->y = crtc_info->y; |
| 814 XRRFreeCrtcInfo(crtc_info); |
| 815 } else { |
| 816 to_populate->current_mode = 0; |
| 817 to_populate->height = 0; |
| 818 to_populate->y = 0; |
| 819 } |
| 820 // Find the native_mode and leave the mirror_mode for the pass after the |
| 821 // loop. |
| 822 to_populate->native_mode = GetOutputNativeMode(output_info); |
| 823 to_populate->mirror_mode = 0; |
| 824 |
| 825 // See if this output refers to an internal display. |
| 826 to_populate->is_internal = IsInternalOutput(output_info); |
| 827 |
| 828 VLOG(1) << "Found display #" << found_count |
| 829 << " with output " << (int)to_populate->output |
| 830 << " crtc " << (int)to_populate->crtc |
| 831 << " current mode " << (int)to_populate->current_mode; |
| 832 ++found_count; |
| 833 } else { |
| 834 XRRFreeOutputInfo(output_info); |
| 835 } |
| 836 } |
| 837 |
| 838 if (2 == found_count) { |
| 839 // Find the mirror modes (if there are any). |
| 840 bool mirror_mode_found = FindMirrorModeForOutputs(display, |
| 841 screen, |
| 842 one->output, |
| 843 two->output, |
| 844 &one->mirror_mode, |
| 845 &two->mirror_mode); |
| 846 if (!mirror_mode_found) { |
| 847 bool mirror_mode_added = AddMirrorModeToInternalOutput(display, |
| 848 screen, |
| 849 one->output, |
| 850 two->output, |
| 851 &one->mirror_mode, |
| 852 &two->mirror_mode); |
| 853 if (!mirror_mode_added) { |
| 854 // We can't mirror so set mirror_mode to 0. |
| 855 one->mirror_mode = 0; |
| 856 two->mirror_mode = 0; |
| 857 } |
| 858 } |
| 859 } |
| 860 |
| 861 XRRFreeOutputInfo(one_info); |
| 862 XRRFreeOutputInfo(two_info); |
| 863 return found_count; |
| 864 } |
| 865 |
| 866 bool OutputConfigurator::AddMirrorModeToInternalOutput( |
| 867 Display* display, |
| 868 XRRScreenResources* screen, |
| 869 RROutput output_one, |
| 870 RROutput output_two, |
| 871 RRMode* output_one_mode, |
| 872 RRMode* output_two_mode) { |
| 873 // Add new mode only if panel fitting hardware will be able to display it. |
| 874 if (!is_panel_fitting_enabled_) |
| 875 return false; |
| 876 |
| 877 XRROutputInfo* output_one_info = |
| 878 XRRGetOutputInfo(display, screen, output_one); |
| 879 XRROutputInfo* output_two_info = |
| 880 XRRGetOutputInfo(display, screen, output_two); |
| 881 bool success = false; |
| 882 |
| 883 // Both outputs should be connected in mirror mode |
| 884 if (output_one_info->connection == RR_Connected && |
| 885 output_two_info->connection == RR_Connected) { |
| 886 bool one_is_internal = IsInternalOutput(output_one_info); |
| 887 bool two_is_internal = IsInternalOutput(output_two_info); |
| 888 |
| 889 XRROutputInfo* internal_info = NULL; |
| 890 XRROutputInfo* external_info = NULL; |
| 891 |
| 892 if (one_is_internal) { |
| 893 internal_info = output_one_info; |
| 894 external_info = output_two_info; |
| 895 |
| 896 VLOG_IF(1, two_is_internal) << "Two internal outputs detected."; |
| 897 DCHECK(!two_is_internal); |
| 898 } else if (two_is_internal) { |
| 899 internal_info = output_two_info; |
| 900 external_info = output_one_info; |
| 901 } |
| 902 |
| 903 bool internal_output_found = internal_info != NULL; |
| 904 |
| 905 if (internal_output_found) { |
| 906 RRMode internal_native_mode_id = GetOutputNativeMode(internal_info); |
| 907 RRMode external_native_mode_id = GetOutputNativeMode(external_info); |
| 908 |
| 909 if (internal_native_mode_id != None && external_native_mode_id != None) { |
| 910 XRRModeInfo* internal_native_mode = |
| 911 ModeInfoForID(screen, internal_native_mode_id); |
| 912 XRRModeInfo* external_native_mode = |
| 913 ModeInfoForID(screen, external_native_mode_id); |
| 914 |
| 915 // Panel fitting will not work if the internal output maximal resolution |
| 916 // is lower than that of the external output |
| 917 if (internal_native_mode->width >= external_native_mode->width && |
| 918 internal_native_mode->height >= external_native_mode->height) { |
| 919 XRRAddOutputMode(display, one_is_internal ? output_one : output_two, |
| 920 external_native_mode_id); |
| 921 |
| 922 *output_one_mode = *output_two_mode = external_native_mode_id; |
| 923 success = true; |
| 924 } |
| 925 } |
| 926 } |
| 927 } |
| 928 |
| 929 XRRFreeOutputInfo(output_one_info); |
| 930 XRRFreeOutputInfo(output_two_info); |
| 931 |
| 932 return success; |
| 933 } |
| 934 |
| 935 // static |
| 936 bool OutputConfigurator::IsInternalOutput(const XRROutputInfo* output_info) { |
| 937 return IsInternalOutputName(std::string(output_info->name)); |
| 938 } |
| 939 |
| 940 // static |
| 941 RRMode OutputConfigurator::GetOutputNativeMode( |
| 942 const XRROutputInfo* output_info) { |
| 943 if (output_info->nmode <= 0) |
| 944 return None; |
| 945 |
| 946 return output_info->modes[0]; |
| 947 } |
| 948 |
854 } // namespace chromeos | 949 } // namespace chromeos |
OLD | NEW |