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 |
11 // Xlib defines Status as int which causes our include of dbus/bus.h to fail | 11 // Xlib defines Status as int which causes our include of dbus/bus.h to fail |
12 // when it tries to name an enum Status. Thus, we need to undefine it (note | 12 // when it tries to name an enum Status. Thus, we need to undefine it (note |
13 // that this will cause a problem if code needs to use the Status type). | 13 // that this will cause a problem if code needs to use the Status type). |
14 // RootWindow causes similar problems in that there is a Chromium type with that | 14 // RootWindow causes similar problems in that there is a Chromium type with that |
15 // name. | 15 // name. |
16 #undef Status | 16 #undef Status |
17 #undef RootWindow | 17 #undef RootWindow |
18 | 18 |
19 #include "base/bind.h" | 19 #include "base/bind.h" |
20 #include "base/chromeos/chromeos_version.h" | 20 #include "base/chromeos/chromeos_version.h" |
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 "chromeos/dbus/dbus_thread_manager.h" | 25 #include "chromeos/dbus/dbus_thread_manager.h" |
26 #include "chromeos/dbus/power_manager_client.h" | 26 #include "chromeos/dbus/power_manager_client.h" |
27 | 27 |
28 namespace chromeos { | 28 namespace chromeos { |
29 | 29 |
30 typedef struct OutputSnapshot { | |
31 RROutput output; | |
32 RRCrtc crtc; | |
33 RRMode current_mode; | |
34 int height; | |
35 int y; | |
36 RRMode native_mode; | |
37 RRMode mirror_mode; | |
38 bool is_internal; | |
39 } OutputSnapshot; | |
40 | |
30 namespace { | 41 namespace { |
31 // DPI measurements. | 42 // DPI measurements. |
32 const float kMmInInch = 25.4; | 43 const float kMmInInch = 25.4; |
33 const float kDpi96 = 96.0; | 44 const float kDpi96 = 96.0; |
34 const float kPixelsToMmScale = kMmInInch / kDpi96; | 45 const float kPixelsToMmScale = kMmInInch / kDpi96; |
35 | 46 |
36 // The DPI threshold to detech high density screen. | 47 // The DPI threshold to detech high density screen. |
37 // Higher DPI than this will use device_scale_factor=2 | 48 // Higher DPI than this will use device_scale_factor=2 |
38 // Should be kept in sync with display_change_observer_x11.cc | 49 // Should be kept in sync with display_change_observer_x11.cc |
39 const unsigned int kHighDensityDIPThreshold = 160; | 50 const unsigned int kHighDensityDIPThreshold = 160; |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
187 x, | 198 x, |
188 y, | 199 y, |
189 kMode, | 200 kMode, |
190 kOutput); | 201 kOutput); |
191 } | 202 } |
192 int mm_width = width * kPixelsToMmScale; | 203 int mm_width = width * kPixelsToMmScale; |
193 int mm_height = height * kPixelsToMmScale; | 204 int mm_height = height * kPixelsToMmScale; |
194 XRRSetScreenSize(display, window, width, height, mm_width, mm_height); | 205 XRRSetScreenSize(display, window, width, height, mm_width, mm_height); |
195 } | 206 } |
196 | 207 |
197 typedef struct OutputSnapshot { | |
198 RROutput output; | |
199 RRCrtc crtc; | |
200 RRMode current_mode; | |
201 int height; | |
202 int y; | |
203 RRMode native_mode; | |
204 RRMode mirror_mode; | |
205 bool is_internal; | |
206 } OutputSnapshot; | |
207 | |
208 static int GetDualOutputs(Display* display, | |
209 XRRScreenResources* screen, | |
210 OutputSnapshot* one, | |
211 OutputSnapshot* two) { | |
212 int found_count = 0; | |
213 XRROutputInfo* one_info = NULL; | |
214 XRROutputInfo* two_info = NULL; | |
215 | |
216 for (int i = 0; (i < screen->noutput) && (found_count < 2); ++i) { | |
217 RROutput this_id = screen->outputs[i]; | |
218 XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id); | |
219 bool is_connected = (RR_Connected == output_info->connection); | |
220 | |
221 if (is_connected) { | |
222 OutputSnapshot *to_populate = NULL; | |
223 | |
224 if (0 == found_count) { | |
225 to_populate = one; | |
226 one_info = output_info; | |
227 } else { | |
228 to_populate = two; | |
229 two_info = output_info; | |
230 } | |
231 | |
232 to_populate->output = this_id; | |
233 // Now, look up the corresponding CRTC and any related info. | |
234 to_populate->crtc = output_info->crtc; | |
235 if (None != to_populate->crtc) { | |
236 XRRCrtcInfo* crtc_info = | |
237 XRRGetCrtcInfo(display, screen, to_populate->crtc); | |
238 to_populate->current_mode = crtc_info->mode; | |
239 to_populate->height = crtc_info->height; | |
240 to_populate->y = crtc_info->y; | |
241 XRRFreeCrtcInfo(crtc_info); | |
242 } else { | |
243 to_populate->current_mode = 0; | |
244 to_populate->height = 0; | |
245 to_populate->y = 0; | |
246 } | |
247 // Find the native_mode and leave the mirror_mode for the pass after the | |
248 // loop. | |
249 if (output_info->nmode > 0) | |
250 to_populate->native_mode = output_info->modes[0]; | |
251 to_populate->mirror_mode = 0; | |
252 | |
253 // See if this output refers to an internal display. | |
254 to_populate->is_internal = | |
255 OutputConfigurator::IsInternalOutputName( | |
256 std::string(output_info->name)); | |
257 | |
258 VLOG(1) << "Found display #" << found_count | |
259 << " with output " << (int)to_populate->output | |
260 << " crtc " << (int)to_populate->crtc | |
261 << " current mode " << (int)to_populate->current_mode; | |
262 ++found_count; | |
263 } else { | |
264 XRRFreeOutputInfo(output_info); | |
265 } | |
266 } | |
267 | |
268 if (2 == found_count) { | |
269 // Find the mirror modes (if there are any). | |
270 bool can_mirror = FindMirrorModeForOutputs(display, | |
271 screen, | |
272 one->output, | |
273 two->output, | |
274 &one->mirror_mode, | |
275 &two->mirror_mode); | |
276 if (!can_mirror) { | |
277 // We can't mirror so set mirror_mode to 0. | |
278 one->mirror_mode = 0; | |
279 two->mirror_mode = 0; | |
280 } | |
281 } | |
282 | |
283 XRRFreeOutputInfo(one_info); | |
284 XRRFreeOutputInfo(two_info); | |
285 return found_count; | |
286 } | |
287 | |
288 static OutputState InferCurrentState(Display* display, | 208 static OutputState InferCurrentState(Display* display, |
289 XRRScreenResources* screen, | 209 XRRScreenResources* screen, |
290 const OutputSnapshot* outputs, | 210 const OutputSnapshot* outputs, |
291 int output_count) { | 211 int output_count) { |
292 OutputState state = STATE_INVALID; | 212 OutputState state = STATE_INVALID; |
293 switch (output_count) { | 213 switch (output_count) { |
294 case 0: | 214 case 0: |
295 state = STATE_HEADLESS; | 215 state = STATE_HEADLESS; |
296 break; | 216 break; |
297 case 1: | 217 case 1: |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
555 | 475 |
556 // "Projecting" is defined as having more than 1 output connected while at | 476 // "Projecting" is defined as having more than 1 output connected while at |
557 // least one of them is an internal output. | 477 // least one of them is an internal output. |
558 return has_internal_output && (connected_output_count > 1); | 478 return has_internal_output && (connected_output_count > 1); |
559 } | 479 } |
560 | 480 |
561 } // namespace | 481 } // namespace |
562 | 482 |
563 OutputConfigurator::OutputConfigurator() | 483 OutputConfigurator::OutputConfigurator() |
564 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()), | 484 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()), |
485 is_panel_fitting_enabled_(false), | |
486 connected_output_count_(0), | |
565 xrandr_event_base_(0), | 487 xrandr_event_base_(0), |
566 output_state_(STATE_INVALID) { | 488 output_state_(STATE_INVALID) { |
489 } | |
490 | |
491 void OutputConfigurator::Init(bool is_panel_fitting_enabled) { | |
567 if (!is_running_on_chrome_os_) | 492 if (!is_running_on_chrome_os_) |
568 return; | 493 return; |
569 | 494 |
495 is_panel_fitting_enabled_ = is_panel_fitting_enabled; | |
496 | |
570 // Cache the initial output state. | 497 // Cache the initial output state. |
571 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | 498 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
572 CHECK(display != NULL); | 499 CHECK(display != NULL); |
573 XGrabServer(display); | 500 XGrabServer(display); |
574 Window window = DefaultRootWindow(display); | 501 Window window = DefaultRootWindow(display); |
575 XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window); | 502 XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window); |
576 CHECK(screen != NULL); | 503 CHECK(screen != NULL); |
577 | 504 |
578 // Detect our initial state. | 505 // Detect our initial state. |
579 OutputSnapshot outputs[2] = { {0}, {0} }; | 506 OutputSnapshot outputs[2] = { {0}, {0} }; |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
808 } | 735 } |
809 | 736 |
810 void OutputConfigurator::RemoveObserver(Observer* observer) { | 737 void OutputConfigurator::RemoveObserver(Observer* observer) { |
811 observers_.RemoveObserver(observer); | 738 observers_.RemoveObserver(observer); |
812 } | 739 } |
813 | 740 |
814 // static | 741 // static |
815 bool OutputConfigurator::IsInternalOutputName(const std::string& name) { | 742 bool OutputConfigurator::IsInternalOutputName(const std::string& name) { |
816 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0; | 743 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0; |
817 } | 744 } |
745 // static | |
746 bool OutputConfigurator::IsInternalOutput(const XRROutputInfo* output_info) { | |
747 return IsInternalOutputName(std::string(output_info->name)); | |
748 } | |
749 | |
750 // static | |
751 RRMode OutputConfigurator::GetOutputNativeMode( | |
752 const XRROutputInfo* output_info) { | |
753 if (output_info->nmode <= 0) { | |
754 return None; | |
755 } | |
oshima
2012/09/21 19:38:12
nuke {}
ynovikov
2012/09/21 23:05:36
Done.
| |
756 | |
757 return output_info->modes[0]; | |
758 } | |
818 | 759 |
819 void OutputConfigurator::NotifyOnDisplayChanged() { | 760 void OutputConfigurator::NotifyOnDisplayChanged() { |
820 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); | 761 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); |
821 } | 762 } |
822 | 763 |
764 bool OutputConfigurator::AddMirrorModeToInternalOutput( | |
765 Display* display, | |
766 XRRScreenResources* screen, | |
767 RROutput output_one, | |
768 RROutput output_two, | |
769 RRMode* output_one_mode, | |
770 RRMode* output_two_mode) { | |
771 // Add new mode only if panel fitting hardware will be able to display it. | |
772 if (!is_panel_fitting_enabled_) { | |
773 return false; | |
774 } | |
oshima
2012/09/21 19:38:12
nuke {}
ynovikov
2012/09/21 23:05:36
Done.
| |
775 | |
776 XRROutputInfo* output_one_info = | |
777 XRRGetOutputInfo(display, screen, output_one); | |
778 XRROutputInfo* output_two_info = | |
779 XRRGetOutputInfo(display, screen, output_two); | |
780 bool success = false; | |
781 | |
782 // Both outputs should be connected in mirror mode | |
783 if (output_one_info->connection == RR_Connected && | |
784 output_two_info->connection == RR_Connected) { | |
785 bool one_is_internal = IsInternalOutput(output_one_info); | |
786 bool two_is_internal = IsInternalOutput(output_two_info); | |
787 | |
788 XRROutputInfo* internal_info = NULL; | |
789 XRROutputInfo* external_info = NULL; | |
790 | |
791 if (one_is_internal) { | |
792 internal_info = output_one_info; | |
793 external_info = output_two_info; | |
794 | |
795 VLOG_IF(1, two_is_internal) << "Two internal outputs detected."; | |
796 DCHECK(!two_is_internal); | |
797 } else if (two_is_internal) { | |
798 internal_info = output_two_info; | |
799 external_info = output_one_info; | |
800 } | |
801 | |
802 bool internal_output_found = internal_info != NULL; | |
803 | |
804 if (internal_output_found) { | |
805 RRMode internal_native_mode_id = GetOutputNativeMode(internal_info); | |
806 RRMode external_native_mode_id = GetOutputNativeMode(external_info); | |
807 | |
808 if (internal_native_mode_id != None && external_native_mode_id != None) { | |
809 XRRModeInfo* internal_native_mode = | |
810 ModeInfoForID(screen, internal_native_mode_id); | |
811 XRRModeInfo* external_native_mode = | |
812 ModeInfoForID(screen, external_native_mode_id); | |
813 | |
814 // Panel fitting will not work if the internal output maximal resolution | |
815 // is lower than that of the external output | |
816 if (internal_native_mode->width >= external_native_mode->width && | |
817 internal_native_mode->height >= external_native_mode->height) { | |
818 XRRAddOutputMode(display, one_is_internal ? output_one : output_two, | |
819 external_native_mode_id); | |
820 | |
821 *output_one_mode = *output_two_mode = external_native_mode_id; | |
822 success = true; | |
823 } | |
824 } | |
825 } | |
826 } | |
827 | |
828 XRRFreeOutputInfo(output_one_info); | |
829 XRRFreeOutputInfo(output_two_info); | |
830 | |
831 return success; | |
832 } | |
833 | |
834 int OutputConfigurator::GetDualOutputs(Display* display, | |
cwolfe
2012/09/21 19:17:21
Just for my sanity, here's a diff -b between the v
ynovikov
2012/09/21 23:05:36
Done.
| |
835 XRRScreenResources* screen, | |
836 OutputSnapshot* one, | |
837 OutputSnapshot* two) { | |
838 int found_count = 0; | |
839 XRROutputInfo* one_info = NULL; | |
840 XRROutputInfo* two_info = NULL; | |
841 | |
842 for (int i = 0; (i < screen->noutput) && (found_count < 2); ++i) { | |
843 RROutput this_id = screen->outputs[i]; | |
844 XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id); | |
845 bool is_connected = (RR_Connected == output_info->connection); | |
846 | |
847 if (is_connected) { | |
848 OutputSnapshot *to_populate = NULL; | |
849 | |
850 if (0 == found_count) { | |
851 to_populate = one; | |
852 one_info = output_info; | |
853 } else { | |
854 to_populate = two; | |
855 two_info = output_info; | |
856 } | |
857 | |
858 to_populate->output = this_id; | |
859 // Now, look up the corresponding CRTC and any related info. | |
860 to_populate->crtc = output_info->crtc; | |
861 if (None != to_populate->crtc) { | |
862 XRRCrtcInfo* crtc_info = | |
863 XRRGetCrtcInfo(display, screen, to_populate->crtc); | |
864 to_populate->current_mode = crtc_info->mode; | |
865 to_populate->height = crtc_info->height; | |
866 to_populate->y = crtc_info->y; | |
867 XRRFreeCrtcInfo(crtc_info); | |
868 } else { | |
869 to_populate->current_mode = 0; | |
870 to_populate->height = 0; | |
871 to_populate->y = 0; | |
872 } | |
873 // Find the native_mode and leave the mirror_mode for the pass after the | |
874 // loop. | |
875 to_populate->native_mode = GetOutputNativeMode(output_info); | |
876 to_populate->mirror_mode = 0; | |
877 | |
878 // See if this output refers to an internal display. | |
879 to_populate->is_internal = IsInternalOutput(output_info); | |
880 | |
881 VLOG(1) << "Found display #" << found_count | |
882 << " with output " << (int)to_populate->output | |
883 << " crtc " << (int)to_populate->crtc | |
884 << " current mode " << (int)to_populate->current_mode; | |
885 ++found_count; | |
oshima
2012/09/21 19:38:12
you probably want to increment this before VLOG.
ynovikov
2012/09/21 23:05:36
I didn't change this code, just moved it.
It does
| |
886 } else { | |
887 XRRFreeOutputInfo(output_info); | |
888 } | |
889 } | |
890 | |
891 if (2 == found_count) { | |
892 // Find the mirror modes (if there are any). | |
893 bool mirror_mode_found = FindMirrorModeForOutputs(display, | |
894 screen, | |
895 one->output, | |
896 two->output, | |
897 &one->mirror_mode, | |
898 &two->mirror_mode); | |
899 if (!mirror_mode_found) { | |
900 bool mirror_mode_added = AddMirrorModeToInternalOutput(display, | |
901 screen, | |
902 one->output, | |
903 two->output, | |
904 &one->mirror_mode, | |
905 &two->mirror_mode); | |
906 if (!mirror_mode_added) { | |
907 // We can't mirror so set mirror_mode to 0. | |
908 one->mirror_mode = 0; | |
909 two->mirror_mode = 0; | |
910 } | |
911 } | |
912 } | |
913 | |
914 XRRFreeOutputInfo(one_info); | |
915 XRRFreeOutputInfo(two_info); | |
916 return found_count; | |
917 } | |
918 | |
823 } // namespace chromeos | 919 } // namespace chromeos |
OLD | NEW |