Changeset View
Changeset View
Standalone View
Standalone View
kded/config.cpp
Show All 10 Lines | |||||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | 13 | GNU General Public License for more details. | ||
14 | 14 | | |||
15 | You should have received a copy of the GNU General Public License | 15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | *********************************************************************/ | 17 | *********************************************************************/ | ||
18 | #include "config.h" | 18 | #include "config.h" | ||
19 | #include "output.h" | ||||
19 | #include "kscreen_daemon_debug.h" | 20 | #include "kscreen_daemon_debug.h" | ||
20 | #include "generator.h" | | |||
21 | #include "device.h" | 21 | #include "device.h" | ||
22 | 22 | | |||
23 | #include <QStringList> | | |||
24 | #include <QCryptographicHash> | | |||
25 | #include <QFile> | 23 | #include <QFile> | ||
26 | #include <QStandardPaths> | 24 | #include <QStandardPaths> | ||
27 | #include <QRect> | 25 | #include <QRect> | ||
28 | #include <QStringBuilder> | | |||
29 | #include <QJsonDocument> | 26 | #include <QJsonDocument> | ||
30 | #include <QDir> | 27 | #include <QDir> | ||
31 | #include <QLoggingCategory> | 28 | #include <QLoggingCategory> | ||
32 | 29 | | |||
33 | #include <kscreen/config.h> | 30 | #include <kscreen/config.h> | ||
34 | #include <kscreen/output.h> | 31 | #include <kscreen/output.h> | ||
35 | #include <kscreen/edid.h> | | |||
36 | 32 | | |||
37 | QString Config::s_fixedConfigFileName = QStringLiteral("fixed-config"); | 33 | QString Config::s_fixedConfigFileName = QStringLiteral("fixed-config"); | ||
38 | QString Config::s_dirPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/"); | 34 | QString Config::s_dirPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/"); | ||
39 | QString Config::s_configsDirName = QStringLiteral("" /*"configs/"*/); // TODO: KDE6 - move these files into the subfolder | 35 | QString Config::s_configsDirName = QStringLiteral("" /*"configs/"*/); // TODO: KDE6 - move these files into the subfolder | ||
40 | 36 | | |||
41 | QString Config::configsDirPath() | 37 | QString Config::configsDirPath() | ||
42 | { | 38 | { | ||
43 | return s_dirPath % s_configsDirName; | 39 | return s_dirPath % s_configsDirName; | ||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Line(s) | 103 | { | |||
117 | } else { | 113 | } else { | ||
118 | file.setFileName(configsDirPath() % fileName); | 114 | file.setFileName(configsDirPath() % fileName); | ||
119 | } | 115 | } | ||
120 | if (!file.open(QIODevice::ReadOnly)) { | 116 | if (!file.open(QIODevice::ReadOnly)) { | ||
121 | qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName(); | 117 | qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName(); | ||
122 | return nullptr; | 118 | return nullptr; | ||
123 | } | 119 | } | ||
124 | 120 | | |||
125 | KScreen::OutputList outputList = config->outputs(); | | |||
126 | QJsonDocument parser; | 121 | QJsonDocument parser; | ||
127 | QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); | 122 | QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); | ||
128 | Q_FOREACH(KScreen::OutputPtr output, outputList) { | 123 | Output::readInOutputs(config->outputs(), outputs); | ||
129 | if (!output->isConnected() && output->isEnabled()) { | | |||
130 | output->setEnabled(false); | | |||
131 | } | | |||
132 | } | | |||
133 | 124 | | |||
134 | QSize screenSize; | 125 | QSize screenSize; | ||
135 | Q_FOREACH(const QVariant &info, outputs) { | 126 | for (const auto &output : config->outputs()) { | ||
136 | KScreen::OutputPtr output = findOutput(config, info.toMap()); | 127 | if (!output->isConnected() || !output->isEnabled()) { | ||
137 | if (!output) { | | |||
138 | continue; | 128 | continue; | ||
139 | } | 129 | } | ||
140 | 130 | | |||
141 | if (output->isEnabled()) { | | |||
142 | const QRect geom = output->geometry(); | 131 | const QRect geom = output->geometry(); | ||
143 | if (geom.x() + geom.width() > screenSize.width()) { | 132 | if (geom.x() + geom.width() > screenSize.width()) { | ||
144 | screenSize.setWidth(geom.x() + geom.width()); | 133 | screenSize.setWidth(geom.x() + geom.width()); | ||
145 | } | 134 | } | ||
146 | if (geom.y() + geom.height() > screenSize.height()) { | 135 | if (geom.y() + geom.height() > screenSize.height()) { | ||
147 | screenSize.setHeight(geom.y() + geom.height()); | 136 | screenSize.setHeight(geom.y() + geom.height()); | ||
148 | } | 137 | } | ||
149 | } | 138 | } | ||
150 | | ||||
151 | outputList.remove(output->id()); | | |||
152 | outputList.insert(output->id(), output); | | |||
153 | } | | |||
154 | config->setOutputs(outputList); | | |||
155 | config->screen()->setCurrentSize(screenSize); | 139 | config->screen()->setCurrentSize(screenSize); | ||
156 | 140 | | |||
157 | if (!canBeApplied(config)) { | 141 | if (!canBeApplied(config)) { | ||
158 | return nullptr; | 142 | return nullptr; | ||
159 | } | 143 | } | ||
160 | auto cfg = std::unique_ptr<Config>(new Config(config)); | 144 | auto cfg = std::unique_ptr<Config>(new Config(config)); | ||
161 | cfg->setValidityFlags(m_validityFlags); | 145 | cfg->setValidityFlags(m_validityFlags); | ||
162 | return cfg; | 146 | return cfg; | ||
Show All 19 Lines | 165 | { | |||
182 | return writeFile(filePath()); | 166 | return writeFile(filePath()); | ||
183 | } | 167 | } | ||
184 | 168 | | |||
185 | bool Config::writeOpenLidFile() | 169 | bool Config::writeOpenLidFile() | ||
186 | { | 170 | { | ||
187 | return writeFile(filePath() % QStringLiteral("_lidOpened")); | 171 | return writeFile(filePath() % QStringLiteral("_lidOpened")); | ||
188 | } | 172 | } | ||
189 | 173 | | |||
190 | static QVariantMap metadata(const KScreen::OutputPtr &output) | | |||
191 | { | | |||
192 | QVariantMap metadata; | | |||
193 | metadata[QStringLiteral("name")] = output->name(); | | |||
194 | if (!output->edid() || !output->edid()->isValid()) { | | |||
195 | return metadata; | | |||
196 | } | | |||
197 | | ||||
198 | metadata[QStringLiteral("fullname")] = output->edid()->deviceId(); | | |||
199 | return metadata; | | |||
200 | } | | |||
201 | | ||||
202 | bool Config::writeFile(const QString &filePath) | 174 | bool Config::writeFile(const QString &filePath) | ||
203 | { | 175 | { | ||
204 | if (!m_data) { | 176 | if (!m_data) { | ||
205 | return false; | 177 | return false; | ||
206 | } | 178 | } | ||
207 | const KScreen::OutputList outputs = m_data->outputs(); | 179 | const KScreen::OutputList outputs = m_data->outputs(); | ||
208 | 180 | | |||
209 | QVariantList outputList; | 181 | QVariantList outputList; | ||
210 | Q_FOREACH(const KScreen::OutputPtr &output, outputs) { | 182 | Q_FOREACH(const KScreen::OutputPtr &output, outputs) { | ||
183 | QVariantMap info; | ||||
184 | | ||||
211 | if (!output->isConnected()) { | 185 | if (!output->isConnected()) { | ||
212 | continue; | 186 | continue; | ||
213 | } | 187 | } | ||
188 | if (!Output::writeGlobalPart(output, info)) { | ||||
189 | continue; | ||||
190 | } | ||||
214 | 191 | | |||
215 | QVariantMap info; | | |||
216 | | ||||
217 | info[QStringLiteral("id")] = output->hash(); | | |||
218 | info[QStringLiteral("primary")] = output->isPrimary(); | 192 | info[QStringLiteral("primary")] = output->isPrimary(); | ||
219 | info[QStringLiteral("enabled")] = output->isEnabled(); | 193 | info[QStringLiteral("enabled")] = output->isEnabled(); | ||
220 | info[QStringLiteral("rotation")] = output->rotation(); | | |||
221 | info[QStringLiteral("scale")] = output->scale(); | | |||
222 | 194 | | |||
223 | QVariantMap pos; | 195 | QVariantMap pos; | ||
224 | pos[QStringLiteral("x")] = output->pos().x(); | 196 | pos[QStringLiteral("x")] = output->pos().x(); | ||
225 | pos[QStringLiteral("y")] = output->pos().y(); | 197 | pos[QStringLiteral("y")] = output->pos().y(); | ||
226 | info[QStringLiteral("pos")] = pos; | 198 | info[QStringLiteral("pos")] = pos; | ||
227 | 199 | | |||
228 | if (output->isEnabled()) { | 200 | // try to update global output data | ||
229 | const KScreen::ModePtr mode = output->currentMode(); | 201 | Output::writeGlobal(output); | ||
230 | if (!mode) { | | |||
231 | qWarning() << "CurrentMode is null" << output->name(); | | |||
232 | return false; | | |||
233 | } | | |||
234 | | ||||
235 | QVariantMap modeInfo; | | |||
236 | modeInfo[QStringLiteral("refresh")] = mode->refreshRate(); | | |||
237 | | ||||
238 | QVariantMap modeSize; | | |||
239 | modeSize[QStringLiteral("width")] = mode->size().width(); | | |||
240 | modeSize[QStringLiteral("height")] = mode->size().height(); | | |||
241 | modeInfo[QStringLiteral("size")] = modeSize; | | |||
242 | | ||||
243 | info[QStringLiteral("mode")] = modeInfo; | | |||
244 | } | | |||
245 | | ||||
246 | info[QStringLiteral("metadata")] = metadata(output); | | |||
247 | 202 | | |||
248 | outputList.append(info); | 203 | outputList.append(info); | ||
249 | } | 204 | } | ||
250 | 205 | | |||
251 | QFile file(filePath); | 206 | QFile file(filePath); | ||
252 | if (!file.open(QIODevice::WriteOnly)) { | 207 | if (!file.open(QIODevice::WriteOnly)) { | ||
253 | qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); | 208 | qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); | ||
254 | return false; | 209 | return false; | ||
255 | } | 210 | } | ||
256 | file.write(QJsonDocument::fromVariant(outputList).toJson()); | 211 | file.write(QJsonDocument::fromVariant(outputList).toJson()); | ||
257 | qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); | 212 | qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); | ||
258 | 213 | | |||
259 | return true; | 214 | return true; | ||
260 | } | 215 | } | ||
261 | 216 | | |||
262 | KScreen::OutputPtr Config::findOutput(const KScreen::ConfigPtr &config, const QVariantMap& info) | | |||
263 | { | | |||
264 | const KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, | | |||
265 | // to be able to tell apart multiple identical outputs, these need special treatment | | |||
266 | QStringList duplicateIds; | | |||
267 | QStringList allIds; | | |||
268 | allIds.reserve(outputs.count()); | | |||
269 | Q_FOREACH (const KScreen::OutputPtr &output, outputs) { | | |||
270 | const auto outputId = output->hash(); | | |||
271 | if (allIds.contains(outputId) && !duplicateIds.contains(outputId)) { | | |||
272 | duplicateIds << outputId; | | |||
273 | } | | |||
274 | allIds << outputId; | | |||
275 | } | | |||
276 | allIds.clear(); | | |||
277 | | ||||
278 | Q_FOREACH(KScreen::OutputPtr output, outputs) { | | |||
279 | if (!output->isConnected()) { | | |||
280 | continue; | | |||
281 | } | | |||
282 | const auto outputId = output->hash(); | | |||
283 | if (outputId != info[QStringLiteral("id")].toString()) { | | |||
284 | continue; | | |||
285 | } | | |||
286 | | ||||
287 | // We may have identical outputs connected, these will have the same id in the config | | |||
288 | // in order to find the right one, also check the output's name (usually the connector) | | |||
289 | if (!output->name().isEmpty() && duplicateIds.contains(outputId)) { | | |||
290 | const auto metadata = info[QStringLiteral("metadata")].toMap(); | | |||
291 | const auto outputName = metadata[QStringLiteral("name")].toString(); | | |||
292 | if (output->name() != outputName) { | | |||
293 | continue; | | |||
294 | } | | |||
295 | } | | |||
296 | | ||||
297 | const QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); | | |||
298 | QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); | | |||
299 | output->setPos(point); | | |||
300 | output->setPrimary(info[QStringLiteral("primary")].toBool()); | | |||
301 | output->setEnabled(info[QStringLiteral("enabled")].toBool()); | | |||
302 | output->setRotation(static_cast<KScreen::Output::Rotation>(info[QStringLiteral("rotation")].toInt())); | | |||
303 | output->setScale(info.value(QStringLiteral("scale"), 1).toInt()); | | |||
304 | | ||||
305 | const QVariantMap modeInfo = info[QStringLiteral("mode")].toMap(); | | |||
306 | const QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap(); | | |||
307 | const QSize size = QSize(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt()); | | |||
308 | | ||||
309 | qCDebug(KSCREEN_KDED) << "Finding a mode for" << size << "@" << modeInfo[QStringLiteral("refresh")].toFloat(); | | |||
310 | | ||||
311 | KScreen::ModeList modes = output->modes(); | | |||
312 | KScreen::ModePtr matchingMode; | | |||
313 | Q_FOREACH(const KScreen::ModePtr &mode, modes) { | | |||
314 | if (mode->size() != size) { | | |||
315 | continue; | | |||
316 | } | | |||
317 | if (!qFuzzyCompare(mode->refreshRate(), modeInfo[QStringLiteral("refresh")].toFloat())) { | | |||
318 | continue; | | |||
319 | } | | |||
320 | | ||||
321 | qCDebug(KSCREEN_KDED) << "\tFound: " << mode->id() << " " << mode->size() << "@" << mode->refreshRate(); | | |||
322 | matchingMode = mode; | | |||
323 | break; | | |||
324 | } | | |||
325 | | ||||
326 | if (!matchingMode) { | | |||
327 | qCWarning(KSCREEN_KDED) << "\tFailed to find a matching mode - this means that our config is corrupted" | | |||
328 | "or a different device with the same serial number has been connected (very unlikely)." | | |||
329 | "Falling back to preferred modes."; | | |||
330 | matchingMode = output->preferredMode(); | | |||
331 | | ||||
332 | if (!matchingMode) { | | |||
333 | qCWarning(KSCREEN_KDED) << "\tFailed to get a preferred mode, falling back to biggest mode."; | | |||
334 | matchingMode = Generator::biggestMode(modes); | | |||
335 | | ||||
336 | if (!matchingMode) { | | |||
337 | qCWarning(KSCREEN_KDED) << "\tFailed to get biggest mode. Which means there are no modes. Turning off the screen."; | | |||
338 | output->setEnabled(false); | | |||
339 | return output; | | |||
340 | } | | |||
341 | } | | |||
342 | } | | |||
343 | | ||||
344 | output->setCurrentModeId(matchingMode->id()); | | |||
345 | return output; | | |||
346 | } | | |||
347 | | ||||
348 | qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current config - this means that our config is corrupted" | | |||
349 | "or a different device with the same serial number has been connected (very unlikely)."; | | |||
350 | return KScreen::OutputPtr(); | | |||
351 | } | | |||
352 | | ||||
353 | void Config::log() | 217 | void Config::log() | ||
354 | { | 218 | { | ||
355 | if (!m_data) { | 219 | if (!m_data) { | ||
356 | return; | 220 | return; | ||
357 | } | 221 | } | ||
358 | const auto outputs = m_data->outputs(); | 222 | const auto outputs = m_data->outputs(); | ||
359 | for (const auto o : outputs) { | 223 | for (const auto o : outputs) { | ||
360 | if (o->isConnected()) { | 224 | if (o->isConnected()) { | ||
361 | qCDebug(KSCREEN_KDED) << o; | 225 | qCDebug(KSCREEN_KDED) << o; | ||
362 | } | 226 | } | ||
363 | } | 227 | } | ||
364 | } | 228 | } |