Changeset View
Changeset View
Standalone View
Standalone View
kded/config.cpp
- This file was moved from kded/serializer.cpp.
1 | /************************************************************************************* | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | * Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org> * | 2 | Copyright 2012 Alejandro Fiestas Olivares <afiestas@kde.org> | ||
3 | * * | 3 | Copyright 2019 Roman Gilg <subdiff@gmail.com> | ||
4 | * This program is free software; you can redistribute it and/or * | 4 | | ||
5 | * modify it under the terms of the GNU General Public License * | 5 | This program is free software; you can redistribute it and/or modify | ||
6 | * as published by the Free Software Foundation; either version 2 * | 6 | it under the terms of the GNU General Public License as published by | ||
7 | * of the License, or (at your option) any later version. * | 7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | * * | 8 | (at your option) any later version. | ||
9 | * This program is distributed in the hope that it will be useful, * | 9 | | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | 10 | This program is distributed in the hope that it will be useful, | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * GNU General Public License for more details. * | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * * | 13 | GNU General Public License for more details. | ||
14 | * You should have received a copy of the GNU General Public License * | 14 | | ||
15 | * along with this program; if not, write to the Free Software * | 15 | You should have received a copy of the GNU General Public License | ||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * | 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | *************************************************************************************/ | 17 | *********************************************************************/ | ||
18 | 18 | #include "config.h" | |||
19 | #include "serializer.h" | | |||
20 | #include "kscreen_daemon_debug.h" | 19 | #include "kscreen_daemon_debug.h" | ||
21 | #include "generator.h" | 20 | #include "generator.h" | ||
21 | #include "device.h" | ||||
22 | 22 | | |||
23 | #include <QStringList> | 23 | #include <QStringList> | ||
24 | #include <QCryptographicHash> | 24 | #include <QCryptographicHash> | ||
25 | #include <QFile> | 25 | #include <QFile> | ||
26 | #include <QStandardPaths> | 26 | #include <QStandardPaths> | ||
27 | #include <QRect> | 27 | #include <QRect> | ||
28 | #include <QStringBuilder> | 28 | #include <QStringBuilder> | ||
29 | #include <QJsonDocument> | 29 | #include <QJsonDocument> | ||
30 | #include <QDir> | 30 | #include <QDir> | ||
31 | #include <QLoggingCategory> | 31 | #include <QLoggingCategory> | ||
32 | 32 | | |||
33 | #include <kscreen/config.h> | 33 | #include <kscreen/config.h> | ||
34 | #include <kscreen/output.h> | 34 | #include <kscreen/output.h> | ||
35 | #include <kscreen/edid.h> | 35 | #include <kscreen/edid.h> | ||
36 | 36 | | |||
37 | QString Serializer::sConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/"); | 37 | QString Config::s_fixedConfigFileName = QStringLiteral("fixed-config"); | ||
38 | QString Serializer::sFixedConfig = QStringLiteral("fixed-config"); | 38 | QString Config::s_dirPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/"); | ||
39 | void Serializer::setConfigPath(const QString &path) | 39 | | ||
40 | Config::Config(KScreen::ConfigPtr config) | ||||
41 | : m_data(config) | ||||
42 | { | ||||
43 | } | ||||
44 | | ||||
45 | void Config::setDirPath(const QString &path) | ||||
40 | { | 46 | { | ||
41 | sConfigPath = path; | 47 | s_dirPath = path; | ||
42 | if (!sConfigPath.endsWith(QLatin1Char('/'))) { | 48 | if (!s_dirPath.endsWith(QLatin1Char('/'))) { | ||
43 | sConfigPath += QLatin1Char('/'); | 49 | s_dirPath += QLatin1Char('/'); | ||
44 | } | 50 | } | ||
45 | } | 51 | } | ||
46 | 52 | | |||
47 | QString Serializer::configFileName(const QString &configId) | 53 | QString Config::filePath() | ||
48 | { | 54 | { | ||
49 | if (!QDir().mkpath(sConfigPath)) { | 55 | if (!QDir().mkpath(s_dirPath)) { | ||
50 | return QString(); | 56 | return QString(); | ||
51 | } | 57 | } | ||
52 | return sConfigPath % configId; | 58 | return s_dirPath % id(); | ||
53 | } | 59 | } | ||
54 | 60 | | |||
55 | QString Serializer::configId(const KScreen::ConfigPtr &config) | 61 | QString Config::id() const | ||
56 | { | 62 | { | ||
57 | if (!config) { | 63 | if (!m_data) { | ||
58 | return QString(); | 64 | return QString(); | ||
59 | } | 65 | } | ||
60 | return config->connectedOutputsHash(); | 66 | return m_data->connectedOutputsHash(); | ||
67 | } | ||||
68 | | ||||
69 | bool Config::fileExists() const | ||||
70 | { | ||||
71 | return (QFile::exists(s_dirPath % id()) || QFile::exists(s_dirPath % s_fixedConfigFileName)); | ||||
61 | } | 72 | } | ||
62 | 73 | | |||
63 | bool Serializer::configExists(const KScreen::ConfigPtr &config) | 74 | std::unique_ptr<Config> Config::readFile() | ||
64 | { | 75 | { | ||
65 | return Serializer::configExists(Serializer::configId(config)); | 76 | if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) { | ||
77 | // We may look for a config that has been set when the lid was closed, Bug: 353029 | ||||
78 | const QString lidOpenedFilePath(filePath() % QStringLiteral("_lidOpened")); | ||||
79 | const QFile srcFile(lidOpenedFilePath); | ||||
80 | | ||||
81 | if (srcFile.exists()) { | ||||
82 | QFile::remove(filePath()); | ||||
83 | if (QFile::copy(lidOpenedFilePath, filePath())) { | ||||
84 | QFile::remove(lidOpenedFilePath); | ||||
85 | qCDebug(KSCREEN_KDED) << "Restored lid opened config to" << id(); | ||||
86 | } | ||||
87 | } | ||||
88 | } | ||||
89 | return readFile(id()); | ||||
66 | } | 90 | } | ||
67 | 91 | | |||
68 | bool Serializer::configExists(const QString &id) | 92 | std::unique_ptr<Config> Config::readOpenLidFile() | ||
69 | { | 93 | { | ||
70 | return (QFile::exists(sConfigPath % id) || QFile::exists(sConfigPath % sFixedConfig)); | 94 | const QString openLidFilePath = filePath() % QStringLiteral("_lidOpened"); | ||
95 | auto config = readFile(openLidFilePath); | ||||
96 | QFile::remove(openLidFilePath); | ||||
97 | return config; | ||||
71 | } | 98 | } | ||
72 | 99 | | |||
73 | KScreen::ConfigPtr Serializer::loadConfig(const KScreen::ConfigPtr ¤tConfig, const QString &id) | 100 | std::unique_ptr<Config> Config::readFile(const QString &fileName) | ||
74 | { | 101 | { | ||
75 | KScreen::ConfigPtr config = currentConfig->clone(); | 102 | if (!m_data) { | ||
103 | return nullptr; | ||||
104 | } | ||||
105 | KScreen::ConfigPtr config = m_data->clone(); | ||||
76 | 106 | | |||
77 | QFile file; | 107 | QFile file; | ||
78 | if (QFile::exists(sConfigPath % sFixedConfig)) { | 108 | if (QFile::exists(s_dirPath % s_fixedConfigFileName)) { | ||
79 | file.setFileName(sConfigPath % sFixedConfig); | 109 | file.setFileName(s_dirPath % s_fixedConfigFileName); | ||
80 | qCDebug(KSCREEN_KDED) << "found a fixed config, will use " << file.fileName(); | 110 | qCDebug(KSCREEN_KDED) << "found a fixed config, will use " << file.fileName(); | ||
81 | } else { | 111 | } else { | ||
82 | file.setFileName(configFileName(id)); | 112 | file.setFileName(s_dirPath % fileName); | ||
83 | } | 113 | } | ||
84 | if (!file.open(QIODevice::ReadOnly)) { | 114 | if (!file.open(QIODevice::ReadOnly)) { | ||
85 | qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName(); | 115 | qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName(); | ||
86 | return KScreen::ConfigPtr(); | 116 | return nullptr; | ||
87 | } | 117 | } | ||
88 | 118 | | |||
89 | KScreen::OutputList outputList = config->outputs(); | 119 | KScreen::OutputList outputList = config->outputs(); | ||
90 | QJsonDocument parser; | 120 | QJsonDocument parser; | ||
91 | QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); | 121 | QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); | ||
92 | Q_FOREACH(KScreen::OutputPtr output, outputList) { | 122 | Q_FOREACH(KScreen::OutputPtr output, outputList) { | ||
93 | if (!output->isConnected() && output->isEnabled()) { | 123 | if (!output->isConnected() && output->isEnabled()) { | ||
94 | output->setEnabled(false); | 124 | output->setEnabled(false); | ||
95 | } | 125 | } | ||
96 | } | 126 | } | ||
97 | 127 | | |||
98 | QSize screenSize; | 128 | QSize screenSize; | ||
99 | Q_FOREACH(const QVariant &info, outputs) { | 129 | Q_FOREACH(const QVariant &info, outputs) { | ||
100 | KScreen::OutputPtr output = Serializer::findOutput(config, info.toMap()); | 130 | KScreen::OutputPtr output = findOutput(config, info.toMap()); | ||
101 | if (!output) { | 131 | if (!output) { | ||
102 | continue; | 132 | continue; | ||
103 | } | 133 | } | ||
104 | 134 | | |||
105 | if (output->isEnabled()) { | 135 | if (output->isEnabled()) { | ||
106 | const QRect geom = output->geometry(); | 136 | const QRect geom = output->geometry(); | ||
107 | if (geom.x() + geom.width() > screenSize.width()) { | 137 | if (geom.x() + geom.width() > screenSize.width()) { | ||
108 | screenSize.setWidth(geom.x() + geom.width()); | 138 | screenSize.setWidth(geom.x() + geom.width()); | ||
109 | } | 139 | } | ||
110 | if (geom.y() + geom.height() > screenSize.height()) { | 140 | if (geom.y() + geom.height() > screenSize.height()) { | ||
111 | screenSize.setHeight(geom.y() + geom.height()); | 141 | screenSize.setHeight(geom.y() + geom.height()); | ||
112 | } | 142 | } | ||
113 | } | 143 | } | ||
114 | 144 | | |||
115 | outputList.remove(output->id()); | 145 | outputList.remove(output->id()); | ||
116 | outputList.insert(output->id(), output); | 146 | outputList.insert(output->id(), output); | ||
117 | } | 147 | } | ||
118 | config->setOutputs(outputList); | 148 | config->setOutputs(outputList); | ||
119 | config->screen()->setCurrentSize(screenSize); | 149 | config->screen()->setCurrentSize(screenSize); | ||
120 | 150 | | |||
151 | if (!canBeApplied(config)) { | ||||
152 | return nullptr; | ||||
153 | } | ||||
154 | auto cfg = std::unique_ptr<Config>(new Config(config)); | ||||
155 | cfg->setValidityFlags(m_validityFlags); | ||||
156 | return cfg; | ||||
157 | } | ||||
121 | 158 | | |||
122 | return config; | 159 | bool Config::canBeApplied() const | ||
160 | { | ||||
161 | return canBeApplied(m_data); | ||||
162 | } | ||||
163 | | ||||
164 | bool Config::canBeApplied(KScreen::ConfigPtr config) const | ||||
165 | { | ||||
166 | #ifdef KDED_UNIT_TEST | ||||
167 | Q_UNUSED(config); | ||||
168 | return true; | ||||
169 | #else | ||||
170 | return KScreen::Config::canBeApplied(config, m_validityFlags); | ||||
171 | #endif | ||||
123 | } | 172 | } | ||
124 | 173 | | |||
125 | bool Serializer::saveConfig(const KScreen::ConfigPtr &config, const QString &configId) | 174 | bool Config::writeFile() | ||
126 | { | 175 | { | ||
127 | if (!config || configId.isEmpty()) { | 176 | return writeFile(filePath()); | ||
177 | } | ||||
178 | | ||||
179 | bool Config::writeOpenLidFile() | ||||
180 | { | ||||
181 | return writeFile(filePath() % QStringLiteral("_lidOpened")); | ||||
182 | } | ||||
183 | | ||||
184 | static QVariantMap metadata(const KScreen::OutputPtr &output) | ||||
185 | { | ||||
186 | QVariantMap metadata; | ||||
187 | metadata[QStringLiteral("name")] = output->name(); | ||||
188 | if (!output->edid() || !output->edid()->isValid()) { | ||||
189 | return metadata; | ||||
190 | } | ||||
191 | | ||||
192 | metadata[QStringLiteral("fullname")] = output->edid()->deviceId(); | ||||
193 | return metadata; | ||||
194 | } | ||||
195 | | ||||
196 | bool Config::writeFile(const QString &filePath) | ||||
197 | { | ||||
198 | if (!m_data) { | ||||
128 | return false; | 199 | return false; | ||
129 | } | 200 | } | ||
130 | const KScreen::OutputList outputs = config->outputs(); | 201 | const KScreen::OutputList outputs = m_data->outputs(); | ||
131 | 202 | | |||
132 | QVariantList outputList; | 203 | QVariantList outputList; | ||
133 | Q_FOREACH(const KScreen::OutputPtr &output, outputs) { | 204 | Q_FOREACH(const KScreen::OutputPtr &output, outputs) { | ||
134 | if (!output->isConnected()) { | 205 | if (!output->isConnected()) { | ||
135 | continue; | 206 | continue; | ||
136 | } | 207 | } | ||
137 | 208 | | |||
138 | QVariantMap info; | 209 | QVariantMap info; | ||
Show All 22 Lines | 222 | if (output->isEnabled()) { | |||
161 | QVariantMap modeSize; | 232 | QVariantMap modeSize; | ||
162 | modeSize[QStringLiteral("width")] = mode->size().width(); | 233 | modeSize[QStringLiteral("width")] = mode->size().width(); | ||
163 | modeSize[QStringLiteral("height")] = mode->size().height(); | 234 | modeSize[QStringLiteral("height")] = mode->size().height(); | ||
164 | modeInfo[QStringLiteral("size")] = modeSize; | 235 | modeInfo[QStringLiteral("size")] = modeSize; | ||
165 | 236 | | |||
166 | info[QStringLiteral("mode")] = modeInfo; | 237 | info[QStringLiteral("mode")] = modeInfo; | ||
167 | } | 238 | } | ||
168 | 239 | | |||
169 | info[QStringLiteral("metadata")] = Serializer::metadata(output); | 240 | info[QStringLiteral("metadata")] = metadata(output); | ||
170 | 241 | | |||
171 | outputList.append(info); | 242 | outputList.append(info); | ||
172 | } | 243 | } | ||
173 | 244 | | |||
174 | QFile file(configFileName(configId)); | 245 | QFile file(filePath); | ||
175 | if (!file.open(QIODevice::WriteOnly)) { | 246 | if (!file.open(QIODevice::WriteOnly)) { | ||
176 | qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); | 247 | qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); | ||
177 | return false; | 248 | return false; | ||
178 | } | 249 | } | ||
179 | | ||||
180 | file.write(QJsonDocument::fromVariant(outputList).toJson()); | 250 | file.write(QJsonDocument::fromVariant(outputList).toJson()); | ||
181 | qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); | 251 | qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); | ||
182 | 252 | | |||
183 | return true; | 253 | return true; | ||
184 | } | 254 | } | ||
185 | 255 | | |||
186 | void Serializer::removeConfig(const QString &id) | 256 | KScreen::OutputPtr Config::findOutput(const KScreen::ConfigPtr &config, const QVariantMap& info) | ||
187 | { | | |||
188 | QFile::remove(configFileName(id)); | | |||
189 | } | | |||
190 | | ||||
191 | bool Serializer::moveConfig(const QString &srcId, const QString &destId) | | |||
192 | { | | |||
193 | const QFile srcFile(configFileName(srcId)); | | |||
194 | if (srcFile.exists()) { | | |||
195 | removeConfig(destId); | | |||
196 | if (QFile::copy(configFileName(srcId), configFileName(destId))) { | | |||
197 | removeConfig(srcId); | | |||
198 | qCDebug(KSCREEN_KDED) << "Restored config" << srcId << "to" << destId; | | |||
199 | return true; | | |||
200 | } | | |||
201 | } | | |||
202 | return false; | | |||
203 | } | | |||
204 | | ||||
205 | KScreen::OutputPtr Serializer::findOutput(const KScreen::ConfigPtr &config, const QVariantMap& info) | | |||
206 | { | 257 | { | ||
207 | const KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, | 258 | const KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, | ||
208 | // to be able to tell apart multiple identical outputs, these need special treatment | 259 | // to be able to tell apart multiple identical outputs, these need special treatment | ||
209 | QStringList duplicateIds; | 260 | QStringList duplicateIds; | ||
210 | QStringList allIds; | 261 | QStringList allIds; | ||
211 | allIds.reserve(outputs.count()); | 262 | allIds.reserve(outputs.count()); | ||
212 | Q_FOREACH (const KScreen::OutputPtr &output, outputs) { | 263 | Q_FOREACH (const KScreen::OutputPtr &output, outputs) { | ||
213 | const auto outputId = output->hash(); | 264 | const auto outputId = output->hash(); | ||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Line(s) | 272 | Q_FOREACH(KScreen::OutputPtr output, outputs) { | |||
288 | return output; | 339 | return output; | ||
289 | } | 340 | } | ||
290 | 341 | | |||
291 | qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current config - this means that our config is corrupted" | 342 | qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current config - this means that our config is corrupted" | ||
292 | "or a different device with the same serial number has been connected (very unlikely)."; | 343 | "or a different device with the same serial number has been connected (very unlikely)."; | ||
293 | return KScreen::OutputPtr(); | 344 | return KScreen::OutputPtr(); | ||
294 | } | 345 | } | ||
295 | 346 | | |||
296 | QVariantMap Serializer::metadata(const KScreen::OutputPtr &output) | 347 | void Config::log() | ||
297 | { | 348 | { | ||
298 | QVariantMap metadata; | 349 | if (!m_data) { | ||
299 | metadata[QStringLiteral("name")] = output->name(); | 350 | return; | ||
300 | if (!output->edid() || !output->edid()->isValid()) { | 351 | } | ||
301 | return metadata; | 352 | const auto outputs = m_data->outputs(); | ||
353 | for (const auto o : outputs) { | ||||
354 | if (o->isConnected()) { | ||||
355 | qCDebug(KSCREEN_KDED) << o; | ||||
356 | } | ||||
302 | } | 357 | } | ||
303 | | ||||
304 | metadata[QStringLiteral("fullname")] = output->edid()->deviceId(); | | |||
305 | return metadata; | | |||
306 | } | 358 | } |