diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) project(libkscreen) -set(PROJECT_VERSION "5.18.80") +set(PROJECT_VERSION "5.18.81") set(QT_MIN_VERSION "5.12.0") set(KF5_MIN_VERSION "5.66.0") diff --git a/autotests/testconfigserializer.cpp b/autotests/testconfigserializer.cpp --- a/autotests/testconfigserializer.cpp +++ b/autotests/testconfigserializer.cpp @@ -25,6 +25,7 @@ #include "../src/screen.h" #include "../src/mode.h" #include "../src/output.h" +#include "../src/edid.h" class TestConfigSerializer : public QObject { @@ -87,6 +88,18 @@ } } + void testSerializeQuaterion() + { + QQuaternion quaternion(0.123f, 0.456f, 0.789f, 1.234f); + QJsonObject obj = KScreen::ConfigSerializer::serializeQuaternion(quaternion); + QVERIFY(!obj.isEmpty()); + + QCOMPARE(static_cast(obj[QLatin1String("x")].toDouble()), quaternion.x()); + QCOMPARE(static_cast(obj[QLatin1String("y")].toDouble()), quaternion.y()); + QCOMPARE(static_cast(obj[QLatin1String("z")].toDouble()), quaternion.z()); + QCOMPARE(static_cast(obj[QLatin1String("scalar")].toDouble()), quaternion.scalar()); + } + void testSerializeScreen() { KScreen::ScreenPtr screen(new KScreen::Screen); @@ -148,6 +161,7 @@ output->setIcon(QString()); output->setModes(modes); output->setPos(QPoint(1280, 0)); + output->setScale(2.0); output->setSize(mode->size()); output->setRotation(KScreen::Output::None); output->setCurrentModeId(QStringLiteral("1")); @@ -167,6 +181,7 @@ QCOMPARE(obj[QLatin1String("icon")].toString(), output->icon()); const QJsonArray arr = obj[QLatin1String("modes")].toArray(); QCOMPARE(arr.size(), output->modes().count()); + QCOMPARE(obj[QLatin1String("scale")].toDouble(), output->scale()); QJsonObject pos = obj[QLatin1String("pos")].toObject(); QCOMPARE(pos[QLatin1String("x")].toInt(), output->pos().x()); @@ -188,6 +203,25 @@ const QJsonObject sizeMm = obj[QLatin1String("sizeMM")].toObject(); QCOMPARE(sizeMm[QLatin1String("width")].toInt(), output->sizeMm().width()); QCOMPARE(sizeMm[QLatin1String("height")].toInt(), output->sizeMm().height()); + // EDID is null so nothing should've been serialized + QVERIFY(!obj.contains(QLatin1String("edid"))); + } + + void testSerializeEdid() + { + const QByteArray raw = "AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="; + KScreen::Edid edid(QByteArray::fromBase64(raw)); + + const auto obj = KScreen::ConfigSerializer::serializeEdid(&edid); + QCOMPARE(obj[QLatin1String("name")].toString(), QStringLiteral("DELL U2410")); + QCOMPARE(obj[QLatin1String("vendor")].toString(), QStringLiteral("Dell Inc.")); + QCOMPARE(obj[QLatin1String("serial")].toString(), QStringLiteral("F525M245AKLL")); + QCOMPARE(obj[QLatin1String("eisaId")].toString(), QStringLiteral("")); + QCOMPARE(obj[QLatin1String("hash")].toString(), QStringLiteral("be55eeb5fcc1e775f321c1ae3aa02ef0")); + QCOMPARE(obj[QLatin1String("pnpId")].toString(), QStringLiteral("DEL")); + QCOMPARE(obj[QLatin1String("width")].toInt(), 52); + QCOMPARE(obj[QLatin1String("height")].toInt(), 32); + QCOMPARE(obj[QLatin1String("gamma")].toDouble(), 2.f); } }; diff --git a/autotests/testkwaylandbackend.cpp b/autotests/testkwaylandbackend.cpp --- a/autotests/testkwaylandbackend.cpp +++ b/autotests/testkwaylandbackend.cpp @@ -291,14 +291,15 @@ QCOMPARE(o->edid()->vendor(), edid->vendor()); QCOMPARE(o->edid()->eisaId(), edid->eisaId()); QCOMPARE(o->edid()->serial(), edid->serial()); - QCOMPARE(o->edid()->hash(), edid->hash()); + QCOMPARE(o->edid()->hash(), QString::fromLatin1(m_server->outputs().last()->uuid())); QCOMPARE(o->edid()->width(), edid->width()); QCOMPARE(o->edid()->height(), edid->height()); - QCOMPARE(o->edid()->gamma(), edid->gamma()); - QCOMPARE(o->edid()->red(), edid->red()); - QCOMPARE(o->edid()->green(), edid->green()); - QCOMPARE(o->edid()->blue(), edid->blue()); - QCOMPARE(o->edid()->white(), edid->white()); + // Wayland does not support gamma and color correction yet + //QCOMPARE(o->edid()->gamma(), edid->gamma()); + //QCOMPARE(o->edid()->red(), edid->red()); + //QCOMPARE(o->edid()->green(), edid->green()); + //QCOMPARE(o->edid()->blue(), edid->blue()); + //QCOMPARE(o->edid()->white(), edid->white()); } void testWaylandBackend::verifyFeatures() diff --git a/autotests/testqscreenbackend.cpp b/autotests/testqscreenbackend.cpp --- a/autotests/testqscreenbackend.cpp +++ b/autotests/testqscreenbackend.cpp @@ -135,7 +135,8 @@ QEXPECT_FAIL("", "The X server doesn't return a sensible physical output size", Continue); QVERIFY(output->sizeMm() != QSize()); } - QVERIFY(output->edid() != nullptr); + + QVERIFY(output->edid() == nullptr); QCOMPARE(output->rotation(), Output::None); QVERIFY(!ids.contains(output->id())); ids << output->id(); diff --git a/backends/fake/fake.h b/backends/fake/fake.h --- a/backends/fake/fake.h +++ b/backends/fake/fake.h @@ -39,7 +39,7 @@ QString serviceName() const override; KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; - QByteArray edid(int outputId) const override; + KScreen::EdidPtr edid(int outputId) const override; bool isValid() const override; void setConnected(int outputId, bool connected); diff --git a/backends/fake/fake.cpp b/backends/fake/fake.cpp --- a/backends/fake/fake.cpp +++ b/backends/fake/fake.cpp @@ -103,7 +103,7 @@ return true; } -QByteArray Fake::edid(int outputId) const +EdidPtr Fake::edid(int outputId) const { Q_UNUSED(outputId); QFile file(mConfigFile); @@ -119,9 +119,9 @@ continue; } - return QByteArray::fromBase64(output[QStringLiteral("edid")].toByteArray()); + return EdidPtr::create(QByteArray::fromBase64(output[QStringLiteral("edid")].toByteArray())); } - return QByteArray(); + return {}; } void Fake::setConnected(int outputId, bool connected) diff --git a/backends/kwayland/waylandbackend.h b/backends/kwayland/waylandbackend.h --- a/backends/kwayland/waylandbackend.h +++ b/backends/kwayland/waylandbackend.h @@ -42,7 +42,7 @@ KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; bool isValid() const override; - QByteArray edid(int outputId) const override; + KScreen::EdidPtr edid(int outputId) const override; private: WaylandConfig *m_internalConfig; diff --git a/backends/kwayland/waylandbackend.cpp b/backends/kwayland/waylandbackend.cpp --- a/backends/kwayland/waylandbackend.cpp +++ b/backends/kwayland/waylandbackend.cpp @@ -69,13 +69,14 @@ m_internalConfig->applyConfig(newconfig); } -QByteArray WaylandBackend::edid(int outputId) const +EdidPtr WaylandBackend::edid(int outputId) const { WaylandOutput *output = m_internalConfig->outputMap().value(outputId); if (!output) { - return QByteArray(); + return {}; } - return output->outputDevice()->edid(); + + return output->edid(); } bool WaylandBackend::isValid() const diff --git a/backends/kwayland/waylandoutput.h b/backends/kwayland/waylandoutput.h --- a/backends/kwayland/waylandoutput.h +++ b/backends/kwayland/waylandoutput.h @@ -50,6 +50,7 @@ KScreen::OutputPtr toKScreenOutput(); void updateKScreenOutput(KScreen::OutputPtr &output); + KScreen::EdidPtr edid() const; quint32 id() const; QString name() const; diff --git a/backends/kwayland/waylandoutput.cpp b/backends/kwayland/waylandoutput.cpp --- a/backends/kwayland/waylandoutput.cpp +++ b/backends/kwayland/waylandoutput.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -216,10 +217,28 @@ return QStringLiteral("%1 %2").arg(m_device->manufacturer(), m_device->model()); } +EdidPtr WaylandOutput::edid() const +{ + const auto device = outputDevice(); + auto edid = new Edid::Private; + edid->vendorName = device->manufacturer(); + edid->monitorName = device->model(); + edid->serialNumber = device->serialNumber(); + edid->eisaId = device->eisaId(); + // KWin reports physical size in mm, but X11 always reports in cm, so be consistent here + edid->width = device->physicalSize().width() / 10; + edid->height = device->physicalSize().height() / 10; + edid->checksum = QString::fromLatin1(device->uuid()); + edid->valid = true; + + return EdidPtr::create(edid); +} + QDebug operator<<(QDebug dbg, const WaylandOutput *output) { dbg << "WaylandOutput(Id:" << output->id() <<", Name:" << \ QString(output->outputDevice()->manufacturer() + QLatin1Char(' ') + \ output->outputDevice()->model()) << ")"; return dbg; } + diff --git a/backends/xrandr/xrandr.h b/backends/xrandr/xrandr.h --- a/backends/xrandr/xrandr.h +++ b/backends/xrandr/xrandr.h @@ -24,6 +24,7 @@ #include #include "../xcbwrapper.h" +#include "types.h" class QRect; class QTimer; @@ -45,7 +46,7 @@ KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; bool isValid() const override; - QByteArray edid(int outputId) const override; + KScreen::EdidPtr edid(int outputId) const override; static QByteArray outputEdid(xcb_randr_output_t outputId); static xcb_randr_get_screen_resources_reply_t* screenResources(); diff --git a/backends/xrandr/xrandr.cpp b/backends/xrandr/xrandr.cpp --- a/backends/xrandr/xrandr.cpp +++ b/backends/xrandr/xrandr.cpp @@ -216,14 +216,14 @@ qCDebug(KSCREEN_XRANDR) << "XRandR::setConfig done!"; } -QByteArray XRandR::edid(int outputId) const +EdidPtr XRandR::edid(int outputId) const { const XRandROutput *output = s_internalConfig->output(outputId); if (!output) { - return QByteArray(); + return {}; } - return output->edid(); + return EdidPtr::create(output->edid()); } bool XRandR::isValid() const diff --git a/interfaces/org.kde.KScreen.Backend.xml b/interfaces/org.kde.KScreen.Backend.xml --- a/interfaces/org.kde.KScreen.Backend.xml +++ b/interfaces/org.kde.KScreen.Backend.xml @@ -18,7 +18,8 @@ - + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,11 +73,12 @@ REQUIRED_HEADERS KScreen_REQ_HEADERS ) -install(FILES ${KScreen_HEADERS} +install(FILES ${KScreen_HEADERS} ${KScreen_PRIVATE_HEADERS} DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/KScreen/KScreen COMPONENT Devel) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kscreen_export.h backendmanager_p.h # needed for unit-tests in KScreen + configserializer_p.h ${KScreen_REQ_HEADERS} DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/KScreen/kscreen) diff --git a/src/abstractbackend.h b/src/abstractbackend.h --- a/src/abstractbackend.h +++ b/src/abstractbackend.h @@ -86,14 +86,15 @@ virtual bool isValid() const = 0; /** - * Returns encoded EDID data for given output + * Returns parsed EDID data for given output * - * Default implementation does nothing and returns null QByteArray. Backends - * that don't support EDID don't have to reimplement this method. + * Default implementation does nothing and returns null pointer. Backends + * that do not have direct access to EDID information can assemble at + * least a partial EDID object from the data it has available. * * @param outputd ID of output to return EDID data for */ - virtual QByteArray edid(int outputId) const; + virtual KScreen::EdidPtr edid(int outputId) const; Q_SIGNALS: /** diff --git a/src/abstractbackend.cpp b/src/abstractbackend.cpp --- a/src/abstractbackend.cpp +++ b/src/abstractbackend.cpp @@ -24,8 +24,8 @@ Q_UNUSED(arguments); } -QByteArray KScreen::AbstractBackend::edid(int outputId) const +KScreen::EdidPtr KScreen::AbstractBackend::edid(int outputId) const { Q_UNUSED(outputId); - return QByteArray(); + return {}; } diff --git a/src/backendlauncher/backenddbuswrapper.h b/src/backendlauncher/backenddbuswrapper.h --- a/src/backendlauncher/backenddbuswrapper.h +++ b/src/backendlauncher/backenddbuswrapper.h @@ -43,7 +43,7 @@ QVariantMap getConfig() const; QVariantMap setConfig(const QVariantMap &config); - QByteArray getEdid(int output) const; + QVariantMap getEdid(int output) const; inline KScreen::AbstractBackend *backend() const { return mBackend; } diff --git a/src/backendlauncher/backenddbuswrapper.cpp b/src/backendlauncher/backenddbuswrapper.cpp --- a/src/backendlauncher/backenddbuswrapper.cpp +++ b/src/backendlauncher/backenddbuswrapper.cpp @@ -21,6 +21,7 @@ #include "backendloader.h" #include "backendadaptor.h" #include "kscreen_backendLauncher_debug.h" +#include "edid.h" #include "src/configserializer_p.h" #include "src/config.h" @@ -93,14 +94,14 @@ return obj.toVariantMap(); } -QByteArray BackendDBusWrapper::getEdid(int output) const +QVariantMap BackendDBusWrapper::getEdid(int output) const { - const QByteArray edidData = mBackend->edid(output); - if (edidData.isEmpty()) { - return QByteArray(); + const auto edid = mBackend->edid(output); + if (!edid) { + return {}; } - return edidData; + return KScreen::ConfigSerializer::serializeEdid(edid.data()).toVariantMap(); } void BackendDBusWrapper::backendConfigChanged(const KScreen::ConfigPtr &config) diff --git a/src/backendmanager.cpp b/src/backendmanager.cpp --- a/src/backendmanager.cpp +++ b/src/backendmanager.cpp @@ -363,7 +363,7 @@ // And listen for its change. connect(mInterface, &org::kde::kscreen::Backend::configChanged, [&](const QVariantMap &newConfig) { - mConfig = KScreen::ConfigSerializer::deserializeConfig(newConfig); + mConfig = KScreen::ConfigSerializer::deserializeConfig(newConfig, ConfigSerializer::ExcludeEDID); }); } diff --git a/src/configserializer.cpp b/src/configserializer.cpp --- a/src/configserializer.cpp +++ b/src/configserializer.cpp @@ -24,6 +24,7 @@ #include "output.h" #include "screen.h" #include "edid.h" +#include "edid_p.h" #include "kscreen_debug.h" #include @@ -49,7 +50,17 @@ return obj; } -QJsonObject ConfigSerializer::serializeConfig(const ConfigPtr &config) +QJsonObject ConfigSerializer::serializeQuaternion(const QQuaternion &quaternion) +{ + QJsonObject obj; + obj[QLatin1String("x")] = quaternion.x(); + obj[QLatin1String("y")] = quaternion.y(); + obj[QLatin1String("z")] = quaternion.z(); + obj[QLatin1String("scalar")] = quaternion.scalar(); + return obj; +} + +QJsonObject ConfigSerializer::serializeConfig(const ConfigPtr &config, SerializerFlags flags) { QJsonObject obj; @@ -61,7 +72,7 @@ QJsonArray outputs; Q_FOREACH (const OutputPtr &output, config->outputs()) { - outputs.append(serializeOutput(output)); + outputs.append(serializeOutput(output, flags)); } obj[QLatin1String("outputs")] = outputs; if (config->screen()) { @@ -71,7 +82,7 @@ return obj; } -QJsonObject ConfigSerializer::serializeOutput(const OutputPtr &output) +QJsonObject ConfigSerializer::serializeOutput(const OutputPtr &output, SerializerFlags flags) { QJsonObject obj; @@ -90,9 +101,11 @@ obj[QLatin1String("enabled")] = output->isEnabled(); obj[QLatin1String("primary")] = output->isPrimary(); obj[QLatin1String("clones")] = serializeList(output->clones()); - //obj[QLatin1String("edid")] = output->edid()->raw(); obj[QLatin1String("sizeMM")] = serializeSize(output->sizeMm()); obj[QLatin1String("replicationSource")] = output->replicationSource(); + if (!(flags & ExcludeEDID) && output->edid() != nullptr) { + obj[QLatin1String("edid")] = serializeEdid(output->edid()); + } QJsonArray modes; Q_FOREACH (const ModePtr &mode, output->modes()) { @@ -124,7 +137,25 @@ obj[QLatin1String("maxSize")] = serializeSize(screen->maxSize()); obj[QLatin1String("minSize")] = serializeSize(screen->minSize()); obj[QLatin1String("maxActiveOutputsCount")] = screen->maxActiveOutputsCount(); + return obj; +} +QJsonObject ConfigSerializer::serializeEdid(const Edid *edid) +{ + QJsonObject obj; + obj[QLatin1String("name")] = edid->name(); + obj[QLatin1String("vendor")] = edid->vendor(); + obj[QLatin1String("serial")] = edid->serial(); + obj[QLatin1String("eisaId")] = edid->eisaId(); + obj[QLatin1String("hash")] = edid->hash(); + obj[QLatin1String("pnpId")] = edid->pnpId(); + obj[QLatin1String("width")] = static_cast(edid->width()); + obj[QLatin1String("height")] = static_cast(edid->height()); + obj[QLatin1String("gamma")] = edid->gamma(); + obj[QLatin1String("red")] = serializeQuaternion(edid->red()); + obj[QLatin1String("blue")] = serializeQuaternion(edid->blue()); + obj[QLatin1String("green")] = serializeQuaternion(edid->green()); + obj[QLatin1String("white")] = serializeQuaternion(edid->white()); return obj; } @@ -175,7 +206,37 @@ return QSize(w, h); } -ConfigPtr ConfigSerializer::deserializeConfig(const QVariantMap &map) +QQuaternion KScreen::ConfigSerializer::deserializeQuaternion(const QDBusArgument &arg) +{ + QQuaternion quaternion; + arg.beginMap(); + while (!arg.atEnd()) { + QString key; + QVariant value; + arg.beginMapEntry(); + arg >> key >> value; + if (key == QLatin1String("x")) { + quaternion.setX(value.toFloat()); + } else if (key == QLatin1String("y")) { + quaternion.setY(value.toFloat()); + } else if (key == QLatin1String("z")) { + quaternion.setZ(value.toFloat()); + } else if (key == QLatin1String("scalar")) { + quaternion.setScalar(value.toFloat()); + } else { + qCWarning(KSCREEN) << "Invalid key in Quaternion struct:" << key; + return {}; + } + arg.endMapEntry(); + } + arg.endMap(); + + return quaternion; +} + + + +ConfigPtr ConfigSerializer::deserializeConfig(const QVariantMap &map, SerializerFlags flags) { ConfigPtr config(new Config); @@ -190,7 +251,7 @@ while (!outputsArg.atEnd()) { QVariant value; outputsArg >> value; - const KScreen::OutputPtr output = deserializeOutput(value.value()); + const KScreen::OutputPtr output = deserializeOutput(value.value(), flags); if (!output) { return ConfigPtr(); } @@ -212,7 +273,7 @@ return config; } -OutputPtr ConfigSerializer::deserializeOutput(const QDBusArgument &arg) +OutputPtr ConfigSerializer::deserializeOutput(const QDBusArgument &arg, SerializerFlags flags) { OutputPtr output(new Output); @@ -271,6 +332,8 @@ } arg.endArray(); output->setModes(modes); + } else if (!(flags & ExcludeEDID) && key == QLatin1String("edid")) { + output->setEdid(deserializeEdid(value.value())); } else { qCWarning(KSCREEN) << "Invalid key in Output map: " << key; return OutputPtr(); @@ -339,3 +402,51 @@ arg.endMap(); return screen; } + +Edid *ConfigSerializer::deserializeEdid(const QDBusArgument &arg) +{ + auto edid = new Edid::Private; + arg.beginMap(); + QString key; + QVariant value; + while (!arg.atEnd()) { + arg.beginMapEntry(); + arg >> key >> value; + if (key == QLatin1String("name")) { + edid->monitorName = value.toString(); + } else if (key == QLatin1String("vendor")) { + edid->vendorName = value.toString(); + } else if (key == QLatin1String("serial")) { + edid->serialNumber = value.toString(); + } else if (key == QLatin1String("eisaId")) { + edid->eisaId = value.toString(); + } else if (key == QLatin1String("hash")) { + edid->checksum = value.toString(); + } else if (key == QLatin1String("pnpId")) { + edid->pnpId = value.toString(); + } else if (key == QLatin1String("width")) { + edid->width = static_cast(value.toInt()); + } else if (key == QLatin1String("height")) { + edid->height = static_cast(value.toInt()); + } else if (key == QLatin1String("gamma")) { + edid->gamma = value.toReal(); + } else if (key == QLatin1String("red")) { + edid->red = deserializeQuaternion(value.value()); + } else if (key == QLatin1String("blue")) { + edid->blue = deserializeQuaternion(value.value()); + } else if (key == QLatin1String("green")) { + edid->green = deserializeQuaternion(value.value()); + } else if (key == QLatin1String("white")) { + edid->white = deserializeQuaternion(value.value()); + } else { + qCWarning(KSCREEN) << "Invalid key in Edid map:" << key; + delete edid; + return {}; + } + arg.endMapEntry(); + } + arg.endMap(); + + edid->valid = true; + return new Edid(edid); +} diff --git a/src/configserializer_p.h b/src/configserializer_p.h --- a/src/configserializer_p.h +++ b/src/configserializer_p.h @@ -24,54 +24,67 @@ #include #include #include +#include #include "types.h" #include "kscreen_export.h" namespace KScreen { -namespace ConfigSerializer -{ +class Edid; -KSCREEN_EXPORT QJsonObject serializePoint(const QPoint &point); -KSCREEN_EXPORT QJsonObject serializeSize(const QSize &size); -template -KSCREEN_EXPORT QJsonArray serializeList(const QList &list) +class KSCREEN_EXPORT ConfigSerializer { - QJsonArray arr; - Q_FOREACH (const T &t, list) { - arr.append(t); +public: + enum SerializerFlag { + NoFlags = 0, + ExcludeEDID = 1 + }; + using SerializerFlags = QFlags; + + static QJsonObject serializePoint(const QPoint &point); + static QJsonObject serializeSize(const QSize &size); + template + static QJsonArray serializeList(const QList &list) + { + QJsonArray arr; + Q_FOREACH (const T &t, list) { + arr.append(t); + } + return arr; } - return arr; -} + static QJsonObject serializeQuaternion(const QQuaternion &quaternion); -KSCREEN_EXPORT QJsonObject serializeConfig(const KScreen::ConfigPtr &config); -KSCREEN_EXPORT QJsonObject serializeOutput(const KScreen::OutputPtr &output); -KSCREEN_EXPORT QJsonObject serializeMode(const KScreen::ModePtr &mode); -KSCREEN_EXPORT QJsonObject serializeScreen(const KScreen::ScreenPtr &screen); + static QJsonObject serializeConfig(const ConfigPtr &config, SerializerFlags flags = NoFlags); + static QJsonObject serializeOutput(const OutputPtr &output, SerializerFlags flags = NoFlags); + static QJsonObject serializeMode(const ModePtr &mode); + static QJsonObject serializeScreen(const ScreenPtr &screen); + static QJsonObject serializeEdid(const Edid *edid); -KSCREEN_EXPORT QPoint deserializePoint(const QDBusArgument &map); -KSCREEN_EXPORT QSize deserializeSize(const QDBusArgument &map); -template -KSCREEN_EXPORT QList deserializeList(const QDBusArgument &arg) -{ - QList list; - arg.beginArray(); - while (!arg.atEnd()) { - QVariant v; - arg >> v; - list.append(v.value()); + static QPoint deserializePoint(const QDBusArgument &map); + static QSize deserializeSize(const QDBusArgument &map); + template + static QList deserializeList(const QDBusArgument &arg) + { + QList list; + arg.beginArray(); + while (!arg.atEnd()) { + QVariant v; + arg >> v; + list.append(v.value()); + } + arg.endArray(); + return list; } - arg.endArray(); - return list; -} -KSCREEN_EXPORT KScreen::ConfigPtr deserializeConfig(const QVariantMap &map); -KSCREEN_EXPORT KScreen::OutputPtr deserializeOutput(const QDBusArgument &output); -KSCREEN_EXPORT KScreen::ModePtr deserializeMode(const QDBusArgument &mode); -KSCREEN_EXPORT KScreen::ScreenPtr deserializeScreen(const QDBusArgument &screen); + static QQuaternion deserializeQuaternion(const QDBusArgument &arg); -} + static ConfigPtr deserializeConfig(const QVariantMap &map, SerializerFlags flags = NoFlags); + static OutputPtr deserializeOutput(const QDBusArgument &output, SerializerFlags flags = NoFlags); + static ModePtr deserializeMode(const QDBusArgument &mode); + static ScreenPtr deserializeScreen(const QDBusArgument &screen); + static Edid* deserializeEdid(const QDBusArgument &edid); +}; } diff --git a/src/doctor/doctor.cpp b/src/doctor/doctor.cpp --- a/src/doctor/doctor.cpp +++ b/src/doctor/doctor.cpp @@ -39,6 +39,7 @@ #include "../edid.h" #include "../log.h" #include "../output.h" +#include "../configserializer_p.h" Q_LOGGING_CATEGORY(KSCREEN_DOCTOR, "kscreen.doctor") @@ -53,15 +54,6 @@ const static QString bold = QStringLiteral("\033[01;39m"); const static QString cr = QStringLiteral("\033[0;0m"); -namespace KScreen -{ -namespace ConfigSerializer -{ -// Exported private symbol in configserializer_p.h in KScreen -extern QJsonObject serializeConfig(const KScreen::ConfigPtr &config); -} -} - using namespace KScreen; Doctor::Doctor(QObject *parent) diff --git a/src/edid.h b/src/edid.h --- a/src/edid.h +++ b/src/edid.h @@ -24,8 +24,8 @@ #include "kscreen_export.h" #include -#include #include +#include namespace KScreen { @@ -49,8 +49,12 @@ Q_PROPERTY(QQuaternion white READ white CONSTANT) public: + class Private; + explicit Edid(); explicit Edid(const QByteArray &data, QObject *parent = nullptr); + explicit Edid(Private *dd); + ~Edid() override; Q_REQUIRED_RESULT Edid* clone() const; @@ -75,10 +79,7 @@ private: Q_DISABLE_COPY(Edid) - class Private; - Private * const d; - - explicit Edid(Private *dd); + QScopedPointer const d; }; } diff --git a/src/edid.cpp b/src/edid.cpp --- a/src/edid.cpp +++ b/src/edid.cpp @@ -18,6 +18,7 @@ *************************************************************************************/ #include "edid.h" +#include "edid_p.h" #include "kscreen_debug_edid.h" #include @@ -45,57 +46,6 @@ using namespace KScreen; -class Q_DECL_HIDDEN Edid::Private -{ - public: - Private(): - valid(false), - width(0), - height(0), - gamma(0) - { } - - Private(const Private &other): - valid(other.valid), - monitorName(other.monitorName), - vendorName(other.vendorName), - serialNumber(other.serialNumber), - eisaId(other.eisaId), - checksum(other.checksum), - pnpId(other.pnpId), - width(other.width), - height(other.height), - gamma(other.gamma), - red(other.red), - green(other.green), - blue(other.blue), - white(other.white) - { - } - - bool parse(const QByteArray &data); - int edidGetBit(int in, int bit) const; - int edidGetBits(int in, int begin, int end) const; - float edidDecodeFraction(int high, int low) const; - QString edidParseString(const quint8 *data) const; - - bool valid; - QString monitorName; - QString vendorName; - QString serialNumber; - QString eisaId; - QString checksum; - QString pnpId; - uint width; - uint height; - qreal gamma; - QQuaternion red; - QQuaternion green; - QQuaternion blue; - QQuaternion white; -}; - - Edid::Edid() : QObject() , d(new Private()) @@ -115,10 +65,7 @@ { } -Edid::~Edid() -{ - delete d; -} +Edid::~Edid() = default; Edid *Edid::clone() const { @@ -134,21 +81,21 @@ { QString id = QStringLiteral("xrandr"); // if no info was added check if the fallbacName is provided - if (vendor().isNull() && name().isNull() && serial().isNull()) { + if (vendor().isEmpty() && name().isEmpty() && serial().isEmpty()) { if (!fallbackName.isEmpty()) { id.append(QLatin1Char('-') % fallbackName); } else { // all info we have are empty strings id.append(QLatin1String("-unknown")); } } else if (d->valid) { - if (!vendor().isNull()) { + if (!vendor().isEmpty()) { id.append(QLatin1Char('-') % vendor()); } - if (!name().isNull()) { + if (!name().isEmpty()) { id.append(QLatin1Char('-') % name()); } - if (!serial().isNull()) { + if (!serial().isEmpty()) { id.append(QLatin1Char('-') % serial()); } } diff --git a/src/types.h b/src/edid_p.h copy from src/types.h copy to src/edid_p.h --- a/src/types.h +++ b/src/edid_p.h @@ -1,5 +1,6 @@ /************************************************************************************* - * Copyright 2014 Daniel Vrátil * + * Copyright (C) 2012 - 2014 by Daniel Vrátil * + * (C) 2018 by Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -16,27 +17,37 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ -#ifndef KSCREEN_TYPES_H -#define KSCREEN_TYPES_H +#ifndef KSCREEN_EDID_P_H_ +#define KSCREEN_EDID_P_H_ -#include -#include +#include "edid.h" -namespace KScreen -{ +namespace KScreen { -class Config; -typedef QSharedPointer ConfigPtr; -class Screen; -typedef QSharedPointer ScreenPtr; -class Output; -typedef QSharedPointer OutputPtr; -typedef QMap OutputList; +class Q_DECL_HIDDEN Edid::Private +{ +public: + bool parse(const QByteArray &data); + int edidGetBit(int in, int bit) const; + int edidGetBits(int in, int begin, int end) const; + float edidDecodeFraction(int high, int low) const; + QString edidParseString(const quint8 *data) const; -class Mode; -typedef QSharedPointer ModePtr; -typedef QMap ModeList; + bool valid = false; + QString monitorName; + QString vendorName; + QString serialNumber; + QString eisaId; + QString checksum; + QString pnpId; + uint width = 0; + uint height = 0; + qreal gamma = 0; + QQuaternion red; + QQuaternion green; + QQuaternion blue; + QQuaternion white; +}; } - #endif diff --git a/src/getconfigoperation.cpp b/src/getconfigoperation.cpp --- a/src/getconfigoperation.cpp +++ b/src/getconfigoperation.cpp @@ -25,6 +25,7 @@ #include "backendmanager_p.h" #include "configserializer_p.h" #include "backendinterface.h" +#include "edid.h" using namespace KScreen; @@ -132,18 +133,18 @@ Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_Q(GetConfigOperation); - QDBusPendingReply reply = *watcher; + QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { q->setError(reply.error().message()); q->emitResult(); return; } - const QByteArray edidData = reply.value(); const int outputId = watcher->property("outputId").toInt(); - config->output(outputId)->setEdid(edidData); + auto edid = ConfigSerializer::deserializeEdid(reply.reply().arguments().at(0).value()); + config->output(outputId)->setEdid(edid); if (--pendingEDIDs == 0) { q->emitResult(); } @@ -193,8 +194,10 @@ } Q_FOREACH (auto output, config->outputs()) { if (output->edid() == nullptr) { - const QByteArray edidData = backend->edid(output->id()); - output->setEdid(edidData); + auto edid = backend->edid(output->id()); + if (edid) { + output->setEdid(edid->clone()); + } } } } diff --git a/src/output.h b/src/output.h --- a/src/output.h +++ b/src/output.h @@ -238,14 +238,16 @@ void setReplicationSource(int source); void setEdid(const QByteArray &rawData); + void setEdid(Edid *edid); /** * edid returns the output's EDID information if available. * * The output maintains ownership of the returned Edid, so the caller should not delete it. * Note that the edid is only valid as long as the output is alive. */ - Edid* edid() const; + Edid *edid() const; + /** * Returns the physical size of the screen in milimeters. diff --git a/src/output.cpp b/src/output.cpp --- a/src/output.cpp +++ b/src/output.cpp @@ -550,6 +550,11 @@ d->edid.reset(new Edid(rawData)); } +void Output::setEdid(Edid *edid) +{ + d->edid.reset(edid); +} + Edid *Output::edid() const { return d->edid.data(); diff --git a/src/types.h b/src/types.h --- a/src/types.h +++ b/src/types.h @@ -37,6 +37,9 @@ typedef QSharedPointer ModePtr; typedef QMap ModeList; +class Edid; +typedef QSharedPointer EdidPtr; + } #endif diff --git a/tests/kwayland/waylandconfigreader.cpp b/tests/kwayland/waylandconfigreader.cpp --- a/tests/kwayland/waylandconfigreader.cpp +++ b/tests/kwayland/waylandconfigreader.cpp @@ -82,6 +82,8 @@ outputdevice->setPhysicalSize(QSize(edid.width() * 10, edid.height() * 10)); outputdevice->setManufacturer(edid.vendor()); outputdevice->setModel(edid.name()); + outputdevice->setSerialNumber(edid.serial()); + outputdevice->setEisaId(edid.eisaId()); } else { outputdevice->setPhysicalSize(sizeFromJson(outputConfig[QStringLiteral("sizeMM")])); outputdevice->setManufacturer(outputConfig[QStringLiteral("manufacturer")].toString());