File indexing completed on 2024-05-05 05:30:25

0001 /*
0002     SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org>
0003     SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 #include "config.h"
0008 #include "../common/control.h"
0009 #include "device.h"
0010 #include "kscreen_daemon_debug.h"
0011 #include "output.h"
0012 
0013 #include <QDir>
0014 #include <QFile>
0015 #include <QJsonDocument>
0016 #include <QRect>
0017 #include <QStandardPaths>
0018 #include <QStringBuilder>
0019 
0020 #include <cstdint>
0021 
0022 #include <kscreen/output.h>
0023 #include <kscreen/screen.h>
0024 
0025 QString Config::s_fixedConfigFileName = QStringLiteral("fixed-config");
0026 QString Config::s_configsDirName = QString();
0027 /*QStringLiteral("configs");*/ // TODO: KDE6 - Replace QString w/ QStringLiteral move these files into the subfolder
0028 
0029 QString Config::configsDirPath()
0030 {
0031     return Globals::dirPath() % s_configsDirName;
0032 }
0033 
0034 Config::Config(KScreen::ConfigPtr config, QObject *parent)
0035     : QObject(parent)
0036     , m_data(config)
0037     , m_control(new ControlConfig(config, this))
0038 {
0039 }
0040 
0041 QString Config::filePath() const
0042 {
0043     if (!QDir().mkpath(configsDirPath())) {
0044         return QString();
0045     }
0046     return configsDirPath() % id();
0047 }
0048 
0049 QString Config::id() const
0050 {
0051     if (!m_data) {
0052         return QString();
0053     }
0054     return m_data->connectedOutputsHash();
0055 }
0056 
0057 void Config::activateControlWatching()
0058 {
0059     connect(m_control, &ControlConfig::changed, this, &Config::controlChanged);
0060     m_control->activateWatcher();
0061 }
0062 
0063 bool Config::fileExists() const
0064 {
0065     return (QFile::exists(configsDirPath() % id()) || QFile::exists(configsDirPath() % s_fixedConfigFileName));
0066 }
0067 
0068 std::unique_ptr<Config> Config::readFile()
0069 {
0070     if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) {
0071         // We may look for a config that has been set when the lid was closed, Bug: 353029
0072         const QString lidOpenedFilePath(filePath() % QStringLiteral("_lidOpened"));
0073         const QFile srcFile(lidOpenedFilePath);
0074 
0075         if (srcFile.exists()) {
0076             QFile::remove(filePath());
0077             if (QFile::copy(lidOpenedFilePath, filePath())) {
0078                 QFile::remove(lidOpenedFilePath);
0079                 qCDebug(KSCREEN_KDED) << "Restored lid opened config to" << id();
0080             }
0081         }
0082     }
0083     return readFile(id());
0084 }
0085 
0086 std::unique_ptr<Config> Config::readOpenLidFile()
0087 {
0088     const QString openLidFile = id() % QStringLiteral("_lidOpened");
0089     auto config = readFile(openLidFile);
0090     QFile::remove(configsDirPath() % openLidFile);
0091     return config;
0092 }
0093 
0094 std::unique_ptr<Config> Config::readFile(const QString &fileName)
0095 {
0096     if (!m_data) {
0097         return nullptr;
0098     }
0099     auto config = std::unique_ptr<Config>(new Config(m_data->clone()));
0100     config->setValidityFlags(m_validityFlags);
0101 
0102     QFile file;
0103     if (QFile::exists(configsDirPath() % s_fixedConfigFileName)) {
0104         file.setFileName(configsDirPath() % s_fixedConfigFileName);
0105         qCDebug(KSCREEN_KDED) << "found a fixed config, will use " << file.fileName();
0106     } else {
0107         file.setFileName(configsDirPath() % fileName);
0108     }
0109     if (!file.open(QIODevice::ReadOnly)) {
0110         qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName();
0111         return nullptr;
0112     }
0113 
0114     QJsonDocument parser;
0115     QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList();
0116     Output::readInOutputs(config->data(), outputs);
0117 
0118     QSize screenSize;
0119     const auto configOutputs = config->data()->outputs();
0120     for (const auto &output : configOutputs) {
0121         if (!output->isPositionable()) {
0122             continue;
0123         }
0124 
0125         output->setExplicitLogicalSize(config->data()->logicalSizeForOutput(*output));
0126 
0127         const QRect geom = output->geometry();
0128         if (geom.x() + geom.width() > screenSize.width()) {
0129             screenSize.setWidth(geom.x() + geom.width());
0130         }
0131         if (geom.y() + geom.height() > screenSize.height()) {
0132             screenSize.setHeight(geom.y() + geom.height());
0133         }
0134     }
0135     config->data()->screen()->setCurrentSize(screenSize);
0136 
0137     if (!canBeApplied(config->data())) {
0138         return nullptr;
0139     }
0140     return config;
0141 }
0142 
0143 bool Config::canBeApplied() const
0144 {
0145     return canBeApplied(m_data);
0146 }
0147 
0148 bool Config::canBeApplied(KScreen::ConfigPtr config) const
0149 {
0150 #ifdef KDED_UNIT_TEST
0151     Q_UNUSED(config);
0152     return true;
0153 #else
0154     return KScreen::Config::canBeApplied(config, m_validityFlags);
0155 #endif
0156 }
0157 
0158 bool Config::writeFile()
0159 {
0160     return writeFile(filePath());
0161 }
0162 
0163 bool Config::writeOpenLidFile()
0164 {
0165     return writeFile(filePath() % QStringLiteral("_lidOpened"));
0166 }
0167 
0168 bool Config::writeFile(const QString &filePath)
0169 {
0170     if (id().isEmpty()) {
0171         return false;
0172     }
0173     const KScreen::OutputList outputs = m_data->outputs();
0174 
0175     const auto oldConfig = readFile();
0176     KScreen::OutputList oldOutputs;
0177     if (oldConfig) {
0178         oldOutputs = oldConfig->data()->outputs();
0179     }
0180 
0181     const auto hasDuplicate = [&outputs](const auto output) {
0182         return std::any_of(outputs.begin(), outputs.end(), [output](const auto &o) {
0183             return o != output && o->hashMd5() == output->hashMd5();
0184         });
0185     };
0186 
0187     QVariantList outputList;
0188     for (const KScreen::OutputPtr &output : outputs) {
0189         QVariantMap info;
0190 
0191         const auto oldOutputIt = std::find_if(oldOutputs.constBegin(), oldOutputs.constEnd(), [output](const KScreen::OutputPtr &out) {
0192             return out->hashMd5() == output->hashMd5();
0193         });
0194         const KScreen::OutputPtr oldOutput = oldOutputIt != oldOutputs.constEnd() ? *oldOutputIt : nullptr;
0195 
0196         if (!output->isConnected()) {
0197             continue;
0198         }
0199 
0200         Output::writeGlobalPart(output, info, oldOutput);
0201         info[QStringLiteral("priority")] = output->priority();
0202         info[QStringLiteral("enabled")] = output->isEnabled();
0203 
0204         auto setOutputConfigInfo = [&info](const KScreen::OutputPtr &out) {
0205             if (!out) {
0206                 return;
0207             }
0208 
0209             QVariantMap pos;
0210             pos[QStringLiteral("x")] = out->pos().x();
0211             pos[QStringLiteral("y")] = out->pos().y();
0212             info[QStringLiteral("pos")] = pos;
0213         };
0214         setOutputConfigInfo(output->isEnabled() ? output : oldOutput);
0215 
0216         if (output->isEnabled()) {
0217             // try to update global output data
0218             Output::writeGlobal(output, hasDuplicate(output));
0219         }
0220 
0221         outputList.append(info);
0222     }
0223 
0224     QFile file(filePath);
0225     if (!file.open(QIODevice::WriteOnly)) {
0226         qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString();
0227         return false;
0228     }
0229     file.write(QJsonDocument::fromVariant(outputList).toJson());
0230     qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName();
0231 
0232     return true;
0233 }
0234 
0235 void Config::log()
0236 {
0237     if (!m_data) {
0238         return;
0239     }
0240     const auto outputs = m_data->outputs();
0241     for (const auto &o : outputs) {
0242         if (o->isConnected()) {
0243             qCDebug(KSCREEN_KDED) << o;
0244         }
0245     }
0246 }
0247 
0248 #include "moc_config.cpp"