diff --git a/src/config.cpp b/src/config.cpp index ac975f1..1a43008 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,423 +1,425 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright (C) 2014 by Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "config.h" #include "output.h" #include "backendmanager_p.h" #include "abstractbackend.h" #include "kscreen_debug.h" #include #include #include #include using namespace KScreen; class Q_DECL_HIDDEN Config::Private : public QObject { Q_OBJECT public: Private(Config *parent) : QObject(parent) , valid(true) , supportedFeatures(Config::Feature::None) , tabletModeAvailable(false) , tabletModeEngaged(false) , q(parent) { } KScreen::OutputPtr findPrimaryOutput() const { auto iter = std::find_if(outputs.constBegin(), outputs.constEnd(), [](const KScreen::OutputPtr &output) -> bool { return output->isPrimary(); }); return iter == outputs.constEnd() ? KScreen::OutputPtr() : iter.value(); } void onPrimaryOutputChanged() { const KScreen::OutputPtr output(qobject_cast(sender()), [](void *) {}); Q_ASSERT(output); if (output->isPrimary()) { q->setPrimaryOutput(output); } else { q->setPrimaryOutput(findPrimaryOutput()); } } OutputList::Iterator removeOutput(OutputList::Iterator iter) { if (iter == outputs.end()) { return iter; } OutputPtr output = iter.value(); if (!output) { return outputs.erase(iter); } const int outputId = iter.key(); iter = outputs.erase(iter); if (primaryOutput == output) { q->setPrimaryOutput(OutputPtr()); } output->disconnect(q); Q_EMIT q->outputRemoved(outputId); return iter; } bool valid; ScreenPtr screen; OutputPtr primaryOutput; OutputList outputs; Features supportedFeatures; bool tabletModeAvailable; bool tabletModeEngaged; private: Config *q; }; bool Config::canBeApplied(const ConfigPtr &config) { return canBeApplied(config, ValidityFlag::None); } bool Config::canBeApplied(const ConfigPtr &config, ValidityFlags flags) { if (!config) { qCDebug(KSCREEN) << "canBeApplied: Config not available, returning false"; return false; } ConfigPtr currentConfig = BackendManager::instance()->config(); if (!currentConfig) { qCDebug(KSCREEN) << "canBeApplied: Current config not available, returning false"; return false; } QRect rect; OutputPtr currentOutput; const OutputList outputs = config->outputs(); int enabledOutputsCount = 0; Q_FOREACH(const OutputPtr &output, outputs) { if (!output->isEnabled()) { continue; } ++enabledOutputsCount; currentOutput = currentConfig->output(output->id()); //If there is no such output if (!currentOutput) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "does not exists"; return false; } //If the output is not connected if (!currentOutput->isConnected()) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "is not connected"; return false; } //if there is no currentMode if (output->currentModeId().isEmpty()) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "has no currentModeId"; return false; } //If the mode is not found in the current output if (!currentOutput->mode(output->currentModeId())) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "has no mode:" << output->currentModeId(); return false; } const ModePtr currentMode = output->currentMode(); const QSize outputSize = currentMode->size(); if (output->pos().x() < rect.x()) { rect.setX(output->pos().x()); } if (output->pos().y() < rect.y()) { rect.setY(output->pos().y()); } QPoint bottomRight; if (output->isHorizontal()) { bottomRight = QPoint(output->pos().x() + outputSize.width(), output->pos().y() + outputSize.height()); } else { bottomRight = QPoint(output->pos().x() + outputSize.height(), output->pos().y() + outputSize.width()); } if (bottomRight.x() > rect.width()) { rect.setWidth(bottomRight.x()); } if (bottomRight.y() > rect.height()) { rect.setHeight(bottomRight.y()); } } if (flags & ValidityFlag::RequireAtLeastOneEnabledScreen && enabledOutputsCount == 0) { qCDebug(KSCREEN) << "canBeAppled: There are no enabled screens, at least one required"; return false; } const int maxEnabledOutputsCount = config->screen()->maxActiveOutputsCount(); if (enabledOutputsCount > maxEnabledOutputsCount) { qCDebug(KSCREEN) << "canBeApplied: Too many active screens. Requested: " << enabledOutputsCount << ", Max: " << maxEnabledOutputsCount; return false; } if (rect.width() > config->screen()->maxSize().width()) { qCDebug(KSCREEN) << "canBeApplied: The configuration is too wide:" << rect.width(); return false; } if (rect.height() > config->screen()->maxSize().height()) { qCDebug(KSCREEN) << "canBeApplied: The configuration is too high:" << rect.height(); return false; } return true; } Config::Config() : QObject(nullptr) , d(new Private(this)) { } Config::~Config() { delete d; } ConfigPtr Config::clone() const { ConfigPtr newConfig(new Config()); newConfig->d->screen = d->screen->clone(); for (const OutputPtr &ourOutput : d->outputs) { newConfig->addOutput(ourOutput->clone()); } newConfig->d->primaryOutput = newConfig->d->findPrimaryOutput(); newConfig->setSupportedFeatures(supportedFeatures()); + newConfig->setTabletModeAvailable(tabletModeAvailable()); + newConfig->setTabletModeEngaged(tabletModeEngaged()); return newConfig; } QString Config::connectedOutputsHash() const { QStringList hashedOutputs; const auto outputs = connectedOutputs(); for (const OutputPtr &output : outputs) { hashedOutputs << output->hash(); } std::sort(hashedOutputs.begin(), hashedOutputs.end()); const auto hash = QCryptographicHash::hash(hashedOutputs.join(QString()).toLatin1(), QCryptographicHash::Md5); return QString::fromLatin1(hash.toHex()); } ScreenPtr Config::screen() const { return d->screen; } void Config::setScreen(const ScreenPtr &screen) { d->screen = screen; } OutputPtr Config::output(int outputId) const { return d->outputs.value(outputId); } Config::Features Config::supportedFeatures() const { return d->supportedFeatures; } void Config::setSupportedFeatures(const Config::Features &features) { d->supportedFeatures = features; } bool Config::tabletModeAvailable() const { return d->tabletModeAvailable; } void Config::setTabletModeAvailable(bool available) { d->tabletModeAvailable = available; } bool Config::tabletModeEngaged() const { return d->tabletModeEngaged; } void Config::setTabletModeEngaged(bool engaged) { d->tabletModeEngaged = engaged; } OutputList Config::outputs() const { return d->outputs; } OutputList Config::connectedOutputs() const { OutputList outputs; Q_FOREACH(const OutputPtr &output, d->outputs) { if (!output->isConnected()) { continue; } outputs.insert(output->id(), output); } return outputs; } OutputPtr Config::primaryOutput() const { if (d->primaryOutput) { return d->primaryOutput; } d->primaryOutput = d->findPrimaryOutput(); return d->primaryOutput; } void Config::setPrimaryOutput(const OutputPtr &newPrimary) { // Don't call primaryOutput(): at this point d->primaryOutput is either // initialized, or we need to look for the primary anyway if (d->primaryOutput == newPrimary) { return; } // qCDebug(KSCREEN) << "Primary output changed from" << primaryOutput() // << "(" << (primaryOutput().isNull() ? "none" : primaryOutput()->name()) << ") to" // << newPrimary << "(" << (newPrimary.isNull() ? "none" : newPrimary->name()) << ")"; for (OutputPtr &output : d->outputs) { disconnect(output.data(), &KScreen::Output::isPrimaryChanged, d, &KScreen::Config::Private::onPrimaryOutputChanged); output->setPrimary(output == newPrimary); connect(output.data(), &KScreen::Output::isPrimaryChanged, d, &KScreen::Config::Private::onPrimaryOutputChanged); } d->primaryOutput = newPrimary; Q_EMIT primaryOutputChanged(newPrimary); } void Config::addOutput(const OutputPtr &output) { d->outputs.insert(output->id(), output); connect(output.data(), &KScreen::Output::isPrimaryChanged, d, &KScreen::Config::Private::onPrimaryOutputChanged); Q_EMIT outputAdded(output); if (output->isPrimary()) { setPrimaryOutput(output); } } void Config::removeOutput(int outputId) { d->removeOutput(d->outputs.find(outputId)); } void Config::setOutputs(const OutputList &outputs) { for (auto iter = d->outputs.begin(), end = d->outputs.end(); iter != end; ) { iter = d->removeOutput(iter); end = d->outputs.end(); } for (const OutputPtr &output : outputs) { addOutput(output); } } bool Config::isValid() const { return d->valid; } void Config::setValid(bool valid) { d->valid = valid; } void Config::apply(const ConfigPtr& other) { d->screen->apply(other->screen()); // Remove removed outputs Q_FOREACH (const OutputPtr &output, d->outputs) { if (!other->d->outputs.contains(output->id())) { removeOutput(output->id()); } } Q_FOREACH (const OutputPtr &otherOutput, other->d->outputs) { // Add new outputs if (!d->outputs.contains(otherOutput->id())) { addOutput(otherOutput->clone()); } else { // Update existing outputs d->outputs[otherOutput->id()]->apply(otherOutput); } } // Update validity setValid(other->isValid()); } QDebug operator<<(QDebug dbg, const KScreen::ConfigPtr &config) { if (config) { dbg << "KScreen::Config("; const auto outputs = config->outputs(); for (const auto &output : outputs) { if (output->isConnected()) { dbg << endl << output; } } dbg << ")"; } else { dbg << "KScreen::Config(NULL)"; } return dbg; } #include "config.moc" diff --git a/src/configserializer.cpp b/src/configserializer.cpp index 0cfca96..023fa14 100644 --- a/src/configserializer.cpp +++ b/src/configserializer.cpp @@ -1,341 +1,351 @@ /* * Copyright (C) 2014 Daniel Vratil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "configserializer_p.h" #include "config.h" #include "mode.h" #include "output.h" #include "screen.h" #include "edid.h" #include "kscreen_debug.h" #include #include #include #include using namespace KScreen; QJsonObject ConfigSerializer::serializePoint(const QPoint &point) { QJsonObject obj; obj[QLatin1String("x")] = point.x(); obj[QLatin1String("y")] = point.y(); return obj; } QJsonObject ConfigSerializer::serializeSize(const QSize &size) { QJsonObject obj; obj[QLatin1String("width")] = size.width(); obj[QLatin1String("height")] = size.height(); return obj; } QJsonObject ConfigSerializer::serializeConfig(const ConfigPtr &config) { QJsonObject obj; if (!config) { return obj; } obj[QLatin1String("features")] = static_cast(config->supportedFeatures()); QJsonArray outputs; Q_FOREACH (const OutputPtr &output, config->outputs()) { outputs.append(serializeOutput(output)); } obj[QLatin1String("outputs")] = outputs; if (config->screen()) { obj[QLatin1String("screen")] = serializeScreen(config->screen()); } + obj[QLatin1String("tabletModeAvailable")] = config->tabletModeAvailable(); + obj[QLatin1String("tabletModeEngaged")] = config->tabletModeEngaged(); + return obj; } QJsonObject ConfigSerializer::serializeOutput(const OutputPtr &output) { QJsonObject obj; obj[QLatin1String("id")] = output->id(); obj[QLatin1String("name")] = output->name(); obj[QLatin1String("type")] = static_cast(output->type()); obj[QLatin1String("icon")] = output->icon(); obj[QLatin1String("pos")] = serializePoint(output->pos()); obj[QLatin1String("scale")] = output->scale(); obj[QLatin1String("size")] = serializeSize(output->size()); obj[QLatin1String("rotation")] = static_cast(output->rotation()); obj[QLatin1String("currentModeId")] = output->currentModeId(); obj[QLatin1String("preferredModes")] = serializeList(output->preferredModes()); obj[QLatin1String("connected")] = output->isConnected(); obj[QLatin1String("followPreferredMode")] = output->followPreferredMode(); obj[QLatin1String("enabled")] = output->isEnabled(); obj[QLatin1String("primary")] = output->isPrimary(); obj[QLatin1String("clones")] = serializeList(output->clones()); //obj[QLatin1String("edid")] = output->edid()->raw(); obj[QLatin1String("sizeMM")] = serializeSize(output->sizeMm()); obj[QLatin1String("replicationSource")] = output->replicationSource(); QJsonArray modes; Q_FOREACH (const ModePtr &mode, output->modes()) { modes.append(serializeMode(mode)); } obj[QLatin1String("modes")] = modes; return obj; } QJsonObject ConfigSerializer::serializeMode(const ModePtr &mode) { QJsonObject obj; obj[QLatin1String("id")] = mode->id(); obj[QLatin1String("name")] = mode->name(); obj[QLatin1String("size")] = serializeSize(mode->size()); obj[QLatin1String("refreshRate")] = mode->refreshRate(); return obj; } QJsonObject ConfigSerializer::serializeScreen(const ScreenPtr &screen) { QJsonObject obj; obj[QLatin1String("id")] = screen->id(); obj[QLatin1String("currentSize")] = serializeSize(screen->currentSize()); obj[QLatin1String("maxSize")] = serializeSize(screen->maxSize()); obj[QLatin1String("minSize")] = serializeSize(screen->minSize()); obj[QLatin1String("maxActiveOutputsCount")] = screen->maxActiveOutputsCount(); return obj; } QPoint ConfigSerializer::deserializePoint(const QDBusArgument &arg) { int x = 0, y = 0; arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1Char('x')) { x = value.toInt(); } else if (key == QLatin1Char('y')) { y = value.toInt(); } else { qCWarning(KSCREEN) << "Invalid key in Point map: " << key; return QPoint(); } arg.endMapEntry(); } arg.endMap(); return QPoint(x, y); } QSize ConfigSerializer::deserializeSize(const QDBusArgument &arg) { int w = 0, h = 0; arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("width")) { w = value.toInt(); } else if (key == QLatin1String("height")) { h = value.toInt(); } else { qCWarning(KSCREEN) << "Invalid key in size struct: " << key; return QSize(); } arg.endMapEntry(); } arg.endMap(); return QSize(w, h); } ConfigPtr ConfigSerializer::deserializeConfig(const QVariantMap &map) { ConfigPtr config(new Config); if (map.contains(QLatin1String("features"))) { config->setSupportedFeatures(static_cast(map[QStringLiteral("features")].toInt())); } + if (map.contains(QLatin1String("tabletModeAvailable"))) { + config->setTabletModeAvailable(map[QStringLiteral("tabletModeAvailable")].toBool()); + } + if (map.contains(QLatin1String("tabletModeEngaged"))) { + config->setTabletModeEngaged(map[QStringLiteral("tabletModeEngaged")].toBool()); + } + if (map.contains(QLatin1String("outputs"))) { const QDBusArgument &outputsArg = map[QStringLiteral("outputs")].value(); outputsArg.beginArray(); OutputList outputs; while (!outputsArg.atEnd()) { QVariant value; outputsArg >> value; const KScreen::OutputPtr output = deserializeOutput(value.value()); if (!output) { return ConfigPtr(); } outputs.insert(output->id(), output); } outputsArg.endArray(); config->setOutputs(outputs); } if (map.contains(QLatin1String("screen"))) { const QDBusArgument &screenArg = map[QStringLiteral("screen")].value(); const KScreen::ScreenPtr screen = deserializeScreen(screenArg); if (!screen) { return ConfigPtr(); } config->setScreen(screen); } return config; } OutputPtr ConfigSerializer::deserializeOutput(const QDBusArgument &arg) { OutputPtr output(new Output); arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("id")) { output->setId(value.toInt()); } else if (key == QLatin1String("name")) { output->setName(value.toString()); } else if (key == QLatin1String("type")) { output->setType(static_cast(value.toInt())); } else if (key == QLatin1String("icon")) { output->setIcon(value.toString()); } else if (key == QLatin1String("pos")) { output->setPos(deserializePoint(value.value())); } else if (key == QLatin1String("scale")) { output->setScale(value.toDouble()); } else if (key == QLatin1String("size")) { output->setSize(deserializeSize(value.value())); } else if (key == QLatin1String("rotation")) { output->setRotation(static_cast(value.toInt())); } else if (key == QLatin1String("currentModeId")) { output->setCurrentModeId(value.toString()); } else if (key == QLatin1String("preferredModes")) { output->setPreferredModes(deserializeList(value.value())); } else if (key == QLatin1String("connected")) { output->setConnected(value.toBool()); } else if (key == QLatin1String("followPreferredMode")) { output->setFollowPreferredMode(value.toBool()); } else if (key == QLatin1String("enabled")) { output->setEnabled(value.toBool()); } else if (key == QLatin1String("primary")) { output->setPrimary(value.toBool()); } else if (key == QLatin1String("clones")) { output->setClones(deserializeList(value.value())); } else if (key == QLatin1String("replicationSource")) { output->setReplicationSource(value.toInt()); } else if (key == QLatin1String("sizeMM")) { output->setSizeMm(deserializeSize(value.value())); } else if (key == QLatin1String("modes")) { const QDBusArgument arg = value.value(); ModeList modes; arg.beginArray(); while (!arg.atEnd()) { QVariant value; arg >> value; const KScreen::ModePtr mode = deserializeMode(value.value()); if (!mode) { return OutputPtr(); } modes.insert(mode->id(), mode); } arg.endArray(); output->setModes(modes); } else { qCWarning(KSCREEN) << "Invalid key in Output map: " << key; return OutputPtr(); } arg.endMapEntry(); } arg.endMap(); return output; } ModePtr ConfigSerializer::deserializeMode(const QDBusArgument &arg) { ModePtr mode(new Mode); arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("id")) { mode->setId(value.toString()); } else if (key == QLatin1String("name")) { mode->setName(value.toString()); } else if (key == QLatin1String("size")) { mode->setSize(deserializeSize(value.value())); } else if (key == QLatin1String("refreshRate")) { mode->setRefreshRate(value.toFloat()); } else { qCWarning(KSCREEN) << "Invalid key in Mode map: " << key; return ModePtr(); } arg.endMapEntry(); } arg.endMap(); return mode; } ScreenPtr ConfigSerializer::deserializeScreen(const QDBusArgument &arg) { ScreenPtr screen(new Screen); arg.beginMap(); QString key; QVariant value; while (!arg.atEnd()) { arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("id")) { screen->setId(value.toInt()); } else if (key == QLatin1String("maxActiveOutputsCount")) { screen->setMaxActiveOutputsCount(value.toInt()); } else if (key == QLatin1String("currentSize")) { screen->setCurrentSize(deserializeSize(value.value())); } else if (key == QLatin1String("maxSize")) { screen->setMaxSize(deserializeSize(value.value())); } else if (key == QLatin1String("minSize")) { screen->setMinSize(deserializeSize(value.value())); } else { qCWarning(KSCREEN) << "Invalid key in Screen map:" << key; return ScreenPtr(); } arg.endMapEntry(); } arg.endMap(); return screen; }