File indexing completed on 2024-04-21 16:17:33

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005  *
0006  */
0007 
0008 #include "configserializer_p.h"
0009 
0010 #include "config.h"
0011 #include "kscreen_debug.h"
0012 #include "mode.h"
0013 #include "output.h"
0014 #include "screen.h"
0015 
0016 #include <QDBusArgument>
0017 #include <QFile>
0018 #include <QJsonDocument>
0019 #include <QRect>
0020 
0021 #include <cstdint>
0022 #include <optional>
0023 
0024 using namespace KScreen;
0025 
0026 QJsonObject ConfigSerializer::serializePoint(const QPoint &point)
0027 {
0028     QJsonObject obj;
0029     obj[QLatin1String("x")] = point.x();
0030     obj[QLatin1String("y")] = point.y();
0031     return obj;
0032 }
0033 
0034 QJsonObject ConfigSerializer::serializeSize(const QSize &size)
0035 {
0036     QJsonObject obj;
0037     obj[QLatin1String("width")] = size.width();
0038     obj[QLatin1String("height")] = size.height();
0039     return obj;
0040 }
0041 
0042 QJsonObject ConfigSerializer::serializeConfig(const ConfigPtr &config)
0043 {
0044     QJsonObject obj;
0045 
0046     if (!config) {
0047         return obj;
0048     }
0049 
0050     obj[QLatin1String("features")] = static_cast<int>(config->supportedFeatures());
0051 
0052     QJsonArray outputs;
0053     for (const OutputPtr &output : config->outputs()) {
0054         outputs.append(serializeOutput(output));
0055     }
0056     obj[QLatin1String("outputs")] = outputs;
0057     if (config->screen()) {
0058         obj[QLatin1String("screen")] = serializeScreen(config->screen());
0059     }
0060 
0061     obj[QLatin1String("tabletModeAvailable")] = config->tabletModeAvailable();
0062     obj[QLatin1String("tabletModeEngaged")] = config->tabletModeEngaged();
0063 
0064     return obj;
0065 }
0066 
0067 QJsonObject ConfigSerializer::serializeOutput(const OutputPtr &output)
0068 {
0069     QJsonObject obj;
0070 
0071     obj[QLatin1String("id")] = output->id();
0072     obj[QLatin1String("name")] = output->name();
0073     obj[QLatin1String("type")] = static_cast<int>(output->type());
0074     obj[QLatin1String("icon")] = output->icon();
0075     obj[QLatin1String("pos")] = serializePoint(output->pos());
0076     obj[QLatin1String("scale")] = output->scale();
0077     obj[QLatin1String("size")] = serializeSize(output->size());
0078     obj[QLatin1String("rotation")] = static_cast<int>(output->rotation());
0079     obj[QLatin1String("currentModeId")] = output->currentModeId();
0080     obj[QLatin1String("preferredModes")] = serializeList(output->preferredModes());
0081     obj[QLatin1String("connected")] = output->isConnected();
0082     obj[QLatin1String("followPreferredMode")] = output->followPreferredMode();
0083     obj[QLatin1String("enabled")] = output->isEnabled();
0084     obj[QLatin1String("priority")] = static_cast<int>(output->priority());
0085     obj[QLatin1String("clones")] = serializeList(output->clones());
0086     // obj[QLatin1String("edid")] = output->edid()->raw();
0087     obj[QLatin1String("sizeMM")] = serializeSize(output->sizeMm());
0088     obj[QLatin1String("replicationSource")] = output->replicationSource();
0089 
0090     QJsonArray modes;
0091     for (const ModePtr &mode : output->modes()) {
0092         modes.append(serializeMode(mode));
0093     }
0094     obj[QLatin1String("modes")] = modes;
0095 
0096     if (output->capabilities() & Output::Capability::Overscan) {
0097         obj[QLatin1String("overscan")] = static_cast<int>(output->overscan());
0098     }
0099 
0100     if (output->capabilities() & Output::Capability::Vrr) {
0101         obj[QLatin1String("vrrPolicy")] = static_cast<int>(output->vrrPolicy());
0102     }
0103 
0104     if (output->capabilities() & Output::Capability::RgbRange) {
0105         obj[QLatin1String("rgbRange")] = static_cast<int>(output->rgbRange());
0106     }
0107     return obj;
0108 }
0109 
0110 QJsonObject ConfigSerializer::serializeMode(const ModePtr &mode)
0111 {
0112     QJsonObject obj;
0113 
0114     obj[QLatin1String("id")] = mode->id();
0115     obj[QLatin1String("name")] = mode->name();
0116     obj[QLatin1String("size")] = serializeSize(mode->size());
0117     obj[QLatin1String("refreshRate")] = mode->refreshRate();
0118 
0119     return obj;
0120 }
0121 
0122 QJsonObject ConfigSerializer::serializeScreen(const ScreenPtr &screen)
0123 {
0124     QJsonObject obj;
0125 
0126     obj[QLatin1String("id")] = screen->id();
0127     obj[QLatin1String("currentSize")] = serializeSize(screen->currentSize());
0128     obj[QLatin1String("maxSize")] = serializeSize(screen->maxSize());
0129     obj[QLatin1String("minSize")] = serializeSize(screen->minSize());
0130     obj[QLatin1String("maxActiveOutputsCount")] = screen->maxActiveOutputsCount();
0131 
0132     return obj;
0133 }
0134 
0135 QPoint ConfigSerializer::deserializePoint(const QDBusArgument &arg)
0136 {
0137     int x = 0, y = 0;
0138     arg.beginMap();
0139     while (!arg.atEnd()) {
0140         QString key;
0141         QVariant value;
0142         arg.beginMapEntry();
0143         arg >> key >> value;
0144         if (key == QLatin1Char('x')) {
0145             x = value.toInt();
0146         } else if (key == QLatin1Char('y')) {
0147             y = value.toInt();
0148         } else {
0149             qCWarning(KSCREEN) << "Invalid key in Point map: " << key;
0150             return QPoint();
0151         }
0152         arg.endMapEntry();
0153     }
0154     arg.endMap();
0155     return QPoint(x, y);
0156 }
0157 
0158 QSize ConfigSerializer::deserializeSize(const QDBusArgument &arg)
0159 {
0160     int w = 0, h = 0;
0161     arg.beginMap();
0162     while (!arg.atEnd()) {
0163         QString key;
0164         QVariant value;
0165         arg.beginMapEntry();
0166         arg >> key >> value;
0167         if (key == QLatin1String("width")) {
0168             w = value.toInt();
0169         } else if (key == QLatin1String("height")) {
0170             h = value.toInt();
0171         } else {
0172             qCWarning(KSCREEN) << "Invalid key in size struct: " << key;
0173             return QSize();
0174         }
0175         arg.endMapEntry();
0176     }
0177     arg.endMap();
0178 
0179     return QSize(w, h);
0180 }
0181 
0182 ConfigPtr ConfigSerializer::deserializeConfig(const QVariantMap &map)
0183 {
0184     ConfigPtr config(new Config);
0185 
0186     if (map.contains(QLatin1String("features"))) {
0187         config->setSupportedFeatures(static_cast<Config::Features>(map[QStringLiteral("features")].toInt()));
0188     }
0189 
0190     if (map.contains(QLatin1String("tabletModeAvailable"))) {
0191         config->setTabletModeAvailable(map[QStringLiteral("tabletModeAvailable")].toBool());
0192     }
0193     if (map.contains(QLatin1String("tabletModeEngaged"))) {
0194         config->setTabletModeEngaged(map[QStringLiteral("tabletModeEngaged")].toBool());
0195     }
0196 
0197     if (map.contains(QLatin1String("outputs"))) {
0198         const QDBusArgument &outputsArg = map[QStringLiteral("outputs")].value<QDBusArgument>();
0199         outputsArg.beginArray();
0200         OutputList outputs;
0201         while (!outputsArg.atEnd()) {
0202             QVariant value;
0203             outputsArg >> value;
0204             const KScreen::OutputPtr output = deserializeOutput(value.value<QDBusArgument>());
0205             if (!output) {
0206                 return ConfigPtr();
0207             }
0208             outputs.insert(output->id(), output);
0209         }
0210         outputsArg.endArray();
0211         config->setOutputs(outputs);
0212     }
0213 
0214     if (map.contains(QLatin1String("screen"))) {
0215         const QDBusArgument &screenArg = map[QStringLiteral("screen")].value<QDBusArgument>();
0216         const KScreen::ScreenPtr screen = deserializeScreen(screenArg);
0217         if (!screen) {
0218             return ConfigPtr();
0219         }
0220         config->setScreen(screen);
0221     }
0222 
0223     return config;
0224 }
0225 
0226 OutputPtr ConfigSerializer::deserializeOutput(const QDBusArgument &arg)
0227 {
0228     OutputPtr output(new Output);
0229     std::optional<bool> primary = std::nullopt;
0230     std::optional<uint32_t> priority = std::nullopt;
0231 
0232     arg.beginMap();
0233     while (!arg.atEnd()) {
0234         QString key;
0235         QVariant value;
0236         arg.beginMapEntry();
0237         arg >> key >> value;
0238         if (key == QLatin1String("id")) {
0239             output->setId(value.toInt());
0240         } else if (key == QLatin1String("name")) {
0241             output->setName(value.toString());
0242         } else if (key == QLatin1String("type")) {
0243             output->setType(static_cast<Output::Type>(value.toInt()));
0244         } else if (key == QLatin1String("icon")) {
0245             output->setIcon(value.toString());
0246         } else if (key == QLatin1String("pos")) {
0247             output->setPos(deserializePoint(value.value<QDBusArgument>()));
0248         } else if (key == QLatin1String("scale")) {
0249             output->setScale(value.toDouble());
0250         } else if (key == QLatin1String("size")) {
0251             output->setSize(deserializeSize(value.value<QDBusArgument>()));
0252         } else if (key == QLatin1String("rotation")) {
0253             output->setRotation(static_cast<Output::Rotation>(value.toInt()));
0254         } else if (key == QLatin1String("currentModeId")) {
0255             output->setCurrentModeId(value.toString());
0256         } else if (key == QLatin1String("preferredModes")) {
0257             output->setPreferredModes(deserializeList<QString>(value.value<QDBusArgument>()));
0258         } else if (key == QLatin1String("connected")) {
0259             output->setConnected(value.toBool());
0260         } else if (key == QLatin1String("followPreferredMode")) {
0261             output->setFollowPreferredMode(value.toBool());
0262         } else if (key == QLatin1String("enabled")) {
0263             output->setEnabled(value.toBool());
0264         } else if (key == QLatin1String("primary")) {
0265             // primary is deprecated, but if it appears in config for compatibility reason.
0266             primary = value.toBool();
0267         } else if (key == QLatin1String("priority")) {
0268             // "priority" takes precedence over "primary", but we need to
0269             //  check it after the loop, otherwise it may come before the
0270             //  primary and get overridden.
0271             priority = value.toUInt();
0272         } else if (key == QLatin1String("clones")) {
0273             output->setClones(deserializeList<int>(value.value<QDBusArgument>()));
0274         } else if (key == QLatin1String("replicationSource")) {
0275             output->setReplicationSource(value.toInt());
0276         } else if (key == QLatin1String("sizeMM")) {
0277             output->setSizeMm(deserializeSize(value.value<QDBusArgument>()));
0278         } else if (key == QLatin1String("modes")) {
0279             const QDBusArgument arg = value.value<QDBusArgument>();
0280             ModeList modes;
0281             arg.beginArray();
0282             while (!arg.atEnd()) {
0283                 QVariant value;
0284                 arg >> value;
0285                 const KScreen::ModePtr mode = deserializeMode(value.value<QDBusArgument>());
0286                 if (!mode) {
0287                     return OutputPtr();
0288                 }
0289                 modes.insert(mode->id(), mode);
0290             }
0291             arg.endArray();
0292             output->setModes(modes);
0293         } else if (key == QLatin1String("overscan")) {
0294             output->setOverscan(value.toUInt());
0295         } else if (key == QLatin1String("vrrPolicy")) {
0296             output->setVrrPolicy(static_cast<Output::VrrPolicy>(value.toInt()));
0297         } else if (key == QLatin1String("rgbRange")) {
0298             output->setRgbRange(static_cast<Output::RgbRange>(value.toInt()));
0299         } else {
0300             qCWarning(KSCREEN) << "Invalid key in Output map: " << key;
0301             return OutputPtr();
0302         }
0303         arg.endMapEntry();
0304     }
0305     arg.endMap();
0306     if (primary.has_value()) {
0307         output->setPriority(output->isEnabled() ? (primary.value() ? 1 : 2) : 0);
0308     }
0309     if (priority.has_value()) {
0310         output->setPriority(priority.value());
0311     }
0312     return output;
0313 }
0314 
0315 ModePtr ConfigSerializer::deserializeMode(const QDBusArgument &arg)
0316 {
0317     ModePtr mode(new Mode);
0318 
0319     arg.beginMap();
0320     while (!arg.atEnd()) {
0321         QString key;
0322         QVariant value;
0323         arg.beginMapEntry();
0324         arg >> key >> value;
0325 
0326         if (key == QLatin1String("id")) {
0327             mode->setId(value.toString());
0328         } else if (key == QLatin1String("name")) {
0329             mode->setName(value.toString());
0330         } else if (key == QLatin1String("size")) {
0331             mode->setSize(deserializeSize(value.value<QDBusArgument>()));
0332         } else if (key == QLatin1String("refreshRate")) {
0333             mode->setRefreshRate(value.toFloat());
0334         } else {
0335             qCWarning(KSCREEN) << "Invalid key in Mode map: " << key;
0336             return ModePtr();
0337         }
0338         arg.endMapEntry();
0339     }
0340     arg.endMap();
0341     return mode;
0342 }
0343 
0344 ScreenPtr ConfigSerializer::deserializeScreen(const QDBusArgument &arg)
0345 {
0346     ScreenPtr screen(new Screen);
0347 
0348     arg.beginMap();
0349     QString key;
0350     QVariant value;
0351     while (!arg.atEnd()) {
0352         arg.beginMapEntry();
0353         arg >> key >> value;
0354         if (key == QLatin1String("id")) {
0355             screen->setId(value.toInt());
0356         } else if (key == QLatin1String("maxActiveOutputsCount")) {
0357             screen->setMaxActiveOutputsCount(value.toInt());
0358         } else if (key == QLatin1String("currentSize")) {
0359             screen->setCurrentSize(deserializeSize(value.value<QDBusArgument>()));
0360         } else if (key == QLatin1String("maxSize")) {
0361             screen->setMaxSize(deserializeSize(value.value<QDBusArgument>()));
0362         } else if (key == QLatin1String("minSize")) {
0363             screen->setMinSize(deserializeSize(value.value<QDBusArgument>()));
0364         } else {
0365             qCWarning(KSCREEN) << "Invalid key in Screen map:" << key;
0366             return ScreenPtr();
0367         }
0368         arg.endMapEntry();
0369     }
0370     arg.endMap();
0371     return screen;
0372 }