// 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 <math.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"
#define OPENSLXCONFIG "/opt/openslx/config"
#define OPENSLXCONFIG_IPKEY "SLX_PXE_CLIENT_IP"
//#define DEBUG
//_____________________________________________________________________________
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.
#ifdef DEBUG
if ( true ) {
#else
if ( ((_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 )) ) {
#endif
qDebug() << "BEAMER CONNECTED!";
// 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];
}
// Deprecated, will be deleted after confirmation from above
//setResFromConfig();
// 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;
XFree (atom_name);
break;
}
XFree (atom_name);
}
free(props);
// Intersect them by the resolution sorted, dont care about O(n³)
QList<QPair<XRRModeInfo*, XRRModeInfo*> > commonModes;
// Iterate over the modes the monitor supports
for (int i = 0; i < _outputMap[_monitor]->nmode; ++i) {
XRRModeInfo* monitorMode = _modeMap[_outputMap[_monitor]->modes[i]];
// Skip interlaces modes
if ( monitorMode->modeFlags & RR_Interlace )
continue;
// Iterate over the modes the beamer supports
for (int j = 0; j < _outputMap[_beamer]->nmode; ++j) {
XRRModeInfo* beamerMode = _modeMap[_outputMap[_beamer]->modes[j]];
// Skip interlaces modes
if ( beamerMode->modeFlags & RR_Interlace )
continue;
// Only if the modes have the same size, list them in common modes
if ( monitorMode->height == beamerMode->height
&& monitorMode->width == beamerMode->width ) {
// qDebug() << "Potential common mode"
// << monitorMode->width
// << monitorMode->height ;
// Build a sorted list of common modes in descending order
QList<QPair<XRRModeInfo*, XRRModeInfo*> >::iterator k = commonModes.begin();
for (;;++k) {
// If at the end, the mode to insert is the smallest, insert.
// This has to be first to avoid segfaults
if (k == commonModes.end()) {
commonModes.insert(k, qMakePair (monitorMode, beamerMode));
// qDebug() << "Smallest"
// << monitorMode->width
// << monitorMode->height ;
break;
}
// If the mode to insert is larger than k, insert.
if ( monitorMode->width > k->first->width) {
commonModes.insert(k, qMakePair (monitorMode, beamerMode));
// qDebug() << "Insert"
// << monitorMode->width
// << monitorMode->height ;
break;
}
// If the width is the same ...
if ( monitorMode->width == k->first->width ) {
// ... and the height is the same, the mode already exists
if ( monitorMode->height == k->first->height ) {
// qDebug() << "Already exists"
// << monitorMode->width
// << monitorMode->height ;
break;
}
// ... and the height is larger, insert.
if ( monitorMode->height > k->first->height ) {
commonModes.insert(k, qMakePair (monitorMode, beamerMode));
// qDebug() << "Insert"
// << monitorMode->width
// << monitorMode->height ;
break;
}
}
}
}
}
}
// If the beamer transmits no reliable EDID data add modes
#ifdef DEBUG
gotEDID = false;
#endif
if (gotEDID) {
qDebug() << "GOT EDID!";
// 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<QPair<XRRModeInfo*, XRRModeInfo*> >::iterator i = commonModes.begin();
i != commonModes.end(); ++i ) {
float modeAspectRatio = ((float)i->first->width / i->first->height);
if ( abs(modeAspectRatio - aspectRatio) < 0.05 ) // APPROX
_ui->comboBox->addItem(i->first->name,
QList<QVariant>()
<< QVariant((qulonglong)i->first->id)
<< QVariant((qulonglong)i->second->id));
}
} else {
qDebug() << "NO EDID!";
// Fill widget with data without AR match
// Fill widget with data
for ( QList<QPair<XRRModeInfo*, XRRModeInfo*> >::iterator i = commonModes.begin();
i != commonModes.end(); ++i ) {
qDebug() << "Insert into QComboBox"
<< i->first->width << "x" << i->first->height
<< "\t\tBEAMER:" << QVariant((qulonglong)i->first->id).toULongLong()
<< " \tMONITOR:" << QVariant((qulonglong)i->second->id).toULongLong();
_ui->comboBox->addItem(i->first->name,
QList<QVariant>()
<< QVariant((qulonglong)i->first->id)
<< QVariant((qulonglong)i->second->id));
}
}
// Remove borders and stuff
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): Future 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
qDebug() << "_MODES_";
for (int i = 0; i < _screenResources->nmode; ++i) {
_modeMap.insert(
_screenResources->modes[i].id,
&_screenResources->modes[i]);
qDebug() << _screenResources->modes[i].id << "\t"
<< _screenResources->modes[i].width << "x" << _screenResources->modes[i].height;
}
// 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);
}
//// Deprecated, will be deleted after confirmation from above
////___________________________________________________________________________
//void Widget::setResFromConfig() {
// // Try to get the ip of the machine
// QSettings SLXsettings(OPENSLXCONFIG, QSettings::NativeFormat);
// QString IP;
// if ( SLXsettings.contains(OPENSLXCONFIG_IPKEY) ) {
// IP = SLXsettings.value(OPENSLXCONFIG_IPKEY).toString();
// qDebug() << "IP is" << IP;
// } else
// qDebug() << "No IP found!";
//
// // remove the ' around the ip
// IP.remove('\'');
//
// // Try to get beamer config related to the ip of the machine
// 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(IP) ) {
// // First get a useful representation
// unsigned int width, height;
// QStringList modeAsStrings =
// settings.value(IP)
// .toString().split("x", QString::SkipEmptyParts);
// qDebug() << "Found Ip setting" << modeAsStrings;
// 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;
// }
//
// qDebug() << "Found modes are" << m1 << m2;
// qDebug() << "Witdth" << width ;
// qDebug() << "Height" << height ;
//
// // Apply the mode via xrandr
// QProcess p;
// QStringList arguments;
// arguments << "--output" << _outputMap[_monitor]->name
// << "--mode" << settings.value(IP).toString()
// << "--output" << _outputMap[_beamer]->name
// << "--mode" << settings.value(IP).toString()
// << "--same-as" <<_outputMap[_monitor]->name;
// qDebug() << arguments ;
//
// p.start("xrandr", arguments);
// p.waitForFinished();
//
// qDebug() << p.readAllStandardOutput();
// qDebug() << p.readAllStandardError();
//
//
////
//// // 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 mode m1 " << m1;
//// qDebug() << XRRSetCrtcConfig(_display,
//// _screenResources,
//// _outputMap[_monitor]->crtc,
//// CurrentTime,
//// 0, 0,
//// m1,
//// RR_Rotate_0,
//// &_monitor, 1);
////
//// qDebug() << "Apply mode m2 " << m2;
//// qDebug() << XRRSetCrtcConfig(_display,
//// _screenResources,
//// _outputMap[_beamer]->crtc,
//// CurrentTime,
//// 0, 0,
//// m2,
//// RR_Rotate_0,
//// &_beamer, 1);
// }
// settings.endGroup();
//}
//___________________________________________________________________________
void Widget::handleButton()
{
// Backup the crtcinfos
qDebug() << "Backup the crtc infos";
CrtcMap backup;
for ( CrtcMap::iterator it = _crtcMap.begin();
it != _crtcMap.end(); ++it ) {
qDebug() << " Key is " << it.key();
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;
qDebug() << " Mode is " << backup[it.key()]->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];
}
}
// Get the modes from QComboBox
QList<QVariant> modes = _ui->comboBox->itemData( _ui->comboBox->currentIndex()).toList();
XRRModeInfo* monitorMode = _modeMap[modes[0].toULongLong()];
XRRModeInfo* beamerMode = _modeMap[modes[1].toULongLong()];
// Set screensize
uint width = monitorMode->width;
uint height = monitorMode->height;
qDebug() << " Set screensize to" << width << height;
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" << monitorMode->id << beamerMode->id;
XRRSetCrtcConfig(_display,
_screenResources,
_outputMap[_monitor]->crtc,
CurrentTime,
0, 0, monitorMode->id,
RR_Rotate_0,
&_monitor, 1);
XRRSetCrtcConfig(_display,
_screenResources,
_outputMap[_beamer]->crtc,
CurrentTime,
0, 0, beamerMode->id,
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 *keepDialog = new TimeOutDialog(15, this);
keepDialog->setWindowTitle(" ");
keepDialog->setLabelText("Do you want to keep this resolution?");
keepDialog->setCancelButtonText("Keep");
keepDialog->move( width/2 - this->width()/2, height/2 - this->height());
keepDialog->exec();
// If the dialog was not canceled revert the resolution
qDebug() << "// If the dialog was not canceled revert the resolution";
if ( ! keepDialog->wasCanceled()) {
// First disconnect all crts to avoid conflicts
qDebug() << "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 ) {
// Dangerzone. Only access existing modes!
if ( it.value()->mode != None ) {
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));
}
}
qDebug() << "screensize is" << ScreenSize;
// Set screensize
qDebug() << "Apply 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 ) {
qDebug() << "CRTC" << it.key() << "MODE" << it.value()->mode;
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();
// // 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();
// }
// // 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);
// }
// }
// 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);
// // 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[_monitor]->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) {
// if ((*i)->modeFlags & RR_Interlace) continue;
// for (QSet<XRRModeInfo*>::iterator j = monitorModes.begin();
// j != monitorModes.end(); ++j) {
// if ((*j)->modeFlags & RR_Interlace) continue;
// 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;
// }
// }
// }// }
// // 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
// RRMode m1,m2;
// qDebug() << "// Find a mode that matches the string";
// for (int i = 0; i < _outputMap[_monitor]->nmode; ++i){
// if ( width == _modeMap[_outputMap[_monitor]->modes[i]]->width
// && height == _modeMap[_outputMap[_monitor]->modes[i]]->height
// && !(_modeMap[_outputMap[_monitor]->modes[i]]->modeFlags & RR_Interlace) ) // skip interlaced modes
// m1 = _modeMap[_outputMap[_monitor]->modes[i]]->id;
// }
// qDebug() << "// Find a mode that matches the string";
// for (int i = 0; i < _outputMap[_beamer]->nmode; ++i){
// if ( width == _modeMap[_outputMap[_beamer]->modes[i]]->width
// && height == _modeMap[_outputMap[_beamer]->modes[i]]->height
// && !(_modeMap[_outputMap[_beamer]->modes[i]]->modeFlags & RR_Interlace) ) // skip interlaced modes
// m2 = _modeMap[_outputMap[_beamer]->modes[i]]->id;
// }
// struct ifaddrs * ifAddrStruct=NULL;
// struct ifaddrs * ifa=NULL;
// void * tmpAddrPtr=NULL;
// QString ipV4;
// qDebug() << "Ip is" << 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);