From 193daa35e13865d72ad25acb881108b5e8b7ff36 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 11 Sep 2018 17:50:33 +0200 Subject: Keep revert state in separate class ti handle interleaving mode changes --- src/main.cpp | 3 +- src/timeoutdialog.cpp | 35 ++++++- src/timeoutdialog.h | 1 + src/widget.cpp | 26 ++--- src/xprivate.cpp | 31 ------ src/xprivate.h | 3 - src/xx.cpp | 268 +++++++++++++++++++++++++++++++++++++------------- src/xx.h | 30 ++++-- 8 files changed, 273 insertions(+), 124 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fabf36c..317383d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,8 @@ int main(int argc, char *argv[]) if (CommandLine::center()) { ScreenSetup::inst()->setCenteredClone(); } else if (CommandLine::autoSetup()) { - ScreenSetup::inst()->setDefaultMode(CommandLine::testMode()); + ScreenMode mode; + ScreenSetup::inst()->setDefaultMode(CommandLine::testMode(), mode); } else { ScreenSetup::inst()->getCurrentMode(); } diff --git a/src/timeoutdialog.cpp b/src/timeoutdialog.cpp index 6f12ca2..9eefc42 100644 --- a/src/timeoutdialog.cpp +++ b/src/timeoutdialog.cpp @@ -6,22 +6,26 @@ #include #include #include +#include TimeOutDialog::TimeOutDialog(int time, QWidget *parent) : QDialog(parent), _ui(new Ui::TimeOutDialog), _time(time) { _ui->setupUi(this); + this->installEventFilter(this); + _ui->buttonBox->installEventFilter(this); + for (auto b : _ui->buttonBox->buttons()) { + b->installEventFilter(this); + b->setFocusPolicy(Qt::NoFocus); + } + this->setFocus(); QObject::connect(&_timer, &QTimer::timeout, this, &TimeOutDialog::update); QObject::connect(_ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton *button) { _timer.stop(); if (button == _ui->buttonBox->button(QDialogButtonBox::Discard)) { _time = 0; } - }); - QObject::connect(_ui->buttonBox, &QDialogButtonBox::rejected, [this]() { - _timer.stop(); - _time = 0; // So wasCanceled() returns true - }); + }); // QProgressDialog takes ownership of QProgressBar _ui->progressBar->setMaximum(_time); @@ -37,6 +41,27 @@ void TimeOutDialog::hideEvent(QHideEvent *e) _timer.stop(); } +bool TimeOutDialog::eventFilter(QObject *, QEvent *event) +{ + if (event->type() != QEvent::KeyPress) + return false; + if (!_timer.isActive()) + return false; + QKeyEvent *e = static_cast(event); + if (e->key() == Qt::Key_Escape) { + // Handle as discard + _time = 0; + _timer.stop(); + return true; + } + if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { + // Handle as accept + _timer.stop(); + return true; + } + return false; +} + void TimeOutDialog::update() { --_time; diff --git a/src/timeoutdialog.h b/src/timeoutdialog.h index b70f753..d80de1c 100644 --- a/src/timeoutdialog.h +++ b/src/timeoutdialog.h @@ -27,6 +27,7 @@ private: protected: void hideEvent(QHideEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; private slots: void update(); diff --git a/src/widget.cpp b/src/widget.cpp index fba46a7..069e8db 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -139,10 +139,12 @@ Widget::Widget(QWidget *parent) : if (currentCount == _lastScreenCount) { // Nothing seems to have changed. This might happen when another tool changes the screen config. Let's not interfere. } else if (currentCount > _lastScreenCount) { // Screen count increased - auto setup + _lastScreenCount = currentCount; ScreenSetup::inst()->initModes(); - ScreenSetup::inst()->setDefaultMode(CommandLine::testMode()); - if (!keepResolution()) { - ScreenSetup::inst()->revertChanges(); + ScreenMode mode; + auto ret = ScreenSetup::inst()->setDefaultMode(CommandLine::testMode(), mode); + if (!ret.ok() || !keepResolution()) { + ret.revert(); } } else { // Screen count decreased - pop up GUI and don't just deconfig the screen this->show(); @@ -569,8 +571,7 @@ bool Widget::keepResolution() //______________________________________________________________________________ void Widget::connectButtons() { - - // Swap dualscreen + // Swap dualscreen connect(_ui->btnDualSwap, &QPushButton::clicked, [=](bool) { _ui->dualContainer->addItem(_ui->dualContainer->takeAt(1)); _ui->dualContainer->addItem(_ui->dualContainer->takeAt(0)); @@ -578,9 +579,10 @@ void Widget::connectButtons() { // Apply CLONE connect(_ui->btnCloneApply, &QPushButton::clicked, [=](bool) { - if (!ScreenSetup::inst()->setClone(_ui->cboCloneResolution->currentData().toSize()) || !keepResolution()) { + auto ret = ScreenSetup::inst()->setClone(_ui->cboCloneResolution->currentData().toSize()); + if (!ret.ok() || !keepResolution()) { qDebug() << "reverting clone"; - ScreenSetup::inst()->revertChanges(); + ret.revert(); } ScreenSetup::inst()->updateScreenResources(); initControls(); @@ -593,9 +595,10 @@ void Widget::connectButtons() { if (_ui->btnDualSwap->isChecked()) { qSwap(left, right); } - if (!ScreenSetup::inst()->setCustom({left, right}) || !keepResolution()) { + auto ret = ScreenSetup::inst()->setCustom({left, right}); + if (!ret.ok() || !keepResolution()) { qDebug() << "reverting dualhead"; - ScreenSetup::inst()->revertChanges(); + ret.revert(); } ScreenSetup::inst()->updateScreenResources(); initControls(); @@ -613,9 +616,10 @@ void Widget::connectButtons() { continue; list[index].second.append(e->info.output); } - if (!ScreenSetup::inst()->setCustom(list) || !keepResolution()) { + auto ret = ScreenSetup::inst()->setCustom(list); + if (!ret.ok() || !keepResolution()) { qDebug() << "reverting custom"; - ScreenSetup::inst()->revertChanges(); + ret.revert(); } ScreenSetup::inst()->updateScreenResources(); initControls(); diff --git a/src/xprivate.cpp b/src/xprivate.cpp index 0233f5b..70c1a3f 100644 --- a/src/xprivate.cpp +++ b/src/xprivate.cpp @@ -60,7 +60,6 @@ XPrivate::~XPrivate() void XPrivate::freeResources() { - freeCrtcBackup(); // Clear the modemap (nothing to be freed, stored in screenResources) _modeMap.clear(); _resolutions.clear(); @@ -292,36 +291,6 @@ void XPrivate::disconnectAllCrtcs() } } -void XPrivate::freeCrtcBackup() -{ - for (auto entry : _crtcBackup) { - free(entry->outputs); - free(entry); - } - _crtcBackup.clear(); - qDebug() << "CRTC freed"; -} - -void XPrivate::createCrtcBackup() -{ - freeCrtcBackup(); - for (CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); ++it) { - const auto src = it.value(); - XRRCrtcInfo *copy = static_cast(calloc(1, sizeof(XRRCrtcInfo))); - copy->outputs = static_cast(calloc(size_t(src->noutput), sizeof(RROutput))); - copy->x = src->x; - copy->y = src->y; - copy->mode = src->mode; - copy->rotation = src->rotation; - copy->noutput = src->noutput; - for (int i = 0; i < src->noutput; ++i) { - copy->outputs[i] = src->outputs[i]; - } - _crtcBackup[it.key()] = copy; - } - qDebug() << "Created CRTC backup with entries:" << _crtcBackup.size(); -} - /** * Copy first "num" modes from output to all other outputs if they * dont have a suitable mode yet. diff --git a/src/xprivate.h b/src/xprivate.h index fd0e9f3..d3e9129 100644 --- a/src/xprivate.h +++ b/src/xprivate.h @@ -45,8 +45,6 @@ public: void freeResources(); void updateScreenResources(); bool readEdid(OutputInfo* output); - void createCrtcBackup(); - void freeCrtcBackup(); void disconnectAllCrtcs(); XRRModeInfo* getPreferredMode(OutputInfo *oi, XRRModeInfo *fallback = nullptr) const; void setScreenSize(const QSize &size); @@ -62,7 +60,6 @@ public: XRRScreenResources* _screenResources; ModeMap _modeMap; CrtcMap _crtcMap; - CrtcMap _crtcBackup; OutputMap _outputMap; ResolutionVector _resolutions; }; diff --git a/src/xx.cpp b/src/xx.cpp index a5f9d92..d0b6a3d 100644 --- a/src/xx.cpp +++ b/src/xx.cpp @@ -4,9 +4,137 @@ #include #include -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// +/* + * This clusterfuck exists because there are name clashes between X11/Xrandr headers + * and Qt classes. Hence the split into xx.* and xprivate.* as well as those idiotic + * matrjoschka classes. Or I'm just stupid. + */ + +class BackupInternalInternal +{ +private: + XPrivate *x; +public: + CrtcMap map; + BackupInternalInternal() : x(nullptr) { + qDebug() << "new BackupInternalInternal"; + } + ~BackupInternalInternal() { + qDebug() << "delete BackupInternalInternal"; + freeBackup(); + } + void createBackup(XPrivate *x) { + this->x = x; + freeBackup(); + for (CrtcMap::iterator it = x->_crtcMap.begin(); it != x->_crtcMap.end(); ++it) { + const auto src = it.value(); + XRRCrtcInfo *copy = static_cast(calloc(1, sizeof(XRRCrtcInfo))); + copy->outputs = static_cast(calloc(size_t(src->noutput), sizeof(RROutput))); + copy->x = src->x; + copy->y = src->y; + copy->mode = src->mode; + copy->rotation = src->rotation; + copy->noutput = src->noutput; + for (int i = 0; i < src->noutput; ++i) { + copy->outputs[i] = src->outputs[i]; + } + map[it.key()] = copy; + } + qDebug() << "Created CRTC backup with entries:" << map.size(); + } + void freeBackup() { + for (auto i : map) { + free(i->outputs); + free(i); + } + map.clear(); + } + void revertChanges() + { + if (map.isEmpty()) + return; + qDebug() << "Starting revert"; + XGrabServer(x->_display); + x->disconnectAllCrtcs(); + QSize screenSize; + for (auto e : map) { + if (e->mode == None || !x->_modeMap.contains(e->mode)) + continue; + screenSize = screenSize.expandedTo(QSize(e->x + int(x->_modeMap[e->mode]->width), e->y + int(x->_modeMap[e->mode]->height))); + } + x->setScreenSize(screenSize); + for (CrtcMap::iterator it = map.begin(); it != map.end(); ++it) { + auto e = it.value(); + if (e->mode == None) + continue; + XRRSetCrtcConfig(x->_display, x->_screenResources, it.key(), CurrentTime, + e->x, e->y, e->mode, e->rotation, e->outputs, e->noutput); + } + XUngrabServer(x->_display); + XSync(x->_display, False); + freeBackup(); + } +}; + +class BackupInternal +{ +public: + BackupInternal() { + qDebug() << "new BackupInternal"; + } + ~BackupInternal() { + qDebug() << "delete BackupInternal"; + } + QSharedPointer backup; +}; + +ConfigBackup::ConfigBackup() +{ + _ok = false; + a = new BackupInternal; +} + +ConfigBackup::ConfigBackup(XPrivate *x) +{ + _ok = false; + a = new BackupInternal; + a->backup = QSharedPointer(new BackupInternalInternal); + a->backup->createBackup(x); +} + +ConfigBackup::~ConfigBackup() +{ + delete a; +} + +ConfigBackup& ConfigBackup::operator=(const ConfigBackup &other) +{ + if (this == &other) + return *this; + delete a; + _ok = other._ok; + a = new BackupInternal; + a->backup = other.a->backup; + return *this; +} + +ConfigBackup::ConfigBackup(const ConfigBackup &other) +{ + _ok = other._ok; + a = new BackupInternal; + a->backup = other.a->backup; +} + +void ConfigBackup::revert() +{ + a->backup->revertChanges(); +} + +static QWeakPointer currentBackup; + +/* + * Slightly more normal code starts here + */ static ScreenInfo initScreenInfo(const OutputInfo *oi, const ModeMap &om) @@ -183,10 +311,14 @@ ScreenMode ScreenSetup::getCurrentMode() return ScreenMode::Clone; } -ScreenMode ScreenSetup::setDefaultMode(bool dryRun) +ConfigBackup ScreenSetup::setDefaultMode(bool dryRun, ScreenMode &mode) { - if (a->_outputMap.size() == 1) // Only one output exists, do nothing - return ScreenMode::Single; + ConfigBackup retval; + if (a->_outputMap.size() == 1) { // Only one output exists, do nothing + retval._ok = (a->_outputMap.begin().value()->mode != nullptr); + mode = ScreenMode::Single; + return retval; + } QMap screenMap; QMap projectorMap; for (auto o : a->_outputMap) { @@ -201,11 +333,13 @@ ScreenMode ScreenSetup::setDefaultMode(bool dryRun) auto screens = screenMap.values(); qDebug() << projectors.size() << "projectors," << screens.size() << "screens."; QList outputSizes = a->getTotalSize(projectors, screens); - if (outputSizes.isEmpty()) - return ScreenMode::Advanced; // Dunno lol + if (outputSizes.isEmpty()) { + mode = ScreenMode::Advanced; // Dunno lol + return retval; + } QSize screenSize = getTotalSizeHorz(outputSizes); if (!dryRun) { - a->createCrtcBackup(); + retval = createCrtcBackup(); XGrabServer(a->_display); a->disconnectAllCrtcs(); // Set new screen size @@ -216,25 +350,34 @@ ScreenMode ScreenSetup::setDefaultMode(bool dryRun) int offset = 0; for (int i = 0; i < outputSizes.size(); ++i) { + bool ok = false; const QSize &size = outputSizes.at(i); if (i < projectors.size()) { - a->setOutputResolution(projectors.at(i), offset, 0, size, dryRun); + ok = a->setOutputResolution(projectors.at(i), offset, 0, size, dryRun) || ok; } if (i < screens.size()) { - a->setOutputResolution(screens.at(i), offset, 0, size, dryRun); + ok = a->setOutputResolution(screens.at(i), offset, 0, size, dryRun) || ok; } offset += size.width(); + if (ok) { + retval._ok = true; + } } if (!dryRun) { XUngrabServer(a->_display); XSync(a->_display, False); } updateScreenResources(); // Re-Read - if (outputSizes.size() == 1) // One output size, at least 2 outputs in total -- clone mode - return ScreenMode::Clone; - if (outputSizes.size() == 2 && a->_outputMap.size() == 2) // Two outputs, two sizes -- extended - return ScreenMode::Dual; - return ScreenMode::Advanced; // Must be more than 2 outputs -> something more involved + if (outputSizes.size() == 1) { + // One output size, at least 2 outputs in total -- clone mode + mode = ScreenMode::Clone; + } else if (outputSizes.size() == 2 && a->_outputMap.size() == 2) { + // Two outputs, two sizes -- extended + mode = ScreenMode::Dual; + } else { + mode = ScreenMode::Advanced; // Must be more than 2 outputs -> something more involved + } + return retval; } //___________________________________________________________________________ @@ -282,9 +425,9 @@ ScreenSetup::~ScreenSetup() delete a; } -bool ScreenSetup::setCenteredClone() +ConfigBackup ScreenSetup::setCenteredClone() { - a->createCrtcBackup(); + ConfigBackup retval = createCrtcBackup(); XRRModeInfo *fallback = nullptr; for (auto m : a->_modeMap) { if (m->width == 1024 && m->height == 768) { @@ -294,46 +437,48 @@ bool ScreenSetup::setCenteredClone() } XGrabServer(a->_display); a->disconnectAllCrtcs(); - QSize screenSize; - bool ok = false; + QSize screenSize; for (auto oi : a->_outputMap) { auto mode = a->getPreferredMode(oi, fallback); if (mode != nullptr) { if (int(mode->width) > screenSize.width()) { - screenSize.setWidth(mode->width); + screenSize.setWidth(int(mode->width)); } if (int(mode->height) > screenSize.height()) { - screenSize.setHeight(mode->height); - } - // TODO: CENTER - const int x = (screenSize.width() - mode->width) / 2; - const int y = (screenSize.height() - mode->height) / 2; - ok = a->setOutputResolution(oi, x, y, QSize(int(mode->width), int(mode->height))) || ok; + screenSize.setHeight(int(mode->height)); + } + const int x = (screenSize.width() - int(mode->width)) / 2; + const int y = (screenSize.height() - int(mode->height)) / 2; + if (a->setOutputResolution(oi, x, y, QSize(int(mode->width), int(mode->height)))) { + retval._ok = true; + } } } a->setScreenSize(screenSize); XUngrabServer(a->_display); XSync(a->_display, False); - return ok; + return retval; } -bool ScreenSetup::setClone(const QSize &resolution) +ConfigBackup ScreenSetup::setClone(const QSize &resolution) { - a->createCrtcBackup(); + ConfigBackup retval = createCrtcBackup(); XGrabServer(a->_display); a->disconnectAllCrtcs(); - a->setScreenSize(resolution); - bool ok = false; + a->setScreenSize(resolution); for (auto oi : a->_outputMap) { - ok = a->setOutputResolution(oi, 0, 0, resolution) || ok; + if (a->setOutputResolution(oi, 0, 0, resolution)) { + retval._ok = true; + } } XUngrabServer(a->_display); XSync(a->_display, False); - return ok; + return retval; } -bool ScreenSetup::setCustom(const QList>> &list) +ConfigBackup ScreenSetup::setCustom(const QList>> &list) { + ConfigBackup retval; QList sizes; for (auto e : list) { if (e.second.isEmpty()) @@ -341,16 +486,15 @@ bool ScreenSetup::setCustom(const QList>> &list) sizes.append(e.first); } if (sizes.isEmpty()) - return false; - a->createCrtcBackup(); + return retval; + retval = createCrtcBackup(); auto screenSize = getTotalSizeHorz(sizes); if (screenSize.isEmpty()) - return false; + return retval; XGrabServer(a->_display); a->disconnectAllCrtcs(); a->setScreenSize(screenSize); - int x = 0; - bool ok = false; + int x = 0; for (auto e : list) { if (e.second.isEmpty()) continue; @@ -359,39 +503,16 @@ bool ScreenSetup::setCustom(const QList>> &list) for (auto oi : a->_outputMap) { if (oi->outputName != outputName) continue; - ok = a->setOutputResolution(oi, x, 0, res) || ok; + if (a->setOutputResolution(oi, x, 0, res)) { + retval._ok = true; + } } } x += res.width(); } XUngrabServer(a->_display); XSync(a->_display, False); - return ok; -} - -void ScreenSetup::revertChanges() -{ - if (a->_crtcBackup.isEmpty()) - return; - qDebug() << "Starting revert"; - XGrabServer(a->_display); - a->disconnectAllCrtcs(); - QSize screenSize; - for (auto e : a->_crtcBackup) { - if (e->mode == None || !a->_modeMap.contains(e->mode)) - continue; - screenSize = screenSize.expandedTo(QSize(e->x + int(a->_modeMap[e->mode]->width), e->y + int(a->_modeMap[e->mode]->height))); - } - a->setScreenSize(screenSize); - for (CrtcMap::iterator it = a->_crtcBackup.begin(); it != a->_crtcBackup.end(); ++it) { - auto e = it.value(); - if (e->mode == None) - continue; - XRRSetCrtcConfig(a->_display, a->_screenResources, it.key(), CurrentTime, - e->x, e->y, e->mode, e->rotation, e->outputs, e->noutput); - } - XUngrabServer(a->_display); - XSync(a->_display, False); + return retval; } static bool modeBiggerThan(const QSize &a, const QSize &b) @@ -423,6 +544,19 @@ ResolutionVector ScreenSetup::getCommonModes() const return ret; } +ConfigBackup ScreenSetup::createCrtcBackup() +{ + auto bd = currentBackup.toStrongRef(); + if (bd.data() != nullptr && !bd->map.isEmpty()) { + ConfigBackup backup; + backup.a->backup = bd; + return backup; + } + ConfigBackup backup(a); + currentBackup = backup.a->backup.toWeakRef(); + return backup; +} + /** * Return number of connected (no active) outputs according to last query */ diff --git a/src/xx.h b/src/xx.h index 2e2e3f2..de52db3 100644 --- a/src/xx.h +++ b/src/xx.h @@ -8,6 +8,7 @@ struct ScreenInfo; class XPrivate; class QSocketNotifier; +class BackupInternal; typedef QVector ResolutionVector; @@ -39,6 +40,23 @@ enum class ConnectionEvent Unknown, }; +class ConfigBackup +{ + friend class ScreenSetup; +protected: + explicit ConfigBackup(XPrivate*); + explicit ConfigBackup(); +private: + bool _ok; + BackupInternal* a; + ConfigBackup& operator=(const ConfigBackup &other); +public: + ConfigBackup(const ConfigBackup &other); + ~ConfigBackup(); + bool ok() const { return _ok; } + void revert(); +}; + class ScreenSetup : public QObject { Q_OBJECT @@ -46,12 +64,11 @@ public: void updateScreenResources(); void initModes(); ScreenMode getCurrentMode(); - ScreenMode setDefaultMode(bool dryRun = false); - bool createMode(unsigned int resX, unsigned int resY, float refresh, QString name); - void revertChanges(); - bool setCenteredClone(); - bool setClone(const QSize &resolution); - bool setCustom(const QList>> &list); + ConfigBackup setDefaultMode(bool dryRun, ScreenMode &mode); + bool createMode(unsigned int resX, unsigned int resY, float refresh, QString name); + ConfigBackup setCenteredClone(); + ConfigBackup setClone(const QSize &resolution); + ConfigBackup setCustom(const QList>> &list); ResolutionVector getCommonModes() const; int getOutputCount() const; int queryCurrentOutputCount() const; @@ -67,6 +84,7 @@ public: private: ScreenSetup(); ~ScreenSetup(); + ConfigBackup createCrtcBackup(); static ScreenSetup * _instance; XPrivate *a; -- cgit v1.2.3-55-g7522