File indexing completed on 2024-04-14 15:37:37

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005  *
0006  */
0007 
0008 #include "getconfigoperation.h"
0009 #include "backendinterface.h"
0010 #include "backendmanager_p.h"
0011 #include "config.h"
0012 #include "configoperation_p.h"
0013 #include "configserializer_p.h"
0014 #include "log.h"
0015 #include "output.h"
0016 
0017 using namespace KScreen;
0018 
0019 namespace KScreen
0020 {
0021 class GetConfigOperationPrivate : public ConfigOperationPrivate
0022 {
0023     Q_OBJECT
0024 
0025 public:
0026     GetConfigOperationPrivate(GetConfigOperation::Options options, GetConfigOperation *qq);
0027 
0028     void backendReady(org::kde::kscreen::Backend *backend) override;
0029     void onConfigReceived(QDBusPendingCallWatcher *watcher);
0030     void onEDIDReceived(QDBusPendingCallWatcher *watcher);
0031 
0032 public:
0033     GetConfigOperation::Options options;
0034     ConfigPtr config;
0035     // For in-process
0036     void loadEdid(KScreen::AbstractBackend *backend);
0037 
0038     // For out-of-process
0039     int pendingEDIDs;
0040     QPointer<org::kde::kscreen::Backend> mBackend;
0041 
0042 private:
0043     Q_DECLARE_PUBLIC(GetConfigOperation)
0044 };
0045 
0046 }
0047 
0048 GetConfigOperationPrivate::GetConfigOperationPrivate(GetConfigOperation::Options options, GetConfigOperation *qq)
0049     : ConfigOperationPrivate(qq)
0050     , options(options)
0051 {
0052 }
0053 
0054 void GetConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend)
0055 {
0056     Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess);
0057     ConfigOperationPrivate::backendReady(backend);
0058 
0059     Q_Q(GetConfigOperation);
0060 
0061     if (!backend) {
0062         q->setError(tr("Failed to prepare backend"));
0063         q->emitResult();
0064         return;
0065     }
0066 
0067     mBackend = backend;
0068     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mBackend->getConfig(), this);
0069     connect(watcher, &QDBusPendingCallWatcher::finished, this, &GetConfigOperationPrivate::onConfigReceived);
0070 }
0071 
0072 void GetConfigOperationPrivate::onConfigReceived(QDBusPendingCallWatcher *watcher)
0073 {
0074     Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess);
0075     Q_Q(GetConfigOperation);
0076 
0077     QDBusPendingReply<QVariantMap> reply = *watcher;
0078     watcher->deleteLater();
0079     if (reply.isError()) {
0080         q->setError(reply.error().message());
0081         q->emitResult();
0082         return;
0083     }
0084 
0085     config = ConfigSerializer::deserializeConfig(reply.value());
0086     if (!config) {
0087         q->setError(tr("Failed to deserialize backend response"));
0088         q->emitResult();
0089         return;
0090     }
0091 
0092     if (options & GetConfigOperation::NoEDID || config->outputs().isEmpty()) {
0093         q->emitResult();
0094         return;
0095     }
0096 
0097     pendingEDIDs = 0;
0098     if (!mBackend) {
0099         q->setError(tr("Backend invalidated"));
0100         q->emitResult();
0101         return;
0102     }
0103     const auto outputs = config->outputs();
0104     for (const OutputPtr &output : outputs) {
0105         if (!output->isConnected()) {
0106             continue;
0107         }
0108 
0109         QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mBackend->getEdid(output->id()), this);
0110         watcher->setProperty("outputId", output->id());
0111         connect(watcher, &QDBusPendingCallWatcher::finished, this, &GetConfigOperationPrivate::onEDIDReceived);
0112         ++pendingEDIDs;
0113     }
0114 }
0115 
0116 void GetConfigOperationPrivate::onEDIDReceived(QDBusPendingCallWatcher *watcher)
0117 {
0118     Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess);
0119     Q_Q(GetConfigOperation);
0120 
0121     QDBusPendingReply<QByteArray> reply = *watcher;
0122     watcher->deleteLater();
0123     if (reply.isError()) {
0124         q->setError(reply.error().message());
0125         q->emitResult();
0126         return;
0127     }
0128 
0129     const QByteArray edidData = reply.value();
0130     const int outputId = watcher->property("outputId").toInt();
0131 
0132     config->output(outputId)->setEdid(edidData);
0133     if (--pendingEDIDs == 0) {
0134         q->emitResult();
0135     }
0136 }
0137 
0138 GetConfigOperation::GetConfigOperation(Options options, QObject *parent)
0139     : ConfigOperation(new GetConfigOperationPrivate(options, this), parent)
0140 {
0141 }
0142 
0143 GetConfigOperation::~GetConfigOperation()
0144 {
0145 }
0146 
0147 KScreen::ConfigPtr GetConfigOperation::config() const
0148 {
0149     Q_D(const GetConfigOperation);
0150     return d->config;
0151 }
0152 
0153 void GetConfigOperation::start()
0154 {
0155     Q_D(GetConfigOperation);
0156     if (BackendManager::instance()->method() == BackendManager::InProcess) {
0157         auto backend = d->loadBackend();
0158         if (!backend) {
0159             return; // loadBackend() already set error and called emitResult() for us
0160         }
0161         d->config = backend->config()->clone();
0162         d->loadEdid(backend);
0163         emitResult();
0164     } else {
0165         d->requestBackend();
0166     }
0167 }
0168 
0169 void GetConfigOperationPrivate::loadEdid(KScreen::AbstractBackend *backend)
0170 {
0171     Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess);
0172     if (options & KScreen::ConfigOperation::NoEDID) {
0173         return;
0174     }
0175     if (!config) {
0176         return;
0177     }
0178     auto outputs = config->outputs();
0179     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
0180         auto output = *it;
0181         if (output->edid() == nullptr) {
0182             const QByteArray edidData = backend->edid(output->id());
0183             output->setEdid(edidData);
0184         }
0185     }
0186 }
0187 
0188 #include "getconfigoperation.moc"