From 2901482b60f53ba9846db779601297838ad9f622 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 17 Sep 2020 15:17:01 +0200 Subject: Force-Add resolutions when using --resolutions option If the user explicitly passes desired resolutions on the command line, assume they know what they're doing, and go ahead and force things the way they were requested. Also add more debug spam to relevant code, so we can track down where/why things fail. --- src/xprivate.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xprivate.h | 1 + src/xx.cpp | 43 ++++++++++++++++++++++++++++++++-------- 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/src/xprivate.cpp b/src/xprivate.cpp index 7cb0949..53059e4 100644 --- a/src/xprivate.cpp +++ b/src/xprivate.cpp @@ -1,4 +1,5 @@ #include "xprivate.h" +#include "cvt.h" #include #include @@ -658,3 +659,62 @@ XRRModeInfo* XPrivate::setOutputResolution(QStringList &args, OutputInfo *oi, in } return best; } + +/** + * !! oi pointer might be invalid after calling this !! + * @return true if updateScreenResources() should be called later + */ +bool XPrivate::addResolutionToOutput(OutputInfo *oi, const QSize &res) +{ + if (!getOutputModeForResolution(oi->output, res).empty()) + return false; // Nothing to do + XRRModeInfo *bestMode = nullptr; + for (auto *mode : _modeMap) { + if (int(mode->width) != res.width() || int(mode->height) != res.height()) + continue; + if (toVertRefresh(mode) < 59 || toVertRefresh(mode) > 70) + continue; // Play it safe + if (bestMode == nullptr || abs(toVertRefresh(bestMode) - 60) > abs(toVertRefresh(mode) - 60)) { + bestMode = mode; // As close to 60 as possible + } + } + if (bestMode != nullptr) { + qDebug() << "Adding existing mode" << res.width() << 'x' << res.height() << "to" << oi->outputName; + XRRAddOutputMode(_display, oi->id, bestMode->id); + return true; + } else { + // From scratch + qDebug() << "Creating requested mode" << res.width() << 'x' << res.height() << "from scratch"; + QByteArray ba = QString::asprintf("%dx%d_BG", res.width(), res.height()).toLocal8Bit(); + mode *mode = vert_refresh(res.width(), res.height(), 60, 0, 0, 0); + if (mode == nullptr) + return false; // Failed + 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; + m.hSyncStart= static_cast(mode->hss); + m.hSyncEnd = static_cast(mode->hse); + m.hTotal = static_cast(mode->hfl); + m.hSkew = 0; + m.vSyncStart= static_cast(mode->vss); + 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.modeFlags = RR_VSyncPositive | RR_HSyncNegative; + free(mode); + RRMode xid = XRRCreateMode(_display, DefaultRootWindow(_display), &m); + if (xid <= 0) { + qDebug() << "Creating mode failed"; + return false; + } + qDebug() << "Adding created mode to" << oi->outputName; + XRRAddOutputMode(_display, oi->id, xid); + // Stuff changed, update now in case other screens want the same resolution + updateScreenResources(); + return false; // We just updated ourselves, don't signal "update required" + } +} diff --git a/src/xprivate.h b/src/xprivate.h index 0152fb1..bc8791e 100644 --- a/src/xprivate.h +++ b/src/xprivate.h @@ -56,6 +56,7 @@ public: XRRModeInfo* setOutputResolution(QStringList &args, OutputInfo *oi, int x, int y, const QSize &size); QList getTotalSize(const QList &projectors, const QList &screens) const; void copyModesToAll(RROutput id, int num); + bool addResolutionToOutput(OutputInfo *oi, const QSize &res); Display* _display; Atom _EDID_ATOM; diff --git a/src/xx.cpp b/src/xx.cpp index 179d286..d4a4608 100644 --- a/src/xx.cpp +++ b/src/xx.cpp @@ -267,6 +267,7 @@ void ScreenSetup::initModes() if (!a->getOutputModeForResolution(info->output, mode->width, mode->height).isEmpty()) continue; XRRAddOutputMode(a->_display, info->id, mode->id); + qDebug() << "Adding mode" << mode->width << 'x' << mode->height << "to" << info->outputName; } } #undef RES @@ -274,6 +275,7 @@ void ScreenSetup::initModes() for (auto res : wanted) { unsigned int x = res & 0xffff; unsigned int y = res >> 16; + qDebug() << "Creating missing wanted resolution of" << x << "x" << y; createMode(x, y, 60, QString::asprintf("%ux%u", x, y)); } if (!wanted.isEmpty()) { // Modes were added, update for final loop below @@ -282,7 +284,8 @@ void ScreenSetup::initModes() // Finally copy all those the projector supports to other outputs for (auto key : a->_outputMap.keys()) { OutputInfo *oi = a->_outputMap[key]; - if (oi->outputType == Projector::Yes) { + if (oi->outputType == Projector::Yes && oi->output->npreferred > 0) { // Only if has edid + qDebug() << "Copying" << oi->output->nmode << "modes to all from" << oi->outputName; a->copyModesToAll(key, oi->output->nmode); } } @@ -497,7 +500,10 @@ bool ScreenSetup::createMode(unsigned int resX, unsigned int resY, float refresh } RRMode xid = XRRCreateMode(a->_display, DefaultRootWindow(a->_display), &m); - qDebug() << "Return value of create was" << xid; + if (xid <= 0) { + qDebug() << "Return value of create mode was" << xid << "(" << resX << 'x' << resY << ")"; + return false; + } // Immediately add to all screens for (OutputInfo *info : a->_outputMap) { XRRAddOutputMode(a->_display, info->id, xid); @@ -591,21 +597,42 @@ ConfigBackup ScreenSetup::setCustom(const QList>> &l retval = createCrtcBackup(); auto screenSize = getTotalSizeHorz(sizes); if (screenSize.isEmpty()) - return retval; - int x = 0; + return retval; + // Make sure desired resolutions exist on outputs + bool reload = false; + for (auto e : list) { + if (e.second.isEmpty()) + continue; + const QSize &res = e.first; + for (auto outputName : e.second) { + for (auto oi : a->_outputMap) { + if (oi->outputName != outputName) + continue; + // Now add resolution if not found + if (a->addResolutionToOutput(oi, res)) { + reload = true; + } + } + } + } + if (reload) { + updateScreenResources(); + } + int x = 0; for (auto e : list) { if (e.second.isEmpty()) continue; const QSize &res = e.first; unsigned int w = 0; + // Find according output, apply resolution for (auto outputName : e.second) { for (auto oi : a->_outputMap) { if (oi->outputName != outputName) continue; - auto *mode = a->setOutputResolution(cmd, oi, x, 0, res); - if (mode != nullptr && mode->width > w) { - w = mode->width; - } + auto *mode = a->setOutputResolution(cmd, oi, x, 0, res); + if (mode != nullptr && mode->width > w) { + w = mode->width; + } } } x += w; -- cgit v1.2.3-55-g7522