// Copyright 2013, University of Freiburg,
// Author: Manuel Schneider <ms1144>
#include <stdio.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <iostream>
#include <algorithm>
#include <QString>
#include <QDebug>
#include <QSet>
#include "widget.h"
#include "ui_widget.h"
#include "timeoutdialog.h"
#define INTERFACE "eth0"
#define GROUP_SPECIFIC "SpecificSettings"
//___________________________________________________________________________
Widget::Widget(QWidget *parent) :
QWidget(parent),
_ui(new Ui::Widget)
{
_ui->setupUi(this);
// Get initial data (to be freed)
_display = XOpenDisplay(NULL);
_screenResources = XRRGetScreenResourcesCurrent(_display, DefaultRootWindow(_display));
// Get the information about the X elements
updateScreenResources();
switch ( _connectedOutputList.size() ){
/*************************************************************************/
case 1:// In case of one connected output - xrandr --auto
qDebug() << "Normal output";
exit(0);
break;
/*************************************************************************/
case 2: // In case of two connected outputs
// Check if one of the connected outputs is a beamer. This program only
// adresses cases in which eiter of the outouts is a beamer. Meaning
// either output one has dimension zero or output tow has no dimension.
// The case of two beamers is not covered.
if (true/*((_outputMap[_connectedOutputList[0]]->mm_width == 0
&& _outputMap[_connectedOutputList[0]]->mm_height == 0 )
&& ! (_outputMap[_connectedOutputList[1]]->mm_width == 0
&& _outputMap[_connectedOutputList[1]]->mm_height == 0 ))
|| ( ! (_outputMap[_connectedOutputList[0]]->mm_width == 0
&& _outputMap[_connectedOutputList[0]]->mm_height == 0 )
&& (_outputMap[_connectedOutputList[1]]->mm_width == 0
&& _outputMap[_connectedOutputList[1]]->mm_height == 0 ))*/) {
std::cout << "BEAMER CONNECTED!" << std::endl;
// Get a human readable reference
if (_outputMap[_connectedOutputList[0]]->mm_width == 0
&& _outputMap[_connectedOutputList[0]]->mm_height == 0 ) {
_beamer = _connectedOutputList[0];
_monitor = _connectedOutputList[1];
} else {
_beamer = _connectedOutputList[1];
_monitor = _connectedOutputList[0];
}
// Get the ip adress of the interface
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
QString ipV4;
getifaddrs(&ifAddrStruct);
// Iterate through the adresses.
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
// If the address is IP V4 and the interface is _if
if (ifa ->ifa_addr->sa_family==AF_INET
&& ( strcmp(ifa->ifa_name, INTERFACE) == 0) ) {
// Get the IP
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
// convert to readable form
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
ipV4 = addressBuffer;
}
}
// clean up
if (ifAddrStruct!=NULL)
freeifaddrs(ifAddrStruct);
// Open Qsettings file
QSettings settings(QCoreApplication::arguments()[1], QSettings::NativeFormat);
// Find any information saved about this ip. This config is assumed
// to be tested on this system and will be applied immediately.
settings.beginGroup(GROUP_SPECIFIC);
if ( settings.contains(ipV4) ) {
qDebug() << ipV4 ;
// First get a useful representation
unsigned int width, height;
QStringList modeAsStrings =
settings.value(ipV4)
.toString().split("x", QString::SkipEmptyParts);
width = modeAsStrings.at(0).toInt();
height = modeAsStrings.at(1).toInt();
// Find a mode that matches the string
// No sanity check here as the modes have to exist and be tested
RRMode m1,m2;
for (int i = 0; i < _outputMap[_monitor]->nmode; ++i){
if ( width == _modeMap[_outputMap[_monitor]->modes[i]]->width
&& height == _modeMap[_outputMap[_monitor]->modes[i]]->height )
m1 = _modeMap[_outputMap[_monitor]->modes[i]]->id;
}
for (int i = 0; i < _outputMap[_beamer]->nmode; ++i){
if ( width == _modeMap[_outputMap[_beamer]->modes[i]]->width
&& height == _modeMap[_outputMap[_beamer]->modes[i]]->height )
m2 = _modeMap[_outputMap[_beamer]->modes[i]]->id;
}
// Set screensize
XRRSetScreenSize(_display, DefaultRootWindow(_display),
width,
height,
25.4 * width / 96, // standard dpi that X uses
25.4 * height / 96); // standard dpi that X uses
// Apply the modes
XRRSetCrtcConfig(_display,
_screenResources,
_outputMap[_monitor]->crtc,
CurrentTime,
0, 0,
m1,
RR_Rotate_0,
&_monitor, 1);
XRRSetCrtcConfig(_display,
_screenResources,
_outputMap[_beamer]->crtc,
CurrentTime,
0, 0,
m2,
RR_Rotate_0,
&_beamer, 1);
}
settings.endGroup();
// Check if the beamer transmitted reliable data.
bool gotEDID = false;
int nprop;
Atom *props = XRRListOutputProperties(_display, _beamer, &nprop);
for (int i = 0; i < nprop; ++i) {
char *atom_name = XGetAtomName (_display, props[i]);
if ( strcmp (atom_name, "EDID") == 0) {
gotEDID = true;
break;
}
}
free(props);
// // TO BE DONE VIA BASH
// // Iterate over the modes in the config file
// for (QList<QString>::const_iterator it =
// Config::inst()->getModeLines().begin();
// it != Config::inst()->getModeLines().end(); ++it) {
// // Add mode to xrandr
// QProcess p;
// QStringList arguments;
// arguments << "--current" << "--newmode"
// << it->split(" ", QString::SkipEmptyParts);
// p.start("xrandr", arguments);
// p.waitForFinished();
// }
// Get the names of the modes and intersect
QSet<XRRModeInfo*> beamerModes, monitorModes;
for (int i = 0; i < _outputMap[_beamer]->nmode; ++i)
beamerModes.insert(_modeMap[_outputMap[_beamer]->modes[i]]);
for (int i = 0; i < _outputMap[_beamer]->nmode; ++i)
monitorModes.insert(_modeMap[_outputMap[_monitor]->modes[i]]);
// Intersect them by the resolution sorted
// dont care about O(n³)
QList<XRRModeInfo*> commonModes;
for (QSet<XRRModeInfo*>::iterator i = beamerModes.begin();
i != beamerModes.end(); ++i) {
for (QSet<XRRModeInfo*>::iterator j = monitorModes.begin();
j != monitorModes.end(); ++j) {
QList<XRRModeInfo*>::iterator k = commonModes.begin();
for (;;) {
if (k == commonModes.end()){
commonModes.insert(k, *i);
break;
}
if ( (*i)->width == (*k)->width ) {
if ( (*i)->height == (*k)->height )
break;
if ( (*i)->height > (*k)->height ) {
commonModes.insert(k, *i);
break;
}
}
if ( (*i)->width > (*k)->width ) {
commonModes.insert(k, *i);
break;
}
++k;
}
}
}
// If the beamer transmits no reliable EDID data add modes
if (false/*gotEDID*/) {
std::cout << "GOT EDID!" << std::endl;
// Extract the preferred mode of the beamer
RRMode preferredBeamerModeId;
for (int i = 0; i < _outputMap[_beamer]->nmode; ++i) {
if (i < _outputMap[_beamer]->npreferred) {
preferredBeamerModeId = _outputMap[_beamer]->modes[i];
break;
}
}
// Compute the aspect ratio of the beamer
float aspectRatio = (float)_modeMap[preferredBeamerModeId]->width
/ _modeMap[preferredBeamerModeId]->height;
// Fill widget with data
for ( QList<XRRModeInfo*>::iterator i = commonModes.begin();
i != commonModes.end(); ++i ) {
float modeAspectRatio = ((float)(*i)->width / (*i)->height);
if ( modeAspectRatio == aspectRatio ) // TODO APPROX
_ui->comboBox->addItem((*i)->name);
}
// // TO BE DONE VIA BASH
// // Iterate over all modes and add them to the outputs,
// // if the AR matches.
// for(ModeMap::iterator it = _modeMap.begin();
// it != _modeMap.end(); ++it) {
// float MODEAR = ((float)(*it)->width / (*it)->height);
// if ( MODEAR == AR ) {
// XRRAddOutputMode(_display, _beamer, (*it)->id);
// XRRAddOutputMode(_display, _monitor, (*it)->id);
// }
// }
} else {
std::cout << "NO EDID!" << std::endl;
// Fill widget with data without AR match
// Fill widget with data
for ( QList<XRRModeInfo*>::iterator i = commonModes.begin();
i != commonModes.end(); ++i ) {
_ui->comboBox->addItem((*i)->name);
}
// TO BE DONE VIA BASH
// // Iterate over all modes and add them to the outputs
// for(ModeMap::iterator it = _modeMap.begin();
// it != _modeMap.end(); ++it) {
// XRRAddOutputMode(_display, _beamer, (*it)->id);
// XRRAddOutputMode(_display, _monitor, (*it)->id);
// }
}
// Remove borders and stuff COMMENT FOR DEBUIGGIN
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
// setStyleSheet("background:transparent;");
setAttribute(Qt::WA_TranslucentBackground);
// Resize widget to its content
resize(sizeHint());
// Center dialog on screenbottom
const QRect desktopRect = QApplication::desktop()->screenGeometry();
this->move( desktopRect.width()/2-this->width()/2,
desktopRect.height()-this->height());
// Connect button signal to appropriate slot
connect(_ui->pushButton, SIGNAL(clicked()), this, SLOT(handleButton()));
} // End of the beamer section
/*********************************************************************/
// If NEITHER of the outputs is a beamer (likely dualscreen setup)
else {
// TODO(manuel): Furture feature. Setup dualscreen
qDebug() << "Dual output";
exit(0);
}
break;
/*************************************************************************/
default:
// If there are more than 3 outputs
// its up to the user. Quit.
qDebug() << ">2 outputs. Quit.";
exit(0);
break;
}
/*************************************************************************/
}
//___________________________________________________________________________
void Widget::updateScreenResources()
{
// Clear the modemap (nothing to be freed, stored in screenResources)
_modeMap.clear();
// Create the modemap
for (int i = 0; i < _screenResources->nmode; ++i) {
_modeMap.insert(
_screenResources->modes[i].id,
&_screenResources->modes[i]);
}
// Clear the crtmap
for (CrtcMap::iterator it = _crtcMap.begin();
it != _crtcMap.end();) {
XRRFreeCrtcInfo(*it);
it = _crtcMap.erase(it);
}
// Create crtcMap
for (int i = 0; i < _screenResources->ncrtc; ++i) {
XRRCrtcInfo * info = XRRGetCrtcInfo(
_display,
_screenResources,
_screenResources->crtcs[i]);
_crtcMap.insert(
_screenResources->crtcs[i],
info);
}
// Clear the outputmap
for (OutputMap::iterator it = _outputMap.begin();
it != _outputMap.end();) {
XRRFreeOutputInfo(*it);
it = _outputMap.erase(it);
}
// Create outputmap and connectedOutputMap
for (int i = 0; i < _screenResources->noutput; ++i) {
XRROutputInfo* info = XRRGetOutputInfo(
_display,
_screenResources,
_screenResources->outputs[i]);
_outputMap.insert(
_screenResources->outputs[i],
info);
// Store the connected ones separate
if ( info->connection == RR_Connected )
_connectedOutputList.push_back(_screenResources->outputs[i]);
}
}
//___________________________________________________________________________
Widget::~Widget()
{
delete _ui;
XCloseDisplay(_display);
XRRFreeScreenResources(_screenResources);
}
void Widget::handleButton()
{
// Backup the crtcinfos
qDebug() << "// Backup the crtcinfos";
CrtcMap backup;
for ( CrtcMap::iterator it = _crtcMap.begin();
it != _crtcMap.end(); ++it ) {
backup[it.key()] = new XRRCrtcInfo;
backup[it.key()]->x = it.value()->x;
backup[it.key()]->y = it.value()->y;
backup[it.key()]->mode = it.value()->mode;
backup[it.key()]->rotation = it.value()->rotation;
backup[it.key()]->noutput = it.value()->noutput;
backup[it.key()]->outputs = new RROutput[it.value()->noutput];
for (int i = 0; i < it.value()->noutput; ++i) {
backup[it.key()]->outputs[i] = it.value()->outputs[i];
}
}
// First get a useful representation
qDebug() << "// First get a useful representation";
unsigned int width, height;
QStringList modeAsStrings = _ui->comboBox->currentText().split("x", QString::SkipEmptyParts);
width = modeAsStrings.at(0).toInt();
height = modeAsStrings.at(1).toInt();
// Find a mode that matches the string
qDebug() << "// Find a mode that matches the string";
RRMode m1,m2;
for (int i = 0; i < _outputMap[_monitor]->nmode; ++i){
if ( width == _modeMap[_outputMap[_monitor]->modes[i]]->width
&& height == _modeMap[_outputMap[_monitor]->modes[i]]->height )
m1 = _modeMap[_outputMap[_monitor]->modes[i]]->id;
}
for (int i = 0; i < _outputMap[_beamer]->nmode; ++i){
if ( width == _modeMap[_outputMap[_beamer]->modes[i]]->width
&& height == _modeMap[_outputMap[_beamer]->modes[i]]->height )
m2 = _modeMap[_outputMap[_beamer]->modes[i]]->id;
}
// Set screensize
qDebug() << "// Set screensize";
XRRSetScreenSize(_display, DefaultRootWindow(_display),
width,
height,
25.4 * width / 96, // standard dpi that X uses
25.4 * height / 96); // standard dpi that X uses
// Apply the modes
qDebug() << "// Apply the modes";
XRRSetCrtcConfig(_display,
_screenResources,
_outputMap[_monitor]->crtc,
CurrentTime,
0, 0, m1,
RR_Rotate_0,
&_monitor, 1);
XRRSetCrtcConfig(_display,
_screenResources,
_outputMap[_beamer]->crtc,
CurrentTime,
0, 0, m2,
RR_Rotate_0,
&_beamer, 1);
// Center widget on screenbottom
qDebug() << "// Center widget on screenbottom";
this->move( width/2 - this->width()/2, height - this->height());
// Show a dialog asking if the res should be kept
qDebug() << "// Show a dialog asking if the res should be kept";
TimeOutDialog *t = new TimeOutDialog(15, this);
t->setWindowTitle(" ");
t->setLabelText("Do you want to keep this resolution?");
t->setCancelButtonText("Keep");
t->exec();
// Center the dialog
qDebug() << "// Center the dialog";
t->move( width/2 - this->width()/2, height/2 - this->height());
// If the dialog was not canceled revert the resolution
qDebug() << "// If the dialog was not canceled revert the resolution";
if ( ! t->wasCanceled()) {
// First disconnect all crts to avoid conflicts
qDebug() << "// First disconnect all crts to avoid conflicts";
for ( CrtcMap::iterator it = _crtcMap.begin();
it != _crtcMap.end(); ++it ) {
XRRSetCrtcConfig(_display,
_screenResources,
it.key(),
CurrentTime,
0, 0,
None,
RR_Rotate_0,
NULL, 0);
}
// Then calc backed up screensize
qDebug() << "// Then calc backed up screensize";
QSize ScreenSize(0,0);
for ( CrtcMap::iterator it = backup.begin();
it != backup.end(); ++it ) {
ScreenSize.setWidth(
std::max((uint)ScreenSize.width(),
it.value()->x+_modeMap[it.value()->mode]->width));
ScreenSize.setHeight(
std::max((uint)ScreenSize.height(),
it.value()->y+_modeMap[it.value()->mode]->height));
}
// Set screensize
qDebug() << "// Set screensize";
XRRSetScreenSize(_display, DefaultRootWindow(_display),
ScreenSize.width(),
ScreenSize.height(),
25.4 * ScreenSize.width() / 96, // standard dpi that X uses
25.4 * ScreenSize.height() / 96); // standard dpi that X uses
// Then apply the backup
qDebug() << "Then apply the backup";
for ( CrtcMap::iterator it = backup.begin();
it != backup.end(); ++it ) {
XRRSetCrtcConfig(_display,
_screenResources,
it.key(),
CurrentTime,
it.value()->x,
it.value()->y,
it.value()->mode,
it.value()->rotation,
it.value()->outputs,
it.value()->noutput);
}
// Again center dialog on screenbottom
qDebug() << "Again center dialog on screenbottom";
this->move( ScreenSize.width()/2 - this->width()/2,
ScreenSize.height() - this->height());
} // End of Revert section
// Delete the backup
qDebug() << "Delete the backup";
for ( CrtcMap::iterator it = backup.begin();
it != backup.end(); ++it ) {
delete[] it.value()->outputs;
delete it.value();
}
// Intenal settings changed. Update!
updateScreenResources();
}
/////////////////////////// CODE PAPIERKORB /////////////////////////////////
// // Apply the mode via xrandr
// QProcess p;
// QStringList arguments;
// arguments << "--output" << _outputMap[_monitor]->name
// << "--mode" << _ui->comboBox->currentText()
// << "--output" << _outputMap[_beamer]->name
// << "--mode" << _ui->comboBox->currentText()
// << "--same-as" <<_outputMap[_monitor]->name;
// p.start("xrandr", arguments);
// p.waitForFinished();