diff --git a/common/control.cpp b/common/control.cpp index 44e2b06..90240e8 100644 --- a/common/control.cpp +++ b/common/control.cpp @@ -1,154 +1,219 @@ /******************************************************************** Copyright 2019 Roman Gilg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "control.h" #include "globals.h" #include #include #include #include #include QString Control::s_dirName = QStringLiteral("control/"); QString Control::dirPath() { return Globals::dirPath() % s_dirName; } Control::OutputRetention Control::convertVariantToOutputRetention(QVariant variant) { if (variant.canConvert()) { const auto retention = variant.toInt(); if (retention == (int)OutputRetention::Global) { return OutputRetention::Global; } if (retention == (int)OutputRetention::Individual) { return OutputRetention::Individual; } } return OutputRetention::Undefined; } ControlConfig::ControlConfig(KScreen::ConfigPtr config) : m_config(config) { // qDebug() << "Looking for control file:" << config->connectedOutputsHash(); QFile file(filePath(config->connectedOutputsHash())); if (file.open(QIODevice::ReadOnly)) { + // This might not be reached, bus this is ok. The control file will + // eventually be created on first write later on. QJsonDocument parser; m_info = parser.fromJson(file.readAll()).toVariant().toMap(); } // TODO: use a file watcher in case of changes to the control file while // object exists? // As global outputs are indexed by a hash of their edid, which is not unique, // to be able to tell apart multiple identical outputs, these need special treatment { QStringList allIds; const auto outputs = config->outputs(); allIds.reserve(outputs.count()); for (const KScreen::OutputPtr &output : outputs) { const auto outputId = output->hash(); if (allIds.contains(outputId) && !m_duplicateOutputIds.contains(outputId)) { m_duplicateOutputIds << outputId; } allIds << outputId; } } // TODO: this is same in Output::readInOutputs of the daemon. Combine? // TODO: connect to outputs added/removed signals and reevaluate duplicate ids // in case of such a change while object exists? } QString ControlConfig::filePath(const QString &hash) { const QString dir = dirPath() % QStringLiteral("configs/"); if (!QDir().mkpath(dir)) { return QString(); } return dir % hash; } QString ControlConfig::filePath() { if (!m_config) { return QString(); } return ControlConfig::filePath(m_config->connectedOutputsHash()); } +bool ControlConfig::infoIsOutput(const QVariantMap &info, const QString &outputId, const QString &outputName) const +{ + const QString outputIdInfo = info[QStringLiteral("id")].toString(); + if (outputIdInfo.isEmpty()) { + return false; + } + if (outputId != outputIdInfo) { + return false; + } + + if (!outputName.isEmpty() && m_duplicateOutputIds.contains(outputId)) { + // We may have identical outputs connected, these will have the same id in the config + // in order to find the right one, also check the output's name (usually the connector) + const auto metadata = info[QStringLiteral("metadata")].toMap(); + const auto outputNameInfo = metadata[QStringLiteral("name")].toString(); + if (outputName != outputNameInfo) { + // was a duplicate id, but info not for this output + return false; + } + } + return true; +} + Control::OutputRetention ControlConfig::getOutputRetention(const KScreen::OutputPtr &output) const { return getOutputRetention(output->hash(), output->name()); } Control::OutputRetention ControlConfig::getOutputRetention(const QString &outputId, const QString &outputName) const { - const QVariantList outputsInfo = m_info[QStringLiteral("outputs")].toList(); + const QVariantList outputsInfo = getOutputs(); for (const auto variantInfo : outputsInfo) { const QVariantMap info = variantInfo.toMap(); - - const QString outputIdInfo = info[QStringLiteral("id")].toString(); - if (outputIdInfo.isEmpty()) { + if (!infoIsOutput(info, outputId, outputName)) { continue; } - if (outputId != outputIdInfo) { - continue; - } - - if (!outputName.isEmpty() && m_duplicateOutputIds.contains(outputId)) { - // We may have identical outputs connected, these will have the same id in the config - // in order to find the right one, also check the output's name (usually the connector) - const auto metadata = info[QStringLiteral("metadata")].toMap(); - const auto outputNameInfo = metadata[QStringLiteral("name")].toString(); - if (outputName != outputNameInfo) { - // was a duplicate id, but info not for this output - continue; - } - } return convertVariantToOutputRetention(info[QStringLiteral("retention")]); } // info for output not found return OutputRetention::Undefined; } +static QVariantMap metadata(const QString &outputName) +{ + QVariantMap metadata; + metadata[QStringLiteral("name")] = outputName; + return metadata; +} + +void ControlConfig::setOutputRetention(const QString &outputId, const QString &outputName, OutputRetention value) +{ + QList::iterator it; + QVariantList outputsInfo = getOutputs(); + + for (it = outputsInfo.begin(); it != outputsInfo.end(); ++it) { + QVariantMap outputInfo = (*it).toMap(); + if (!infoIsOutput(outputInfo, outputId, outputName)) { + continue; + } + outputInfo[QStringLiteral("retention")] = (int)value; + *it = outputInfo; + setOutputs(outputsInfo); + return; + } + // no entry yet, create one + QVariantMap outputInfo; + outputInfo[QStringLiteral("id")] = outputId; + outputInfo[QStringLiteral("metadata")] = metadata(outputName); + outputInfo[QStringLiteral("retention")] = (int)value; + + outputsInfo << outputInfo; + setOutputs(outputsInfo); +} + +bool ControlConfig::writeFile() +{ + // write updated data to file + QFile file(filePath()); + if (!file.open(QIODevice::WriteOnly)) { + // TODO: logging category? +// qCWarning(KSCREEN_COMMON) << "Failed to open config control file for writing! " << file.errorString(); + return false; + } + file.write(QJsonDocument::fromVariant(m_info).toJson()); +// qCDebug(KSCREEN_COMMON) << "Config control saved on: " << file.fileName(); + return true; +} + +QVariantList ControlConfig::getOutputs() const +{ + return m_info[QStringLiteral("outputs")].toList(); +} + +void ControlConfig::setOutputs(QVariantList outputsInfo) +{ + m_info[QStringLiteral("outputs")] = outputsInfo; +} + ControlOutput::ControlOutput(KScreen::OutputPtr output) : m_output(output) { } QString ControlOutput::filePath(const QString &hash) { const QString dir = dirPath() % QStringLiteral("outputs/"); if (!QDir().mkpath(dir)) { return QString(); } return dir % hash; } QString ControlOutput::filePath() { if (!m_output) { return QString(); } return ControlOutput::filePath(m_output->hash()); } diff --git a/common/control.h b/common/control.h index 77c809c..1856155 100644 --- a/common/control.h +++ b/common/control.h @@ -1,76 +1,83 @@ /******************************************************************** Copyright 2019 Roman Gilg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef COMMON_CONTROL_H #define COMMON_CONTROL_H #include #include class Control { public: enum class OutputRetention { Undefined = -1, Global = 0, Individual = 1, }; virtual ~Control() = default; virtual QString filePath() = 0; protected: static QString dirPath(); static OutputRetention convertVariantToOutputRetention(QVariant variant); private: static QString s_dirName; }; class ControlConfig : public Control { public: ControlConfig(KScreen::ConfigPtr config); OutputRetention getOutputRetention(const KScreen::OutputPtr &output) const; OutputRetention getOutputRetention(const QString &outputId, const QString &outputName) const; + void setOutputRetention(const QString &outputId, const QString &outputName, OutputRetention value); + + bool writeFile(); QString filePath() override; static QString filePath(const QString &hash); private: + QVariantList getOutputs() const; + void setOutputs(QVariantList outputsInfo); + bool infoIsOutput(const QVariantMap &info, const QString &outputId, const QString &outputName) const; + KScreen::ConfigPtr m_config; QVariantMap m_info; QStringList m_duplicateOutputIds; }; class ControlOutput : public Control { public: ControlOutput(KScreen::OutputPtr output); // TODO: scale auto value QString filePath() override; static QString filePath(const QString &hash); private: KScreen::OutputPtr m_output; }; #endif