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