File indexing completed on 2024-05-05 17:39:43

0001 /*
0002  *  SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-or-later
0005  */
0006 
0007 #include "waylandconfigreader.h"
0008 
0009 #include <QDebug>
0010 
0011 #include <QFile>
0012 #include <QJsonArray>
0013 #include <QJsonDocument>
0014 #include <QJsonObject>
0015 #include <QUuid>
0016 
0017 #include "edid.h"
0018 
0019 using namespace KScreen;
0020 
0021 static QList<int> s_outputIds;
0022 
0023 void WaylandConfigReader::outputsFromConfig(const QString &configfile,
0024                                             KWayland::Server::Display *display,
0025                                             QList<KWayland::Server::OutputDeviceInterface *> &outputs)
0026 {
0027     qDebug() << "Loading server from" << configfile;
0028     QFile file(configfile);
0029     file.open(QIODevice::ReadOnly);
0030 
0031     QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
0032     QJsonObject json = jsonDoc.object();
0033 
0034     QJsonArray omap = json[QStringLiteral("outputs")].toArray();
0035     for (const QJsonValue &value : omap) {
0036         const QVariantMap &output = value.toObject().toVariantMap();
0037         if (output[QStringLiteral("connected")].toBool()) {
0038             outputs << createOutputDevice(output, display);
0039             // qDebug() << "new Output created: " << output["name"].toString();
0040         } else {
0041             // qDebug() << "disconnected Output" << output["name"].toString();
0042         }
0043     }
0044     auto outpus = WaylandConfigReader::createOutputs(display, outputs);
0045     s_outputIds.clear();
0046 }
0047 
0048 OutputDeviceInterface *WaylandConfigReader::createOutputDevice(const QVariantMap &outputConfig, KWayland::Server::Display *display)
0049 {
0050     KWayland::Server::OutputDeviceInterface *outputdevice = display->createOutputDevice(display);
0051 
0052     QByteArray data = QByteArray::fromBase64(outputConfig[QStringLiteral("edid")].toByteArray());
0053     outputdevice->setEdid(data);
0054     Edid edid(data, display);
0055 
0056     //     qDebug() << "EDID Info: ";
0057     if (edid.isValid()) {
0058         // qDebug() << "\tDevice ID: " << edid.deviceId();
0059         // qDebug() << "\tName: " << edid.name();
0060         // qDebug() << "\tVendor: " << edid.vendor();
0061         // qDebug() << "\tSerial: " << edid.serial();
0062         // qDebug() << "\tEISA ID: " << edid.eisaId();
0063         // qDebug() << "\tHash: " << edid.hash();
0064         // qDebug() << "\tWidth (mm): " << edid.width();
0065         // qDebug() << "\tHeight (mm): " << edid.height();
0066         // qDebug() << "\tGamma: " << edid.gamma();
0067         // qDebug() << "\tRed: " << edid.red();
0068         // qDebug() << "\tGreen: " << edid.green();
0069         // qDebug() << "\tBlue: " << edid.blue();
0070         // qDebug() << "\tWhite: " << edid.white();
0071         outputdevice->setPhysicalSize(QSize(edid.width() * 10, edid.height() * 10));
0072         outputdevice->setManufacturer(edid.vendor());
0073         outputdevice->setModel(edid.name());
0074     } else {
0075         outputdevice->setPhysicalSize(sizeFromJson(outputConfig[QStringLiteral("sizeMM")]));
0076         outputdevice->setManufacturer(outputConfig[QStringLiteral("manufacturer")].toString());
0077         outputdevice->setModel(outputConfig[QStringLiteral("model")].toString());
0078     }
0079     auto uuid = QUuid::createUuid().toByteArray();
0080     auto _id = outputConfig[QStringLiteral("id")].toInt();
0081     if (_id) {
0082         uuid = QString::number(_id).toLocal8Bit();
0083     }
0084     outputdevice->setUuid(uuid);
0085 
0086     const QMap<int, KWayland::Server::OutputDeviceInterface::Transform> transformMap = {{0, KWayland::Server::OutputDeviceInterface::Transform::Normal},
0087                                                                                         {1, KWayland::Server::OutputDeviceInterface::Transform::Normal},
0088                                                                                         {2, KWayland::Server::OutputDeviceInterface::Transform::Rotated270},
0089                                                                                         {3, KWayland::Server::OutputDeviceInterface::Transform::Rotated180},
0090                                                                                         {4, KWayland::Server::OutputDeviceInterface::Transform::Rotated90}};
0091 
0092     outputdevice->setTransform(transformMap[outputConfig[QStringLiteral("rotation")].toInt()]);
0093     int currentModeId = outputConfig[QStringLiteral("currentModeId")].toInt();
0094     QVariantList preferredModes = outputConfig[QStringLiteral("preferredModes")].toList();
0095 
0096     int mode_id = 0;
0097     for (const QVariant &_mode : outputConfig[QStringLiteral("modes")].toList()) {
0098         mode_id++;
0099         const QVariantMap &mode = _mode.toMap();
0100         OutputDeviceInterface::Mode m0;
0101         const QSize _size = sizeFromJson(mode[QStringLiteral("size")]);
0102 
0103         auto refreshRateIt = mode.constFind(QStringLiteral("refreshRate"));
0104         if (refreshRateIt != mode.constEnd()) {
0105             m0.refreshRate = qRound(refreshRateIt->toReal() * 1000); // config has it in Hz
0106         }
0107         bool isCurrent = currentModeId == mode[QStringLiteral("id")].toInt();
0108         bool isPreferred = preferredModes.contains(mode[QStringLiteral("id")]);
0109 
0110         OutputDeviceInterface::ModeFlags flags;
0111         if (isCurrent && isPreferred) {
0112             flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current | OutputDeviceInterface::ModeFlag::Preferred);
0113         } else if (isCurrent) {
0114             flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current);
0115         } else if (isPreferred) {
0116             flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred);
0117         }
0118 
0119         auto idIt = mode.constFind(QStringLiteral("id"));
0120         if (idIt != mode.constEnd()) {
0121             m0.id = idIt->toInt();
0122         } else {
0123             m0.id = mode_id;
0124         }
0125         m0.size = _size;
0126         m0.flags = flags;
0127         outputdevice->addMode(m0);
0128 
0129         if (isCurrent) {
0130             outputdevice->setCurrentMode(m0.id);
0131         }
0132     }
0133 
0134     outputdevice->setGlobalPosition(pointFromJson(outputConfig[QStringLiteral("pos")]));
0135     outputdevice->setEnabled(outputConfig[QStringLiteral("enabled")].toBool() ? OutputDeviceInterface::Enablement::Enabled
0136                                                                               : OutputDeviceInterface::Enablement::Disabled);
0137     outputdevice->create();
0138 
0139     return outputdevice;
0140 }
0141 
0142 QList<KWayland::Server::OutputInterface *> KScreen::WaylandConfigReader::createOutputs(KWayland::Server::Display *display,
0143                                                                                        QList<KWayland::Server::OutputDeviceInterface *> &outputdevices)
0144 {
0145     const QMap<KWayland::Server::OutputDeviceInterface::Transform, KWayland::Server::OutputInterface::Transform> transformMap = {
0146         {KWayland::Server::OutputDeviceInterface::Transform::Normal, KWayland::Server::OutputInterface::Transform::Normal},
0147         {KWayland::Server::OutputDeviceInterface::Transform::Rotated270, KWayland::Server::OutputInterface::Transform::Rotated270},
0148         {KWayland::Server::OutputDeviceInterface::Transform::Rotated180, KWayland::Server::OutputInterface::Transform::Rotated180},
0149         {KWayland::Server::OutputDeviceInterface::Transform::Rotated90, KWayland::Server::OutputInterface::Transform::Rotated90},
0150     };
0151 
0152     QList<KWayland::Server::OutputInterface *> outputs;
0153     for (const auto outputdevice : outputdevices) {
0154         qDebug() << "New Output!";
0155         KWayland::Server::OutputInterface *output = display->createOutput(display);
0156 
0157         // Sync properties from outputdevice to the newly created output interface
0158         output->setManufacturer(outputdevice->manufacturer());
0159         output->setModel(outputdevice->model());
0160         // output->setUuid(outputdevice->uuid());
0161 
0162         for (const auto mode : outputdevice->modes()) {
0163             bool isCurrent = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current);
0164             bool isPreferred = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current);
0165             OutputInterface::ModeFlags flags;
0166             if (isPreferred && isCurrent) {
0167                 flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current | OutputInterface::ModeFlag::Preferred);
0168             } else if (isCurrent) {
0169                 flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current);
0170             } else if (isPreferred) {
0171                 flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred);
0172             }
0173 
0174             OutputInterface::Mode m0;
0175 
0176             m0.size = mode.size;
0177             output->addMode(m0.size, m0.flags, m0.refreshRate);
0178 
0179             if (isCurrent) {
0180                 output->setCurrentMode(m0.size, m0.refreshRate);
0181             }
0182             // qDebug() << "mode added:" << m0.size << m0.refreshRate << isCurrent;
0183         }
0184 
0185         output->setGlobalPosition(outputdevice->globalPosition());
0186         output->setPhysicalSize(outputdevice->physicalSize());
0187         output->setTransform(transformMap.value(outputdevice->transform()));
0188 
0189         output->setDpmsSupported(true);
0190         output->setDpmsMode(OutputInterface::DpmsMode::On);
0191         QObject::connect(output, &OutputInterface::dpmsModeRequested, [](KWayland::Server::OutputInterface::DpmsMode requestedMode) {
0192             Q_UNUSED(requestedMode);
0193             // FIXME: make sure this happens in the scope of an object!
0194             qDebug() << "DPMS Mode change requested";
0195         });
0196         output->create();
0197         outputs << output;
0198     }
0199     return outputs;
0200 }
0201 
0202 QSize WaylandConfigReader::sizeFromJson(const QVariant &data)
0203 {
0204     QVariantMap map = data.toMap();
0205 
0206     QSize size;
0207     size.setWidth(map[QStringLiteral("width")].toInt());
0208     size.setHeight(map[QStringLiteral("height")].toInt());
0209 
0210     return size;
0211 }
0212 
0213 QPoint WaylandConfigReader::pointFromJson(const QVariant &data)
0214 {
0215     QVariantMap map = data.toMap();
0216 
0217     QPoint point;
0218     point.setX(map[QStringLiteral("x")].toInt());
0219     point.setY(map[QStringLiteral("y")].toInt());
0220 
0221     return point;
0222 }
0223 
0224 QRect WaylandConfigReader::rectFromJson(const QVariant &data)
0225 {
0226     QRect rect;
0227     rect.setSize(WaylandConfigReader::sizeFromJson(data));
0228     rect.setBottomLeft(WaylandConfigReader::pointFromJson(data));
0229 
0230     return rect;
0231 }