diff options
Diffstat (limited to 'src/widget.cpp')
-rw-r--r-- | src/widget.cpp | 453 |
1 files changed, 311 insertions, 142 deletions
diff --git a/src/widget.cpp b/src/widget.cpp index 751c69d..52ae889 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -7,6 +7,54 @@ #include <QDebug> #include <QtWidgets/QAction> +class ScreenWidget : public QWidget +{ +public: + ScreenWidget(float scale, QWidget *parent = nullptr) : QWidget(parent), scale(scale) + { + QWidget::setStyleSheet(".QWidget { border-radius: 3px;border: 2px solid black; }"); + QWidget::setLayout(new QVBoxLayout(this)); + } +protected: + float scale; + void resizeEvent(QResizeEvent *event) override + { + event->accept(); + float diff = (float(event->size().width()) / float(event->size().height())) - scale; + if(diff > .01f) { + QWidget::resize(int(float(event->size().height()) * scale), event->size().height()); + } else if (diff < -.01f) { + QWidget::resize(event->size().width(), int(float(event->size().width()) / scale)); + } + } +}; + +class AdvancedScreen +{ +public: + ScreenWidget *screen; + QComboBox *cboResolution; + QSize desiredResolution; +}; + +class AdvancedOutput +{ +public: + AdvancedOutput(const ScreenInfo& si) : info(si) {} + ScreenInfo info; + QLabel *assignmentLabel; + QLabel *rowLabel; + QComboBox *cboPosition; +}; +Q_DECLARE_METATYPE(AdvancedOutput*) + +static void addBoldListener(QComboBox *combo) +{ + combo->connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) { + combo->setFont(qvariant_cast<QFont>(combo->itemData(index, Qt::FontRole))); + }); +} + //______________________________________________________________________________ Widget::Widget(QWidget *parent) : QWidget(parent), @@ -14,16 +62,20 @@ Widget::Widget(QWidget *parent) : { _ui->setupUi(this); setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); -} - -void Widget::bringToTopTimer() -{ - auto combos = this->findChildren<QComboBox*>(); - for (auto combo : combos) { - if (combo->view()->isVisible()) - return; - } - raise(); + QTimer *top = new QTimer(this); + connect(top, &QTimer::timeout, [=]() { + auto combos = this->findChildren<QComboBox*>(); + for (auto combo : combos) { + if (combo->view()->isVisible()) + return; + } + raise(); + }); + top->start(1000); + addBoldListener(_ui->cboCloneResolution); + addBoldListener(_ui->cboDualLeft); + addBoldListener(_ui->cboDualRight); + connectButtons(); } //______________________________________________________________________________ @@ -35,7 +87,7 @@ void Widget::showEvent(QShowEvent *event) { QWidget::showEvent(event); ScreenMode mode = ScreenSetup::inst()->getCurrentMode(); - if (ScreenSetup::inst()->getOutputCount() == 2 || mode == ScreenMode::Dual) { + if (ScreenSetup::inst()->getOutputCount() >= 2 || mode == ScreenMode::Dual) { if (_ui->tabWidget->widget(1) != _ui->tabDual) { _ui->tabWidget->insertTab(1, _ui->tabDual, tr("Dual Screen")); } @@ -59,85 +111,212 @@ void Widget::showEvent(QShowEvent *event) initControls(); } +static void fillCombo(QComboBox *combo, const ResolutionVector &resolutions, const QSize &preselected, const QSize &preferred = QSize()) +{ + combo->clear(); + QFont font = combo->font(); + font.setBold(false); + combo->setFont(font); + bool foundPreselected = false; + for (auto res : resolutions) { + combo->addItem(QCoreApplication::tr("%1x%2").arg(res.width()).arg(res.height()), QVariant(res)); + if (res == preferred) { + QFont boldFont(font); + boldFont.setBold(true); + combo->setItemData(combo->count() - 1, boldFont, Qt::FontRole); + } else { + combo->setItemData(combo->count() - 1, font, Qt::FontRole); + } + if (res == preselected) { + combo->setCurrentIndex(combo->count() - 1); + foundPreselected = true; + } else if (!foundPreselected && res == preferred) { + combo->setCurrentIndex(combo->count() - 1); + } + } + combo->setFont(qvariant_cast<QFont>(combo->currentData(Qt::FontRole))); +} + +static bool dualHeadLess(const ScreenInfo &a, const ScreenInfo &b) +{ + if (a.position != -1 && a.position < b.position) + return true; + return a.position == b.position && a.output < b.output; +} + +void Widget::comboBold(int index) +{ + QComboBox *cbo = qobject_cast<QComboBox*>(QObject::sender()); + if (cbo != nullptr) { + cbo->setFont(qvariant_cast<QFont>(cbo->itemData(index, Qt::FontRole))); + } +} + void Widget::initControls() { + if (_ui->btnDualSwap->isChecked()) { + _ui->btnDualSwap->toggle(); + } + auto resolutions = ScreenSetup::inst()->getVirtualResolutions(); + auto screenMap = ScreenSetup::inst()->getScreenPositions(); auto modes = ScreenSetup::inst()->getCommonModes(); - for (auto mode : modes) { - _ui->cboCloneResolution->addItem(QString::asprintf("%ux%u", mode.first, mode.second)); + auto screenList = screenMap.values(); + qSort(screenList.begin(), screenList.end(), dualHeadLess); + // Clone + QSize preferredClone; + for (auto screen : screenList) { + if (!screen.preferredResolution.isEmpty()) { + preferredClone = screen.preferredResolution; + if (screen.isProjector) + break; + } } + fillCombo(_ui->cboCloneResolution, modes, screenList[0].currentResolution, preferredClone); + // Dual + if (screenList.size() >= 2) { + auto lists = QList<QComboBox*>({_ui->cboDualLeft, _ui->cboDualRight}); + int j = 0; + for (int i = 0; i < screenList.size() && j < 2; ++i) { + fillCombo(lists[j], screenList[i].modes, screenList[i].currentResolution, screenList[i].preferredResolution); + lists[j]->setProperty("output", screenList[i].output); + QLabel *sl = ( j == 0 ? _ui->lblDualLeft : _ui->lblDualRight ); + sl->setText(screenList[i].output + "\n" + screenList[i].name); + if (screenList[i].currentResolution.isEmpty()) + continue; + ++j; + } + } + + // // Clear advanced controls - auto widgets = _ui->advancedCombos->findChildren<QWidget*>(); - for (auto w : widgets) { - _ui->advancedCombos->removeWidget(w); - w->deleteLater(); + for (auto e : _advancedScreens) { + delete e; + } + for (auto e : _advancedOutput) { + if (e->assignmentLabel->layout() == nullptr) { + e->assignmentLabel->deleteLater(); + } + delete e; + } + _advancedScreens.clear(); + _advancedOutput.clear(); + QLayoutItem *w; + while ((w = _ui->advancedCombos->takeAt(0)) != nullptr) { + if (w->widget() != nullptr) { + w->widget()->deleteLater(); + } + delete w; + } + while ((w = _ui->advancedContainer->takeAt(0)) != nullptr) { + if (w->widget() != nullptr) { + w->widget()->deleteLater(); + } + delete w; } // Create new - QHash<QString, int> positions = ScreenSetup::inst()->getScreenPositions(); - ScreenSetup::ResolutionVector resolutions = ScreenSetup::inst()->getVirtualResolutions(); - int row = 0; - for (auto it = positions.begin(); it != positions.end(); ++it) { - _ui->advancedCombos->addWidget(new QLabel(it.key()), row, 0, 1, -1); - _ui->advancedCombos->addWidget(new QComboBox(), row, 2); + // Screens + QStringList positionList(tr("(off)")); + for (int i = 0; i < screenMap.size(); ++i) { + QSize res; + if (i < resolutions.size()) { + res = resolutions[i]; + } else { + res = QSize(16, 9); + } + AdvancedScreen *a = new AdvancedScreen(); + a->screen = new ScreenWidget(float(res.width()) / float(res.height())); + a->desiredResolution = res; + a->cboResolution = new QComboBox(); + _ui->advancedContainer->addWidget(a->screen); + _advancedScreens.append(a); + a->screen->layout()->addWidget(a->cboResolution); + a->screen->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + positionList.append(QString::number(_advancedScreens.size())); + connect(a->cboResolution, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) { + if (!_ignoreResolutionChange) { + a->desiredResolution = a->cboResolution->itemData(index).toSize(); + } + }); + addBoldListener(a->cboResolution); + } + // Header + _ui->advancedCombos->addWidget(new QLabel(tr("Output")), 0, 0); + _ui->advancedCombos->addWidget(new QLabel(tr("Position")), 0, 2); + // List + int row = 1; + for (auto it = screenMap.begin(); it != screenMap.end(); ++it) { + AdvancedOutput *a = new AdvancedOutput(it.value()); + a->assignmentLabel = new QLabel(it.key(), this); + a->assignmentLabel->hide(); + a->rowLabel = new QLabel(it.key()); + _ui->advancedCombos->addWidget(a->rowLabel, row, 0); + _ui->advancedCombos->addWidget(new QLabel(a->info.name), row, 1); + QComboBox *cbo = new QComboBox(); + a->cboPosition = cbo; + cbo->setProperty("output", QVariant::fromValue(a)); + _ui->advancedCombos->addWidget(cbo, row, 2); + _ui->advancedCombos->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum), row, 3); + _advancedOutput.append(a); row++; + // Logic + cbo->addItems(positionList); + // TODO Signal + connect(cbo, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) { + a->info.position = index - 1; + if (a->assignmentLabel->layout() != nullptr) { + a->assignmentLabel->layout()->removeWidget(cbo); + } + if (index > 0) { + _advancedScreens[index - 1]->screen->layout()->addWidget(a->assignmentLabel); + a->assignmentLabel->show(); + } else { + a->assignmentLabel->hide(); + } + // Refill boxes + _ignoreResolutionChange = true; + for (int i = 0; i < _advancedScreens.size(); ++i) { + QSize preferred, fallbackPreferred; + // Iterate over all virtual screens + AdvancedScreen *screen = _advancedScreens[i]; + bool first = true; + ResolutionVector common; + for (auto output : _advancedOutput) { + if (output->info.position != i) + continue; // Output not mapped to this virtual screen + if (first) { + common = output->info.modes; + first = false; + } else { + QMutableVectorIterator<QSize> it(common); + while (it.hasNext()) { + if (!output->info.modes.contains(it.next())) { + it.remove(); + } + } + } + if (!output->info.preferredResolution.isEmpty()) { + QSize &p = output->info.isProjector ? preferred : fallbackPreferred; + if (p.isEmpty() || !common.contains(p)) { + p = output->info.preferredResolution; + } + } + } + // Set list + // TODO: Highlight preferred mode + if (preferred.isEmpty()) { + preferred = fallbackPreferred; + } + qDebug() << "Preferred:" << preferred; + fillCombo(screen->cboResolution, common, screen->desiredResolution, preferred); + } + _ignoreResolutionChange = false; + }); + cbo->setCurrentIndex(a->info.position + 1); } } -//______________________________________________________________________________ -void Widget::handleButton() { - // Apply - - /* - - // Get the modes which has to be applied from QComboBox - QList<QVariant> 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, nullptr, 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 ****************************/ - +bool Widget::keepResolution() +{ // Show a dialog asking if the res should be kept TimeOutDialog keepDialog(15, this); keepDialog.setWindowModality(Qt::ApplicationModal); @@ -150,77 +329,67 @@ void Widget::handleButton() { */ keepDialog.show(); - while (keepDialog.isActive()) { + do { QCoreApplication::processEvents(); - } - - /* - // If the dialog was not canceled revert the resolution - if ( !keepDialog.wasCanceled()) { - qDebug() << "User did not cancel timeout, reverting!"; - - // 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, nullptr, 0); - } + } while (keepDialog.isActive()); - // 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)); - } - } + return keepDialog.wasCanceled(); +} - // 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); - } +//______________________________________________________________________________ +void Widget::connectButtons() { - // Sync... whatever... - XSync (_display, False); + // Swap dualscreen + connect(_ui->btnDualSwap, &QPushButton::clicked, [=](bool) { + _ui->dualContainer->addItem(_ui->dualContainer->takeAt(1)); + _ui->dualContainer->addItem(_ui->dualContainer->takeAt(0)); + }); - // center dialog on screenbottom - qDebug() << "Again center dialog on screenbottom"; - this->move( ScreenSize.width()/2 - this->width()/2, - ScreenSize.height() - this->height()); + // Apply CLONE + connect(_ui->btnCloneApply, &QPushButton::clicked, [=](bool) { + if (!ScreenSetup::inst()->setClone(_ui->cboCloneResolution->currentData().toSize()) || !keepResolution()) { + qDebug() << "reverting clone"; + ScreenSetup::inst()->revertChanges(); + } + ScreenSetup::inst()->updateScreenResources(); + initControls(); + }); - // Set the mouse pointer in the middle of the screen - QCursor::setPos(monitorMode->width/2, monitorMode->height/2); - } + // Apply DUALHEAD + connect(_ui->btnDualApply, &QPushButton::clicked, [=](bool) { + QPair<QSize, QList<QString>> left = { _ui->cboDualLeft->currentData().toSize(), { _ui->cboDualLeft->property("output").toString() } }; + QPair<QSize, QList<QString>> right = { _ui->cboDualRight->currentData().toSize(), { _ui->cboDualRight->property("output").toString() } }; + if (_ui->btnDualSwap->isChecked()) { + qSwap(left, right); + } + if (!ScreenSetup::inst()->setCustom({left, right}) || !keepResolution()) { + qDebug() << "reverting dualhead"; + ScreenSetup::inst()->revertChanges(); + } + ScreenSetup::inst()->updateScreenResources(); + initControls(); + }); - // Delete the backup - for ( CrtcMap::iterator it = backup.begin(); - it != backup.end(); ++it ) { - delete[] it.value()->outputs; - delete it.value(); - } + // Apply custom + connect(_ui->btnAdvancedApply, &QPushButton::clicked, [=](bool) { + QList<QPair<QSize, QList<QString>>> list; + for (auto e : _advancedScreens) { + list.append({ e->cboResolution->currentData().toSize(), QList<QString>() }); + } + for (auto e : _advancedOutput) { + int index = e->cboPosition->currentIndex() - 1; + if (index < 0 || index >= list.size()) + continue; + list[index].second.append(e->cboPosition->property("output").toString()); + } + if (!ScreenSetup::inst()->setCustom(list) || !keepResolution()) { + qDebug() << "reverting custom"; + ScreenSetup::inst()->revertChanges(); + } + ScreenSetup::inst()->updateScreenResources(); + initControls(); + }); - */ } //////////////////////////////////////////////////////////////////////////////// |