diff --git a/src/config.cpp b/src/config.cpp index f53f415..74fba3f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,384 +1,383 @@ /************************************************************************************* * 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 "debug_p.h" #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) , 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; 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; - QSize outputSize; 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()); return newConfig; } 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; } 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(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("; for (const auto output : config->outputs()) { if (output->isConnected()) { dbg << endl << output; } } dbg << ")"; } else { dbg << "KScreen::Config(NULL)"; } return dbg; } #include "config.moc" diff --git a/src/getconfigoperation.cpp b/src/getconfigoperation.cpp index ca7d75d..4cb9afb 100644 --- a/src/getconfigoperation.cpp +++ b/src/getconfigoperation.cpp @@ -1,201 +1,200 @@ /* * 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 "getconfigoperation.h" #include "configoperation_p.h" #include "config.h" #include "output.h" #include "log.h" #include "backendmanager_p.h" #include "configserializer_p.h" #include "backendinterface.h" using namespace KScreen; namespace KScreen { class GetConfigOperationPrivate : public ConfigOperationPrivate { Q_OBJECT public: GetConfigOperationPrivate(GetConfigOperation::Options options, GetConfigOperation *qq); void backendReady(org::kde::kscreen::Backend* backend) override; void onConfigReceived(QDBusPendingCallWatcher *watcher); void onEDIDReceived(QDBusPendingCallWatcher *watcher); public: GetConfigOperation::Options options; ConfigPtr config; // For in-process void loadEdid(KScreen::AbstractBackend* backend); // For out-of-process int pendingEDIDs; QPointer mBackend; private: Q_DECLARE_PUBLIC(GetConfigOperation) }; } GetConfigOperationPrivate::GetConfigOperationPrivate(GetConfigOperation::Options options, GetConfigOperation* qq) : ConfigOperationPrivate(qq) , options(options) { } void GetConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); ConfigOperationPrivate::backendReady(backend); Q_Q(GetConfigOperation); if (!backend) { q->setError(tr("Failed to prepare backend")); q->emitResult(); return; } mBackend = backend; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mBackend->getConfig(), this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &GetConfigOperationPrivate::onConfigReceived); } void GetConfigOperationPrivate::onConfigReceived(QDBusPendingCallWatcher *watcher) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_Q(GetConfigOperation); QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { q->setError(reply.error().message()); q->emitResult(); return; } config = ConfigSerializer::deserializeConfig(reply.value()); if (!config) { q->setError(tr("Failed to deserialize backend response")); q->emitResult(); return; } if (options & GetConfigOperation::NoEDID || config->outputs().isEmpty()) { q->emitResult(); return; } pendingEDIDs = 0; if (!mBackend) { q->setError(tr("Backend invalidated")); q->emitResult(); return; } Q_FOREACH (const OutputPtr &output, config->outputs()) { if (!output->isConnected()) { continue; } QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mBackend->getEdid(output->id()), this); watcher->setProperty("outputId", output->id()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &GetConfigOperationPrivate::onEDIDReceived); ++pendingEDIDs; } } void GetConfigOperationPrivate::onEDIDReceived(QDBusPendingCallWatcher* watcher) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_Q(GetConfigOperation); QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { q->setError(reply.error().message()); q->emitResult(); return; } const QByteArray edidData = reply.value(); const int outputId = watcher->property("outputId").toInt(); config->output(outputId)->setEdid(edidData); if (--pendingEDIDs == 0) { q->emitResult(); } } GetConfigOperation::GetConfigOperation(Options options, QObject* parent) : ConfigOperation(new GetConfigOperationPrivate(options, this), parent) { } GetConfigOperation::~GetConfigOperation() { } KScreen::ConfigPtr GetConfigOperation::config() const { Q_D(const GetConfigOperation); return d->config; } void GetConfigOperation::start() { Q_D(GetConfigOperation); if (BackendManager::instance()->method() == BackendManager::InProcess) { auto backend = d->loadBackend(); d->config = backend->config()->clone(); d->loadEdid(backend); emitResult(); } else { d->requestBackend(); } } void GetConfigOperationPrivate::loadEdid(KScreen::AbstractBackend* backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess); - Q_Q(GetConfigOperation); if (options & KScreen::ConfigOperation::NoEDID) { return; } if (!config) { return; } Q_FOREACH (auto output, config->outputs()) { if (output->edid() == nullptr) { const QByteArray edidData = backend->edid(output->id()); output->setEdid(edidData); } } } #include "getconfigoperation.moc"