diff --git a/src/backendmanager.cpp b/src/backendmanager.cpp index 91c6445..4960c76 100644 --- a/src/backendmanager.cpp +++ b/src/backendmanager.cpp @@ -1,429 +1,430 @@ /* * Copyright (C) 2014 Daniel Vratil * Copyright 2015 Sebastian Kügler * * 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 "backendmanager_p.h" #include "abstractbackend.h" #include "config.h" #include "configmonitor.h" #include "backendinterface.h" #include "debug_p.h" #include "getconfigoperation.h" #include "configserializer_p.h" #include "log.h" #include #include #include #include #include #include #include #include #include #include using namespace KScreen; Q_DECLARE_METATYPE(org::kde::kscreen::Backend*) const int BackendManager::sMaxCrashCount = 4; BackendManager *BackendManager::sInstance = nullptr; BackendManager *BackendManager::instance() { if (!sInstance) { sInstance = new BackendManager(); } return sInstance; } BackendManager::BackendManager() : QObject() , mInterface(0) , mCrashCount(0) , mShuttingDown(false) , mRequestsCounter(0) , mLoader(0) , mMethod(OutOfProcess) { Log::instance(); // Decide wether to run in, or out-of-process // if KSCREEN_BACKEND_INPROCESS is set explicitely, we respect that const auto _inprocess = qgetenv("KSCREEN_BACKEND_INPROCESS"); if (!_inprocess.isEmpty()) { const QByteArrayList falses({QByteArray("0"), QByteArray("false")}); if (!falses.contains(_inprocess.toLower())) { mMethod = InProcess; } else { mMethod = OutOfProcess; } } else { // For XRandR backends, use out of process if (preferredBackend().fileName().startsWith(QLatin1String("KSC_XRandR"))) { mMethod = OutOfProcess; } else { mMethod = InProcess; } } initMethod(); } void BackendManager::initMethod() { if (mMethod == OutOfProcess) { qRegisterMetaType("OrgKdeKscreenBackendInterface"); mServiceWatcher.setConnection(QDBusConnection::sessionBus()); connect(&mServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &BackendManager::backendServiceUnregistered); mResetCrashCountTimer.setSingleShot(true); mResetCrashCountTimer.setInterval(60000); connect(&mResetCrashCountTimer, &QTimer::timeout, this, [=]() { mCrashCount = 0; }); } } void BackendManager::setMethod(BackendManager::Method m) { if (mMethod == m) { return; } shutdownBackend(); mMethod = m; initMethod(); } BackendManager::Method BackendManager::method() const { return mMethod; } BackendManager::~BackendManager() { if (mMethod == InProcess) { shutdownBackend(); } } QFileInfo BackendManager::preferredBackend(const QString &backend) { /** this is the logic to pick a backend, in order of priority * * - backend argument is used if not empty * - env var KSCREEN_BACKEND is considered * - if platform is X11, the XRandR backend is picked * - if platform is wayland, KWayland backend is picked * - if neither is the case, QScreen backend is picked * - the QScreen backend is also used as fallback * */ QString backendFilter; const auto env_kscreen_backend = qgetenv("KSCREEN_BACKEND"); if (!backend.isEmpty()) { backendFilter = backend; } else if (!env_kscreen_backend.isEmpty()) { backendFilter = env_kscreen_backend; } else { if (QX11Info::isPlatformX11()) { backendFilter = QStringLiteral("XRandR"); } else if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { backendFilter = QStringLiteral("KWayland"); } else { backendFilter = QStringLiteral("QScreen"); } } QFileInfo fallback; Q_FOREACH (const QFileInfo &f, listBackends()) { // Here's the part where we do the match case-insensitive if (f.baseName().toLower() == QStringLiteral("ksc_%1").arg(backendFilter.toLower())) { return f; } if (f.baseName() == QLatin1String("KSC_QScreen")) { fallback = f; } } // qCWarning(KSCREEN) << "No preferred backend found. KSCREEN_BACKEND is set to " << env_kscreen_backend; // qCWarning(KSCREEN) << "falling back to " << fallback.fileName(); return fallback; } QFileInfoList BackendManager::listBackends() { // Compile a list of installed backends first const QString backendFilter = QStringLiteral("KSC_*"); const QStringList paths = QCoreApplication::libraryPaths(); QFileInfoList finfos; for (const QString &path : paths) { const QDir dir(path + QLatin1String("/kf5/kscreen/"), backendFilter, QDir::SortFlags(QDir::QDir::Name), QDir::NoDotAndDotDot | QDir::Files); finfos.append(dir.entryInfoList()); } return finfos; } KScreen::AbstractBackend *BackendManager::loadBackendPlugin(QPluginLoader *loader, const QString &name, const QVariantMap &arguments) { const auto finfo = preferredBackend(name); loader->setFileName(finfo.filePath()); QObject *instance = loader->instance(); if (!instance) { qCDebug(KSCREEN) << loader->errorString(); return nullptr; } auto backend = qobject_cast(instance); if (backend) { backend->init(arguments); if (!backend->isValid()) { qCDebug(KSCREEN) << "Skipping" << backend->name() << "backend"; delete backend; return nullptr; } //qCDebug(KSCREEN) << "Loaded" << backend->name() << "backend"; return backend; } else { qCDebug(KSCREEN) << finfo.fileName() << "does not provide valid KScreen backend"; } return nullptr; } KScreen::AbstractBackend *BackendManager::loadBackendInProcess(const QString &name) { Q_ASSERT(mMethod == InProcess); if (mMethod == OutOfProcess) { qCWarning(KSCREEN) << "You are trying to load a backend in process, while the BackendManager is set to use OutOfProcess communication. Use loadBackendPlugin() instead."; return nullptr; } if (m_inProcessBackend.first != nullptr && m_inProcessBackend.first->name() == name) { return m_inProcessBackend.first; } else if (m_inProcessBackend.first != nullptr && m_inProcessBackend.first->name() != name) { shutdownBackend(); } if (mLoader == nullptr) { mLoader = new QPluginLoader(this); } QVariantMap arguments; auto beargs = QString::fromLocal8Bit(qgetenv("KSCREEN_BACKEND_ARGS")); if (beargs.startsWith("TEST_DATA=")) { arguments["TEST_DATA"] = beargs.remove("TEST_DATA="); } auto backend = BackendManager::loadBackendPlugin(mLoader, name, arguments); //qCDebug(KSCREEN) << "Connecting ConfigMonitor to backend."; ConfigMonitor::instance()->connectInProcessBackend(backend); m_inProcessBackend = qMakePair(backend, arguments); + setConfig(backend->config()); return backend; } void BackendManager::requestBackend() { Q_ASSERT(mMethod == OutOfProcess); if (mInterface && mInterface->isValid()) { ++mRequestsCounter; QMetaObject::invokeMethod(this, "emitBackendReady", Qt::QueuedConnection); return; } // Another request already pending if (mRequestsCounter > 0) { return; } ++mRequestsCounter; const QByteArray args = qgetenv("KSCREEN_BACKEND_ARGS"); QVariantMap arguments; if (!args.isEmpty()) { QList arglist = args.split(';'); Q_FOREACH (const QByteArray &arg, arglist) { const int pos = arg.indexOf('='); if (pos == -1) { continue; } arguments.insert(arg.left(pos), arg.mid(pos + 1)); } } startBackend(QString::fromLatin1(qgetenv("KSCREEN_BACKEND")), arguments); } void BackendManager::emitBackendReady() { Q_ASSERT(mMethod == OutOfProcess); Q_EMIT backendReady(mInterface); --mRequestsCounter; if (mShutdownLoop.isRunning()) { mShutdownLoop.quit(); } } void BackendManager::startBackend(const QString &backend, const QVariantMap &arguments) { // This will autostart the launcher if it's not running already, calling // requestBackend(backend) will: // a) if the launcher is started it will force it to load the correct backend, // b) if the launcher is already running it will make sure it's running with // the same backend as the one we requested and send an error otherwise QDBusConnection conn = QDBusConnection::sessionBus(); QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KScreen"), QStringLiteral("/"), QStringLiteral("org.kde.KScreen"), QStringLiteral("requestBackend")); call.setArguments({ backend, arguments }); QDBusPendingCall pending = conn.asyncCall(call); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending); connect(watcher, &QDBusPendingCallWatcher::finished, this, &BackendManager::onBackendRequestDone); } void BackendManager::onBackendRequestDone(QDBusPendingCallWatcher *watcher) { Q_ASSERT(mMethod == OutOfProcess); watcher->deleteLater(); QDBusPendingReply reply = *watcher; // Most probably we requested an explicit backend that is different than the // one already loaded in the launcher if (reply.isError()) { qCWarning(KSCREEN) << "Failed to request backend:" << reply.error().name() << ":" << reply.error().message(); invalidateInterface(); emitBackendReady(); return; } // Most probably request and explicit backend which is not available or failed // to initialize, or the launcher did not find any suitable backend for the // current platform. if (!reply.value()) { qCWarning(KSCREEN) << "Failed to request backend: unknown error"; invalidateInterface(); emitBackendReady(); return; } // The launcher has successfully loaded the backend we wanted and registered // it to DBus (hopefuly), let's try to get an interface for the backend. if (mInterface) { invalidateInterface(); } mInterface = new org::kde::kscreen::Backend(QStringLiteral("org.kde.KScreen"), QStringLiteral("/backend"), QDBusConnection::sessionBus()); if (!mInterface->isValid()) { qCWarning(KSCREEN) << "Backend successfully requested, but we failed to obtain a valid DBus interface for it"; invalidateInterface(); emitBackendReady(); return; } // The backend is GO, so let's watch for it's possible disappearance, so we // can invalidate the interface mServiceWatcher.addWatchedService(mBackendService); // Immediatelly request config connect(new GetConfigOperation(GetConfigOperation::NoEDID), &GetConfigOperation::finished, [&](ConfigOperation *op) { mConfig = qobject_cast(op)->config(); emitBackendReady(); }); // And listen for its change. connect(mInterface, &org::kde::kscreen::Backend::configChanged, [&](const QVariantMap &newConfig) { mConfig = KScreen::ConfigSerializer::deserializeConfig(newConfig); }); } void BackendManager::backendServiceUnregistered(const QString &serviceName) { Q_ASSERT(mMethod == OutOfProcess); mServiceWatcher.removeWatchedService(serviceName); invalidateInterface(); requestBackend(); } void BackendManager::invalidateInterface() { Q_ASSERT(mMethod == OutOfProcess); delete mInterface; mInterface = 0; mBackendService.clear(); } ConfigPtr BackendManager::config() const { return mConfig; } void BackendManager::setConfig(ConfigPtr c) { //qCDebug(KSCREEN) << "BackendManager::setConfig, outputs:" << c->outputs().count(); mConfig = c; } void BackendManager::shutdownBackend() { if (mMethod == InProcess) { delete mLoader; mLoader = nullptr; m_inProcessBackend.second.clear(); delete m_inProcessBackend.first; m_inProcessBackend.first = nullptr; } else { if (mBackendService.isEmpty() && !mInterface) { return; } // If there are some currently pending requests, then wait for them to // finish before quitting while (mRequestsCounter > 0) { mShutdownLoop.exec(); } mServiceWatcher.removeWatchedService(mBackendService); mShuttingDown = true; QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KScreen"), QStringLiteral("/"), QStringLiteral("org.kde.KScreen"), QStringLiteral("quit")); // Call synchronously QDBusConnection::sessionBus().call(call); invalidateInterface(); while (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.KScreen"))) { QThread::msleep(100); } } } diff --git a/src/configmonitor.cpp b/src/configmonitor.cpp index 4fa1afb..8fe7a1c 100644 --- a/src/configmonitor.cpp +++ b/src/configmonitor.cpp @@ -1,258 +1,255 @@ /************************************************************************************* * Copyright 2012 - 2014 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 "configmonitor.h" #include "backendmanager_p.h" #include "backendinterface.h" #include "abstractbackend.h" #include "configserializer_p.h" #include "getconfigoperation.h" #include "debug_p.h" #include "output.h" #include using namespace KScreen; class ConfigMonitor::Private : public QObject { Q_OBJECT public: Private(ConfigMonitor *q); void updateConfigs(); void onBackendReady(org::kde::kscreen::Backend *backend); void backendConfigChanged(const QVariantMap &config); void configDestroyed(QObject* removedConfig); void getConfigFinished(ConfigOperation *op); void updateConfigs(const KScreen::ConfigPtr &newConfig); void edidReady(QDBusPendingCallWatcher *watcher); QList> watchedConfigs; QPointer mBackend; bool mFirstBackend; QMap> mPendingEDIDRequests; private: ConfigMonitor *q; }; ConfigMonitor::Private::Private(ConfigMonitor *q) : QObject(q) , mFirstBackend(true) , q(q) { } void ConfigMonitor::Private::onBackendReady(org::kde::kscreen::Backend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); if (backend == mBackend) { return; } if (mBackend) { disconnect(mBackend.data(), &org::kde::kscreen::Backend::configChanged, this, &ConfigMonitor::Private::backendConfigChanged); } mBackend = QPointer(backend); // If we received a new backend interface, then it's very likely that it is // because the backend process has crashed - just to be sure we haven't missed // any change, request the current config now and update our watched configs // // Only request the config if this is not initial backend request, because it // can happen that if a change happened before now, or before we get the config, // the result will be invalid. This can happen when KScreen KDED launches and // detects changes need to be done. if (!mFirstBackend && !watchedConfigs.isEmpty()) { connect(new GetConfigOperation(), &GetConfigOperation::finished, this, &Private::getConfigFinished); } mFirstBackend = false; connect(mBackend.data(), &org::kde::kscreen::Backend::configChanged, this, &ConfigMonitor::Private::backendConfigChanged); } void ConfigMonitor::Private::getConfigFinished(ConfigOperation* op) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); if (op->hasError()) { qCWarning(KSCREEN) << "Failed to retrieve current config: " << op->errorString(); return; } const KScreen::ConfigPtr config = qobject_cast(op)->config(); updateConfigs(config); } void ConfigMonitor::Private::backendConfigChanged(const QVariantMap &configMap) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); ConfigPtr newConfig = ConfigSerializer::deserializeConfig(configMap); if (!newConfig) { qCWarning(KSCREEN) << "Failed to deserialize config from DBus change notification"; return; } Q_FOREACH (OutputPtr output, newConfig->connectedOutputs()) { if (!output->edid() && output->isConnected()) { QDBusPendingReply reply = mBackend->getEdid(output->id()); mPendingEDIDRequests[newConfig].append(output->id()); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply); watcher->setProperty("outputId", output->id()); watcher->setProperty("config", QVariant::fromValue(newConfig)); connect(watcher, &QDBusPendingCallWatcher::finished, this, &ConfigMonitor::Private::edidReady); } } if (mPendingEDIDRequests.contains(newConfig)) { qCDebug(KSCREEN) << "Requesting missing EDID for outputs" << mPendingEDIDRequests[newConfig]; } else { updateConfigs(newConfig); } } void ConfigMonitor::Private::edidReady(QDBusPendingCallWatcher* watcher) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); const int outputId = watcher->property("outputId").toInt(); const ConfigPtr config = watcher->property("config").value(); Q_ASSERT(mPendingEDIDRequests.contains(config)); Q_ASSERT(mPendingEDIDRequests[config].contains(outputId)); watcher->deleteLater(); mPendingEDIDRequests[config].removeOne(outputId); const QDBusPendingReply reply = *watcher; if (reply.isError()) { qCWarning(KSCREEN) << "Error when retrieving EDID: " << reply.error().message(); } else { const QByteArray edid = reply.argumentAt<0>(); if (!edid.isEmpty()) { OutputPtr output = config->output(outputId); output->setEdid(edid); } } if (mPendingEDIDRequests[config].isEmpty()) { mPendingEDIDRequests.remove(config); updateConfigs(config); } } void ConfigMonitor::Private::updateConfigs(const KScreen::ConfigPtr &newConfig) { - Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); QMutableListIterator> iter(watchedConfigs); while (iter.hasNext()) { KScreen::ConfigPtr config = iter.next().toStrongRef(); if (!config) { iter.remove(); continue; } config->apply(newConfig); iter.setValue(config.toWeakRef()); } Q_EMIT q->configurationChanged(); } void ConfigMonitor::Private::configDestroyed(QObject *removedConfig) { for (auto iter = watchedConfigs.begin(); iter != watchedConfigs.end(); ++iter) { if (iter->data() == removedConfig) { iter = watchedConfigs.erase(iter); // Iterate over the entire list in case there are duplicates } } } ConfigMonitor *ConfigMonitor::instance() { static ConfigMonitor *s_instance = nullptr; if (s_instance == nullptr) { s_instance = new ConfigMonitor(); } return s_instance; } ConfigMonitor::ConfigMonitor(): QObject(), d(new Private(this)) { if (BackendManager::instance()->method() == BackendManager::OutOfProcess) { connect(BackendManager::instance(), &BackendManager::backendReady, d, &ConfigMonitor::Private::onBackendReady); BackendManager::instance()->requestBackend(); } } ConfigMonitor::~ConfigMonitor() { delete d; } void ConfigMonitor::addConfig(const ConfigPtr &config) { const QWeakPointer weakConfig = config.toWeakRef(); if (!d->watchedConfigs.contains(weakConfig)) { connect(weakConfig.data(), &QObject::destroyed, d, &Private::configDestroyed); d->watchedConfigs << weakConfig; } } void ConfigMonitor::removeConfig(const ConfigPtr &config) { const QWeakPointer weakConfig = config.toWeakRef(); if (d->watchedConfigs.contains(config)) { disconnect(weakConfig.data(), &QObject::destroyed, d, &Private::configDestroyed); d->watchedConfigs.removeAll(config); } } void ConfigMonitor::connectInProcessBackend(KScreen::AbstractBackend* backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess); connect(backend, &AbstractBackend::configChanged, [=](KScreen::ConfigPtr config) { if (config.isNull()) { return; } - const QWeakPointer weakConfig = config.toWeakRef(); - if (d->watchedConfigs.contains(weakConfig)) { - emit configurationChanged(); - } + qCDebug(KSCREEN) << "Backend change!" << config; + d->updateConfigs(config); }); } #include "configmonitor.moc" diff --git a/src/getconfigoperation.cpp b/src/getconfigoperation.cpp index 00bee43..e40f088 100644 --- a/src/getconfigoperation.cpp +++ b/src/getconfigoperation.cpp @@ -1,202 +1,201 @@ /* * 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) Q_DECL_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(); - KScreen::BackendManager::instance()->setConfig(d->config); + 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"