| // 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"; |
| static const float kScreenDpi = 96.0; |
| static const float kInchInMm = 25.4; |
| |
| MonitorReconfigureMain::MonitorReconfigureMain(Display* display, |
| Window window, |
| XRRScreenResources* screen_info) |
| : display_(display), |
| window_(window), |
| screen_info_(screen_info), |
| lcd_output_(0), |
| lcd_output_info_(NULL), |
| external_output_(0), |
| external_output_info_(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; |
| ResolutionSelector::Mode lcd_resolution; |
| ResolutionSelector::Mode external_resolution; |
| ResolutionSelector::Mode screen_resolution; |
| |
| CHECK(selector.FindBestResolutions(lcd_modes, |
| external_modes, |
| &lcd_resolution, |
| &external_resolution, |
| &screen_resolution)); |
| CHECK(!lcd_resolution.name.empty() || !external_resolution.name.empty()); |
| |
| // Grab the server during mode changes. |
| XGrabServer(display_); |
| |
| // Disable the LCD if we were told to do so (because we're using a higher |
| // resolution that'd be clipped on the LCD). |
| // Otherwise enable the LCD if appropriate. |
| if (lcd_resolution.name.empty()) |
| DisableDevice(lcd_output_, lcd_output_info_); |
| else |
| SetDeviceResolution(lcd_output_, lcd_output_info_, lcd_resolution); |
| |
| // 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. |
| // Otherwise enable the external device if appropriate. |
| if (external_resolution.name.empty()) |
| DisableDevice(external_output_, external_output_info_); |
| else |
| SetDeviceResolution(external_output_, external_output_info_, |
| external_resolution); |
| |
| // Set the fb resolution. |
| SetScreenResolution(screen_resolution); |
| |
| // Now let the server go and sync all changes. |
| XUngrabServer(display_); |
| XSync(display_, False); |
| } |
| |
| void MonitorReconfigureMain::DetermineOutputs() { |
| CHECK(screen_info_->noutput > 1) << "Expected at least two outputs"; |
| CHECK(!lcd_output_info_); |
| CHECK(!external_output_info_); |
| |
| RROutput first_output = screen_info_->outputs[0]; |
| RROutput second_output = screen_info_->outputs[1]; |
| |
| XRROutputInfo* first_output_info = |
| XRRGetOutputInfo(display_, screen_info_, first_output); |
| XRROutputInfo* second_output_info = |
| XRRGetOutputInfo(display_, screen_info_, second_output); |
| |
| if (strcmp(first_output_info->name, kLcdOutputName) == 0) { |
| lcd_output_ = first_output; |
| lcd_output_info_ = first_output_info; |
| external_output_ = second_output; |
| external_output_info_ = second_output_info; |
| } else { |
| lcd_output_ = second_output; |
| lcd_output_info_ = second_output_info; |
| external_output_ = first_output; |
| external_output_info_ = first_output_info; |
| } |
| |
| LOG(INFO) << "LCD name: " << lcd_output_info_->name << " (xid " |
| << lcd_output_ << ")"; |
| for (int i = 0; i < lcd_output_info_->nmode; ++i) { |
| XRRModeInfo* mode = mode_map_[lcd_output_info_->modes[i]]; |
| LOG(INFO) << " Mode: " << mode->width << "x" << mode->height |
| << " (xid " << mode->id << ")"; |
| } |
| |
| LOG(INFO) << "External name: " << external_output_info_->name |
| << " (xid " << external_output_ << ")"; |
| for (int i = 0; i < external_output_info_->nmode; ++i) { |
| XRRModeInfo* mode = mode_map_[external_output_info_->modes[i]]; |
| LOG(INFO) << " Mode: " << mode->width << "x" << mode->height |
| << " (xid " << mode->id << ")"; |
| } |
| } |
| |
| bool MonitorReconfigureMain::IsExternalMonitorConnected() { |
| return (external_output_info_->connection == RR_Connected); |
| } |
| |
| void MonitorReconfigureMain::SortModesByResolution( |
| RROutput output, |
| vector<ResolutionSelector::Mode>* modes_out) { |
| modes_out->clear(); |
| |
| XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_info_, output); |
| for (int i = 0; i < output_info->nmode; ++i) { |
| const XRRModeInfo* mode = mode_map_[output_info->modes[i]]; |
| DCHECK(mode); |
| LOG(INFO) << "Adding mode " << mode->width << " " << mode->height |
| << " (xid " << mode->id << ")"; |
| modes_out->push_back( |
| ResolutionSelector::Mode(mode->width, mode->height, mode->name, |
| mode->id)); |
| } |
| |
| sort(modes_out->begin(), modes_out->end(), |
| ResolutionSelector::ModeResolutionComparator()); |
| |
| XRRFreeOutputInfo(output_info); |
| } |
| |
| bool MonitorReconfigureMain::SetDeviceResolution( |
| RROutput output, const XRROutputInfo* output_info, |
| const ResolutionSelector::Mode& resolution) { |
| Status s = XRRSetCrtcConfig(display_, screen_info_, output_info->crtcs[0], |
| CurrentTime, 0, 0, resolution.id, RR_Rotate_0, |
| &output, 1); |
| return (s == RRSetConfigSuccess); |
| } |
| |
| bool MonitorReconfigureMain::SetScreenResolution( |
| const ResolutionSelector::Mode& resolution) { |
| LOG(INFO) << "Setting resolution " << resolution.name.c_str() << " " |
| << resolution.width << "x" << resolution.height; |
| XRRSetScreenSize(display_, window_, |
| resolution.width, |
| resolution.height, |
| resolution.width * kInchInMm / kScreenDpi, |
| resolution.height * kInchInMm / kScreenDpi); |
| |
| return true; |
| } |
| |
| bool MonitorReconfigureMain::DisableDevice(RROutput output, |
| const XRROutputInfo* output_info) { |
| if (!output_info->crtc) |
| return true; |
| LOG(INFO) << "Disabling output " << output_info->name; |
| Status s = XRRSetCrtcConfig(display_, screen_info_, output_info->crtc, |
| CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0); |
| return (s == RRSetConfigSuccess); |
| } |
| |
| } // 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, window, |
| screen_info); |
| main_app.Run(); |
| XRRFreeScreenResources(screen_info); |
| return 0; |
| } |