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