1 /*
2 * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6
7 #include "parser.h"
8 #include "fake.h"
9
10 #include "config.h"
11 #include "output.h"
12
13 #include <QFile>
14 #include <QJsonArray>
15 #include <QJsonDocument>
16 #include <QJsonObject>
17 #include <QLoggingCategory>
18 #include <QMetaObject>
19 #include <QMetaProperty>
20
21 using namespace KScreen;
22
fromJson(const QByteArray & data)23 ConfigPtr Parser::fromJson(const QByteArray &data)
24 {
25 ConfigPtr config(new Config);
26
27 const QJsonObject json = QJsonDocument::fromJson(data).object();
28
29 ScreenPtr screen = Parser::screenFromJson(json[QStringLiteral("screen")].toObject().toVariantMap());
30 config->setScreen(screen);
31
32 const QVariantList outputs = json[QStringLiteral("outputs")].toArray().toVariantList();
33 if (outputs.isEmpty()) {
34 return config;
35 }
36
37 OutputList outputList;
38 for (const QVariant &value : outputs) {
39 const OutputPtr output = Parser::outputFromJson(value.toMap());
40 outputList.insert(output->id(), output);
41 }
42
43 config->setOutputs(outputList);
44 return config;
45 }
46
fromJson(const QString & path)47 ConfigPtr Parser::fromJson(const QString &path)
48 {
49 QFile file(path);
50 if (!file.open(QIODevice::ReadOnly)) {
51 qWarning() << file.errorString();
52 qWarning() << "File: " << path;
53 return ConfigPtr();
54 }
55
56 return Parser::fromJson(file.readAll());
57 }
58
screenFromJson(const QVariantMap & data)59 ScreenPtr Parser::screenFromJson(const QVariantMap &data)
60 {
61 ScreenPtr screen(new Screen);
62 screen->setId(data[QStringLiteral("id")].toInt());
63 screen->setMinSize(Parser::sizeFromJson(data[QStringLiteral("minSize")].toMap()));
64 screen->setMaxSize(Parser::sizeFromJson(data[QStringLiteral("maxSize")].toMap()));
65 screen->setCurrentSize(Parser::sizeFromJson(data[QStringLiteral("currentSize")].toMap()));
66 screen->setMaxActiveOutputsCount(data[QStringLiteral("maxActiveOutputsCount")].toInt());
67
68 return screen;
69 }
70
qvariant2qobject(const QVariantMap & variant,QObject * object)71 void Parser::qvariant2qobject(const QVariantMap &variant, QObject *object)
72 {
73 const QMetaObject *metaObject = object->metaObject();
74 for (QVariantMap::const_iterator iter = variant.begin(); iter != variant.end(); ++iter) {
75 const int propertyIndex = metaObject->indexOfProperty(qPrintable(iter.key()));
76 if (propertyIndex == -1) {
77 // qWarning() << "Skipping non-existent property" << iter.key();
78 continue;
79 }
80 const QMetaProperty metaProperty = metaObject->property(propertyIndex);
81 if (!metaProperty.isWritable()) {
82 // qWarning() << "Skipping read-only property" << iter.key();
83 continue;
84 }
85
86 const QVariant property = object->property(iter.key().toLatin1().constData());
87 Q_ASSERT(property.isValid());
88 if (property.isValid()) {
89 QVariant value = iter.value();
90 if (value.canConvert(property.type())) {
91 value.convert(property.type());
92 object->setProperty(iter.key().toLatin1().constData(), value);
93 } else if (QLatin1String("QVariant") == QLatin1String(property.typeName())) {
94 object->setProperty(iter.key().toLatin1().constData(), value);
95 }
96 }
97 }
98 }
99
outputFromJson(QMap<QString,QVariant> map)100 OutputPtr Parser::outputFromJson(QMap<QString, QVariant> map)
101 {
102 OutputPtr output(new Output);
103 output->setId(map[QStringLiteral("id")].toInt());
104
105 QStringList preferredModes;
106 const QVariantList prefModes = map[QStringLiteral("preferredModes")].toList();
107 for (const QVariant &mode : prefModes) {
108 preferredModes.append(mode.toString());
109 }
110 output->setPreferredModes(preferredModes);
111 map.remove(QStringLiteral("preferredModes"));
112
113 ModeList modelist;
114 const QVariantList modes = map[QStringLiteral("modes")].toList();
115 for (const QVariant &modeValue : modes) {
116 const ModePtr mode = Parser::modeFromJson(modeValue);
117 modelist.insert(mode->id(), mode);
118 }
119 output->setModes(modelist);
120 map.remove(QStringLiteral("modes"));
121
122 if (map.contains(QStringLiteral("clones"))) {
123 QList<int> clones;
124 for (const QVariant &id : map[QStringLiteral("clones")].toList()) {
125 clones.append(id.toInt());
126 }
127
128 output->setClones(clones);
129 map.remove(QStringLiteral("clones"));
130 }
131
132 const QByteArray type = map[QStringLiteral("type")].toByteArray().toUpper();
133 if (type.contains("LVDS") || type.contains("EDP") || type.contains("IDP") || type.contains("7")) {
134 output->setType(Output::Panel);
135 } else if (type.contains("VGA")) {
136 output->setType(Output::VGA);
137 } else if (type.contains("DVI")) {
138 output->setType(Output::DVI);
139 } else if (type.contains("DVI-I")) {
140 output->setType(Output::DVII);
141 } else if (type.contains("DVI-A")) {
142 output->setType(Output::DVIA);
143 } else if (type.contains("DVI-D")) {
144 output->setType(Output::DVID);
145 } else if (type.contains("HDMI") || type.contains("6")) {
146 output->setType(Output::HDMI);
147 } else if (type.contains("Panel")) {
148 output->setType(Output::Panel);
149 } else if (type.contains("TV")) {
150 output->setType(Output::TV);
151 } else if (type.contains("TV-Composite")) {
152 output->setType(Output::TVComposite);
153 } else if (type.contains("TV-SVideo")) {
154 output->setType(Output::TVSVideo);
155 } else if (type.contains("TV-Component")) {
156 output->setType(Output::TVComponent);
157 } else if (type.contains("TV-SCART")) {
158 output->setType(Output::TVSCART);
159 } else if (type.contains("TV-C4")) {
160 output->setType(Output::TVC4);
161 } else if (type.contains("DisplayPort") || type.contains("14")) {
162 output->setType(Output::DisplayPort);
163 } else if (type.contains("Unknown")) {
164 output->setType(Output::Unknown);
165 } else {
166 qCWarning(KSCREEN_FAKE) << "Output Type not translated:" << type;
167 }
168 map.remove(QStringLiteral("type"));
169
170 if (map.contains(QStringLiteral("pos"))) {
171 output->setPos(Parser::pointFromJson(map[QStringLiteral("pos")].toMap()));
172 map.remove(QStringLiteral("pos"));
173 }
174
175 if (map.contains(QStringLiteral("size"))) {
176 output->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap()));
177 map.remove(QStringLiteral("size"));
178 }
179
180 auto scale = QStringLiteral("scale");
181 if (map.contains(scale)) {
182 qDebug() << "Scale found:" << map[scale].toReal();
183 output->setScale(map[scale].toReal());
184 map.remove(scale);
185 }
186
187 // Remove some extra properties that we do not want or need special treatment
188 map.remove(QStringLiteral("edid"));
189
190 Parser::qvariant2qobject(map, output.data());
191 return output;
192 }
193
modeFromJson(const QVariant & data)194 ModePtr Parser::modeFromJson(const QVariant &data)
195 {
196 const QVariantMap map = data.toMap();
197 ModePtr mode(new Mode);
198 Parser::qvariant2qobject(map, mode.data());
199
200 mode->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap()));
201
202 return mode;
203 }
204
sizeFromJson(const QVariant & data)205 QSize Parser::sizeFromJson(const QVariant &data)
206 {
207 const QVariantMap map = data.toMap();
208
209 QSize size;
210 size.setWidth(map[QStringLiteral("width")].toInt());
211 size.setHeight(map[QStringLiteral("height")].toInt());
212
213 return size;
214 }
215
pointFromJson(const QVariant & data)216 QPoint Parser::pointFromJson(const QVariant &data)
217 {
218 const QVariantMap map = data.toMap();
219
220 QPoint point;
221 point.setX(map[QStringLiteral("x")].toInt());
222 point.setY(map[QStringLiteral("y")].toInt());
223
224 return point;
225 }
226
rectFromJson(const QVariant & data)227 QRect Parser::rectFromJson(const QVariant &data)
228 {
229 QRect rect;
230 rect.setSize(Parser::sizeFromJson(data));
231 rect.setBottomLeft(Parser::pointFromJson(data));
232
233 return rect;
234 }
235
validate(const QByteArray & data)236 bool Parser::validate(const QByteArray &data)
237 {
238 Q_UNUSED(data);
239 return true;
240 }
241
validate(const QString & data)242 bool Parser::validate(const QString &data)
243 {
244 Q_UNUSED(data);
245 return true;
246 }
247