blob: 41065f4e9e283b34a59296cc4de3a8377f7142fc [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";
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;
}