diff options
-rw-r--r-- | src/xprivate.cpp | 60 | ||||
-rw-r--r-- | src/xprivate.h | 1 | ||||
-rw-r--r-- | 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 <QDebug> #include <QRegularExpression> @@ -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<unsigned int>(mode->hr); + m.height = static_cast<unsigned int>(mode->vr); + m.dotClock = static_cast<unsigned long>(mode->pclk) * 1000ul * 1000ul; + m.hSyncStart= static_cast<unsigned int>(mode->hss); + m.hSyncEnd = static_cast<unsigned int>(mode->hse); + m.hTotal = static_cast<unsigned int>(mode->hfl); + m.hSkew = 0; + m.vSyncStart= static_cast<unsigned int>(mode->vss); + m.vSyncEnd = static_cast<unsigned int>(mode->vse); + m.vTotal = static_cast<unsigned int>(mode->vfl); + m.id = 0; + m.name = ba.data(); + m.nameLength= static_cast<unsigned int>(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<QSize> getTotalSize(const QList<OutputInfo*> &projectors, const QList<OutputInfo*> &screens) const; void copyModesToAll(RROutput id, int num); + bool addResolutionToOutput(OutputInfo *oi, const QSize &res); Display* _display; Atom _EDID_ATOM; @@ -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<QPair<QSize, QList<QString>>> &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; |