From 615c5a7f858f80ccb1d7201b8ea628e35237db6e Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Sat, 25 Aug 2018 21:29:35 +0200 Subject: Getting there, slowly --- src/i18n/de.ts | 14 ++--- src/timeoutdialog.cpp | 48 +++++++++-------- src/timeoutdialog.h | 23 ++++---- src/widget.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++------- src/widget.h | 2 + src/x.cpp | 55 ++++++++++++-------- src/x.h | 2 +- 7 files changed, 206 insertions(+), 79 deletions(-) diff --git a/src/i18n/de.ts b/src/i18n/de.ts index d93a667..6fbd9c4 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -4,7 +4,7 @@ QCoreApplication - + %1x%2 @@ -43,7 +43,7 @@ - + Dual Screen @@ -58,27 +58,27 @@ - + (off) - + Output - + Position - + Do you want to keep this resolution? - + Keep diff --git a/src/timeoutdialog.cpp b/src/timeoutdialog.cpp index 418d791..f9787fb 100644 --- a/src/timeoutdialog.cpp +++ b/src/timeoutdialog.cpp @@ -3,40 +3,46 @@ #include "timeoutdialog.h" #include +#include TimeOutDialog::TimeOutDialog(int time, QWidget *parent) - : QProgressDialog(parent), _time(time) + : QProgressDialog(parent), _time(time) { - QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); - QObject::connect(this, SIGNAL(canceled()), this, SLOT(cancel())); - - - // QProgressDialog takes ownership of QProgressBar - QProgressBar *qbar = new QProgressBar(this); - qbar->setFormat(trUtf8("%v seconds")); - qbar->setMaximum(_time); - qbar->setMinimum(0); - qbar->setValue(_time); - setBar(qbar); - _timer.start(1000); + QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); + QObject::connect(this, SIGNAL(canceled()), this, SLOT(cancel())); + + + // QProgressDialog takes ownership of QProgressBar + QProgressBar *qbar = new QProgressBar(this); + qbar->setFormat(trUtf8("%v seconds")); + qbar->setMaximum(_time); + qbar->setMinimum(0); + qbar->setValue(_time); + setBar(qbar); + _timer.start(1000); } +void TimeOutDialog::hideEvent(QHideEvent *e) +{ + QProgressDialog::hideEvent(e); + _timer.stop(); +} //___________________________________________________________________________ void TimeOutDialog::update() { - if (_time == 0) { - _timer.stop(); - accept(); - } - else - setValue(_time); - --_time; + --_time; + if (_time == 0) { + _timer.stop(); + accept(); + } else { + setValue(_time); + } } //___________________________________________________________________________ void TimeOutDialog::cancel() { - _timer.stop(); + _timer.stop(); } diff --git a/src/timeoutdialog.h b/src/timeoutdialog.h index eb0f969..eb853f3 100644 --- a/src/timeoutdialog.h +++ b/src/timeoutdialog.h @@ -9,21 +9,24 @@ class TimeOutDialog : public QProgressDialog { - Q_OBJECT + Q_OBJECT - public: - TimeOutDialog(int time, QWidget *parent = 0); +public: + TimeOutDialog(int time, QWidget *parent = 0); bool isActive() const { return _timer.isActive(); } - private: - int _time; - QTimer _timer; +private: + int _time; + QTimer _timer; - public slots: - void cancel(); +protected: + void hideEvent(QHideEvent *event) override; - private slots: - void update(); +public slots: + void cancel(); + +private slots: + void update(); }; #endif // TIMEOUTDIALOG_H diff --git a/src/widget.cpp b/src/widget.cpp index a5f32fa..34684b5 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include class ScreenWidget : public QWidget { @@ -16,6 +18,10 @@ public: QWidget::setStyleSheet(".QWidget { border-radius: 3px;border: 2px solid black; }"); QWidget::setLayout(new QVBoxLayout(this)); } + void setScaling(float scale) + { + this->scale = scale; + } protected: float scale; void resizeEvent(QResizeEvent *event) override @@ -59,12 +65,39 @@ static void addBoldListener(QComboBox *combo) //______________________________________________________________________________ Widget::Widget(QWidget *parent) : QWidget(parent), - _ui(new Ui::Widget) + _ui(new Ui::Widget), + _popupCount(0) { _ui->setupUi(this); setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); QTimer *top = new QTimer(this); connect(top, &QTimer::timeout, [=]() { + // Move window to current screen + QRect win = this->geometry(); + QPoint cursor = QCursor::pos(); + const QScreen *winScreen = nullptr, *mouseScreen = nullptr; + //qDebug() << "Mouse at" << cursor << " window at" << win; + for (auto screen : _qtScreens) { + QRect geo = screen->geometry(); + if (geo.contains(win)) { + winScreen = screen; + //qDebug() << "Window on screen" << geo; + } + if (geo.contains(cursor)) { + mouseScreen = screen; + //qDebug() << "Mouse on screen" << geo; + } + } + if (mouseScreen != nullptr && mouseScreen != winScreen) { + QPoint offset = mouseScreen->geometry().topLeft(); + QSize spacing = (mouseScreen->size() - this->size()) / 2; + offset.rx() += spacing.width(); + offset.ry() += spacing.height(); + this->move(offset); + } + // Raise window if appropriate + if (_popupCount > 0) + return; auto combos = this->findChildren(); for (auto combo : combos) { if (combo->view()->isVisible()) @@ -77,6 +110,22 @@ Widget::Widget(QWidget *parent) : addBoldListener(_ui->cboDualLeft); addBoldListener(_ui->cboDualRight); connectButtons(); + auto fun = [this](const QScreen *scrn) { + qDebug() << "QT SEES SCREEN" << scrn->geometry(); + _qtScreens.append(scrn); + /* + connect(scrn, &QScreen::geometryChanged, [this](const QRect &geom) { + + }); + */ + }; + for (auto scrn : QGuiApplication::screens()) { + fun(scrn); + } + connect(qApp, &QGuiApplication::screenAdded, fun); + connect(qApp, &QGuiApplication::screenRemoved, [this](const QScreen *scrn) { + _qtScreens.removeAll(scrn); + }); } //______________________________________________________________________________ @@ -178,7 +227,14 @@ void Widget::initControls() auto lists = QList({_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); + QSize selected; + if (ScreenSetup::inst()->getCurrentMode() == ScreenMode::Dual) { + // When we're not in dualhead mode, pre-select the preferred solution, so in case the user wants + // to switch to dualhead, they just need to switch to the "dual" tab and hit apply to get + // each screen configured to its preferred resolution. + selected = screenList[i].currentResolution; + } + fillCombo(lists[j], screenList[i].modes, selected, 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); @@ -225,7 +281,7 @@ void Widget::initControls() res = QSize(16, 9); } AdvancedScreen *a = new AdvancedScreen(); - a->screen = new ScreenWidget(float(res.width()) / float(res.height())); + a->screen = new ScreenWidget(res.isEmpty() ? 1.33f : float(res.width()) / float(res.height())); a->desiredResolution = res; a->cboResolution = new QComboBox(); _ui->advancedContainer->addWidget(a->screen); @@ -233,12 +289,19 @@ void Widget::initControls() 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::of(&QComboBox::currentIndexChanged), [=](int index) { + connect(a->cboResolution, QOverload::of(&QComboBox::currentIndexChanged), [a, this](int index) { if (!_ignoreResolutionChange) { a->desiredResolution = a->cboResolution->itemData(index).toSize(); } }); addBoldListener(a->cboResolution); + connect(a->cboResolution, QOverload::of(&QComboBox::currentIndexChanged), [a, this](int index) { + if (index < 0) + return; + QSize res = a->cboResolution->itemData(index).toSize(); + a->screen->setScaling(res.isEmpty() ? 1.33f : float(res.width()) / float(res.height())); + _ui->advancedContainer->update(); + }); } // Header _ui->advancedCombos->addWidget(new QLabel(tr("Output")), 0, 0); @@ -262,10 +325,10 @@ void Widget::initControls() // Logic cbo->addItems(positionList); // TODO Signal - connect(cbo, QOverload::of(&QComboBox::currentIndexChanged), [=](int index) { + connect(cbo, QOverload::of(&QComboBox::currentIndexChanged), [a, this](int index) { a->info.position = index - 1; if (a->assignmentLabel->layout() != nullptr) { - a->assignmentLabel->layout()->removeWidget(cbo); + a->assignmentLabel->layout()->removeWidget(a->cboPosition); } if (index > 0) { _advancedScreens[index - 1]->screen->layout()->addWidget(a->assignmentLabel); @@ -318,23 +381,63 @@ void Widget::initControls() bool Widget::keepResolution() { - // 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(); + _popupCount += 1; + // Qt needs some time to notice the screen setup has changed + QCoreApplication::processEvents(); + QThread::msleep(10); + QCoreApplication::processEvents(); + QList list; + this->setDisabled(true); + for (auto screen : _qtScreens) { + QRect geo = screen->geometry(); + bool skip = false; + // See if it overlaps with an existing screen + for (auto other : _qtScreens) { + if (other == screen) + break; + if (other->geometry().intersects(geo)) { + skip = true; + break; + } + } + if (skip) + continue; + // Show a dialog asking if the res should be kept + TimeOutDialog *keepDialog = new TimeOutDialog(15, this); + //keepDialog->setWindowModality(Qt::WindowModal); + keepDialog->setWindowTitle(" "); + keepDialog->setLabelText(trUtf8("Do you want to keep this resolution?")); + keepDialog->setCancelButtonText(trUtf8("Keep")); + keepDialog->setWindowFlag(Qt::WindowStaysOnTopHint, true); + QSize s = (geo.size() - keepDialog->size()) / 2; + QPoint tl = geo.topLeft(); + tl.rx() += s.width(); + tl.ry() += s.height(); + keepDialog->move(tl); + keepDialog->show(); + list.append(keepDialog); + } + bool wasCanceled = false; + bool active; do { + active = true; QCoreApplication::processEvents(); - } while (keepDialog.isActive()); + for (auto win : list) { + active = active && win->isActive(); + wasCanceled = wasCanceled || win->wasCanceled(); + } + } while (active && !wasCanceled); + + for (auto win : list) { + win->deleteLater(); + } + + this->setDisabled(false); + + _popupCount -= 1; - return keepDialog.wasCanceled(); + return wasCanceled; } //______________________________________________________________________________ diff --git a/src/widget.h b/src/widget.h index 96e6f1a..4e3c932 100644 --- a/src/widget.h +++ b/src/widget.h @@ -30,6 +30,8 @@ private: bool _ignoreResolutionChange; QVector _advancedScreens; QVector _advancedOutput; + QList _qtScreens; + bool _popupCount; void initControls(); void connectButtons(); diff --git a/src/x.cpp b/src/x.cpp index df257bb..73662c5 100644 --- a/src/x.cpp +++ b/src/x.cpp @@ -40,7 +40,9 @@ ScreenInfo::ScreenInfo(const OutputInfo *oi, const ModeMap &om) if (om.contains(oi->output->modes[i])) { auto m = om.value(oi->output->modes[i]); const QSize size(int(m->width), int(m->height)); - modes.append(size); + if (!modes.contains(size)) { + modes.append(size); + } if (i == 0 && oi->output->npreferred > 0) { preferredResolution = size; } @@ -415,12 +417,12 @@ static QSize getTotalSizeHorz(const QList &list) return ret; } -void ScreenSetup::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun) +bool ScreenSetup::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun) { RRMode mode = getOutputModeForResolution(oi->output, size); if (mode == None) { if (oi->output->nmode == 0) - return; + return false; qDebug() << oi->outputName << "doesn't support" << size << "- falling back to its default"; mode = oi->output->modes[0]; } @@ -434,18 +436,27 @@ void ScreenSetup::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &oi->id, 1); } qDebug() << "Set" << oi->outputName << "to" << _modeMap[mode]->width << "x" << _modeMap[mode]->height << "-- offset" << x << "/" << y; + return true; } ScreenMode ScreenSetup::getCurrentMode() { - if (_outputMap.size() == 1) - return ScreenMode::Single; - if (_outputMap.size() > 2) - return ScreenMode::Advanced; + int numConnected = 0; + bool notAtOrigin = false; for (auto oi : _outputMap) { - if (oi->crtc->x != 0 || oi->crtc->y != 0) - return ScreenMode::Dual; + if (oi->mode != nullptr) { + ++numConnected; + if (oi->crtc->x != 0 || oi->crtc->y != 0) { + notAtOrigin = true; + } + } } + if (numConnected == 1) + return ScreenMode::Single; + if (numConnected > 2) + return ScreenMode::Advanced; + if (notAtOrigin) + return ScreenMode::Dual; return ScreenMode::Clone; } @@ -471,6 +482,7 @@ ScreenMode ScreenSetup::setDefaultMode(bool dryRun) return ScreenMode::Advanced; // Dunno lol QSize screenSize = getTotalSizeHorz(outputSizes); if (!dryRun) { + XGrabServer(_display); disconnectAllCrtcs(); // Set new screen size setScreenSize(screenSize); @@ -489,7 +501,10 @@ ScreenMode ScreenSetup::setDefaultMode(bool dryRun) } offset += size.width(); } - XSync(_display, False); + if (!dryRun) { + XUngrabServer(_display); + XSync(_display, False); + } updateScreenResources(); // Re-Read if (outputSizes.size() == 1) // One output size, at least 2 outputs in total -- clone mode return ScreenMode::Clone; @@ -661,16 +676,14 @@ void ScreenSetup::createCrtcBackup() bool ScreenSetup::setClone(const QSize &resolution) { createCrtcBackup(); + XGrabServer(_display); disconnectAllCrtcs(); setScreenSize(resolution); bool ok = false; for (auto oi : _outputMap) { - RRMode mode = getOutputModeForResolution(oi->output, resolution); - if (mode != None) { - XRRSetCrtcConfig(_display, _screenResources, oi->output->crtc, CurrentTime, 0, 0, mode, RR_Rotate_0, &oi->id, 1); - ok = true; - } + ok = setOutputResolution(oi, 0, 0, resolution) || ok; } + XUngrabServer(_display); XSync(_display, False); return ok; } @@ -685,6 +698,7 @@ bool ScreenSetup::setCustom(const QList>> &list) auto screenSize = getTotalSizeHorz(sizes); if (screenSize.isEmpty()) return false; + XGrabServer(_display); disconnectAllCrtcs(); setScreenSize(screenSize); int x = 0; @@ -695,15 +709,12 @@ bool ScreenSetup::setCustom(const QList>> &list) for (auto oi : _outputMap) { if (oi->outputName != outputName) continue; - RRMode mode = getOutputModeForResolution(oi->output, res); - if (mode == None) - continue; - XRRSetCrtcConfig(_display, _screenResources, oi->output->crtc, CurrentTime, x, 0, mode, RR_Rotate_0, &oi->id, 1); - ok = true; + ok = setOutputResolution(oi, x, 0, res) || ok; } } x += res.width(); } + XUngrabServer(_display); XSync(_display, False); return ok; } @@ -713,6 +724,7 @@ void ScreenSetup::revertChanges() if (_crtcBackup.isEmpty()) return; qDebug() << "Starting revert"; + XGrabServer(_display); disconnectAllCrtcs(); QSize screenSize; for (auto e : _crtcBackup) { @@ -727,7 +739,8 @@ void ScreenSetup::revertChanges() XRRSetCrtcConfig(_display, _screenResources, it.key(), CurrentTime, e->x, e->y, e->mode, e->rotation, e->outputs, e->noutput); } - XSync (_display, False); + XUngrabServer(_display); + XSync(_display, False); } static bool modeBiggerThan(const QSize &a, const QSize &b) diff --git a/src/x.h b/src/x.h index 8a37b62..b5df6e0 100644 --- a/src/x.h +++ b/src/x.h @@ -48,7 +48,6 @@ public: void initModes(); XRRModeInfo* getPreferredMode(OutputInfo *oi) const; QList getTotalSize(const QList &projectors, const QList &screens) const; - void setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun = false); ScreenMode getCurrentMode(); ScreenMode setDefaultMode(bool dryRun = false); void copyModesToAll(RROutput id, int num); @@ -80,6 +79,7 @@ private: RRMode getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const; RRMode getOutputModeForResolution(const XRROutputInfo *output, const QSize &resolution) const; RRCrtc getFreeCrtc(const XRROutputInfo* output) const; + bool setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun = false); static ScreenSetup * _instance; Display* _display; -- cgit v1.2.3-55-g7522