diff --git a/common/control.cpp b/common/control.cpp index 7f3e75f..491bf99 100644 --- a/common/control.cpp +++ b/common/control.cpp @@ -1,224 +1,233 @@ /******************************************************************** 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() +QString Control::dirPath() const { return Globals::dirPath() % s_dirName; } +QString Control::filePathFromHash(const QString &hash) const +{ + return dirPath() % hash; +} + 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())); + QFile file(filePathFromHash(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) +QString ControlConfig::dirPath() const { - const QString dir = dirPath() % QStringLiteral("configs/"); - if (!QDir().mkpath(dir)) { - return QString(); - } - return dir % hash; + return Control::dirPath() % QStringLiteral("configs/"); } -QString ControlConfig::filePath() +QString ControlConfig::filePath() const { if (!m_config) { return QString(); } - return ControlConfig::filePath(m_config->connectedOutputsHash()); + return filePathFromHash(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 = getOutputs(); for (const auto variantInfo : outputsInfo) { const QVariantMap info = variantInfo.toMap(); if (!infoIsOutput(info, outputId, outputName)) { 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 KScreen::OutputPtr &output, OutputRetention value) { setOutputRetention(output->hash(), output->name(), value); } 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() { + const QString path = filePath(); + + if (m_info.isEmpty()) { + // Nothing to write. Default control. Remove file if it exists. + QFile::remove(path); + return true; + } + if (!QDir().mkpath(dirPath())) { + // TODO: error message + return false; + } + // write updated data to file - QFile file(filePath()); + QFile file(path); 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) +QString ControlOutput::dirPath() const { - const QString dir = dirPath() % QStringLiteral("outputs/"); - if (!QDir().mkpath(dir)) { - return QString(); - } - return dir % hash; + return Control::dirPath() % QStringLiteral("outputs/"); } -QString ControlOutput::filePath() +QString ControlOutput::filePath() const { if (!m_output) { return QString(); } - return ControlOutput::filePath(m_output->hash()); + return filePathFromHash(m_output->hash()); } diff --git a/common/control.h b/common/control.h index 2335a70..3b29c6e 100644 --- a/common/control.h +++ b/common/control.h @@ -1,90 +1,91 @@ /******************************************************************** 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 #include class Control { Q_GADGET public: enum class OutputRetention { Undefined = -1, Global = 0, Individual = 1, }; Q_ENUM(OutputRetention) virtual ~Control() = default; - virtual QString filePath() = 0; - protected: - static QString dirPath(); + virtual QString dirPath() const; + virtual QString filePath() const = 0; + QString filePathFromHash(const QString &hash) const; + static OutputRetention convertVariantToOutputRetention(QVariant variant); private: static QString s_dirName; }; class ControlConfig : public Control { Q_GADGET public: ControlConfig(KScreen::ConfigPtr config); OutputRetention getOutputRetention(const KScreen::OutputPtr &output) const; OutputRetention getOutputRetention(const QString &outputId, const QString &outputName) const; void setOutputRetention(const KScreen::OutputPtr &output, OutputRetention value); void setOutputRetention(const QString &outputId, const QString &outputName, OutputRetention value); bool writeFile(); - QString filePath() override; - static QString filePath(const QString &hash); + QString dirPath() const override; + QString filePath() const override; 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 { Q_GADGET public: ControlOutput(KScreen::OutputPtr output); // TODO: scale auto value - QString filePath() override; - static QString filePath(const QString &hash); + QString dirPath() const override; + QString filePath() const override; private: KScreen::OutputPtr m_output; }; #endif