diff options
-rw-r--r-- | src/main.cpp | 10 | ||||
-rw-r--r-- | src/widget.cpp | 2 | ||||
-rw-r--r-- | src/xprivate.cpp | 67 | ||||
-rw-r--r-- | src/xprivate.h | 12 | ||||
-rw-r--r-- | src/xx.cpp | 131 | ||||
-rw-r--r-- | src/xx.h | 3 |
6 files changed, 89 insertions, 136 deletions
diff --git a/src/main.cpp b/src/main.cpp index 8df19e2..59c886b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) ScreenSetup::inst()->setCenteredClone(); } else if (CommandLine::autoSetup()) { ScreenMode mode; - ScreenSetup::inst()->setDefaultMode(CommandLine::testMode(), mode); + ScreenSetup::inst()->setDefaultMode(mode); } else { ScreenSetup::inst()->getCurrentMode(); } @@ -98,12 +98,10 @@ static void parseCommandLine(const QCoreApplication &a) QCommandLineOption oBackground(QStringList() << "b" << "background", 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); TODO Not fully implemented so disabled for now - */ + parser.addOption(oTest); // Wakeup beamergui daemon via DBus connect QCommandLineOption oWakeup(QStringList() << "w" << "wakeup", QCoreApplication::translate("main", "Connect to system bus to trigger wakeup of wainting beamergui.")); @@ -113,10 +111,8 @@ static void parseCommandLine(const QCoreApplication &a) 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); - /* + parser.process(a); _testMode = parser.isSet(oTest); - */ _autoSetup = parser.isSet(oAutoSetup); _showGui = parser.isSet(oShowGui); _backgroundMode = parser.isSet(oBackground); diff --git a/src/widget.cpp b/src/widget.cpp index 12bbfd1..6b231fa 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -142,7 +142,7 @@ Widget::Widget(QWidget *parent) : _lastScreenCount = currentCount; ScreenSetup::inst()->initModes(); ScreenMode mode; - auto ret = ScreenSetup::inst()->setDefaultMode(CommandLine::testMode(), mode); + auto ret = ScreenSetup::inst()->setDefaultMode(mode); if (!ret.ok() || !keepResolution()) { ret.revert(); } diff --git a/src/xprivate.cpp b/src/xprivate.cpp index b8f10b6..370c6f5 100644 --- a/src/xprivate.cpp +++ b/src/xprivate.cpp @@ -7,13 +7,6 @@ static Display* __display; static XErrorHandler old_handler; -static double toVertRefresh(const XRRModeInfo *mode) -{ - if (mode->hTotal > 0 && mode->vTotal > 0) - return (double(mode->dotClock) / (double(mode->hTotal) * double(mode->vTotal))); - return 0; -} - /** * Check list of known model names that falsely report a screen size or similar */ @@ -173,6 +166,7 @@ void XPrivate::updateScreenResources() qDebug() << "updateScreenResources: Outputs"; QHash<RROutput, QString> tempMap; QMap<Projector, int> typeCount; + _allOutputs.clear(); for (int i = 0; i < _screenResources->noutput; ++i) { XRROutputInfo* info = XRRGetOutputInfo( _display, @@ -183,6 +177,7 @@ void XPrivate::updateScreenResources() continue; } const QString outputName = QString::fromLocal8Bit(info->name, info->nameLen); + _allOutputs.append(outputName); tempMap.insert(_screenResources->outputs[i], outputName); if (info->connection == RR_Disconnected) { qDebug() << "Ignoring disconnected output" << outputName; @@ -446,26 +441,6 @@ void XPrivate::addMissingModesFromDtd(XRRScreenResources *res, RROutput outputId } } -void XPrivate::setScreenSize(const QSize &size) -{ - XRRSetScreenSize(_display, DefaultRootWindow(_display), - size.width(), size.height(), - int(25.4 * size.width() / 96.0), // standard dpi that X uses - int(25.4 * size.height() / 96.0)); // standard dpi that X uses -} - -void XPrivate::disconnectAllCrtcs() -{ - // Disconnect everything - for (auto crtc : _crtcMap.keys()) { - Status s = XRRSetCrtcConfig(_display, _screenResources, crtc, CurrentTime, - 0, 0, None, RR_Rotate_0, nullptr, 0); - if (s != RRSetConfigSuccess) { - qDebug() << "Disconnecting CRTC" << crtc << "failed"; - } - } -} - /** * Copy first "num" modes from output to all other outputs if they * dont have a suitable mode yet. @@ -514,6 +489,8 @@ QList<RRMode> XPrivate::getOutputModeForResolution(const XRROutputInfo *output, } // Sort others descending by dotClock qSort(others.begin(), others.end(), [](const XRRModeInfo *a, const XRRModeInfo *b) { + if ((a->modeFlags & RR_DoubleScan) != (b->modeFlags & RR_DoubleScan)) + return (a->modeFlags & RR_DoubleScan) == 0; // Prefer non-interlaced resolutions return toVertRefresh(a) > toVertRefresh(b); }); QList<RRMode> ret; @@ -661,38 +638,22 @@ QList<QSize> XPrivate::getTotalSize(const QList<OutputInfo*> &projectors, const return modes; } -bool XPrivate::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun) +void XPrivate::setOutputResolution(QStringList &args, OutputInfo *oi, int x, int y, const QSize &size) { QList<RRMode> modes = getOutputModeForResolution(oi->output, size); - Status s = RRSetConfigSuccess; if (modes.isEmpty()) { 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"; + return; + qDebug() << "falling back to its default mode"; modes.append(oi->output->modes[0]); } - if (!dryRun) { - for (RRMode mode : modes) { - qDebug() << "Trying" << toVertRefresh(_modeMap[mode]) << "Hz"; - s = XRRSetCrtcConfig(_display, - _screenResources, - oi->output->crtc, - CurrentTime, - x, y, mode, - RR_Rotate_0, - &oi->id, 1); - if (s == RRSetConfigSuccess) { - if (x == 0 && y == 0) { - XRRSetOutputPrimary(_display, DefaultRootWindow(_display), oi->id); - } - break; - } - } - } - qDebug() << (s != RRSetConfigSuccess ? "Failed to" : "") << "Set" << oi->outputName << "to" << size << "-- offset" << x << "/" << y; - if (s == RRSetConfigSuccess) { - return true; + // modes list is known to be sorted by preferred resolution/rate, then sorted by highest to lowest rate + // so we just pick the first one for now, as the GUI doesn't offer picking the rate + XRRModeInfo *best = _modeMap[modes.first()]; + args << "--output" << oi->outputName << "--mode" << best->name << "--rate" << QString::number(toVertRefresh(best), 'f', 2); + args << "--x" << QString::number(x) << "--y" << QString::number(y); + if (x == 0 && y == 0 && !args.contains(QLatin1String("--primary"))) { + args.append("--primary"); } - return false; } diff --git a/src/xprivate.h b/src/xprivate.h index 2937442..d0f2d53 100644 --- a/src/xprivate.h +++ b/src/xprivate.h @@ -49,13 +49,11 @@ public: bool readEdid(OutputInfo* output); void addMissingModesFromExtBlock(XRRScreenResources *res, RROutput outputId, unsigned char *data); void addMissingModesFromDtd(XRRScreenResources *res, RROutput outputId, unsigned char *data); - void disconnectAllCrtcs(); XRRModeInfo* getPreferredMode(const OutputInfo *oi, XRRModeInfo *fallback = nullptr) const; - void setScreenSize(const QSize &size); QList<RRMode> getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const; QList<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); + void setOutputResolution(QStringList &args, OutputInfo *oi, int x, int y, const QSize &size); QList<QSize> getTotalSize(const QList<OutputInfo*> &projectors, const QList<OutputInfo*> &screens) const; void copyModesToAll(RROutput id, int num); @@ -65,7 +63,15 @@ public: ModeMap _modeMap; CrtcMap _crtcMap; OutputMap _outputMap; + QStringList _allOutputs; ResolutionVector _resolutions; }; +static double toVertRefresh(const XRRModeInfo *mode) +{ + if (mode->hTotal > 0 && mode->vTotal > 0) + return (double(mode->dotClock) / (double(mode->hTotal) * double(mode->vTotal))); + return 0; +} + #endif // XPRIVATE_H @@ -1,9 +1,11 @@ #include "xx.h" #include "xprivate.h" #include "cvt.h" +#include "main.h" #include <QDebug> #include <QSocketNotifier> #include <QThread> +#include <QProcess> /* * This clusterfuck exists because there are name clashes between X11/Xrandr headers @@ -55,24 +57,24 @@ public: if (map.isEmpty()) return; qDebug() << "Starting revert"; - XGrabServer(x->_display); - x->disconnectAllCrtcs(); + QStringList cmd; QSize screenSize; for (auto e : map) { if (e->mode == None || !x->_modeMap.contains(e->mode)) continue; + XRRModeInfo *mode = x->_modeMap[e->mode]; + QString rate = QString::number(toVertRefresh(mode), 'f', 2); + for (int i = 0; i < e->noutput; ++i) { + auto *oi = x->_outputMap[e->outputs[i]]; + cmd << "--output" << oi->outputName << "--mode" << mode->name << "--rate" << rate; + cmd << "--x" << QString::number(oi->crtc->x) << "--y" << QString::number(oi->crtc->y); + if (oi->crtc->x == 0 && oi->crtc->y == 0 && !cmd.contains("--primary")) { + cmd << "--primary"; + } + } 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); + ScreenSetup::inst()->runXrandr(cmd); freeBackup(); } }; @@ -220,13 +222,6 @@ ScreenSetup::ScreenSetup() : a(new XPrivate()) } -static double toVertRefresh(const XRRModeInfo *mode) -{ - if (mode->hTotal > 0 && mode->vTotal > 0) - return (double(mode->dotClock) / (double(mode->hTotal) * double(mode->vTotal))); - return 0; -} - void ScreenSetup::addMissingEdidResolutions() { a->addMissingEdidResolutions(); @@ -330,7 +325,7 @@ bool ScreenSetup::hasScreenWithoutEdid() return false; } -ConfigBackup ScreenSetup::setDefaultMode(bool dryRun, ScreenMode &mode) +ConfigBackup ScreenSetup::setDefaultMode(ScreenMode &mode) { ConfigBackup retval; if (a->_outputMap.size() == 1) { // Only one output exists, do nothing @@ -356,36 +351,24 @@ ConfigBackup ScreenSetup::setDefaultMode(bool dryRun, ScreenMode &mode) mode = ScreenMode::Advanced; // Dunno lol return retval; } - QSize screenSize = getTotalSizeHorz(outputSizes); - if (!dryRun) { - retval = createCrtcBackup(); - XGrabServer(a->_display); - a->disconnectAllCrtcs(); - // Set new screen size - a->setScreenSize(screenSize); - } + QSize screenSize = getTotalSizeHorz(outputSizes); + QStringList cmd; + retval = createCrtcBackup(); qDebug() << "Virtual screen size:" << screenSize << "with" << outputSizes.size() << "different screens."; int offset = 0; - for (int i = 0; i < outputSizes.size(); ++i) { - bool ok = false; + for (int i = 0; i < outputSizes.size(); ++i) { const QSize &size = outputSizes.at(i); if (i < projectors.size()) { - ok = a->setOutputResolution(projectors.at(i), offset, 0, size, dryRun) || ok; + a->setOutputResolution(cmd, projectors.at(i), offset, 0, size); } if (i < screens.size()) { - ok = a->setOutputResolution(screens.at(i), offset, 0, size, dryRun) || ok; + a->setOutputResolution(cmd, screens.at(i), offset, 0, size); } - offset += size.width(); - if (ok) { - retval._ok = true; - } - } - if (!dryRun) { - XUngrabServer(a->_display); - XSync(a->_display, False); - } + offset += size.width(); + } + retval._ok = runXrandr(cmd); updateScreenResources(); // Re-Read if (outputSizes.size() == 1) { // One output size, at least 2 outputs in total -- clone mode @@ -478,9 +461,8 @@ ConfigBackup ScreenSetup::setCenteredClone() return retval; break; } - XGrabServer(a->_display); - a->disconnectAllCrtcs(); - QSize screenSize; + QStringList cmd; + QSize screenSize; // Currently unused for (auto oi : a->_outputMap) { auto mode = a->getPreferredMode(oi, fallback); if (mode != nullptr) { @@ -492,36 +474,28 @@ ConfigBackup ScreenSetup::setCenteredClone() } 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); + a->setOutputResolution(cmd, oi, x, y, QSize(int(mode->width), int(mode->height))); + } + } + retval._ok = runXrandr(cmd); return retval; } ConfigBackup ScreenSetup::setClone(const QSize &resolution) { + QStringList cmd; ConfigBackup retval = createCrtcBackup(); - XGrabServer(a->_display); - a->disconnectAllCrtcs(); - a->setScreenSize(resolution); for (auto oi : a->_outputMap) { - if (a->setOutputResolution(oi, 0, 0, resolution)) { - retval._ok = true; - } - } - XUngrabServer(a->_display); - XSync(a->_display, False); + a->setOutputResolution(cmd, oi, 0, 0, resolution); + } + retval._ok = runXrandr(cmd); return retval; } ConfigBackup ScreenSetup::setCustom(const QList<QPair<QSize, QList<QString>>> &list) { ConfigBackup retval; + QStringList cmd; QList<QSize> sizes; for (auto e : list) { if (e.second.isEmpty()) @@ -534,9 +508,6 @@ ConfigBackup ScreenSetup::setCustom(const QList<QPair<QSize, QList<QString>>> &l auto screenSize = getTotalSizeHorz(sizes); if (screenSize.isEmpty()) return retval; - XGrabServer(a->_display); - a->disconnectAllCrtcs(); - a->setScreenSize(screenSize); int x = 0; for (auto e : list) { if (e.second.isEmpty()) @@ -546,15 +517,12 @@ ConfigBackup ScreenSetup::setCustom(const QList<QPair<QSize, QList<QString>>> &l for (auto oi : a->_outputMap) { if (oi->outputName != outputName) continue; - if (a->setOutputResolution(oi, x, 0, res)) { - retval._ok = true; - } - } + a->setOutputResolution(cmd, oi, x, 0, res); + } } x += res.width(); - } - XUngrabServer(a->_display); - XSync(a->_display, False); + } + retval._ok = runXrandr(cmd); return retval; } @@ -634,3 +602,24 @@ const ResolutionVector &ScreenSetup::getVirtualResolutions() const { return a->_resolutions; } + +bool ScreenSetup::runXrandr(QStringList &cmd) +{ + QProcess proc; + // Sloppy: Turn off all outputs not found in argument list. Doesn't actually parse the + // command line, so if you have a mode that's called like an output, funny things might happen. + for (const auto &name : a->_allOutputs) { + if (!cmd.contains(name)) { + cmd << "--output" << name << "--off"; + } + } + qDebug() << "XRANDR:" << cmd; + if (CommandLine::testMode()) + return true; + proc.start("xrandr", cmd); + proc.waitForFinished(5000); + if (proc.state() == QProcess::Running) { + proc.kill(); + } + return proc.exitCode() == 0; +} @@ -68,7 +68,7 @@ public: void initModes(); ScreenMode getCurrentMode(); bool hasScreenWithoutEdid(); - ConfigBackup setDefaultMode(bool dryRun, ScreenMode &mode); + ConfigBackup setDefaultMode(ScreenMode &mode); bool createMode(unsigned int resX, unsigned int resY, float refresh, QString name); ConfigBackup setCenteredClone(); ConfigBackup setClone(const QSize &resolution); @@ -78,6 +78,7 @@ public: int queryCurrentOutputCount() const; QMap<QString, ScreenInfo> getScreenPositions() const; const ResolutionVector &getVirtualResolutions() const; + bool runXrandr(QStringList &cmd); // Singleton inline static ScreenSetup* inst() { |