// Copyright 2013, University of Freiburg, // Author: Manuel Schneider #include #include #include #include #include #include #include #include #include #include #include #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::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 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 commonModes; for (QSet::iterator i = beamerModes.begin(); i != beamerModes.end(); ++i) { for (QSet::iterator j = monitorModes.begin(); j != monitorModes.end(); ++j) { QList::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::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::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); // 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 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 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; 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); // Center widget on screenbottom this->move( width/2 - this->width()/2, height - this->height()); // Show a dialog asking if the res should be kept TimeOutDialog *t = new TimeOutDialog(5, this);/* t->setWindowModality(Qt::WindowModal);*/ t->setLabelText("Do you want to keep this resolution?"); t->setCancelButtonText("Keep"); t->exec(); // Center the dialog t->move( width/2 - this->width()/2, height/2 - this->height()); // If the dialog was not canceled revert the resolution if ( ! t->wasCanceled()) { // 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 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 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 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 this->move( ScreenSize.width()/2 - this->width()/2, ScreenSize.height() - this->height()); } // End of Revert section // Delete the backup for ( CrtcMap::iterator it = backup.begin(); it != backup.end(); ++it ) { delete[] it.value()->outputs; delete it.value(); } } /////////////////////////// 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();