diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt
--- a/kded/CMakeLists.txt
+++ b/kded/CMakeLists.txt
@@ -5,6 +5,7 @@
set(kscreen_daemon_SRCS
daemon.cpp
config.cpp
+ output.cpp
generator.cpp
device.cpp
osd.cpp
diff --git a/kded/config.h b/kded/config.h
--- a/kded/config.h
+++ b/kded/config.h
@@ -31,6 +31,9 @@
~Config() = default;
static void setDirPath(const QString &path);
+ static QString dirPath() {
+ return s_dirPath;
+ }
QString id() const;
@@ -61,11 +64,7 @@
bool canBeApplied(KScreen::ConfigPtr config) const;
- // this could probably be done on m_data
- KScreen::OutputPtr findOutput(const KScreen::ConfigPtr &config, const QVariantMap &info);
-
KScreen::ConfigPtr m_data;
-
KScreen::Config::ValidityFlags m_validityFlags;
static QString s_dirPath;
diff --git a/kded/config.cpp b/kded/config.cpp
--- a/kded/config.cpp
+++ b/kded/config.cpp
@@ -16,23 +16,19 @@
along with this program. If not, see .
*********************************************************************/
#include "config.h"
+#include "output.h"
#include "kscreen_daemon_debug.h"
-#include "generator.h"
#include "device.h"
-#include
-#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
-#include
QString Config::s_fixedConfigFileName = QStringLiteral("fixed-config");
QString Config::s_dirPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/");
@@ -122,36 +118,24 @@
return nullptr;
}
- KScreen::OutputList outputList = config->outputs();
QJsonDocument parser;
QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList();
- Q_FOREACH(KScreen::OutputPtr output, outputList) {
- if (!output->isConnected() && output->isEnabled()) {
- output->setEnabled(false);
- }
- }
+ Output::readInOutputs(config->outputs(), outputs);
QSize screenSize;
- Q_FOREACH(const QVariant &info, outputs) {
- KScreen::OutputPtr output = findOutput(config, info.toMap());
- if (!output) {
+ for (const auto &output : config->outputs()) {
+ if (!output->isConnected() || !output->isEnabled()) {
continue;
}
- if (output->isEnabled()) {
- const QRect geom = output->geometry();
- if (geom.x() + geom.width() > screenSize.width()) {
- screenSize.setWidth(geom.x() + geom.width());
- }
- if (geom.y() + geom.height() > screenSize.height()) {
- screenSize.setHeight(geom.y() + geom.height());
- }
+ const QRect geom = output->geometry();
+ if (geom.x() + geom.width() > screenSize.width()) {
+ screenSize.setWidth(geom.x() + geom.width());
+ }
+ if (geom.y() + geom.height() > screenSize.height()) {
+ screenSize.setHeight(geom.y() + geom.height());
}
-
- outputList.remove(output->id());
- outputList.insert(output->id(), output);
}
- config->setOutputs(outputList);
config->screen()->setCurrentSize(screenSize);
if (!canBeApplied(config)) {
@@ -187,18 +171,6 @@
return writeFile(filePath() % QStringLiteral("_lidOpened"));
}
-static QVariantMap metadata(const KScreen::OutputPtr &output)
-{
- QVariantMap metadata;
- metadata[QStringLiteral("name")] = output->name();
- if (!output->edid() || !output->edid()->isValid()) {
- return metadata;
- }
-
- metadata[QStringLiteral("fullname")] = output->edid()->deviceId();
- return metadata;
-}
-
bool Config::writeFile(const QString &filePath)
{
if (!m_data) {
@@ -208,42 +180,25 @@
QVariantList outputList;
Q_FOREACH(const KScreen::OutputPtr &output, outputs) {
+ QVariantMap info;
+
if (!output->isConnected()) {
continue;
}
+ if (!Output::writeGlobalPart(output, info)) {
+ continue;
+ }
- QVariantMap info;
-
- info[QStringLiteral("id")] = output->hash();
info[QStringLiteral("primary")] = output->isPrimary();
info[QStringLiteral("enabled")] = output->isEnabled();
- info[QStringLiteral("rotation")] = output->rotation();
- info[QStringLiteral("scale")] = output->scale();
QVariantMap pos;
pos[QStringLiteral("x")] = output->pos().x();
pos[QStringLiteral("y")] = output->pos().y();
info[QStringLiteral("pos")] = pos;
- if (output->isEnabled()) {
- const KScreen::ModePtr mode = output->currentMode();
- if (!mode) {
- qWarning() << "CurrentMode is null" << output->name();
- return false;
- }
-
- QVariantMap modeInfo;
- modeInfo[QStringLiteral("refresh")] = mode->refreshRate();
-
- QVariantMap modeSize;
- modeSize[QStringLiteral("width")] = mode->size().width();
- modeSize[QStringLiteral("height")] = mode->size().height();
- modeInfo[QStringLiteral("size")] = modeSize;
-
- info[QStringLiteral("mode")] = modeInfo;
- }
-
- info[QStringLiteral("metadata")] = metadata(output);
+ // try to update global output data
+ Output::writeGlobal(output);
outputList.append(info);
}
@@ -259,97 +214,6 @@
return true;
}
-KScreen::OutputPtr Config::findOutput(const KScreen::ConfigPtr &config, const QVariantMap& info)
-{
- const KScreen::OutputList outputs = config->outputs(); // As individual 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 duplicateIds;
- QStringList allIds;
- allIds.reserve(outputs.count());
- Q_FOREACH (const KScreen::OutputPtr &output, outputs) {
- const auto outputId = output->hash();
- if (allIds.contains(outputId) && !duplicateIds.contains(outputId)) {
- duplicateIds << outputId;
- }
- allIds << outputId;
- }
- allIds.clear();
-
- Q_FOREACH(KScreen::OutputPtr output, outputs) {
- if (!output->isConnected()) {
- continue;
- }
- const auto outputId = output->hash();
- if (outputId != info[QStringLiteral("id")].toString()) {
- continue;
- }
-
- // 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)
- if (!output->name().isEmpty() && duplicateIds.contains(outputId)) {
- const auto metadata = info[QStringLiteral("metadata")].toMap();
- const auto outputName = metadata[QStringLiteral("name")].toString();
- if (output->name() != outputName) {
- continue;
- }
- }
-
- const QVariantMap posInfo = info[QStringLiteral("pos")].toMap();
- QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt());
- output->setPos(point);
- output->setPrimary(info[QStringLiteral("primary")].toBool());
- output->setEnabled(info[QStringLiteral("enabled")].toBool());
- output->setRotation(static_cast(info[QStringLiteral("rotation")].toInt()));
- output->setScale(info.value(QStringLiteral("scale"), 1).toInt());
-
- const QVariantMap modeInfo = info[QStringLiteral("mode")].toMap();
- const QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap();
- const QSize size = QSize(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt());
-
- qCDebug(KSCREEN_KDED) << "Finding a mode for" << size << "@" << modeInfo[QStringLiteral("refresh")].toFloat();
-
- KScreen::ModeList modes = output->modes();
- KScreen::ModePtr matchingMode;
- Q_FOREACH(const KScreen::ModePtr &mode, modes) {
- if (mode->size() != size) {
- continue;
- }
- if (!qFuzzyCompare(mode->refreshRate(), modeInfo[QStringLiteral("refresh")].toFloat())) {
- continue;
- }
-
- qCDebug(KSCREEN_KDED) << "\tFound: " << mode->id() << " " << mode->size() << "@" << mode->refreshRate();
- matchingMode = mode;
- break;
- }
-
- if (!matchingMode) {
- qCWarning(KSCREEN_KDED) << "\tFailed to find a matching mode - this means that our config is corrupted"
- "or a different device with the same serial number has been connected (very unlikely)."
- "Falling back to preferred modes.";
- matchingMode = output->preferredMode();
-
- if (!matchingMode) {
- qCWarning(KSCREEN_KDED) << "\tFailed to get a preferred mode, falling back to biggest mode.";
- matchingMode = Generator::biggestMode(modes);
-
- if (!matchingMode) {
- qCWarning(KSCREEN_KDED) << "\tFailed to get biggest mode. Which means there are no modes. Turning off the screen.";
- output->setEnabled(false);
- return output;
- }
- }
- }
-
- output->setCurrentModeId(matchingMode->id());
- return output;
- }
-
- qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current config - this means that our config is corrupted"
- "or a different device with the same serial number has been connected (very unlikely).";
- return KScreen::OutputPtr();
-}
-
void Config::log()
{
if (!m_data) {
diff --git a/kded/output.h b/kded/output.h
new file mode 100644
--- /dev/null
+++ b/kded/output.h
@@ -0,0 +1,45 @@
+/********************************************************************
+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 KDED_OUTPUT_H
+#define KDED_OUTPUT_H
+
+#include
+
+#include
+
+class Output
+{
+public:
+ static void readInOutputs(KScreen::OutputList outputs, const QVariantList &outputsInfo);
+
+ static void writeGlobal(const KScreen::OutputPtr &output);
+ static bool writeGlobalPart(const KScreen::OutputPtr &output, QVariantMap &info);
+
+ static QString dirPath();
+
+private:
+ static QString globalFileName(const QString &hash);
+ static QVariantMap getGlobalData(KScreen::OutputPtr output);
+
+ static void readIn(KScreen::OutputPtr output, const QVariantMap &info);
+ static bool readInGlobal(KScreen::OutputPtr output);
+ static void readInGlobalPartFromInfo(KScreen::OutputPtr output, const QVariantMap &info);
+
+ static QString s_dirName;
+};
+
+#endif
diff --git a/kded/output.cpp b/kded/output.cpp
new file mode 100644
--- /dev/null
+++ b/kded/output.cpp
@@ -0,0 +1,245 @@
+/********************************************************************
+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 "output.h"
+#include "config.h"
+
+#include "kscreen_daemon_debug.h"
+#include "generator.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+QString Output::s_dirName = QStringLiteral("outputs/");
+
+QString Output::dirPath()
+{
+ return Config::dirPath() % s_dirName;
+}
+
+QString Output::globalFileName(const QString &hash)
+{
+ const auto dir = dirPath();
+ if (!QDir().mkpath(dir)) {
+ return QString();
+ }
+ return dir % hash;
+}
+
+void Output::readInGlobalPartFromInfo(KScreen::OutputPtr output, const QVariantMap &info)
+{
+ output->setRotation(static_cast(info.value(QStringLiteral("rotation"), 1).toInt()));
+ output->setScale(info.value(QStringLiteral("scale"), 1).toInt());
+
+ const QVariantMap modeInfo = info[QStringLiteral("mode")].toMap();
+ const QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap();
+ const QSize size = QSize(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt());
+
+ qCDebug(KSCREEN_KDED) << "Finding a mode for" << size << "@" << modeInfo[QStringLiteral("refresh")].toFloat();
+
+ KScreen::ModeList modes = output->modes();
+ KScreen::ModePtr matchingMode;
+ for(const KScreen::ModePtr &mode : modes) {
+ if (mode->size() != size) {
+ continue;
+ }
+ if (!qFuzzyCompare(mode->refreshRate(), modeInfo[QStringLiteral("refresh")].toFloat())) {
+ continue;
+ }
+
+ qCDebug(KSCREEN_KDED) << "\tFound: " << mode->id() << " " << mode->size() << "@" << mode->refreshRate();
+ matchingMode = mode;
+ break;
+ }
+
+ if (!matchingMode) {
+ qCWarning(KSCREEN_KDED) << "\tFailed to find a matching mode - this means that our config is corrupted"
+ "or a different device with the same serial number has been connected (very unlikely)."
+ "Falling back to preferred modes.";
+ matchingMode = output->preferredMode();
+ }
+ if (!matchingMode) {
+ qCWarning(KSCREEN_KDED) << "\tFailed to get a preferred mode, falling back to biggest mode.";
+ matchingMode = Generator::biggestMode(modes);
+ }
+ if (!matchingMode) {
+ qCWarning(KSCREEN_KDED) << "\tFailed to get biggest mode. Which means there are no modes. Turning off the screen.";
+ output->setEnabled(false);
+ return;
+ }
+
+ output->setCurrentModeId(matchingMode->id());
+}
+
+QVariantMap Output::getGlobalData(KScreen::OutputPtr output)
+{
+ QFile file(globalFileName(output->hashMd5()));
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCDebug(KSCREEN_KDED) << "Failed to open file" << file.fileName();
+ return QVariantMap();
+ }
+ QJsonDocument parser;
+ return parser.fromJson(file.readAll()).toVariant().toMap();
+}
+
+bool Output::readInGlobal(KScreen::OutputPtr output)
+{
+ const QVariantMap info = getGlobalData(output);
+ if (info.empty()) {
+ // if info is empty, the global file does not exists, or is in an unreadable state
+ return false;
+ }
+ readInGlobalPartFromInfo(output, info);
+ return true;
+}
+
+void Output::readIn(KScreen::OutputPtr output, const QVariantMap &info)
+{
+ const QVariantMap posInfo = info[QStringLiteral("pos")].toMap();
+ QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt());
+ output->setPos(point);
+ output->setPrimary(info[QStringLiteral("primary")].toBool());
+ output->setEnabled(info[QStringLiteral("enabled")].toBool());
+
+ if (!readInGlobal(output)) {
+ // read in global part from config info instead
+ readInGlobalPartFromInfo(output, info);
+ }
+}
+
+void Output::readInOutputs(KScreen::OutputList outputs, const QVariantList &outputsInfo)
+{
+ // 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 duplicateIds;
+ {
+ QStringList allIds;
+ allIds.reserve(outputs.count());
+ for (const KScreen::OutputPtr &output : outputs) {
+ const auto outputId = output->hash();
+ if (allIds.contains(outputId) && !duplicateIds.contains(outputId)) {
+ duplicateIds << outputId;
+ }
+ allIds << outputId;
+ }
+ allIds.clear();
+ }
+
+ for (KScreen::OutputPtr output : outputs) {
+ if (!output->isConnected()) {
+ output->setEnabled(false);
+ continue;
+ }
+ const auto outputId = output->hash();
+ bool infoFound = false;
+ for (const auto &variantInfo : outputsInfo) {
+ const QVariantMap info = variantInfo.toMap();
+ if (outputId == info[QStringLiteral("id")].toString()) {
+
+ // 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)
+ if (!output->name().isEmpty() && duplicateIds.contains(outputId)) {
+ const auto metadata = info[QStringLiteral("metadata")].toMap();
+ const auto outputName = metadata[QStringLiteral("name")].toString();
+ if (output->name() != outputName) {
+ infoFound = true;
+ readIn(output, info);
+ }
+ // was a duplicate id, but info not for this output
+ continue;
+ }
+ infoFound = true;
+ readIn(output, info);
+ }
+ }
+ if (!infoFound) {
+ // no info in info for this output, try reading in global output info atleast or set some default values
+
+ qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current info data - this means that our info is corrupted"
+ "or a different device with the same serial number has been connected (very unlikely).";
+ if (!readInGlobal(output)) {
+ // set some default values instead
+ readInGlobalPartFromInfo(output, QVariantMap());
+ }
+ }
+ }
+}
+
+static QVariantMap metadata(const KScreen::OutputPtr &output)
+{
+ QVariantMap metadata;
+ metadata[QStringLiteral("name")] = output->name();
+ if (!output->edid() || !output->edid()->isValid()) {
+ return metadata;
+ }
+
+ metadata[QStringLiteral("fullname")] = output->edid()->deviceId();
+ return metadata;
+}
+
+bool Output::writeGlobalPart(const KScreen::OutputPtr &output, QVariantMap &info)
+{
+ if (!output->isEnabled()) {
+ return false;
+ }
+ const KScreen::ModePtr mode = output->currentMode();
+ if (!mode) {
+ qWarning() << "CurrentMode is null" << output->name();
+ return false;
+ }
+
+ info[QStringLiteral("id")] = output->hash();
+ info[QStringLiteral("rotation")] = output->rotation();
+ info[QStringLiteral("scale")] = output->scale();
+ info[QStringLiteral("metadata")] = metadata(output);
+
+ QVariantMap modeInfo;
+ modeInfo[QStringLiteral("refresh")] = mode->refreshRate();
+
+ QVariantMap modeSize;
+ modeSize[QStringLiteral("width")] = mode->size().width();
+ modeSize[QStringLiteral("height")] = mode->size().height();
+ modeInfo[QStringLiteral("size")] = modeSize;
+
+ info[QStringLiteral("mode")] = modeInfo;
+
+ return true;
+}
+
+void Output::writeGlobal(const KScreen::OutputPtr &output)
+{
+ // get old values and subsequently override
+ QVariantMap info = getGlobalData(output);
+ if (!writeGlobalPart(output, info)) {
+ return;
+ }
+
+ QFile file(globalFileName(output->hashMd5()));
+ if (!file.open(QIODevice::WriteOnly)) {
+ qCWarning(KSCREEN_KDED) << "Failed to open global output file for writing! " << file.errorString();
+ return;
+ }
+
+ file.write(QJsonDocument::fromVariant(info).toJson());
+ return;
+}
diff --git a/tests/kded/CMakeLists.txt b/tests/kded/CMakeLists.txt
--- a/tests/kded/CMakeLists.txt
+++ b/tests/kded/CMakeLists.txt
@@ -8,6 +8,7 @@
${CMAKE_SOURCE_DIR}/kded/generator.cpp
${CMAKE_SOURCE_DIR}/kded/device.cpp
${CMAKE_SOURCE_DIR}/kded/config.cpp
+ ${CMAKE_SOURCE_DIR}/kded/output.cpp
#${CMAKE_SOURCE_DIR}/kded/daemon.cpp
)
ecm_qt_declare_logging_category(test_SRCS HEADER kscreen_daemon_debug.h IDENTIFIER KSCREEN_KDED CATEGORY_NAME kscreen.kded)