From 152233a6a3105b02c351eb10f0fd15015088a74a Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 10 Sep 2018 13:16:36 +0200 Subject: Fixes for adding new modes, selecting best mode for clone without EDID --- src/main.cpp | 36 ++++++++++++++++++++------------ src/main.h | 2 ++ src/widget.cpp | 40 ++++++++++++++++++++++------------- src/widget.h | 3 ++- src/widget.ui | 15 +++++++++++--- src/xprivate.cpp | 63 ++++++++++++++++++++++++++++++++++---------------------- src/xprivate.h | 2 +- src/xx.cpp | 42 +++++++++++++++++++++++++++++++++++-- src/xx.h | 1 + 9 files changed, 145 insertions(+), 59 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7989f8b..98e52d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ #include namespace { -bool _testMode, _autoSetup, _showGui, _backgroundMode, _wakeup; +bool _testMode, _autoSetup, _showGui, _backgroundMode, _wakeup, _center; } namespace CommandLine @@ -18,6 +18,8 @@ bool testMode() { return _testMode; } bool autoSetup() { return _autoSetup; } bool showGui() { return _showGui; } bool backgroundMode() { return _backgroundMode; } +bool wakeup() { return _wakeup; } +bool center() { return _center; } } static void parseCommandLine(const QCoreApplication &a); @@ -49,24 +51,26 @@ int main(int argc, char *argv[]) parseCommandLine(*a); - if (_wakeup) { + if (CommandLine::wakeup()) { return Bus::inst()->registerService() ? 0 : 1; } ScreenSetup::inst()->initModes(); + ScreenMode currentMode; - if (CommandLine::autoSetup()) { + if (CommandLine::center()) { + ScreenSetup::inst()->setCenteredClone(); + currentMode = ScreenMode::Clone; + } else if (CommandLine::autoSetup()) { currentMode = ScreenSetup::inst()->setDefaultMode(CommandLine::testMode()); } else { currentMode = ScreenSetup::inst()->getCurrentMode(); } - bool showNow = (CommandLine::showGui() && currentMode != ScreenMode::Single); - Widget *w = nullptr; - if (CommandLine::backgroundMode() || showNow) { + if (CommandLine::backgroundMode() || CommandLine::showGui()) { w = new Widget(); - if (showNow) { + if (CommandLine::showGui()) { w->show(); } } @@ -84,24 +88,28 @@ static void parseCommandLine(const QCoreApplication &a) parser.addVersionOption(); // Option for adding modes and trying to auto-setup QCommandLineOption oAutoSetup(QStringList() << "a" << "auto", - QCoreApplication::translate("main", "Automatically configure modes and set up screens.")); + QCoreApplication::translate("main", "Automatically configure modes and set up screens.")); parser.addOption(oAutoSetup); // Show config GUI if more than one screen QCommandLineOption oShowGui(QStringList() << "g" << "gui", - QCoreApplication::translate("main", "Show config GUI if more than one screen is connected.")); + QCoreApplication::translate("main", "Show config GUI right away.")); parser.addOption(oShowGui); // Keep running and detect screen setup changes QCommandLineOption oBackground(QStringList() << "b" << "background", - QCoreApplication::translate("main", "Keep running in background and show GUI again when number of screens changes.")); + QCoreApplication::translate("main", "Keep running in background and show GUI when number of screens changes.")); parser.addOption(oBackground); // Test mode -- pretend to do setup QCommandLineOption oTest(QStringList() << "t" << "test", - QCoreApplication::translate("main", "Test mode, don't actually apply any changes.")); - parser.addOption(oTest); + QCoreApplication::translate("main", "Test mode, don't actually apply any changes.")); + //parser.addOption(oTest); TODO Not fully implemented so disabled for now // Wakeup beamergui daemon via DBus connect QCommandLineOption oWakeup(QStringList() << "w" << "wakeup", - QCoreApplication::translate("main", "Connect to system bus to trigger wakeup of wainting beamergui.")); + QCoreApplication::translate("main", "Connect to system bus to trigger wakeup of wainting beamergui.")); parser.addOption(oWakeup); + // Set all outputs to clone mode and center them according to the largest output + QCommandLineOption oCenter(QStringList() << "c" << "center", + QCoreApplication::translate("main", "Set all outputs to clone mode. If size differs, center outputs according to largest output.")); + parser.addOption(oCenter); // PARSE parser.process(a); _testMode = parser.isSet(oTest); @@ -109,4 +117,6 @@ static void parseCommandLine(const QCoreApplication &a) _showGui = parser.isSet(oShowGui); _backgroundMode = parser.isSet(oBackground); _wakeup = parser.isSet(oWakeup); + _center = parser.isSet(oCenter); } + diff --git a/src/main.h b/src/main.h index 9aa2d8d..06a0e70 100644 --- a/src/main.h +++ b/src/main.h @@ -7,6 +7,8 @@ bool testMode(); bool autoSetup(); bool showGui(); bool backgroundMode(); +bool wakeup(); +bool center(); } #endif // MAIN_H diff --git a/src/widget.cpp b/src/widget.cpp index 85aacea..0d7ddcc 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -81,8 +81,8 @@ Widget::Widget(QWidget *parent) : _popupCount(0), _iProjector(QIcon(":projector")), _iScreen(QIcon(":screen")), - _lastScreenCount(0), - _isDbus(true) + _addEventDbus(false), + _addEventOther(false) { _ui->setupUi(this); // Add refresh button @@ -126,16 +126,24 @@ Widget::Widget(QWidget *parent) : // Timer QTimer *t = new QTimer(this); t->setSingleShot(true); - // Worked fine + // Timeout after screen setup changed -- handle connect(t, &QTimer::timeout, [=]() { + // Query how many screens there are now int currentCount = ScreenSetup::inst()->queryCurrentOutputCount(); - qDebug() << "Timeout. Dbus:" << _isDbus << "old count:" << _lastScreenCount << "new count:" << currentCount; - if (_isDbus && _lastScreenCount >= currentCount) - return; // Ignore dbus events if the screen count didn't change (or decreased) - if (this->isHidden()) { + qDebug() << "Timeout. Dbus:" << _addEventDbus << "X:" << _addEventOther << "old count:" << _lastScreenCount << "new count:" << currentCount; + if ((_addEventDbus && !_addEventOther) && currentCount <= _lastScreenCount) { + // Ignore dbus-only events if the screen count didn't change (or decreased) + } else if (this->isHidden()) { + // GUI is currently hidden ScreenSetup::inst()->initModes(); - _isDbus = true; - this->show(); + if (_addEventDbus && currentCount > _lastScreenCount) { // DBUS event was triggered and screen count increased - auto setup + ScreenSetup::inst()->setDefaultMode(CommandLine::testMode()); + if (!keepResolution()) { + ScreenSetup::inst()->revertChanges(); + } + } else { // Not DBus or screen count decreased - pop up GUI + this->show(); + } } else { if (currentCount > _lastScreenCount) { ScreenSetup::inst()->initModes(); @@ -144,10 +152,12 @@ Widget::Widget(QWidget *parent) : _ui->btnReload->setStyleSheet(resetButtonHotStyle); } } + _addEventDbus = false; + _addEventOther = false; }); auto popupGui = [=]() { if (this->isHidden()) { - t->start(1000); + t->start(1500); } else { _ui->btnReload->setStyleSheet(resetButtonHotStyle); } @@ -159,8 +169,7 @@ Widget::Widget(QWidget *parent) : qDebug() << "QT SEES SCREEN" << scrn->geometry(); _qtScreens.append(scrn); if (CommandLine::backgroundMode()) { - qDebug() << "Qt setting FALSE"; - _isDbus = false; + _addEventOther = true; popupGui(); } }); @@ -180,6 +189,7 @@ Widget::Widget(QWidget *parent) : qDebug() << "\\o/ Received DBus connect notification \\o/"; if (isSession) { // Session bus means user triggered, show immediately this->show(); + this->raise(); } else { // Otherwise, systembus means udev event -- take normal route popupGui(); } @@ -192,11 +202,12 @@ Widget::Widget(QWidget *parent) : _ui->btnReload->setStyleSheet(resetButtonHotStyle); } } else { - _isDbus = false; + _addEventOther = true; popupGui(); } }); } + _lastScreenCount = ScreenSetup::inst()->queryCurrentOutputCount(); } //______________________________________________________________________________ @@ -209,6 +220,7 @@ void Widget::showEvent(QShowEvent *event) QWidget::showEvent(event); initControls(true); updateWindowPlacement(true); + raise(); } static void fillCombo(QComboBox *combo, const ResolutionVector &resolutions, const QSize &preselected, const QSize &preferred = QSize()) @@ -509,7 +521,7 @@ bool Widget::keepResolution() if (skip) continue; // Show a dialog asking if the res should be kept - TimeOutDialog *keepDialog = new TimeOutDialog(15, this); + TimeOutDialog *keepDialog = new TimeOutDialog(15); QSize s = (geo.size() - keepDialog->size()) / 2; QPoint tl = geo.topLeft(); tl.rx() += s.width(); diff --git a/src/widget.h b/src/widget.h index ddf14fb..a91b556 100644 --- a/src/widget.h +++ b/src/widget.h @@ -35,7 +35,8 @@ private: bool _popupCount; QIcon _iProjector, _iScreen; int _lastScreenCount; - bool _isDbus; + bool _addEventDbus; + bool _addEventOther; void initControls(bool jumpToTab = false); void connectButtons(); diff --git a/src/widget.ui b/src/widget.ui index fe6f67b..022bfcb 100644 --- a/src/widget.ui +++ b/src/widget.ui @@ -6,7 +6,7 @@ 0 0 - 687 + 638 340 @@ -16,6 +16,9 @@ 0 + + Screen and Projector Setup + :/projector:/projector @@ -124,7 +127,7 @@ border: 2px solid black; - + @@ -160,6 +163,12 @@ border: 2px solid black; + + + 0 + 0 + + <- Swap -> @@ -299,7 +308,7 @@ border: 2px solid black; - Close + Done diff --git a/src/xprivate.cpp b/src/xprivate.cpp index 2f4cd55..0233f5b 100644 --- a/src/xprivate.cpp +++ b/src/xprivate.cpp @@ -284,8 +284,11 @@ void XPrivate::disconnectAllCrtcs() { // Disconnect everything for (auto crtc : _crtcMap.keys()) { - XRRSetCrtcConfig(_display, _screenResources, crtc, CurrentTime, + Status s = XRRSetCrtcConfig(_display, _screenResources, crtc, CurrentTime, 0, 0, None, RR_Rotate_0, nullptr, 0); + if (s != RRSetConfigSuccess) { + qDebug() << "Disconnecting CRTC" << crtc << "failed"; + } } } @@ -396,7 +399,7 @@ next:; return None; } -XRRModeInfo* XPrivate::getPreferredMode(OutputInfo *oi) const +XRRModeInfo* XPrivate::getPreferredMode(OutputInfo *oi, XRRModeInfo *fallback) const { if (oi->output->nmode == 0) { qDebug() << "getPreferredMode: Output" << oi->outputName << "has no modes!?"; @@ -406,7 +409,13 @@ XRRModeInfo* XPrivate::getPreferredMode(OutputInfo *oi) const if (oi->output->npreferred > 0) { mode = oi->output->modes[0]; } else { - mode = getOutputModeForResolution(oi->output, 1920, 1080); + mode = None; + if (fallback != nullptr) { + mode = getOutputModeForResolution(oi->output, fallback->width, fallback->height); + } + if (mode == None) { + mode = getOutputModeForResolution(oi->output, 1920, 1080); + } if (mode == None) { mode = getOutputModeForResolution(oi->output, 1280, 720); } @@ -428,12 +437,12 @@ QList XPrivate::getTotalSize(const QList &projectors, const QList modes; for (int i = 0; i < max; ++i) { XRRModeInfo *mode = nullptr; - if (i < projectors.size()) { - mode = getPreferredMode(projectors.at(i)); - } - if (mode == nullptr && i < screens.size()) { + if (i < screens.size()) { mode = getPreferredMode(screens.at(i)); } + if (i < projectors.size()) { + mode = getPreferredMode(projectors.at(i), mode); + } if (mode != nullptr) { modes.append(QSize(int(mode->width), int(mode->height))); } @@ -443,23 +452,27 @@ QList XPrivate::getTotalSize(const QList &projectors, const bool XPrivate::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun) { - RRMode mode = getOutputModeForResolution(oi->output, size); - if (mode == None) { - qDebug() << "Cannot set" << oi->outputName << "to" << size << " since it's not supported"; - if (oi->output->nmode == 0) - return false; - qDebug() << "falling back to its default"; - mode = oi->output->modes[0]; - } - if (!dryRun) { - XRRSetCrtcConfig(_display, - _screenResources, - oi->output->crtc, - CurrentTime, - x, y, mode, - RR_Rotate_0, - &oi->id, 1); - } - qDebug() << "Set" << oi->outputName << "to" << _modeMap[mode]->width << "x" << _modeMap[mode]->height << "-- offset" << x << "/" << y; + RRMode mode = getOutputModeForResolution(oi->output, size); + Status s = RRSetConfigSuccess; + if (mode == None) { + qDebug() << "Cannot set" << oi->outputName << "to" << size << " since it's not supported"; + if (oi->output->nmode == 0) + return false; + qDebug() << "falling back to its default"; + mode = oi->output->modes[0]; + } + if (!dryRun) { + s = XRRSetCrtcConfig(_display, + _screenResources, + oi->output->crtc, + CurrentTime, + x, y, mode, + RR_Rotate_0, + &oi->id, 1); + } + qDebug() << (s != RRSetConfigSuccess ? "Failed to" : "") << "Set" << oi->outputName << "to" << _modeMap[mode]->width << "x" << _modeMap[mode]->height << "-- offset" << x << "/" << y; + if (s == RRSetConfigSuccess) { return true; + } + return false; } diff --git a/src/xprivate.h b/src/xprivate.h index 6725273..fd0e9f3 100644 --- a/src/xprivate.h +++ b/src/xprivate.h @@ -48,7 +48,7 @@ public: void createCrtcBackup(); void freeCrtcBackup(); void disconnectAllCrtcs(); - XRRModeInfo* getPreferredMode(OutputInfo *oi) const; + XRRModeInfo* getPreferredMode(OutputInfo *oi, XRRModeInfo *fallback = nullptr) const; void setScreenSize(const QSize &size); RRMode getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const; RRMode getOutputModeForResolution(const XRROutputInfo *output, const QSize &resolution) const; diff --git a/src/xx.cpp b/src/xx.cpp index 0a98b00..0707f8b 100644 --- a/src/xx.cpp +++ b/src/xx.cpp @@ -205,6 +205,7 @@ ScreenMode ScreenSetup::setDefaultMode(bool dryRun) return ScreenMode::Advanced; // Dunno lol QSize screenSize = getTotalSizeHorz(outputSizes); if (!dryRun) { + a->createCrtcBackup(); XGrabServer(a->_display); a->disconnectAllCrtcs(); // Set new screen size @@ -244,6 +245,7 @@ bool ScreenSetup::createMode(unsigned int resX, unsigned int resY, float refresh if (mode == nullptr) return false; XRRModeInfo m; + memset(&m, 0, sizeof(m)); m.width = static_cast(mode->hr); m.height = static_cast(mode->vr); m.dotClock = static_cast(mode->pclk) * 1000ul * 1000ul; @@ -255,8 +257,9 @@ bool ScreenSetup::createMode(unsigned int resX, unsigned int resY, float refresh m.vSyncEnd = static_cast(mode->vse); m.vTotal = static_cast(mode->vfl); m.id = 0; - m.name = ba.data(); - m.nameLength = static_cast(ba.length()); + m.name = ba.data(); + m.nameLength= static_cast(ba.length()); + m.modeFlags = RR_VSyncPositive | RR_HSyncNegative; free(mode); for (XRRModeInfo *mode : a->_modeMap) { @@ -279,6 +282,41 @@ ScreenSetup::~ScreenSetup() delete a; } +bool ScreenSetup::setCenteredClone() +{ + a->createCrtcBackup(); + XRRModeInfo *fallback = nullptr; + for (auto m : a->_modeMap) { + if (m->width == 1024 && m->height == 768) { + fallback = m; + break; + } + } + XGrabServer(a->_display); + a->disconnectAllCrtcs(); + QSize screenSize; + bool ok = false; + for (auto oi : a->_outputMap) { + auto mode = a->getPreferredMode(oi, fallback); + if (mode != nullptr) { + if (mode->width > screenSize.width()) { + screenSize.setWidth(mode->width); + } + if (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, 0, 0, QSize(int(mode->width), int(mode->height))) || ok; + } + } + a->setScreenSize(screenSize); + XUngrabServer(a->_display); + XSync(a->_display, False); + return ok; +} + bool ScreenSetup::setClone(const QSize &resolution) { a->createCrtcBackup(); diff --git a/src/xx.h b/src/xx.h index ff0ea11..2e2e3f2 100644 --- a/src/xx.h +++ b/src/xx.h @@ -49,6 +49,7 @@ public: 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); ResolutionVector getCommonModes() const; -- cgit v1.2.3-55-g7522