From 386814176b7a0d9dd1e13c22d2f6ea333692a063 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Thu, 6 May 2021 02:13:19 +0200 Subject: [PATCH] drm: Use KScreen's json files to set up the initial output composition So far we had a composition setup within kwinrc and kscreen. This produces flickering sometime and makes the state a bit more flimsy. This patch changes the kwin's behaviour to use the files produced by kscreen which are anyways available down the line. THis simplifies our behaviour down to just one format that we write to and feed from. This also allows us to leverage it further by using this format for default setups (which consist in the right file in ~/.local/share/kscreen). --- src/plugins/platforms/drm/drm_backend.cpp | 194 +++++++++++----------- src/plugins/platforms/drm/drm_backend.h | 2 - 2 files changed, 100 insertions(+), 96 deletions(-) diff --git a/src/plugins/platforms/drm/drm_backend.cpp b/src/plugins/platforms/drm/drm_backend.cpp index 63f1c83f98..2f819fe6d4 100644 --- a/src/plugins/platforms/drm/drm_backend.cpp +++ b/src/plugins/platforms/drm/drm_backend.cpp @@ -31,12 +31,14 @@ // KWayland #include // KF5 -#include #include #include -#include // Qt #include +#include +#include +#include +#include #include // system #include @@ -67,7 +69,6 @@ DrmBackend::DrmBackend(QObject *parent) DrmBackend::~DrmBackend() { - writeOutputsConfiguration(); qDeleteAll(m_gpus); } @@ -345,43 +346,87 @@ bool DrmBackend::updateOutputs() return true; } -static QString transformToString(DrmOutput::Transform transform) +namespace KWinKScreenIntegration { - switch (transform) { - case DrmOutput::Transform::Normal: - return QStringLiteral("normal"); - case DrmOutput::Transform::Rotated90: - return QStringLiteral("rotate-90"); - case DrmOutput::Transform::Rotated180: - return QStringLiteral("rotate-180"); - case DrmOutput::Transform::Rotated270: - return QStringLiteral("rotate-270"); - case DrmOutput::Transform::Flipped: - return QStringLiteral("flip"); - case DrmOutput::Transform::Flipped90: - return QStringLiteral("flip-90"); - case DrmOutput::Transform::Flipped180: - return QStringLiteral("flip-180"); - case DrmOutput::Transform::Flipped270: - return QStringLiteral("flip-270"); - default: - return QStringLiteral("normal"); + /// See KScreen::Output::hashMd5 + QString outputHash(DrmOutput *output) + { + QCryptographicHash hash(QCryptographicHash::Md5); + if (!output->edid().isEmpty()) { + hash.addData(output->edid()); + } else { + hash.addData(output->name().toLatin1()); + } + return QString::fromLatin1(hash.result().toHex()); } -} -static DrmOutput::Transform stringToTransform(const QString &text) -{ - static const QHash stringToTransform { - { QStringLiteral("normal"), DrmOutput::Transform::Normal }, - { QStringLiteral("rotate-90"), DrmOutput::Transform::Rotated90 }, - { QStringLiteral("rotate-180"), DrmOutput::Transform::Rotated180 }, - { QStringLiteral("rotate-270"), DrmOutput::Transform::Rotated270 }, - { QStringLiteral("flip"), DrmOutput::Transform::Flipped }, - { QStringLiteral("flip-90"), DrmOutput::Transform::Flipped90 }, - { QStringLiteral("flip-180"), DrmOutput::Transform::Flipped180 }, - { QStringLiteral("flip-270"), DrmOutput::Transform::Flipped270 } + /// See KScreen::Config::connectedOutputsHash in libkscreen + QString connectedOutputsHash(const QVector &outputs) + { + QStringList hashedOutputs; + hashedOutputs.reserve(outputs.count()); + for (auto output : qAsConst(outputs)) { + hashedOutputs << outputHash(output); + } + std::sort(hashedOutputs.begin(), hashedOutputs.end()); + const auto hash = QCryptographicHash::hash(hashedOutputs.join(QString()).toLatin1(), QCryptographicHash::Md5); + return QString::fromLatin1(hash.toHex()); + } + + QMap outputsConfig(const QVector &outputs) + { + const QString kscreenJsonPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kscreen/") % connectedOutputsHash(outputs)); + QFile f(kscreenJsonPath); + if (!f.open(QIODevice::ReadOnly)) { + qCWarning(KWIN_DRM) << "Could not open file" << kscreenJsonPath; + return {}; + } + + QJsonParseError error; + const auto doc = QJsonDocument::fromJson(f.readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(KWIN_DRM) << "Failed to parse" << kscreenJsonPath << error.errorString(); + return {}; + } + + QMap ret; + const auto outputsJson = doc.array(); + for (const auto &outputJson : outputsJson) { + const auto outputObject = outputJson.toObject(); + for (auto it = outputs.constBegin(), itEnd = outputs.constEnd(); it != itEnd; ) { + if (!ret.contains(*it) && outputObject["id"] == outputHash(*it)) { + ret[*it] = outputObject; + continue; + } + ++it; + } + } + return ret; + } + + /// See KScreen::Output::Rotation + enum Rotation { + None = 1, + Left = 2, + Inverted = 4, + Right = 8, }; - return stringToTransform.value(text, DrmOutput::Transform::Normal); + + DrmOutput::Transform toDrmTransform(int rotation) + { + switch (Rotation(rotation)) { + case None: + return DrmOutput::Transform::Normal; + case Left: + return DrmOutput::Transform::Rotated90; + case Inverted: + return DrmOutput::Transform::Rotated180; + case Right: + return DrmOutput::Transform::Rotated270; + default: + Q_UNREACHABLE(); + } + } } void DrmBackend::readOutputsConfiguration() @@ -389,72 +434,33 @@ void DrmBackend::readOutputsConfiguration() if (m_outputs.isEmpty()) { return; } - const QString uuid = generateOutputConfigurationUuid(); - const auto outputGroup = kwinApp()->config()->group("DrmOutputs"); - const auto configGroup = outputGroup.group(uuid); + const auto outputsInfo = KWinKScreenIntegration::outputsConfig(m_outputs); + // default position goes from left to right QPoint pos(0, 0); for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) { - qCDebug(KWIN_DRM) << "Reading output configuration for [" << uuid << "] ["<< (*it)->uuid() << "]"; - const auto outputConfig = configGroup.group((*it)->uuid().toString(QUuid::WithoutBraces)); - (*it)->setGlobalPos(outputConfig.readEntry("Position", pos)); - if (outputConfig.hasKey("Scale")) - (*it)->setScale(outputConfig.readEntry("Scale", 1.0)); - (*it)->setTransformInternal(stringToTransform(outputConfig.readEntry("Transform", "normal"))); - pos.setX(pos.x() + (*it)->geometry().width()); - if (outputConfig.hasKey("Mode")) { - QString mode = outputConfig.readEntry("Mode"); - QStringList list = mode.split("_"); - if (list.size() > 1) { - QStringList size = list[0].split("x"); - if (size.size() > 1) { - int width = size[0].toInt(); - int height = size[1].toInt(); - int refreshRate = list[1].toInt(); - (*it)->updateMode(width, height, refreshRate); - } + const QJsonObject outputInfo = outputsInfo[*it]; + qCDebug(KWIN_DRM) << "Reading output configuration for " << *it; + if (!outputInfo.isEmpty()) { + const QJsonObject pos = outputInfo["pos"].toObject(); + (*it)->setGlobalPos({pos["x"].toInt(), pos["y"].toInt()}); + if (const QJsonValue scale = outputInfo["scale"]; !scale.isUndefined()) { + (*it)->setScale(scale.toDouble(1.)); } + (*it)->setTransformInternal(KWinKScreenIntegration::toDrmTransform(outputInfo["rotation"].toInt())); + + if (const QJsonObject mode = outputInfo["mode"].toObject(); !mode.isEmpty()) { + const QJsonObject size = mode["size"].toObject(); + (*it)->updateMode(size["width"].toInt(), size["height"].toInt(), mode["refresh"].toDouble() * 1000); + } + } else { + (*it)->setGlobalPos(pos); + (*it)->setTransformInternal(DrmOutput::Transform::Normal); } + pos.setX(pos.x() + (*it)->geometry().width()); } } -void DrmBackend::writeOutputsConfiguration() -{ - if (m_outputs.isEmpty()) { - return; - } - const QString uuid = generateOutputConfigurationUuid(); - auto configGroup = KSharedConfig::openConfig()->group("DrmOutputs").group(uuid); - // default position goes from left to right - for (auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it) { - qCDebug(KWIN_DRM) << "Writing output configuration for [" << uuid << "] ["<< (*it)->uuid() << "]"; - auto outputConfig = configGroup.group((*it)->uuid().toString(QUuid::WithoutBraces)); - outputConfig.writeEntry("Scale", (*it)->scale()); - outputConfig.writeEntry("Transform", transformToString((*it)->transform())); - QString mode; - mode += QString::number((*it)->modeSize().width()); - mode += "x"; - mode += QString::number((*it)->modeSize().height()); - mode += "_"; - mode += QString::number((*it)->refreshRate()); - outputConfig.writeEntry("Mode", mode); - } -} - -QString DrmBackend::generateOutputConfigurationUuid() const -{ - auto it = m_outputs.constBegin(); - if (m_outputs.size() == 1) { - // special case: one output - return (*it)->uuid().toString(QUuid::WithoutBraces); - } - QCryptographicHash hash(QCryptographicHash::Md5); - for (const DrmOutput *output: qAsConst(m_outputs)) { - hash.addData(output->uuid().toByteArray()); - } - return QString::fromLocal8Bit(hash.result().toHex().left(10)); -} - void DrmBackend::enableOutput(DrmOutput *output, bool enable) { if (enable) { diff --git a/src/plugins/platforms/drm/drm_backend.h b/src/plugins/platforms/drm/drm_backend.h index 0176cfb3bd..8cc73bacb2 100644 --- a/src/plugins/platforms/drm/drm_backend.h +++ b/src/plugins/platforms/drm/drm_backend.h @@ -89,8 +89,6 @@ private: void moveCursor(); void initCursor(); void readOutputsConfiguration(); - void writeOutputsConfiguration(); - QString generateOutputConfigurationUuid() const; void handleUdevEvent(); DrmGpu *addGpu(const QString &fileName);