summaryrefslogtreecommitdiffstats
path: root/src/widget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widget.cpp')
-rw-r--r--src/widget.cpp453
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();
+ });
- */
}
////////////////////////////////////////////////////////////////////////////////