#include "loginrpc.h"
#include "cvt.h"
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QDebug>
#include <QProcess>
#include <QRegularExpression>
LoginRpc::LoginRpc(int port, QObject *parent)
: QObject(parent)
{
QTcpServer *srv = new QTcpServer(this);
srv->listen(QHostAddress::Any, port);
connect(srv, &QTcpServer::newConnection, [=] {
while (srv->hasPendingConnections()) {
QTcpSocket *sock = srv->nextPendingConnection();
handleIncoming(sock);
}
});
}
void LoginRpc::handleIncoming(QTcpSocket *sock) {
connect(sock, &QTcpSocket::readyRead, [=] {
QByteArray ba = sock->readAll(); // XXX We assume everything arrives in one packet
if (ba.length() < 2) {
sock->deleteLater();
return;
}
int vers = (ba[0] << 8) + ba[1];
ba = ba.mid(2);
ba = QByteArray::fromBase64(ba);
if (vers == 1) {
handleCommandV1(QString::fromUtf8(ba));
} else {
qDebug() << "Ignoring unknown Login RPC version" << vers;
}
});
}
void LoginRpc::handleCommandV1(const QString &command)
{
QStringList lines = command.split('\n');
while (lines.count() < 3) {
lines.append(QString());
}
QString res = lines[2];
QStringList parts = res.split("x");
qDebug() << "Got resolution" << res << "parsed to" << parts;
if (parts.size() == 2) {
int x = parts[0].toInt();
int y = parts[1].toInt();
qDebug() << "As int:" << x << y;
if (x > 0 && y > 0) {
x = (x / 8) * 8;
y = (y / 8) * 8;
// TODO: Configurable min max sizes
if (x < 1024) x = 1024;
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);
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
qDebug() << "Creating new mode via xrandr";
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)
<< "-hsync" << "+vsync";
// Create mode
p.start("xrandr", newmode);
p.waitForFinished(2000);
//bool lowResFallback = p.exitCode() != 0;
p.kill();
// 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);
QString virtOut, evdiOut;
QStringList allOutputs, disconnectedOutputs, connectedOutputs;
while (it.hasNext()) {
QRegularExpressionMatch m = it.next();
QString output = m.captured(1);
if (virtOut.isEmpty() && (output == QLatin1String("VIRTUAL1") || output == QLatin1String("VIRTUAL-1")
|| output == QLatin1String("Virtual1") || output == QLatin1String("Virtual-1")
|| output == QLatin1String("default"))) {
virtOut = output;
} else if (output == QLatin1String("DVI-I-1-1")) {
evdiOut = output;
} else {
allOutputs << output;
}
if (m.captured(2) == QStringLiteral("disconnected")) {
disconnectedOutputs << output;
} else {
connectedOutputs << output;
}
}
if (virtOut.isEmpty()) {
virtOut = evdiOut;
} else if (!evdiOut.isEmpty()) {
allOutputs << evdiOut;
}
qDebug() << "Virtual output:" << virtOut << "unwanted additional outputs:" << allOutputs;
p.kill();
int ret = -1;
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();
if (ret != 0) {
allOutputs << virtOut;
}
}
// 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) {
qDebug() << "Ret:" << ret << "- Trying to enable one disconnected output";
QString 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();
}
// Now as fallback, try enabling just one connected output
if (ret != 0) {
qDebug() << "Ret:" << ret << "- Trying to enable one 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();
}
}
}
emit loginRequest(lines[0], lines[1], lines[2]);
}