OLD | NEW |
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS 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 "monitor_reconfig/monitor_reconfigure_main.h" | 5 #include "monitor_reconfig/monitor_reconfigure_main.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cstdio> | 8 #include <cstdio> |
9 #include <cstdlib> | 9 #include <cstdlib> |
10 | 10 |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/string_util.h" | 13 #include "base/string_util.h" |
14 | 14 |
15 using std::map; | 15 using std::map; |
16 using std::sort; | 16 using std::sort; |
17 using std::string; | 17 using std::string; |
18 using std::vector; | 18 using std::vector; |
19 | 19 |
20 namespace monitor_reconfig { | 20 namespace monitor_reconfig { |
21 | 21 |
22 static const char kLcdOutputName[] = "LVDS1"; | 22 static const char kLcdOutputName[] = "LVDS1"; |
| 23 static const float kScreenDpi = 96.0; |
| 24 static const float kInchInMm = 25.4; |
23 | 25 |
24 MonitorReconfigureMain::MonitorReconfigureMain(Display* display, | 26 MonitorReconfigureMain::MonitorReconfigureMain(Display* display, |
| 27 Window window, |
25 XRRScreenResources* screen_info) | 28 XRRScreenResources* screen_info) |
26 : display_(display), | 29 : display_(display), |
| 30 window_(window), |
27 screen_info_(screen_info), | 31 screen_info_(screen_info), |
28 lcd_output_(NULL), | 32 lcd_output_(0), |
29 external_output_(NULL) { | 33 lcd_output_info_(NULL), |
| 34 external_output_(0), |
| 35 external_output_info_(NULL) { |
30 for (int i = 0; i < screen_info_->nmode; ++i) { | 36 for (int i = 0; i < screen_info_->nmode; ++i) { |
31 XRRModeInfo* current_mode = &screen_info_->modes[i]; | 37 XRRModeInfo* current_mode = &screen_info_->modes[i]; |
32 mode_map_[current_mode->id] = current_mode; | 38 mode_map_[current_mode->id] = current_mode; |
33 } | 39 } |
34 DetermineOutputs(); | 40 DetermineOutputs(); |
35 } | 41 } |
36 | 42 |
37 void MonitorReconfigureMain::Run() { | 43 void MonitorReconfigureMain::Run() { |
38 vector<ResolutionSelector::Mode> lcd_modes; | 44 vector<ResolutionSelector::Mode> lcd_modes; |
39 SortModesByResolution(*lcd_output_, &lcd_modes); | 45 SortModesByResolution(lcd_output_, &lcd_modes); |
40 DCHECK(!lcd_modes.empty()); | 46 DCHECK(!lcd_modes.empty()); |
41 | 47 |
42 vector<ResolutionSelector::Mode> external_modes; | 48 vector<ResolutionSelector::Mode> external_modes; |
43 if (IsExternalMonitorConnected()) { | 49 if (IsExternalMonitorConnected()) { |
44 SortModesByResolution(*external_output_, &external_modes); | 50 SortModesByResolution(external_output_, &external_modes); |
45 DCHECK(!external_modes.empty()); | 51 DCHECK(!external_modes.empty()); |
46 } | 52 } |
47 | 53 |
48 ResolutionSelector selector; | 54 ResolutionSelector selector; |
49 string lcd_resolution, external_resolution, screen_resolution; | 55 ResolutionSelector::Mode lcd_resolution; |
| 56 ResolutionSelector::Mode external_resolution; |
| 57 ResolutionSelector::Mode screen_resolution; |
| 58 |
50 CHECK(selector.FindBestResolutions(lcd_modes, | 59 CHECK(selector.FindBestResolutions(lcd_modes, |
51 external_modes, | 60 external_modes, |
52 &lcd_resolution, | 61 &lcd_resolution, |
53 &external_resolution, | 62 &external_resolution, |
54 &screen_resolution)); | 63 &screen_resolution)); |
55 CHECK(!lcd_resolution.empty() || !external_resolution.empty()); | 64 CHECK(!lcd_resolution.name.empty() || !external_resolution.name.empty()); |
| 65 |
| 66 // Grab the server during mode changes. |
| 67 XGrabServer(display_); |
56 | 68 |
57 // Disable the LCD if we were told to do so (because we're using a higher | 69 // Disable the LCD if we were told to do so (because we're using a higher |
58 // resolution that'd be clipped on the LCD). | 70 // resolution that'd be clipped on the LCD). |
59 if (lcd_resolution.empty()) | 71 // Otherwise enable the LCD if appropriate. |
60 DisableDevice(lcd_output_->name); | 72 if (lcd_resolution.name.empty()) |
| 73 DisableDevice(lcd_output_, lcd_output_info_); |
| 74 else |
| 75 SetDeviceResolution(lcd_output_, lcd_output_info_, lcd_resolution); |
61 | 76 |
62 // If there's no external output connected, disable the device before we try | 77 // If there's no external output connected, disable the device before we try |
63 // to set the screen resolution; otherwise xrandr will complain if we're | 78 // to set the screen resolution; otherwise xrandr will complain if we're |
64 // trying to set the screen to a smaller size than what the now-unplugged | 79 // trying to set the screen to a smaller size than what the now-unplugged |
65 // device was using. | 80 // device was using. |
66 if (external_resolution.empty()) | 81 // Otherwise enable the external device if appropriate. |
67 DisableDevice(external_output_->name); | 82 if (external_resolution.name.empty()) |
| 83 DisableDevice(external_output_, external_output_info_); |
| 84 else |
| 85 SetDeviceResolution(external_output_, external_output_info_, |
| 86 external_resolution); |
68 | 87 |
69 // Set the fb to try to avoid the driver's "crtc has no fb" message. | 88 // Set the fb resolution. |
70 // Doing this before enabling the display reduces the likelihood of a | |
71 // visible "snap" when returning to the panel. | |
72 SetScreenResolution(screen_resolution); | 89 SetScreenResolution(screen_resolution); |
73 | 90 |
74 // Enable the LCD if appropriate. | 91 // Now let the server go and sync all changes. |
75 if (!lcd_resolution.empty()) | 92 XUngrabServer(display_); |
76 SetDeviceResolution(lcd_output_->name, lcd_resolution); | 93 XSync(display_, False); |
77 | |
78 // Enable the external device if appropriate. | |
79 if (!external_resolution.empty()) | |
80 SetDeviceResolution(external_output_->name, external_resolution); | |
81 } | 94 } |
82 | 95 |
83 void MonitorReconfigureMain::DetermineOutputs() { | 96 void MonitorReconfigureMain::DetermineOutputs() { |
84 CHECK(screen_info_->noutput > 1) << "Expected at least two outputs"; | 97 CHECK(screen_info_->noutput > 1) << "Expected at least two outputs"; |
85 XRROutputInfo* first_output = | 98 CHECK(!lcd_output_info_); |
86 XRRGetOutputInfo(display_, screen_info_, screen_info_->outputs[0]); | 99 CHECK(!external_output_info_); |
87 XRROutputInfo* second_output = | |
88 XRRGetOutputInfo(display_, screen_info_, screen_info_->outputs[1]); | |
89 | 100 |
90 if (strcmp(first_output->name, kLcdOutputName) == 0) { | 101 RROutput first_output = screen_info_->outputs[0]; |
| 102 RROutput second_output = screen_info_->outputs[1]; |
| 103 |
| 104 XRROutputInfo* first_output_info = |
| 105 XRRGetOutputInfo(display_, screen_info_, first_output); |
| 106 XRROutputInfo* second_output_info = |
| 107 XRRGetOutputInfo(display_, screen_info_, second_output); |
| 108 |
| 109 if (strcmp(first_output_info->name, kLcdOutputName) == 0) { |
91 lcd_output_ = first_output; | 110 lcd_output_ = first_output; |
| 111 lcd_output_info_ = first_output_info; |
92 external_output_ = second_output; | 112 external_output_ = second_output; |
| 113 external_output_info_ = second_output_info; |
93 } else { | 114 } else { |
94 lcd_output_ = second_output; | 115 lcd_output_ = second_output; |
| 116 lcd_output_info_ = second_output_info; |
95 external_output_ = first_output; | 117 external_output_ = first_output; |
| 118 external_output_info_ = first_output_info; |
96 } | 119 } |
97 | 120 |
98 LOG(INFO) << "LCD name: " << lcd_output_->name; | 121 LOG(INFO) << "LCD name: " << lcd_output_info_->name << " (xid " |
99 for (int i = 0; i < lcd_output_->nmode; ++i) { | 122 << lcd_output_ << ")"; |
100 XRRModeInfo* mode = mode_map_[lcd_output_->modes[i]]; | 123 for (int i = 0; i < lcd_output_info_->nmode; ++i) { |
101 LOG(INFO) << " Mode: " << mode->width << "x" << mode->height; | 124 XRRModeInfo* mode = mode_map_[lcd_output_info_->modes[i]]; |
| 125 LOG(INFO) << " Mode: " << mode->width << "x" << mode->height |
| 126 << " (xid " << mode->id << ")"; |
102 } | 127 } |
103 | 128 |
104 LOG(INFO) << "External name: " << external_output_->name; | 129 LOG(INFO) << "External name: " << external_output_info_->name |
105 for (int i = 0; i < external_output_->nmode; ++i) { | 130 << " (xid " << external_output_ << ")"; |
106 XRRModeInfo* mode = mode_map_[external_output_->modes[i]]; | 131 for (int i = 0; i < external_output_info_->nmode; ++i) { |
107 LOG(INFO) << " Mode: " << mode->width << "x" << mode->height; | 132 XRRModeInfo* mode = mode_map_[external_output_info_->modes[i]]; |
| 133 LOG(INFO) << " Mode: " << mode->width << "x" << mode->height |
| 134 << " (xid " << mode->id << ")"; |
108 } | 135 } |
109 } | 136 } |
110 | 137 |
111 bool MonitorReconfigureMain::IsExternalMonitorConnected() { | 138 bool MonitorReconfigureMain::IsExternalMonitorConnected() { |
112 return (external_output_->connection == RR_Connected); | 139 return (external_output_info_->connection == RR_Connected); |
113 } | 140 } |
114 | 141 |
115 void MonitorReconfigureMain::SortModesByResolution( | 142 void MonitorReconfigureMain::SortModesByResolution( |
116 const XRROutputInfo& output_info, | 143 RROutput output, |
117 vector<ResolutionSelector::Mode>* modes_out) { | 144 vector<ResolutionSelector::Mode>* modes_out) { |
118 modes_out->clear(); | 145 modes_out->clear(); |
119 | 146 |
120 for (int i = 0; i < output_info.nmode; ++i) { | 147 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_info_, output); |
121 const XRRModeInfo* mode = mode_map_[output_info.modes[i]]; | 148 for (int i = 0; i < output_info->nmode; ++i) { |
| 149 const XRRModeInfo* mode = mode_map_[output_info->modes[i]]; |
122 DCHECK(mode); | 150 DCHECK(mode); |
| 151 LOG(INFO) << "Adding mode " << mode->width << " " << mode->height |
| 152 << " (xid " << mode->id << ")"; |
123 modes_out->push_back( | 153 modes_out->push_back( |
124 ResolutionSelector::Mode(mode->width, mode->height, mode->name)); | 154 ResolutionSelector::Mode(mode->width, mode->height, mode->name, |
| 155 mode->id)); |
125 } | 156 } |
126 | 157 |
127 sort(modes_out->begin(), modes_out->end(), | 158 sort(modes_out->begin(), modes_out->end(), |
128 ResolutionSelector::ModeResolutionComparator()); | 159 ResolutionSelector::ModeResolutionComparator()); |
| 160 |
| 161 XRRFreeOutputInfo(output_info); |
129 } | 162 } |
130 | 163 |
131 bool MonitorReconfigureMain::SetDeviceResolution( | 164 bool MonitorReconfigureMain::SetDeviceResolution( |
132 const std::string& device_name, const std::string& resolution) { | 165 RROutput output, const XRROutputInfo* output_info, |
133 string command = StringPrintf("xrandr --output %s --mode %s", | 166 const ResolutionSelector::Mode& resolution) { |
134 device_name.c_str(), resolution.c_str()); | 167 Status s = XRRSetCrtcConfig(display_, screen_info_, output_info->crtcs[0], |
135 LOG(INFO) << "Running " << command.c_str(); | 168 CurrentTime, 0, 0, resolution.id, RR_Rotate_0, |
136 return system(command.c_str()) == 0; | 169 &output, 1); |
| 170 return (s == RRSetConfigSuccess); |
137 } | 171 } |
138 | 172 |
139 bool MonitorReconfigureMain::SetScreenResolution( | 173 bool MonitorReconfigureMain::SetScreenResolution( |
140 const std::string& resolution) { | 174 const ResolutionSelector::Mode& resolution) { |
141 string command = StringPrintf("xrandr --fb %s", resolution.c_str()); | 175 LOG(INFO) << "Setting resolution " << resolution.name.c_str() << " " |
142 LOG(INFO) << "Running " << command.c_str(); | 176 << resolution.width << "x" << resolution.height; |
143 return system(command.c_str()) == 0; | 177 XRRSetScreenSize(display_, window_, |
| 178 resolution.width, |
| 179 resolution.height, |
| 180 resolution.width * kInchInMm / kScreenDpi, |
| 181 resolution.height * kInchInMm / kScreenDpi); |
| 182 |
| 183 return true; |
144 } | 184 } |
145 | 185 |
146 bool MonitorReconfigureMain::DisableDevice(const std::string& device_name) { | 186 bool MonitorReconfigureMain::DisableDevice(RROutput output, |
147 string command = StringPrintf("xrandr --output %s --off", | 187 const XRROutputInfo* output_info) { |
148 device_name.c_str()); | 188 if (!output_info->crtc) |
149 LOG(INFO) << "Running " << command.c_str(); | 189 return true; |
150 return system(command.c_str()) == 0; | 190 LOG(INFO) << "Disabling output " << output_info->name; |
| 191 Status s = XRRSetCrtcConfig(display_, screen_info_, output_info->crtc, |
| 192 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0); |
| 193 return (s == RRSetConfigSuccess); |
151 } | 194 } |
152 | 195 |
153 } // end namespace monitor_reconfig | 196 } // end namespace monitor_reconfig |
154 | 197 |
155 int main(int argc, char** argv) { | 198 int main(int argc, char** argv) { |
156 CommandLine::Init(argc, argv); | 199 CommandLine::Init(argc, argv); |
157 logging::InitLogging(NULL, | 200 logging::InitLogging(NULL, |
158 logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, | 201 logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, |
159 logging::DONT_LOCK_LOG_FILE, | 202 logging::DONT_LOCK_LOG_FILE, |
160 logging::APPEND_TO_OLD_LOG_FILE); | 203 logging::APPEND_TO_OLD_LOG_FILE); |
161 Display* display = XOpenDisplay(NULL); | 204 Display* display = XOpenDisplay(NULL); |
162 CHECK(display) << "Could not open display"; | 205 CHECK(display) << "Could not open display"; |
163 | 206 |
164 Window window = RootWindow(display, DefaultScreen(display)); | 207 Window window = RootWindow(display, DefaultScreen(display)); |
165 XRRScreenResources* screen_info = XRRGetScreenResources(display, window); | 208 XRRScreenResources* screen_info = XRRGetScreenResources(display, window); |
166 monitor_reconfig::MonitorReconfigureMain main_app(display, screen_info); | 209 monitor_reconfig::MonitorReconfigureMain main_app(display, window, |
| 210 screen_info); |
167 main_app.Run(); | 211 main_app.Run(); |
| 212 XRRFreeScreenResources(screen_info); |
168 return 0; | 213 return 0; |
169 } | 214 } |
OLD | NEW |