File indexing completed on 2024-04-28 16:45:08
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::autoRotationRequested() const 0064 { 0065 for (KScreen::OutputPtr &output : m_data->outputs()) { 0066 if (m_control->getAutoRotate(output)) { 0067 return true; 0068 } 0069 } 0070 return false; 0071 } 0072 0073 void Config::setDeviceOrientation(QOrientationReading::Orientation orientation) 0074 { 0075 for (KScreen::OutputPtr &output : m_data->outputs()) { 0076 if (!m_control->getAutoRotate(output)) { 0077 continue; 0078 } 0079 auto finalOrientation = orientation; 0080 if (m_control->getAutoRotateOnlyInTabletMode(output) && !m_data->tabletModeEngaged()) { 0081 finalOrientation = QOrientationReading::Orientation::TopUp; 0082 } 0083 if (Output::updateOrientation(output, finalOrientation)) { 0084 // TODO: call Layouter to find fitting positions for other outputs again 0085 return; 0086 } 0087 } 0088 } 0089 0090 bool Config::getAutoRotate() const 0091 { 0092 const auto outputs = m_data->outputs(); 0093 return std::all_of(outputs.cbegin(), outputs.cend(), [this](KScreen::OutputPtr output) { 0094 if (output->type() != KScreen::Output::Type::Panel) { 0095 return true; 0096 } 0097 return m_control->getAutoRotate(output); 0098 }); 0099 } 0100 0101 void Config::setAutoRotate(bool value) 0102 { 0103 for (KScreen::OutputPtr &output : m_data->outputs()) { 0104 if (output->type() != KScreen::Output::Type::Panel) { 0105 continue; 0106 } 0107 if (m_control->getAutoRotate(output) != value) { 0108 m_control->setAutoRotate(output, value); 0109 } 0110 } 0111 m_control->writeFile(); 0112 } 0113 0114 bool Config::fileExists() const 0115 { 0116 return (QFile::exists(configsDirPath() % id()) || QFile::exists(configsDirPath() % s_fixedConfigFileName)); 0117 } 0118 0119 std::unique_ptr<Config> Config::readFile() 0120 { 0121 if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) { 0122 // We may look for a config that has been set when the lid was closed, Bug: 353029 0123 const QString lidOpenedFilePath(filePath() % QStringLiteral("_lidOpened")); 0124 const QFile srcFile(lidOpenedFilePath); 0125 0126 if (srcFile.exists()) { 0127 QFile::remove(filePath()); 0128 if (QFile::copy(lidOpenedFilePath, filePath())) { 0129 QFile::remove(lidOpenedFilePath); 0130 qCDebug(KSCREEN_KDED) << "Restored lid opened config to" << id(); 0131 } 0132 } 0133 } 0134 return readFile(id()); 0135 } 0136 0137 std::unique_ptr<Config> Config::readOpenLidFile() 0138 { 0139 const QString openLidFile = id() % QStringLiteral("_lidOpened"); 0140 auto config = readFile(openLidFile); 0141 QFile::remove(configsDirPath() % openLidFile); 0142 return config; 0143 } 0144 0145 std::unique_ptr<Config> Config::readFile(const QString &fileName) 0146 { 0147 if (!m_data) { 0148 return nullptr; 0149 } 0150 auto config = std::unique_ptr<Config>(new Config(m_data->clone())); 0151 config->setValidityFlags(m_validityFlags); 0152 0153 QFile file; 0154 if (QFile::exists(configsDirPath() % s_fixedConfigFileName)) { 0155 file.setFileName(configsDirPath() % s_fixedConfigFileName); 0156 qCDebug(KSCREEN_KDED) << "found a fixed config, will use " << file.fileName(); 0157 } else { 0158 file.setFileName(configsDirPath() % fileName); 0159 } 0160 if (!file.open(QIODevice::ReadOnly)) { 0161 qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName(); 0162 return nullptr; 0163 } 0164 0165 QJsonDocument parser; 0166 QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); 0167 Output::readInOutputs(config->data(), outputs); 0168 0169 QSize screenSize; 0170 const auto configOutputs = config->data()->outputs(); 0171 for (const auto &output : configOutputs) { 0172 if (!output->isPositionable()) { 0173 continue; 0174 } 0175 0176 output->setExplicitLogicalSize(config->data()->logicalSizeForOutput(*output)); 0177 0178 const QRect geom = output->geometry(); 0179 if (geom.x() + geom.width() > screenSize.width()) { 0180 screenSize.setWidth(geom.x() + geom.width()); 0181 } 0182 if (geom.y() + geom.height() > screenSize.height()) { 0183 screenSize.setHeight(geom.y() + geom.height()); 0184 } 0185 } 0186 config->data()->screen()->setCurrentSize(screenSize); 0187 0188 if (!canBeApplied(config->data())) { 0189 return nullptr; 0190 } 0191 return config; 0192 } 0193 0194 bool Config::canBeApplied() const 0195 { 0196 return canBeApplied(m_data); 0197 } 0198 0199 bool Config::canBeApplied(KScreen::ConfigPtr config) const 0200 { 0201 #ifdef KDED_UNIT_TEST 0202 Q_UNUSED(config); 0203 return true; 0204 #else 0205 return KScreen::Config::canBeApplied(config, m_validityFlags); 0206 #endif 0207 } 0208 0209 bool Config::writeFile() 0210 { 0211 return writeFile(filePath()); 0212 } 0213 0214 bool Config::writeOpenLidFile() 0215 { 0216 return writeFile(filePath() % QStringLiteral("_lidOpened")); 0217 } 0218 0219 bool Config::writeFile(const QString &filePath) 0220 { 0221 if (id().isEmpty()) { 0222 return false; 0223 } 0224 const KScreen::OutputList outputs = m_data->outputs(); 0225 0226 const auto oldConfig = readFile(); 0227 KScreen::OutputList oldOutputs; 0228 if (oldConfig) { 0229 oldOutputs = oldConfig->data()->outputs(); 0230 } 0231 0232 const auto hasDuplicate = [&outputs](const auto output) { 0233 return std::any_of(outputs.begin(), outputs.end(), [output](const auto &o) { 0234 return o != output && o->hashMd5() == output->hashMd5(); 0235 }); 0236 }; 0237 0238 QVariantList outputList; 0239 for (const KScreen::OutputPtr &output : outputs) { 0240 QVariantMap info; 0241 0242 const auto oldOutputIt = std::find_if(oldOutputs.constBegin(), oldOutputs.constEnd(), [output](const KScreen::OutputPtr &out) { 0243 return out->hashMd5() == output->hashMd5(); 0244 }); 0245 const KScreen::OutputPtr oldOutput = oldOutputIt != oldOutputs.constEnd() ? *oldOutputIt : nullptr; 0246 0247 if (!output->isConnected()) { 0248 continue; 0249 } 0250 0251 Output::writeGlobalPart(output, info, oldOutput); 0252 info[QStringLiteral("priority")] = output->priority(); 0253 info[QStringLiteral("enabled")] = output->isEnabled(); 0254 0255 auto setOutputConfigInfo = [&info](const KScreen::OutputPtr &out) { 0256 if (!out) { 0257 return; 0258 } 0259 0260 QVariantMap pos; 0261 pos[QStringLiteral("x")] = out->pos().x(); 0262 pos[QStringLiteral("y")] = out->pos().y(); 0263 info[QStringLiteral("pos")] = pos; 0264 }; 0265 setOutputConfigInfo(output->isEnabled() ? output : oldOutput); 0266 0267 if (output->isEnabled() && m_control->getOutputRetention(output->hash(), output->name()) != Control::OutputRetention::Individual) { 0268 // try to update global output data 0269 Output::writeGlobal(output, hasDuplicate(output)); 0270 } 0271 0272 outputList.append(info); 0273 } 0274 0275 QFile file(filePath); 0276 if (!file.open(QIODevice::WriteOnly)) { 0277 qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); 0278 return false; 0279 } 0280 file.write(QJsonDocument::fromVariant(outputList).toJson()); 0281 qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); 0282 0283 return true; 0284 } 0285 0286 void Config::log() 0287 { 0288 if (!m_data) { 0289 return; 0290 } 0291 const auto outputs = m_data->outputs(); 0292 for (const auto &o : outputs) { 0293 if (o->isConnected()) { 0294 qCDebug(KSCREEN_KDED) << o; 0295 } 0296 } 0297 }