| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "monitor_reconfig/monitor_reconfigure_main.h" |
| |
| #include <algorithm> |
| #include <cstdio> |
| #include <cstdlib> |
| |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| |
| using std::map; |
| using std::sort; |
| using std::string; |
| using std::vector; |
| |
| namespace monitor_reconfig { |
| |
| static const char kLcdOutputName[] = "LVDS1"; |
| |
| MonitorReconfigureMain::MonitorReconfigureMain(Display* display, |
| XRRScreenResources* screen_info) |
| : display_(display), |
| screen_info_(screen_info), |
| lcd_output_(NULL), |
| external_output_(NULL) { |
| for (int i = 0; i < screen_info_->nmode; ++i) { |
| XRRModeInfo* current_mode = &screen_info_->modes[i]; |
| mode_map_[current_mode->id] = current_mode; |
| } |
| DetermineOutputs(); |
| } |
| |
| void MonitorReconfigureMain::Run() { |
| vector<ResolutionSelector::Mode> lcd_modes; |
| SortModesByResolution(*lcd_output_, &lcd_modes); |
| DCHECK(!lcd_modes.empty()); |
| |
| vector<ResolutionSelector::Mode> external_modes; |
| if (IsExternalMonitorConnected()) { |
| SortModesByResolution(*external_output_, &external_modes); |
| DCHECK(!external_modes.empty()); |
| } |
| |
| ResolutionSelector selector; |
| string lcd_resolution, external_resolution, screen_resolution; |
| CHECK(selector.FindBestResolutions(lcd_modes, |
| external_modes, |
| &lcd_resolution, |
| &external_resolution, |
| &screen_resolution)); |
| CHECK(!lcd_resolution.empty() || !external_resolution.empty()); |
| |
| // Disable the LCD if we were told to do so (because we're using a higher |
| // resolution that'd be clipped on the LCD). |
| if (lcd_resolution.empty()) |
| DisableDevice(lcd_output_->name); |
| |
| // If there's no external output connected, disable the device before we try |
| // to set the screen resolution; otherwise xrandr will complain if we're |
| // trying to set the screen to a smaller size than what the now-unplugged |
| // device was using. |
| if (external_resolution.empty()) |
| DisableDevice(external_output_->name); |
| |
| // Set the fb to try to avoid the driver's "crtc has no fb" message. |
| // Doing this before enabling the display reduces the likelihood of a |
| // visible "snap" when returning to the panel. |
| SetScreenResolution(screen_resolution); |
| |
| // Enable the LCD if appropriate. |
| if (!lcd_resolution.empty()) |
| SetDeviceResolution(lcd_output_->name, lcd_resolution); |
| |
| // Enable the external device if appropriate. |
| if (!external_resolution.empty()) |
| SetDeviceResolution(external_output_->name, external_resolution); |
| } |
| |
| void MonitorReconfigureMain::DetermineOutputs() { |
| CHECK(screen_info_->noutput > 1) << "Expected at least two outputs"; |
| XRROutputInfo* first_output = |
| XRRGetOutputInfo(display_, screen_info_, screen_info_->outputs[0]); |
| XRROutputInfo* second_output = |
| XRRGetOutputInfo(display_, screen_info_, screen_info_->outputs[1]); |
| |
| if (strcmp(first_output->name, kLcdOutputName) == 0) { |
| lcd_output_ = first_output; |
| external_output_ = second_output; |
| } else { |
| lcd_output_ = second_output; |
| external_output_ = first_output; |
| } |
| |
| LOG(INFO) << "LCD name: " << lcd_output_->name; |
| for (int i = 0; i < lcd_output_->nmode; ++i) { |
| XRRModeInfo* mode = mode_map_[lcd_output_->modes[i]]; |
| LOG(INFO) << " Mode: " << mode->width << "x" << mode->height; |
| } |
| |
| LOG(INFO) << "External name: " << external_output_->name; |
| for (int i = 0; i < external_output_->nmode; ++i) { |
| XRRModeInfo* mode = mode_map_[external_output_->modes[i]]; |
| LOG(INFO) << " Mode: " << mode->width << "x" << mode->height; |
| } |
| } |
| |
| bool MonitorReconfigureMain::IsExternalMonitorConnected() { |
| return (external_output_->connection == RR_Connected); |
| } |
| |
| void MonitorReconfigureMain::SortModesByResolution( |
| const XRROutputInfo& output_info, |
| vector<ResolutionSelector::Mode>* modes_out) { |
| modes_out->clear(); |
| |
| for (int i = 0; i < output_info.nmode; ++i) { |
| const XRRModeInfo* mode = mode_map_[output_info.modes[i]]; |
| DCHECK(mode); |
| modes_out->push_back( |
| ResolutionSelector::Mode(mode->width, mode->height, mode->name)); |
| } |
| |
| sort(modes_out->begin(), modes_out->end(), |
| ResolutionSelector::ModeResolutionComparator()); |
| } |
| |
| bool MonitorReconfigureMain::SetDeviceResolution( |
| const std::string& device_name, const std::string& resolution) { |
| string command = StringPrintf("xrandr --output %s --mode %s", |
| device_name.c_str(), resolution.c_str()); |
| LOG(INFO) << "Running " << command.c_str(); |
| return system(command.c_str()) == 0; |
| } |
| |
| bool MonitorReconfigureMain::SetScreenResolution( |
| const std::string& resolution) { |
| string command = StringPrintf("xrandr --fb %s", resolution.c_str()); |
| LOG(INFO) << "Running " << command.c_str(); |
| return system(command.c_str()) == 0; |
| } |
| |
| bool MonitorReconfigureMain::DisableDevice(const std::string& device_name) { |
| string command = StringPrintf("xrandr --output %s --off", |
| device_name.c_str()); |
| LOG(INFO) << "Running " << command.c_str(); |
| return system(command.c_str()) == 0; |
| } |
| |
| } // end namespace monitor_reconfig |
| |
| int main(int argc, char** argv) { |
| CommandLine::Init(argc, argv); |
| logging::InitLogging(NULL, |
| logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, |
| logging::DONT_LOCK_LOG_FILE, |
| logging::APPEND_TO_OLD_LOG_FILE); |
| Display* display = XOpenDisplay(NULL); |
| CHECK(display) << "Could not open display"; |
| |
| Window window = RootWindow(display, DefaultScreen(display)); |
| XRRScreenResources* screen_info = XRRGetScreenResources(display, window); |
| monitor_reconfig::MonitorReconfigureMain main_app(display, screen_info); |
| main_app.Run(); |
| return 0; |
| } |