From 17f21da18d0b356e0981b23f1654df99be4e4b66 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 2 Jul 2019 14:55:32 +0200 Subject: Try all possible modes for a given resolution --- src/xprivate.cpp | 101 +++++++++++++++++++++++++++++++++++++------------------ src/xprivate.h | 4 +-- src/xx.cpp | 2 +- 3 files changed, 72 insertions(+), 35 deletions(-) diff --git a/src/xprivate.cpp b/src/xprivate.cpp index 62a4895..1273e1e 100644 --- a/src/xprivate.cpp +++ b/src/xprivate.cpp @@ -3,6 +3,13 @@ #include #include +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 */ @@ -52,6 +59,14 @@ static int modeEqual(const XRRModeInfo *a, const XRRModeInfo *b) a->modeFlags == b->modeFlags); } +static int errorHandler(Display *dpy, XErrorEvent *err) +{ + char buf[1000]; + XGetErrorText(dpy, err->error_code, buf, 1000); + qDebug() << "**X11 error**" << buf; + return 0; +} + /* * Class members */ @@ -65,6 +80,7 @@ XPrivate::XPrivate() qFatal("Cannot open display"); ::exit(1); } + XSetErrorHandler(errorHandler); _EDID_ATOM = XInternAtom(_display, RR_PROPERTY_RANDR_EDID, False); } @@ -452,7 +468,7 @@ void XPrivate::copyModesToAll(RROutput sourceId, int num) if (other == sourceId) continue; const XRROutputInfo *otherInfo = _outputMap[other]->output; - if (getOutputModeForResolution(otherInfo, mode->width, mode->height) != None) + if (!getOutputModeForResolution(otherInfo, mode->width, mode->height).isEmpty()) continue; XRRAddOutputMode(_display, other, outputInfo->modes[i]); } @@ -460,32 +476,42 @@ void XPrivate::copyModesToAll(RROutput sourceId, int num) } /** - * Get the RRMode for the specified output matching the given resolution. + * Get all known modes matching the given resolution. * If multiple modes match the given resolution, the function will - * return the first matching preferred mode. If no preferred - * mode matches, the non-preferred mode with the highest refresh rate - * will be returned. - * Otherwise, None will be returned. + * put the preferred resolution first, then all others sorted by + * dotClock, descending. */ -RRMode XPrivate::getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const +QList XPrivate::getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const { - XRRModeInfo *retval = nullptr; + XRRModeInfo *pref = nullptr; + QList others; for (int i = 0; i < output->nmode; ++i) { - if (!_modeMap.contains(output->modes[i])) - continue; - XRRModeInfo *info = _modeMap[output->modes[i]]; - if (info->width == width && info->height == height) { - if (i < output->npreferred) - return output->modes[i]; - if (retval == nullptr || retval->dotClock < info->dotClock) { - retval = info; - } + if (!_modeMap.contains(output->modes[i])) + continue; + XRRModeInfo *info = _modeMap[output->modes[i]]; + if (info->width == width && info->height == height) { + if (i < output->npreferred && pref == nullptr) { + pref = info; + } else { + others.append(info); } + } + } + // Sort others descending by dotClock + qSort(others.begin(), others.end(), [](const XRRModeInfo *a, const XRRModeInfo *b) { + return toVertRefresh(a) > toVertRefresh(b); + }); + QList ret; + if (pref != nullptr) { + ret.append(pref->id); + } + for (auto e : others) { + ret.append(e->id); } - return retval == nullptr ? None : retval->id; + return ret; } -RRMode XPrivate::getOutputModeForResolution(const XRROutputInfo *output, const QSize &resolution) const +QList XPrivate::getOutputModeForResolution(const XRROutputInfo *output, const QSize &resolution) const { return getOutputModeForResolution(output, static_cast(resolution.width()), static_cast(resolution.height())); } @@ -563,14 +589,20 @@ XRRModeInfo* XPrivate::getPreferredMode(const OutputInfo *oi, XRRModeInfo *fallb if (fallback != nullptr) { maxX = int(fallback->width); maxY = int(fallback->height); - mode = getOutputModeForResolution(oi->output, fallback->width, fallback->height); + auto list = getOutputModeForResolution(oi->output, fallback->width, fallback->height); + if (!list.isEmpty()) { + mode = list.first(); + } } static const std::vector wanted = {QSize(1920, 1080), QSize(1280, 800), QSize(1280, 720), QSize(1152, 864), QSize(1024, 768)}; for (const QSize s : wanted) { if (mode != None) break; if (s.width() <= maxX && s.height() <= maxY) { - mode = getOutputModeForResolution(oi->output, s.width(), s.height()); + auto list = getOutputModeForResolution(oi->output, s.width(), s.height()); + if (!list.isEmpty()) { + mode = list.first(); + } } } if (mode == None) { @@ -616,25 +648,30 @@ 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); + QList modes = getOutputModeForResolution(oi->output, size); Status s = RRSetConfigSuccess; - if (mode == None) { + 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"; - mode = oi->output->modes[0]; + modes.append(oi->output->modes[0]); } if (!dryRun) { - s = XRRSetCrtcConfig(_display, - _screenResources, - oi->output->crtc, - CurrentTime, - x, y, mode, - RR_Rotate_0, - &oi->id, 1); + 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) + break; + } } - qDebug() << (s != RRSetConfigSuccess ? "Failed to" : "") << "Set" << oi->outputName << "to" << _modeMap[mode]->width << "x" << _modeMap[mode]->height << "-- offset" << x << "/" << y; + qDebug() << (s != RRSetConfigSuccess ? "Failed to" : "") << "Set" << oi->outputName << "to" << size << "-- offset" << x << "/" << y; if (s == RRSetConfigSuccess) { return true; } diff --git a/src/xprivate.h b/src/xprivate.h index c7ef29b..2937442 100644 --- a/src/xprivate.h +++ b/src/xprivate.h @@ -52,8 +52,8 @@ public: void disconnectAllCrtcs(); XRRModeInfo* getPreferredMode(const 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; + QList getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const; + QList 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); QList getTotalSize(const QList &projectors, const QList &screens) const; diff --git a/src/xx.cpp b/src/xx.cpp index 0b91161..62bd353 100644 --- a/src/xx.cpp +++ b/src/xx.cpp @@ -265,7 +265,7 @@ void ScreenSetup::initModes() continue; // Is not a wanted resolution // Make sure all outputs got it for (OutputInfo *info : a->_outputMap) { - if (a->getOutputModeForResolution(info->output, mode->width, mode->height) != None) + if (!a->getOutputModeForResolution(info->output, mode->width, mode->height).isEmpty()) continue; XRRAddOutputMode(a->_display, info->id, mode->id); } -- cgit v1.2.3-55-g7522