// Copyright 2013, University of Freiburg, // Author: Manuel Schneider #include #include #include #include #include "widget.h" #include "ui_widget.h" #include "timeoutdialog.h" #include "math.h" //______________________________________________________________________________ Widget::Widget(bool testMode, 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(); if (testMode) { while (_connectedOutputList.size() > 2) { _connectedOutputList.pop_front(); } } switch ( _connectedOutputList.size() ){ /*************************************************************************/ case 1:// In case of one connected output - xrandr --auto qDebug() << "One connected output"; exit(0); break; case 2: // In case of two connected outputs qDebug() << "Two connected outputs"; // Check if we are in clone mode if (testMode || cloneMode()) { qDebug() << "Dual output with cloned screen!"; // TODO make that mess nice and clean double_t w0 = _outputMap[_connectedOutputList[0]]->mm_width; double_t h0 = _outputMap[_connectedOutputList[0]]->mm_height; double_t w1 = _outputMap[_connectedOutputList[1]]->mm_width; double_t h1 = _outputMap[_connectedOutputList[1]]->mm_height; // Get a human readable reference if (w0 == 0 && h0 == 0) { _beamer = _connectedOutputList[0]; _monitor = _connectedOutputList[1]; } else if (w1 == 0 && h1 == 0) { _beamer = _connectedOutputList[1]; _monitor = _connectedOutputList[0]; } else { double_t d0 = sqrt((pow(w0, 2) + pow(h0, 2))); double_t d1 = sqrt((pow(w1, 2) + pow(h1, 2))); if (d0 > d1) { _beamer = _connectedOutputList[0]; _monitor = _connectedOutputList[1]; } else { _beamer = _connectedOutputList[1]; _monitor = _connectedOutputList[0]; } } // 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 interlace 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 interlace 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 ) { // 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)); break; } // If the mode to insert is larger than k, insert. if ( monitorMode->width > k->first->width) { commonModes.insert(k, qMakePair (monitorMode, beamerMode)); 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 ) { break; } // ... and the height is larger, insert. if ( monitorMode->height > k->first->height ) { commonModes.insert(k, qMakePair (monitorMode, beamerMode)); break; } } } } } } // 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); // If the beamer transmits no reliable EDID data add modes #ifdef QT_DEBUG gotEDID = false; #endif if (gotEDID) { qDebug() << "GOT EDID!"; // Extract the preferred mode of the beamer RRMode preferredBeamerModeId = 0; 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 << "(" << i->first->id << ")"; _ui->comboBox->addItem(i->first->name, QList() << QVariant((qulonglong)i->first->id) << QVariant((qulonglong)i->second->id)); } } // Set the current resolution highlighted QString n = _modeMap[_crtcMap[_outputMap[_monitor]->crtc]->mode]->name; int index = _ui->comboBox->findText(n); _ui->comboBox->setCurrentIndex(index); // Remove borders and stuff setWindowFlags(Qt::Widget | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); //setStyleSheet("background:transparent;"); //setAttribute(Qt::WA_TranslucentBackground); // Resize widget to its content resize(sizeHint()); show(); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(bringToTopTimer())); timer->start(1000); // 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())); } /*********************************************************************/ // If NEITHER of the outputs is a beamer (likely dualscreen setup) else { // TODO(manuel): Future feature. Setup dualscreen qDebug() << "Dual output with extended screen!"; exit(0); } break; /*************************************************************************/ default: // If there are more than 3 outputs // its up to the user. Quit. qDebug() << "More than two outputs. Quit."; exit(0); break; } /*************************************************************************/ } void Widget::bringToTopTimer() { if (_ui->comboBox->view()->isVisible()) return; raise(); } //______________________________________________________________________________ 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); } //______________________________________________________________________________ void Widget::handleButton(){ /*************************** Backup the crtcinfos ***************************/ qDebug() << "Backup the crtc infos"; 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]; } } /**************************** Apply the resolution **************************/ // Get the modes which has to be applied from QComboBox QList modes = _ui->comboBox->itemData( _ui->comboBox->currentIndex()).toList(); XRRModeInfo* monitorMode = _modeMap[modes[0].toULongLong()]; XRRModeInfo* beamerMode = _modeMap[modes[1].toULongLong()]; // First disconnect all crts to avoid conflicts for(CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); ++it) { Status st = XRRSetCrtcConfig(_display, _screenResources, it.key(), CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0); qDebug() << "Disconnecting" << it.key() << ":" << st; } // Set screensize XRRSetScreenSize(_display, DefaultRootWindow(_display), monitorMode->width, monitorMode->height, 25.4 * monitorMode->width / 96, // standard dpi that X uses 25.4 * monitorMode->height / 96); // standard dpi that X uses // Apply the modes Status stMon = XRRSetCrtcConfig(_display, _screenResources, _outputMap[_monitor]->crtc, CurrentTime, 0, 0, monitorMode->id, RR_Rotate_0, &_monitor, 1); Status stBem = XRRSetCrtcConfig(_display, _screenResources, _outputMap[_beamer]->crtc, CurrentTime, 0, 0, beamerMode->id, RR_Rotate_0, &_beamer, 1); // Sync... whatever... XSync (_display, False); qDebug() << "Monitor set:" << stMon << ", beamer set: " << stBem; // Center widget on screenbottom this->move( monitorMode->width/2 - this->width()/2, monitorMode->height - this->height() ); // Set the mouse pointer in the middle of the screen QCursor::setPos(monitorMode->width/2, monitorMode->height/2); /*************************** ASK for confirmtion ****************************/ // Show a dialog asking if the res should be kept TimeOutDialog keepDialog(15, this); keepDialog.setWindowModality(Qt::ApplicationModal); keepDialog.setWindowTitle(" "); keepDialog.setLabelText(trUtf8("Do you want to keep this resolution?")); keepDialog.setCancelButtonText(trUtf8("Keep")); keepDialog.move(monitorMode->width/2 - this->width()/2, monitorMode->height/2 - this->height()); keepDialog.show(); while (keepDialog.isActive()) { QCoreApplication::processEvents(); } // If the dialog was not canceled revert the resolution if ( !keepDialog.wasCanceled()) { qDebug() << "User did not cancel timeout, reverting!"; /**************************** Apply the backup ****************************/ // 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 ) { // 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)); } } // Set screensize XRRSetScreenSize(_display, DefaultRootWindow(_display), ScreenSize.width(), ScreenSize.height(), 25.4 * ScreenSize.width() / 96, // dpi used by X 25.4 * ScreenSize.height() / 96); // dpi used by X // 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); } // Sync... whatever... XSync (_display, False); // center dialog on screenbottom qDebug() << "Again center dialog on screenbottom"; this->move( ScreenSize.width()/2 - this->width()/2, ScreenSize.height() - this->height()); // Set the mouse pointer in the middle of the screen QCursor::setPos(monitorMode->width/2, monitorMode->height/2); } // 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(); } bool Widget::cloneMode() { bool cloneMode = true; for (CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); it++) { XRRCrtcInfo* crtc = it.value(); // check if x starts on upper left corner if (crtc->x != 0 || crtc->y != 0) { cloneMode = false; } qDebug() << "width: " << crtc->width << "height: " << crtc->height << "x: " << crtc->x << "y: " << crtc->y << "mode: " << crtc->mode; } return cloneMode; } ////////////////////////////////////////////////////////////////////////////////