// Copyright 2013, University of Freiburg, // Author: Manuel Schneider #include #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" #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 > 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 >::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 >::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((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 >::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((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 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::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 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 commonModes; // for (QSet::iterator i = beamerModes.begin(); // i != beamerModes.end(); ++i) { // if ((*i)->modeFlags & RR_Interlace) continue; // for (QSet::iterator j = monitorModes.begin(); // j != monitorModes.end(); ++j) { // if ((*j)->modeFlags & RR_Interlace) continue; // 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; // } // } // }// } // // 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);