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/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 |