blob: bfabae7731e44a35fdf073ac7cae248300a98b1f [file] [log] [blame]
// 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;
}