1 /*
2  *  SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org>
3  *
4  *  SPDX-License-Identifier: LGPL-2.1-or-later
5  */
6 
7 #include "waylandconfigreader.h"
8 
9 #include <QDebug>
10 
11 #include <QFile>
12 #include <QJsonArray>
13 #include <QJsonDocument>
14 #include <QJsonObject>
15 #include <QUuid>
16 
17 #include "edid.h"
18 
19 using namespace KScreen;
20 
21 static QList<int> s_outputIds;
22 
outputsFromConfig(const QString & configfile,KWayland::Server::Display * display,QList<KWayland::Server::OutputDeviceInterface * > & outputs)23 void WaylandConfigReader::outputsFromConfig(const QString &configfile,
24                                             KWayland::Server::Display *display,
25                                             QList<KWayland::Server::OutputDeviceInterface *> &outputs)
26 {
27     qDebug() << "Loading server from" << configfile;
28     QFile file(configfile);
29     file.open(QIODevice::ReadOnly);
30 
31     QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
32     QJsonObject json = jsonDoc.object();
33 
34     QJsonArray omap = json[QStringLiteral("outputs")].toArray();
35     for (const QJsonValue &value : omap) {
36         const QVariantMap &output = value.toObject().toVariantMap();
37         if (output[QStringLiteral("connected")].toBool()) {
38             outputs << createOutputDevice(output, display);
39             // qDebug() << "new Output created: " << output["name"].toString();
40         } else {
41             // qDebug() << "disconnected Output" << output["name"].toString();
42         }
43     }
44     auto outpus = WaylandConfigReader::createOutputs(display, outputs);
45     s_outputIds.clear();
46 }
47 
createOutputDevice(const QVariantMap & outputConfig,KWayland::Server::Display * display)48 OutputDeviceInterface *WaylandConfigReader::createOutputDevice(const QVariantMap &outputConfig, KWayland::Server::Display *display)
49 {
50     KWayland::Server::OutputDeviceInterface *outputdevice = display->createOutputDevice(display);
51 
52     QByteArray data = QByteArray::fromBase64(outputConfig[QStringLiteral("edid")].toByteArray());
53     outputdevice->setEdid(data);
54     Edid edid(data, display);
55 
56     //     qDebug() << "EDID Info: ";
57     if (edid.isValid()) {
58         // qDebug() << "\tDevice ID: " << edid.deviceId();
59         // qDebug() << "\tName: " << edid.name();
60         // qDebug() << "\tVendor: " << edid.vendor();
61         // qDebug() << "\tSerial: " << edid.serial();
62         // qDebug() << "\tEISA ID: " << edid.eisaId();
63         // qDebug() << "\tHash: " << edid.hash();
64         // qDebug() << "\tWidth (mm): " << edid.width();
65         // qDebug() << "\tHeight (mm): " << edid.height();
66         // qDebug() << "\tGamma: " << edid.gamma();
67         // qDebug() << "\tRed: " << edid.red();
68         // qDebug() << "\tGreen: " << edid.green();
69         // qDebug() << "\tBlue: " << edid.blue();
70         // qDebug() << "\tWhite: " << edid.white();
71         outputdevice->setPhysicalSize(QSize(edid.width() * 10, edid.height() * 10));
72         outputdevice->setManufacturer(edid.vendor());
73         outputdevice->setModel(edid.name());
74     } else {
75         outputdevice->setPhysicalSize(sizeFromJson(outputConfig[QStringLiteral("sizeMM")]));
76         outputdevice->setManufacturer(outputConfig[QStringLiteral("manufacturer")].toString());
77         outputdevice->setModel(outputConfig[QStringLiteral("model")].toString());
78     }
79     auto uuid = QUuid::createUuid().toByteArray();
80     auto _id = outputConfig[QStringLiteral("id")].toInt();
81     if (_id) {
82         uuid = QString::number(_id).toLocal8Bit();
83     }
84     outputdevice->setUuid(uuid);
85 
86     const QMap<int, KWayland::Server::OutputDeviceInterface::Transform> transformMap = {{0, KWayland::Server::OutputDeviceInterface::Transform::Normal},
87                                                                                         {1, KWayland::Server::OutputDeviceInterface::Transform::Normal},
88                                                                                         {2, KWayland::Server::OutputDeviceInterface::Transform::Rotated270},
89                                                                                         {3, KWayland::Server::OutputDeviceInterface::Transform::Rotated180},
90                                                                                         {4, KWayland::Server::OutputDeviceInterface::Transform::Rotated90}};
91 
92     outputdevice->setTransform(transformMap[outputConfig[QStringLiteral("rotation")].toInt()]);
93     int currentModeId = outputConfig[QStringLiteral("currentModeId")].toInt();
94     QVariantList preferredModes = outputConfig[QStringLiteral("preferredModes")].toList();
95 
96     int mode_id = 0;
97     for (const QVariant &_mode : outputConfig[QStringLiteral("modes")].toList()) {
98         mode_id++;
99         const QVariantMap &mode = _mode.toMap();
100         OutputDeviceInterface::Mode m0;
101         const QSize _size = sizeFromJson(mode[QStringLiteral("size")]);
102 
103         auto refreshRateIt = mode.constFind(QStringLiteral("refreshRate"));
104         if (refreshRateIt != mode.constEnd()) {
105             m0.refreshRate = qRound(refreshRateIt->toReal() * 1000); // config has it in Hz
106         }
107         bool isCurrent = currentModeId == mode[QStringLiteral("id")].toInt();
108         bool isPreferred = preferredModes.contains(mode[QStringLiteral("id")]);
109 
110         OutputDeviceInterface::ModeFlags flags;
111         if (isCurrent && isPreferred) {
112             flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current | OutputDeviceInterface::ModeFlag::Preferred);
113         } else if (isCurrent) {
114             flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current);
115         } else if (isPreferred) {
116             flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred);
117         }
118 
119         auto idIt = mode.constFind(QStringLiteral("id"));
120         if (idIt != mode.constEnd()) {
121             m0.id = idIt->toInt();
122         } else {
123             m0.id = mode_id;
124         }
125         m0.size = _size;
126         m0.flags = flags;
127         outputdevice->addMode(m0);
128 
129         if (isCurrent) {
130             outputdevice->setCurrentMode(m0.id);
131         }
132     }
133 
134     outputdevice->setGlobalPosition(pointFromJson(outputConfig[QStringLiteral("pos")]));
135     outputdevice->setEnabled(outputConfig[QStringLiteral("enabled")].toBool() ? OutputDeviceInterface::Enablement::Enabled
136                                                                               : OutputDeviceInterface::Enablement::Disabled);
137     outputdevice->create();
138 
139     return outputdevice;
140 }
141 
createOutputs(KWayland::Server::Display * display,QList<KWayland::Server::OutputDeviceInterface * > & outputdevices)142 QList<KWayland::Server::OutputInterface *> KScreen::WaylandConfigReader::createOutputs(KWayland::Server::Display *display,
143                                                                                        QList<KWayland::Server::OutputDeviceInterface *> &outputdevices)
144 {
145     const QMap<KWayland::Server::OutputDeviceInterface::Transform, KWayland::Server::OutputInterface::Transform> transformMap = {
146         {KWayland::Server::OutputDeviceInterface::Transform::Normal, KWayland::Server::OutputInterface::Transform::Normal},
147         {KWayland::Server::OutputDeviceInterface::Transform::Rotated270, KWayland::Server::OutputInterface::Transform::Rotated270},
148         {KWayland::Server::OutputDeviceInterface::Transform::Rotated180, KWayland::Server::OutputInterface::Transform::Rotated180},
149         {KWayland::Server::OutputDeviceInterface::Transform::Rotated90, KWayland::Server::OutputInterface::Transform::Rotated90},
150     };
151 
152     QList<KWayland::Server::OutputInterface *> outputs;
153     for (const auto outputdevice : outputdevices) {
154         qDebug() << "New Output!";
155         KWayland::Server::OutputInterface *output = display->createOutput(display);
156 
157         // Sync properties from outputdevice to the newly created output interface
158         output->setManufacturer(outputdevice->manufacturer());
159         output->setModel(outputdevice->model());
160         // output->setUuid(outputdevice->uuid());
161 
162         for (const auto mode : outputdevice->modes()) {
163             bool isCurrent = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current);
164             bool isPreferred = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current);
165             OutputInterface::ModeFlags flags;
166             if (isPreferred && isCurrent) {
167                 flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current | OutputInterface::ModeFlag::Preferred);
168             } else if (isCurrent) {
169                 flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current);
170             } else if (isPreferred) {
171                 flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred);
172             }
173 
174             OutputInterface::Mode m0;
175 
176             m0.size = mode.size;
177             output->addMode(m0.size, m0.flags, m0.refreshRate);
178 
179             if (isCurrent) {
180                 output->setCurrentMode(m0.size, m0.refreshRate);
181             }
182             // qDebug() << "mode added:" << m0.size << m0.refreshRate << isCurrent;
183         }
184 
185         output->setGlobalPosition(outputdevice->globalPosition());
186         output->setPhysicalSize(outputdevice->physicalSize());
187         output->setTransform(transformMap.value(outputdevice->transform()));
188 
189         output->setDpmsSupported(true);
190         output->setDpmsMode(OutputInterface::DpmsMode::On);
191         QObject::connect(output, &OutputInterface::dpmsModeRequested, [](KWayland::Server::OutputInterface::DpmsMode requestedMode) {
192             Q_UNUSED(requestedMode);
193             // FIXME: make sure this happens in the scope of an object!
194             qDebug() << "DPMS Mode change requested";
195         });
196         output->create();
197         outputs << output;
198     }
199     return outputs;
200 }
201 
sizeFromJson(const QVariant & data)202 QSize WaylandConfigReader::sizeFromJson(const QVariant &data)
203 {
204     QVariantMap map = data.toMap();
205 
206     QSize size;
207     size.setWidth(map[QStringLiteral("width")].toInt());
208     size.setHeight(map[QStringLiteral("height")].toInt());
209 
210     return size;
211 }
212 
pointFromJson(const QVariant & data)213 QPoint WaylandConfigReader::pointFromJson(const QVariant &data)
214 {
215     QVariantMap map = data.toMap();
216 
217     QPoint point;
218     point.setX(map[QStringLiteral("x")].toInt());
219     point.setY(map[QStringLiteral("y")].toInt());
220 
221     return point;
222 }
223 
rectFromJson(const QVariant & data)224 QRect WaylandConfigReader::rectFromJson(const QVariant &data)
225 {
226     QRect rect;
227     rect.setSize(WaylandConfigReader::sizeFromJson(data));
228     rect.setBottomLeft(WaylandConfigReader::pointFromJson(data));
229 
230     return rect;
231 }
232