summaryrefslogtreecommitdiffstats
path: root/src/loginrpc.cpp
blob: 6ad70ec6dd86af24d93298e39cd7093260c77ca4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#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").arg(x).arg(y);
            mode *mode = vert_refresh(x, y, 60, 0, 0, 0);
            QProcess p;
            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";
            // Get all outputs
            QStringList setMode("--verbose");
            p.setProcessChannelMode(QProcess::MergedChannels);
            p.start("xrandr");
            p.waitForFinished(2000);
            QString out = QString::fromLocal8Bit(p.readAll());
            QRegularExpression re("^(\\S+)\\s.*?(\\w+connected)", QRegularExpression::MultilineOption);
            QRegularExpressionMatchIterator it = re.globalMatch(out);
            QString disconnectedOut;
            QString virtOut;
            QStringList outputs;
            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 (disconnectedOut.isEmpty() && m.captured(2) == "disconnected") {
                    disconnectedOut = output;
                } else {
                    outputs << output;
                }
            }
            if (virtOut.isEmpty() && !disconnectedOut.isEmpty()) {
                virtOut = disconnectedOut;
                disconnectedOut.clear();
            }
            if (!disconnectedOut.isEmpty()) {
                outputs << disconnectedOut;
            }
            if (virtOut.isEmpty() && !outputs.isEmpty()) {
                virtOut = outputs.takeFirst();
            }
            qDebug() << "Virtual output:" << virtOut << "unwanted additional outputs:" << outputs << "(First disconnected:" << disconnectedOut << ")";
            p.kill();
            if (!virtOut.isEmpty()) {
                setMode << "--output" << virtOut << "--mode" << name;
                for (auto output : outputs) {
                    setMode << "--output" << output << "--off";
                }
                qDebug() << "Setting mode" << newmode;
                p.setProcessChannelMode(QProcess::ForwardedChannels);
                // Create mode
                p.start("xrandr", newmode);
                p.waitForFinished(2000);
                p.kill();
                // 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);
                p.kill();
            }
        }
    }
    emit loginRequest(lines[0], lines[1], lines[2]);
}