blob: e0e07a3c2b51e3dbedd18297ab61436c7eff7249 [file] [log] [blame]
// Copyright (c) 2009 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/logging.h"
#include "base/string_util.h"
using std::map;
using std::sort;
using std::string;
using std::vector;
namespace monitor_reconfig {
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::DetermineOutputs() {
XRROutputInfo* first_output =
XRRGetOutputInfo(display_, screen_info_, screen_info_->outputs[0]);
XRROutputInfo* second_output =
XRRGetOutputInfo(display_, screen_info_, screen_info_->outputs[1]);
static const char* kLcdOutputName = "LVDS1";
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<XRRModeInfo*>* modes_out) {
modes_out->clear();
for (int i = 0; i < output_info.nmode; ++i)
modes_out->push_back(mode_map_[output_info.modes[i]]);
sort(modes_out->begin(), modes_out->end(), ModeResolutionComparator());
}
bool MonitorReconfigureMain::FindBestResolutions(
string* lcd_resolution,
string* external_resolution,
string* screen_resolution) {
DCHECK(lcd_resolution);
DCHECK(external_resolution);
DCHECK(screen_resolution);
vector<XRRModeInfo*> lcd_modes, external_modes;
SortModesByResolution(*lcd_output_, &lcd_modes);
SortModesByResolution(*external_output_, &external_modes);
DCHECK(!lcd_modes.empty());
DCHECK(!external_modes.empty());
if ((lcd_modes[0]->width * lcd_modes[0]->height) >=
(external_modes[0]->width * external_modes[0]->height)) {
return FindNearestResolutions(
lcd_modes, external_modes,
lcd_resolution, external_resolution, screen_resolution);
} else {
return FindNearestResolutions(
external_modes, lcd_modes,
external_resolution, lcd_resolution, screen_resolution);
}
}
bool MonitorReconfigureMain::FindNearestResolutions(
const vector<XRRModeInfo*>& larger_device_modes,
const vector<XRRModeInfo*>& smaller_device_modes,
string* larger_resolution,
string* smaller_resolution,
string* screen_resolution) {
DCHECK(larger_resolution);
DCHECK(smaller_resolution);
// Start with the best that the smaller device has to offer.
smaller_resolution->assign(smaller_device_modes[0]->name);
*screen_resolution = *smaller_resolution;
int smaller_width = smaller_device_modes[0]->width;
int smaller_height = smaller_device_modes[0]->height;
for (vector<XRRModeInfo*>::const_reverse_iterator it =
larger_device_modes.rbegin();
it != larger_device_modes.rend(); ++it) {
if ((*it)->width >= smaller_width && (*it)->height >= smaller_height) {
larger_resolution->assign((*it)->name);
return true;
}
}
LOG(WARNING) << "Failed to find a resolution from larger device "
<< "exceeding chosen resolution from smaller device ("
<< *smaller_resolution << ")";
return false;
}
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;
}
void MonitorReconfigureMain::Run() {
// If there's no external monitor connected, just use the highest resolution
// supported by the LCD.
if (!IsExternalMonitorConnected()) {
LOG(INFO) << "No external monitor connected; using max LCD resolution";
vector<XRRModeInfo*> lcd_modes;
SortModesByResolution(*lcd_output_, &lcd_modes);
CHECK(!lcd_modes.empty());
SetDeviceResolution(lcd_output_->name, lcd_modes[0]->name);
SetScreenResolution(lcd_modes[0]->name);
return;
}
string lcd_resolution, external_resolution, screen_resolution;
CHECK(FindBestResolutions(&lcd_resolution,
&external_resolution,
&screen_resolution));
SetDeviceResolution(lcd_output_->name, lcd_resolution);
SetDeviceResolution(external_output_->name, external_resolution);
SetScreenResolution(screen_resolution);
}
} // end namespace monitor_reconfig
int main(int argc, char** argv) {
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;
}