File indexing completed on 2024-04-28 16:49:40

0001 /*
0002  *  SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-or-later
0005  */
0006 
0007 #include "parser.h"
0008 #include "fake.h"
0009 
0010 #include "config.h"
0011 #include "mode.h"
0012 #include "output.h"
0013 #include "screen.h"
0014 
0015 #include <QFile>
0016 #include <QJsonArray>
0017 #include <QJsonDocument>
0018 #include <QJsonObject>
0019 #include <QLoggingCategory>
0020 #include <QMetaObject>
0021 #include <QMetaProperty>
0022 
0023 using namespace KScreen;
0024 
0025 ConfigPtr Parser::fromJson(const QByteArray &data)
0026 {
0027     ConfigPtr config(new Config);
0028 
0029     const QJsonObject json = QJsonDocument::fromJson(data).object();
0030 
0031     ScreenPtr screen = Parser::screenFromJson(json[QStringLiteral("screen")].toObject().toVariantMap());
0032     config->setScreen(screen);
0033 
0034     const QVariantList outputs = json[QStringLiteral("outputs")].toArray().toVariantList();
0035     if (outputs.isEmpty()) {
0036         return config;
0037     }
0038 
0039     OutputList outputList;
0040     for (const QVariant &value : outputs) {
0041         const OutputPtr output = Parser::outputFromJson(value.toMap());
0042         outputList.insert(output->id(), output);
0043     }
0044 
0045     config->setOutputs(outputList);
0046     return config;
0047 }
0048 
0049 ConfigPtr Parser::fromJson(const QString &path)
0050 {
0051     QFile file(path);
0052     if (!file.open(QIODevice::ReadOnly)) {
0053         qWarning() << file.errorString();
0054         qWarning() << "File: " << path;
0055         return ConfigPtr();
0056     }
0057 
0058     return Parser::fromJson(file.readAll());
0059 }
0060 
0061 ScreenPtr Parser::screenFromJson(const QVariantMap &data)
0062 {
0063     ScreenPtr screen(new Screen);
0064     screen->setId(data[QStringLiteral("id")].toInt());
0065     screen->setMinSize(Parser::sizeFromJson(data[QStringLiteral("minSize")].toMap()));
0066     screen->setMaxSize(Parser::sizeFromJson(data[QStringLiteral("maxSize")].toMap()));
0067     screen->setCurrentSize(Parser::sizeFromJson(data[QStringLiteral("currentSize")].toMap()));
0068     screen->setMaxActiveOutputsCount(data[QStringLiteral("maxActiveOutputsCount")].toInt());
0069 
0070     return screen;
0071 }
0072 
0073 void Parser::qvariant2qobject(const QVariantMap &variant, QObject *object)
0074 {
0075     const QMetaObject *metaObject = object->metaObject();
0076     for (QVariantMap::const_iterator iter = variant.begin(); iter != variant.end(); ++iter) {
0077         const int propertyIndex = metaObject->indexOfProperty(qPrintable(iter.key()));
0078         if (propertyIndex == -1) {
0079             // qWarning() << "Skipping non-existent property" << iter.key();
0080             continue;
0081         }
0082         const QMetaProperty metaProperty = metaObject->property(propertyIndex);
0083         if (!metaProperty.isWritable()) {
0084             // qWarning() << "Skipping read-only property" << iter.key();
0085             continue;
0086         }
0087 
0088         const QVariant property = object->property(iter.key().toLatin1().constData());
0089         Q_ASSERT(property.isValid());
0090         if (property.isValid()) {
0091             QVariant value = iter.value();
0092             if (value.canConvert(property.type())) {
0093                 value.convert(property.type());
0094                 object->setProperty(iter.key().toLatin1().constData(), value);
0095             } else if (QLatin1String("QVariant") == QLatin1String(property.typeName())) {
0096                 object->setProperty(iter.key().toLatin1().constData(), value);
0097             }
0098         }
0099     }
0100 }
0101 
0102 OutputPtr Parser::outputFromJson(QMap<QString, QVariant> map)
0103 {
0104     OutputPtr output(new Output);
0105     output->setId(map[QStringLiteral("id")].toInt());
0106 
0107     QStringList preferredModes;
0108     const QVariantList prefModes = map[QStringLiteral("preferredModes")].toList();
0109     for (const QVariant &mode : prefModes) {
0110         preferredModes.append(mode.toString());
0111     }
0112     output->setPreferredModes(preferredModes);
0113     map.remove(QStringLiteral("preferredModes"));
0114 
0115     ModeList modelist;
0116     const QVariantList modes = map[QStringLiteral("modes")].toList();
0117     for (const QVariant &modeValue : modes) {
0118         const ModePtr mode = Parser::modeFromJson(modeValue);
0119         modelist.insert(mode->id(), mode);
0120     }
0121     output->setModes(modelist);
0122     map.remove(QStringLiteral("modes"));
0123 
0124     if (map.contains(QStringLiteral("clones"))) {
0125         QList<int> clones;
0126         for (const QVariant &id : map[QStringLiteral("clones")].toList()) {
0127             clones.append(id.toInt());
0128         }
0129 
0130         output->setClones(clones);
0131         map.remove(QStringLiteral("clones"));
0132     }
0133 
0134     const QByteArray type = map[QStringLiteral("type")].toByteArray().toUpper();
0135     if (type.contains("LVDS") || type.contains("EDP") || type.contains("IDP") || type.contains("7")) {
0136         output->setType(Output::Panel);
0137     } else if (type.contains("VGA")) {
0138         output->setType(Output::VGA);
0139     } else if (type.contains("DVI")) {
0140         output->setType(Output::DVI);
0141     } else if (type.contains("DVI-I")) {
0142         output->setType(Output::DVII);
0143     } else if (type.contains("DVI-A")) {
0144         output->setType(Output::DVIA);
0145     } else if (type.contains("DVI-D")) {
0146         output->setType(Output::DVID);
0147     } else if (type.contains("HDMI") || type.contains("6")) {
0148         output->setType(Output::HDMI);
0149     } else if (type.contains("Panel")) {
0150         output->setType(Output::Panel);
0151     } else if (type.contains("TV")) {
0152         output->setType(Output::TV);
0153     } else if (type.contains("TV-Composite")) {
0154         output->setType(Output::TVComposite);
0155     } else if (type.contains("TV-SVideo")) {
0156         output->setType(Output::TVSVideo);
0157     } else if (type.contains("TV-Component")) {
0158         output->setType(Output::TVComponent);
0159     } else if (type.contains("TV-SCART")) {
0160         output->setType(Output::TVSCART);
0161     } else if (type.contains("TV-C4")) {
0162         output->setType(Output::TVC4);
0163     } else if (type.contains("DisplayPort") || type.contains("14")) {
0164         output->setType(Output::DisplayPort);
0165     } else if (type.contains("Unknown")) {
0166         output->setType(Output::Unknown);
0167     } else {
0168         qCWarning(KSCREEN_FAKE) << "Output Type not translated:" << type;
0169     }
0170     map.remove(QStringLiteral("type"));
0171 
0172     if (map.contains(QStringLiteral("pos"))) {
0173         output->setPos(Parser::pointFromJson(map[QStringLiteral("pos")].toMap()));
0174         map.remove(QStringLiteral("pos"));
0175     }
0176 
0177     if (map.contains(QStringLiteral("size"))) {
0178         output->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap()));
0179         map.remove(QStringLiteral("size"));
0180     }
0181 
0182     // This is not supported in real configs; only set this value in fake test
0183     // configs and don't add logic to kscreen to set this data in real configs.
0184     if (map.contains(QStringLiteral("sizeMM"))) {
0185         output->setSizeMm(Parser::sizeFromJson(map[QStringLiteral("sizeMM")].toMap()));
0186         map.remove(QStringLiteral("sizeMM"));
0187     }
0188 
0189     auto scale = QStringLiteral("scale");
0190     if (map.contains(scale)) {
0191         qDebug() << "Scale found:" << map[scale].toReal();
0192         output->setScale(map[scale].toReal());
0193         map.remove(scale);
0194     }
0195 
0196     // the deprecated "primary" property may exist for compatibility, but "priority" should override it whenever present.
0197     if (map.contains(QStringLiteral("primary"))) {
0198         output->setPriority(map[QStringLiteral("primary")].toBool() ? 1 : 2);
0199         map.remove(QStringLiteral("primary"));
0200     }
0201     if (map.contains(QStringLiteral("priority"))) {
0202         output->setPriority(map[QStringLiteral("priority")].toUInt());
0203         map.remove(QStringLiteral("priority"));
0204     }
0205 
0206     // Remove some extra properties that we do not want or need special treatment
0207     map.remove(QStringLiteral("edid"));
0208 
0209     Parser::qvariant2qobject(map, output.data());
0210     return output;
0211 }
0212 
0213 ModePtr Parser::modeFromJson(const QVariant &data)
0214 {
0215     const QVariantMap map = data.toMap();
0216     ModePtr mode(new Mode);
0217     Parser::qvariant2qobject(map, mode.data());
0218 
0219     mode->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap()));
0220 
0221     return mode;
0222 }
0223 
0224 QSize Parser::sizeFromJson(const QVariant &data)
0225 {
0226     const QVariantMap map = data.toMap();
0227 
0228     QSize size;
0229     size.setWidth(map[QStringLiteral("width")].toInt());
0230     size.setHeight(map[QStringLiteral("height")].toInt());
0231 
0232     return size;
0233 }
0234 
0235 QPoint Parser::pointFromJson(const QVariant &data)
0236 {
0237     const QVariantMap map = data.toMap();
0238 
0239     QPoint point;
0240     point.setX(map[QStringLiteral("x")].toInt());
0241     point.setY(map[QStringLiteral("y")].toInt());
0242 
0243     return point;
0244 }
0245 
0246 QRect Parser::rectFromJson(const QVariant &data)
0247 {
0248     QRect rect;
0249     rect.setSize(Parser::sizeFromJson(data));
0250     rect.setBottomLeft(Parser::pointFromJson(data));
0251 
0252     return rect;
0253 }
0254 
0255 bool Parser::validate(const QByteArray &data)
0256 {
0257     Q_UNUSED(data);
0258     return true;
0259 }
0260 
0261 bool Parser::validate(const QString &data)
0262 {
0263     Q_UNUSED(data);
0264     return true;
0265 }