File indexing completed on 2024-05-05 09:51:11

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 
0108     if (output->capabilities() & Output::Capability::HighDynamicRange) {
0109         obj[QLatin1String("hdr")] = output->isHdrEnabled();
0110     }
0111 
0112     if (output->capabilities() & Output::Capability::HighDynamicRange) {
0113         obj[QLatin1String("sdr-brightness")] = static_cast<int>(output->sdrBrightness());
0114     }
0115 
0116     if (output->capabilities() & Output::Capability::WideColorGamut) {
0117         obj[QLatin1String("wcg")] = output->isWcgEnabled();
0118     }
0119 
0120     return obj;
0121 }
0122 
0123 QJsonObject ConfigSerializer::serializeMode(const ModePtr &mode)
0124 {
0125     QJsonObject obj;
0126 
0127     obj[QLatin1String("id")] = mode->id();
0128     obj[QLatin1String("name")] = mode->name();
0129     obj[QLatin1String("size")] = serializeSize(mode->size());
0130     obj[QLatin1String("refreshRate")] = mode->refreshRate();
0131 
0132     return obj;
0133 }
0134 
0135 QJsonObject ConfigSerializer::serializeScreen(const ScreenPtr &screen)
0136 {
0137     QJsonObject obj;
0138 
0139     obj[QLatin1String("id")] = screen->id();
0140     obj[QLatin1String("currentSize")] = serializeSize(screen->currentSize());
0141     obj[QLatin1String("maxSize")] = serializeSize(screen->maxSize());
0142     obj[QLatin1String("minSize")] = serializeSize(screen->minSize());
0143     obj[QLatin1String("maxActiveOutputsCount")] = screen->maxActiveOutputsCount();
0144 
0145     return obj;
0146 }
0147 
0148 QPoint ConfigSerializer::deserializePoint(const QDBusArgument &arg)
0149 {
0150     int x = 0, y = 0;
0151     arg.beginMap();
0152     while (!arg.atEnd()) {
0153         QString key;
0154         QVariant value;
0155         arg.beginMapEntry();
0156         arg >> key >> value;
0157         if (key == QLatin1Char('x')) {
0158             x = value.toInt();
0159         } else if (key == QLatin1Char('y')) {
0160             y = value.toInt();
0161         } else {
0162             qCWarning(KSCREEN) << "Invalid key in Point map: " << key;
0163             return QPoint();
0164         }
0165         arg.endMapEntry();
0166     }
0167     arg.endMap();
0168     return QPoint(x, y);
0169 }
0170 
0171 QSize ConfigSerializer::deserializeSize(const QDBusArgument &arg)
0172 {
0173     int w = 0, h = 0;
0174     arg.beginMap();
0175     while (!arg.atEnd()) {
0176         QString key;
0177         QVariant value;
0178         arg.beginMapEntry();
0179         arg >> key >> value;
0180         if (key == QLatin1String("width")) {
0181             w = value.toInt();
0182         } else if (key == QLatin1String("height")) {
0183             h = value.toInt();
0184         } else {
0185             qCWarning(KSCREEN) << "Invalid key in size struct: " << key;
0186             return QSize();
0187         }
0188         arg.endMapEntry();
0189     }
0190     arg.endMap();
0191 
0192     return QSize(w, h);
0193 }
0194 
0195 ConfigPtr ConfigSerializer::deserializeConfig(const QVariantMap &map)
0196 {
0197     ConfigPtr config(new Config);
0198 
0199     if (map.contains(QLatin1String("features"))) {
0200         config->setSupportedFeatures(static_cast<Config::Features>(map[QStringLiteral("features")].toInt()));
0201     }
0202 
0203     if (map.contains(QLatin1String("tabletModeAvailable"))) {
0204         config->setTabletModeAvailable(map[QStringLiteral("tabletModeAvailable")].toBool());
0205     }
0206     if (map.contains(QLatin1String("tabletModeEngaged"))) {
0207         config->setTabletModeEngaged(map[QStringLiteral("tabletModeEngaged")].toBool());
0208     }
0209 
0210     if (map.contains(QLatin1String("outputs"))) {
0211         const QDBusArgument &outputsArg = map[QStringLiteral("outputs")].value<QDBusArgument>();
0212         outputsArg.beginArray();
0213         OutputList outputs;
0214         while (!outputsArg.atEnd()) {
0215             QVariant value;
0216             outputsArg >> value;
0217             const KScreen::OutputPtr output = deserializeOutput(value.value<QDBusArgument>());
0218             if (!output) {
0219                 return ConfigPtr();
0220             }
0221             outputs.insert(output->id(), output);
0222         }
0223         outputsArg.endArray();
0224         config->setOutputs(outputs);
0225     }
0226 
0227     if (map.contains(QLatin1String("screen"))) {
0228         const QDBusArgument &screenArg = map[QStringLiteral("screen")].value<QDBusArgument>();
0229         const KScreen::ScreenPtr screen = deserializeScreen(screenArg);
0230         if (!screen) {
0231             return ConfigPtr();
0232         }
0233         config->setScreen(screen);
0234     }
0235 
0236     return config;
0237 }
0238 
0239 OutputPtr ConfigSerializer::deserializeOutput(const QDBusArgument &arg)
0240 {
0241     OutputPtr output(new Output);
0242     std::optional<bool> primary = std::nullopt;
0243     std::optional<uint32_t> priority = std::nullopt;
0244 
0245     arg.beginMap();
0246     while (!arg.atEnd()) {
0247         QString key;
0248         QVariant value;
0249         arg.beginMapEntry();
0250         arg >> key >> value;
0251         if (key == QLatin1String("id")) {
0252             output->setId(value.toInt());
0253         } else if (key == QLatin1String("name")) {
0254             output->setName(value.toString());
0255         } else if (key == QLatin1String("type")) {
0256             output->setType(static_cast<Output::Type>(value.toInt()));
0257         } else if (key == QLatin1String("icon")) {
0258             output->setIcon(value.toString());
0259         } else if (key == QLatin1String("pos")) {
0260             output->setPos(deserializePoint(value.value<QDBusArgument>()));
0261         } else if (key == QLatin1String("scale")) {
0262             output->setScale(value.toDouble());
0263         } else if (key == QLatin1String("size")) {
0264             output->setSize(deserializeSize(value.value<QDBusArgument>()));
0265         } else if (key == QLatin1String("rotation")) {
0266             output->setRotation(static_cast<Output::Rotation>(value.toInt()));
0267         } else if (key == QLatin1String("currentModeId")) {
0268             output->setCurrentModeId(value.toString());
0269         } else if (key == QLatin1String("preferredModes")) {
0270             output->setPreferredModes(deserializeList<QString>(value.value<QDBusArgument>()));
0271         } else if (key == QLatin1String("connected")) {
0272             output->setConnected(value.toBool());
0273         } else if (key == QLatin1String("followPreferredMode")) {
0274             output->setFollowPreferredMode(value.toBool());
0275         } else if (key == QLatin1String("enabled")) {
0276             output->setEnabled(value.toBool());
0277         } else if (key == QLatin1String("primary")) {
0278             // primary is deprecated, but if it appears in config for compatibility reason.
0279             primary = value.toBool();
0280         } else if (key == QLatin1String("priority")) {
0281             // "priority" takes precedence over "primary", but we need to
0282             //  check it after the loop, otherwise it may come before the
0283             //  primary and get overridden.
0284             priority = value.toUInt();
0285         } else if (key == QLatin1String("clones")) {
0286             output->setClones(deserializeList<int>(value.value<QDBusArgument>()));
0287         } else if (key == QLatin1String("replicationSource")) {
0288             output->setReplicationSource(value.toInt());
0289         } else if (key == QLatin1String("sizeMM")) {
0290             output->setSizeMm(deserializeSize(value.value<QDBusArgument>()));
0291         } else if (key == QLatin1String("modes")) {
0292             const QDBusArgument arg = value.value<QDBusArgument>();
0293             ModeList modes;
0294             arg.beginArray();
0295             while (!arg.atEnd()) {
0296                 QVariant value;
0297                 arg >> value;
0298                 const KScreen::ModePtr mode = deserializeMode(value.value<QDBusArgument>());
0299                 if (!mode) {
0300                     return OutputPtr();
0301                 }
0302                 modes.insert(mode->id(), mode);
0303             }
0304             arg.endArray();
0305             output->setModes(modes);
0306         } else if (key == QLatin1String("overscan")) {
0307             output->setOverscan(value.toUInt());
0308         } else if (key == QLatin1String("vrrPolicy")) {
0309             output->setVrrPolicy(static_cast<Output::VrrPolicy>(value.toInt()));
0310         } else if (key == QLatin1String("rgbRange")) {
0311             output->setRgbRange(static_cast<Output::RgbRange>(value.toInt()));
0312         } else {
0313             qCWarning(KSCREEN) << "Invalid key in Output map: " << key;
0314             return OutputPtr();
0315         }
0316         arg.endMapEntry();
0317     }
0318     arg.endMap();
0319     if (primary.has_value()) {
0320         output->setPriority(output->isEnabled() ? (primary.value() ? 1 : 2) : 0);
0321     }
0322     if (priority.has_value()) {
0323         output->setPriority(priority.value());
0324     }
0325     return output;
0326 }
0327 
0328 ModePtr ConfigSerializer::deserializeMode(const QDBusArgument &arg)
0329 {
0330     ModePtr mode(new Mode);
0331 
0332     arg.beginMap();
0333     while (!arg.atEnd()) {
0334         QString key;
0335         QVariant value;
0336         arg.beginMapEntry();
0337         arg >> key >> value;
0338 
0339         if (key == QLatin1String("id")) {
0340             mode->setId(value.toString());
0341         } else if (key == QLatin1String("name")) {
0342             mode->setName(value.toString());
0343         } else if (key == QLatin1String("size")) {
0344             mode->setSize(deserializeSize(value.value<QDBusArgument>()));
0345         } else if (key == QLatin1String("refreshRate")) {
0346             mode->setRefreshRate(value.toFloat());
0347         } else {
0348             qCWarning(KSCREEN) << "Invalid key in Mode map: " << key;
0349             return ModePtr();
0350         }
0351         arg.endMapEntry();
0352     }
0353     arg.endMap();
0354     return mode;
0355 }
0356 
0357 ScreenPtr ConfigSerializer::deserializeScreen(const QDBusArgument &arg)
0358 {
0359     ScreenPtr screen(new Screen);
0360 
0361     arg.beginMap();
0362     QString key;
0363     QVariant value;
0364     while (!arg.atEnd()) {
0365         arg.beginMapEntry();
0366         arg >> key >> value;
0367         if (key == QLatin1String("id")) {
0368             screen->setId(value.toInt());
0369         } else if (key == QLatin1String("maxActiveOutputsCount")) {
0370             screen->setMaxActiveOutputsCount(value.toInt());
0371         } else if (key == QLatin1String("currentSize")) {
0372             screen->setCurrentSize(deserializeSize(value.value<QDBusArgument>()));
0373         } else if (key == QLatin1String("maxSize")) {
0374             screen->setMaxSize(deserializeSize(value.value<QDBusArgument>()));
0375         } else if (key == QLatin1String("minSize")) {
0376             screen->setMinSize(deserializeSize(value.value<QDBusArgument>()));
0377         } else {
0378             qCWarning(KSCREEN) << "Invalid key in Screen map:" << key;
0379             return ScreenPtr();
0380         }
0381         arg.endMapEntry();
0382     }
0383     arg.endMap();
0384     return screen;
0385 }