diff options
author | Simon Rettberg | 2020-02-16 10:12:22 +0100 |
---|---|---|
committer | Simon Rettberg | 2020-02-16 10:12:22 +0100 |
commit | 6289a836449b67d2a3d54d38332343ec8335aa67 (patch) | |
tree | d44323911a3577ec0c7916d8a0a6cbffb28995b7 | |
parent | Make sure screen at (0, 0) is marked primary (diff) | |
download | beamergui-6289a836449b67d2a3d54d38332343ec8335aa67.tar.gz beamergui-6289a836449b67d2a3d54d38332343ec8335aa67.tar.xz beamergui-6289a836449b67d2a3d54d38332343ec8335aa67.zip |
Use xrandr to actually set resolutions
Get rid of out own crap to try and figure out how to configure
the outputs, crtcs, screens, whatever. xrandr probably is
smarter here, so just build a command line and go.
-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() { |