From 6d0152f2fd794fe799c1287b6f0aa45c43714272 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 12 Oct 2022 16:27:17 +0200 Subject: Try even more **** to get a sensible resultion on stubborn systems --- src/loginrpc.cpp | 165 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 114 insertions(+), 51 deletions(-) diff --git a/src/loginrpc.cpp b/src/loginrpc.cpp index 86bfe07..3d6092b 100644 --- a/src/loginrpc.cpp +++ b/src/loginrpc.cpp @@ -4,9 +4,18 @@ #include #include #include +#include #include #include +static int setMode(const QString &virtOut, const QString &name, const QStringList &allOutputs); + +static int sizeDiff(const QSize &a, const QSize &b) +{ + return qAbs((a.width() * a.width()) - (b.width() * b.width())) + + qAbs((a.height() * a.height()) - (b.height() * b.height())); +} + LoginRpc::LoginRpc(int port, QObject *parent) : QObject(parent) { @@ -48,6 +57,11 @@ void LoginRpc::handleCommandV1(const QString &command) QStringList parts = res.split("x"); qDebug() << "Got resolution" << res << "parsed to" << parts; if (parts.size() == 2) { + struct { + QString output; + QString modeName; + QSize size; + } bestConMode, bestDisconMode, *currentReadMode = nullptr; int x = parts[0].toInt(); int y = parts[1].toInt(); qDebug() << "As int:" << x << y; @@ -59,11 +73,17 @@ void LoginRpc::handleCommandV1(const QString &command) if (y < 720) y = 720; if (x > 1920) x = 1920; if (y > 1080) y = 1080; - QString name = QString("%1x%2_30").arg(x).arg(y); - mode *mode = vert_refresh(x, y, 30, 0, 0, 0); + QString name = QString("%1x%2_60").arg(x).arg(y); + mode *mode = vert_refresh(x, y, 60, 0, 0, 0); QProcess p; + // Fetch xrandr default output once p.setProcessChannelMode(QProcess::MergedChannels); + p.start("xrandr", QStringList()); + p.waitForFinished(2000); + p.kill(); + QString xrandrOutput = QString::fromLocal8Bit(p.readAll()); qDebug() << "Creating new mode via xrandr"; + p.setProcessChannelMode(QProcess::ForwardedChannels); QStringList newmode = QStringList() << "--verbose" << "--newmode" << name << QString::asprintf("%.2f", mode->pclk) << QString::number(mode->hr) << QString::number(mode->hss) << QString::number(mode->hse) << QString::number(mode->hfl) << QString::number(mode->vr) << QString::number(mode->vss) << QString::number(mode->vse) << QString::number(mode->vfl) @@ -71,15 +91,49 @@ void LoginRpc::handleCommandV1(const QString &command) // Create mode p.start("xrandr", newmode); p.waitForFinished(2000); - //bool lowResFallback = p.exitCode() != 0; + qDebug() << "Exit code" << p.exitCode(); + bool lowResFallback = p.exitCode() != 0; p.kill(); + do { + // Find best match among existing modes in case --newmode failed, or --addmode fails later + QSize wantedSize(x, y); + QStringList lines = xrandrOutput.split(QRegularExpression("[\r\n]+")); + QString curScreen; + bool curConnected = false; + QRegularExpression scr("^([A-Za-z0-9_-]+)\\s+(connected|disconnected)\\s+"); + QRegularExpression mode("^\\s+([0-9]+)x([0-9]+)(\\S*)\\s+\\d"); + for (const auto &line : lines) { + //qDebug() << "Line" << line; + auto m = scr.match(line); + if (m.hasMatch()) { + // Is a line that starts a new screen section + curScreen = m.captured(1); + curConnected = m.captured(2) == QStringLiteral("connected"); + currentReadMode = curConnected ? &bestConMode : &bestDisconMode; + //qDebug() << "Output" << curScreen << curConnected; + continue; + } + if (currentReadMode != nullptr) { + m = mode.match(line); + if (m.hasMatch()) { + // Is a resolution/mode + QSize s(m.captured(1).toInt(), m.captured(2).toInt()); + //qDebug() << "Matched resolution" << m.captured() << s; + //qDebug() << "Current:" << sizeDiff(currentReadMode->size, wantedSize) << ", This: " << sizeDiff(s, wantedSize); + if (s.width() > 1000 && s.height() > 700 && sizeDiff(currentReadMode->size, wantedSize) > sizeDiff(s, wantedSize)) { + // Better + qDebug() << s << "is better than" << currentReadMode->size << "on" << curScreen << curConnected; + currentReadMode->output = curScreen; + currentReadMode->modeName = m.captured(1) + "x" + m.captured(2) + m.captured(3); + currentReadMode->size = s; + } + } + } + } + } while(0); // Get all outputs - QStringList setMode("--verbose"); - p.start("xrandr", QStringList()); - p.waitForFinished(2000); - QString out = QString::fromLocal8Bit(p.readAll()); QRegularExpression re("^(\\S+)\\s.*?(\\w*connected)", QRegularExpression::MultilineOption); - QRegularExpressionMatchIterator it = re.globalMatch(out); + QRegularExpressionMatchIterator it = re.globalMatch(xrandrOutput); QString virtOut, evdiOut; QStringList allOutputs, disconnectedOutputs, connectedOutputs; while (it.hasNext()) { @@ -106,32 +160,35 @@ void LoginRpc::handleCommandV1(const QString &command) allOutputs << evdiOut; } qDebug() << "Virtual output:" << virtOut << "unwanted additional outputs:" << allOutputs; - p.kill(); int ret = -1; + // Always try virtual output first, even if --newmode failed. Might have to be re-evaluated in the future if (!virtOut.isEmpty()) { - setMode << "--output" << virtOut << "--mode" << name; - for (auto output : allOutputs) { - setMode << "--output" << output << "--off"; - } - qDebug() << "Setting mode" << setMode; - p.setProcessChannelMode(QProcess::ForwardedChannels); // Add to virtual output p.start("xrandr", QStringList() << "--verbose" << "--addmode" << virtOut << name); p.waitForFinished(2000); p.kill(); - // Set all outputs - p.start("xrandr", setMode); - p.waitForFinished(2000); - ret = p.exitCode(); - p.kill(); + ret = setMode(virtOut, name, allOutputs); if (ret != 0) { allOutputs << virtOut; } } + // Overridden, because --newmode failed? + if (lowResFallback) { + if (ret != 0 && !bestDisconMode.modeName.isEmpty()) { + qDebug() << "Ret" << ret << "- Trying to enable best disconnected screen (1)"; + ret = setMode(bestDisconMode.output, bestDisconMode.modeName, allOutputs); + bestDisconMode.modeName.clear(); + } + if (ret != 0 && !bestConMode.modeName.isEmpty()) { + qDebug() << "Ret" << ret << "- Trying to enable best connected screen (1)"; + ret = setMode(bestConMode.output, bestConMode.modeName, allOutputs); + bestConMode.modeName.clear(); + } + } // Either -1 if we didn't have a virtual one, or != 0 if xrandr setting failed // Now as fallback, try enabling a disconnected output only if (ret != 0 && !disconnectedOutputs.isEmpty()) { - qDebug() << "Ret:" << ret << "- Trying to enable one disconnected output"; + qDebug() << "Ret:" << ret << "- Trying to enable one random disconnected output"; QRegularExpression re("\\d+$"); QString dis; int used[10] = {}; @@ -157,49 +214,55 @@ void LoginRpc::handleCommandV1(const QString &command) if (dis.isEmpty()) { dis = disconnectedOutputs.first(); } - setMode.clear(); - setMode << "--verbose" << "--output" << dis << "--mode" << name; - for (auto s : allOutputs) { - if (s == dis) - continue; // Not the one we need - setMode << "--output" << s << "--off"; - } - qDebug() << "Setting mode" << setMode; - p.setProcessChannelMode(QProcess::ForwardedChannels); // Add to output p.start("xrandr", QStringList() << "--verbose" << "--addmode" << dis << name); p.waitForFinished(2000); p.kill(); - // Set - p.start("xrandr", setMode); - p.waitForFinished(2000); - ret = p.exitCode(); - p.kill(); + ret = setMode(dis, name, allOutputs); + } + // Try this stuff again in case --newmode succeeded but --addmode now didn't (NVIDIA) + if (ret != 0 && !bestDisconMode.modeName.isEmpty()) { + qDebug() << "Ret" << ret << "- Trying to enable best disconnected screen (2)"; + ret = setMode(bestDisconMode.output, bestDisconMode.modeName, allOutputs); + bestDisconMode.modeName.clear(); } // Now as fallback, try enabling just one connected output - if (ret != 0) { - qDebug() << "Ret:" << ret << "- Trying to enable one connected output"; + if (ret != 0 && !connectedOutputs.isEmpty()) { + qDebug() << "Ret:" << ret << "- Trying to enable one random connected output"; QString conn = connectedOutputs.first(); - setMode.clear(); - setMode << "--verbose" << "--output" << conn << "--mode" << name; - for (auto s : allOutputs) { - if (s == conn) - continue; // Not the one we need - setMode << "--output" << s << "--off"; - } - qDebug() << "Setting mode" << setMode; - p.setProcessChannelMode(QProcess::ForwardedChannels); // Add to output p.start("xrandr", QStringList() << "--verbose" << "--addmode" << conn << name); p.waitForFinished(2000); p.kill(); - // Set - p.start("xrandr", setMode); - p.waitForFinished(2000); - ret = p.exitCode(); - p.kill(); + ret = setMode(conn, name, allOutputs); + } + // Try this stuff again in case --newmode succeeded but --addmode now didn't (NVIDIA) + if (ret != 0 && !bestConMode.modeName.isEmpty()) { + qDebug() << "Ret" << ret << "- Trying to enable best connected screen (2)"; + ret = setMode(bestConMode.output, bestConMode.modeName, allOutputs); + bestConMode.modeName.clear(); } } } emit loginRequest(lines[0], lines[1], lines[2]); } + +static int setMode(const QString &virtOut, const QString &name, const QStringList &allOutputs) +{ + QStringList setMode(QStringLiteral("--verbose")); + QProcess p; + p.setProcessChannelMode(QProcess::ForwardedChannels); + for (auto output : allOutputs) { + if (output == virtOut) + continue; // Skip self + setMode << "--output" << output << "--off"; + } + setMode << "--output" << virtOut << "--mode" << name; + // Set all outputs + qDebug() << setMode; + p.start("xrandr", setMode); + p.waitForFinished(2000); + int ret = p.exitCode(); + p.kill(); + return ret; +} -- cgit v1.2.3-55-g7522