File indexing completed on 2024-04-14 15:37:38
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 "setconfigoperation.h" 0009 0010 #include "abstractbackend.h" 0011 #include "backendmanager_p.h" 0012 #include "config.h" 0013 #include "configoperation_p.h" 0014 #include "configserializer_p.h" 0015 #include "kscreen_debug.h" 0016 #include "output.h" 0017 0018 #include <QDBusPendingCall> 0019 #include <QDBusPendingCallWatcher> 0020 0021 using namespace KScreen; 0022 0023 namespace KScreen 0024 { 0025 class SetConfigOperationPrivate : public ConfigOperationPrivate 0026 { 0027 Q_OBJECT 0028 0029 public: 0030 explicit SetConfigOperationPrivate(const KScreen::ConfigPtr &config, ConfigOperation *qq); 0031 0032 void backendReady(org::kde::kscreen::Backend *backend) override; 0033 void onConfigSet(QDBusPendingCallWatcher *watcher); 0034 void normalizeOutputPositions(); 0035 void fixPrimaryOutput(); 0036 0037 KScreen::ConfigPtr config; 0038 0039 private: 0040 Q_DECLARE_PUBLIC(SetConfigOperation) 0041 }; 0042 0043 } 0044 0045 SetConfigOperationPrivate::SetConfigOperationPrivate(const ConfigPtr &config, ConfigOperation *qq) 0046 : ConfigOperationPrivate(qq) 0047 , config(config) 0048 { 0049 } 0050 0051 void SetConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend) 0052 { 0053 ConfigOperationPrivate::backendReady(backend); 0054 0055 Q_Q(SetConfigOperation); 0056 0057 if (!backend) { 0058 q->setError(tr("Failed to prepare backend")); 0059 q->emitResult(); 0060 return; 0061 } 0062 0063 const QVariantMap map = ConfigSerializer::serializeConfig(config).toVariantMap(); 0064 if (map.isEmpty()) { 0065 q->setError(tr("Failed to serialize request")); 0066 q->emitResult(); 0067 return; 0068 } 0069 0070 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(backend->setConfig(map), this); 0071 connect(watcher, &QDBusPendingCallWatcher::finished, this, &SetConfigOperationPrivate::onConfigSet); 0072 } 0073 0074 void SetConfigOperationPrivate::onConfigSet(QDBusPendingCallWatcher *watcher) 0075 { 0076 Q_Q(SetConfigOperation); 0077 0078 QDBusPendingReply<QVariantMap> reply = *watcher; 0079 watcher->deleteLater(); 0080 0081 if (reply.isError()) { 0082 q->setError(reply.error().message()); 0083 q->emitResult(); 0084 return; 0085 } 0086 0087 config = ConfigSerializer::deserializeConfig(reply.value()); 0088 if (!config) { 0089 q->setError(tr("Failed to deserialize backend response")); 0090 } 0091 0092 q->emitResult(); 0093 } 0094 0095 SetConfigOperation::SetConfigOperation(const ConfigPtr &config, QObject *parent) 0096 : ConfigOperation(new SetConfigOperationPrivate(config, this), parent) 0097 { 0098 } 0099 0100 SetConfigOperation::~SetConfigOperation() 0101 { 0102 } 0103 0104 ConfigPtr SetConfigOperation::config() const 0105 { 0106 Q_D(const SetConfigOperation); 0107 return d->config; 0108 } 0109 0110 void SetConfigOperation::start() 0111 { 0112 Q_D(SetConfigOperation); 0113 d->normalizeOutputPositions(); 0114 d->fixPrimaryOutput(); 0115 if (BackendManager::instance()->method() == BackendManager::InProcess) { 0116 auto backend = d->loadBackend(); 0117 backend->setConfig(d->config); 0118 emitResult(); 0119 } else { 0120 d->requestBackend(); 0121 } 0122 } 0123 0124 void SetConfigOperationPrivate::normalizeOutputPositions() 0125 { 0126 if (!config) { 0127 return; 0128 } 0129 int offsetX = INT_MAX; 0130 int offsetY = INT_MAX; 0131 const auto outputs = config->outputs(); 0132 for (const KScreen::OutputPtr &output : outputs) { 0133 if (!output->isPositionable()) { 0134 continue; 0135 } 0136 offsetX = qMin(output->pos().x(), offsetX); 0137 offsetY = qMin(output->pos().y(), offsetY); 0138 } 0139 0140 if (!offsetX && !offsetY) { 0141 return; 0142 } 0143 qCDebug(KSCREEN) << "Correcting output positions by:" << QPoint(offsetX, offsetY); 0144 for (const KScreen::OutputPtr &output : outputs) { 0145 if (!output->isConnected() || !output->isEnabled()) { 0146 continue; 0147 } 0148 QPoint newPos = QPoint(output->pos().x() - offsetX, output->pos().y() - offsetY); 0149 qCDebug(KSCREEN) << "Moved output from" << output->pos() << "to" << newPos; 0150 output->setPos(newPos); 0151 } 0152 } 0153 0154 void SetConfigOperationPrivate::fixPrimaryOutput() 0155 { 0156 if (!config || !(config->supportedFeatures() & Config::Feature::PrimaryDisplay)) { 0157 return; 0158 } 0159 const auto outputs = config->outputs(); 0160 if (outputs.isEmpty()) { 0161 return; 0162 } 0163 0164 // Here we make sure that: 0165 // - that our primary is enabled 0166 // - we have at least a primary 0167 // - we have exactly 1 primary 0168 // - we have a primary at all 0169 bool found = false; 0170 KScreen::OutputPtr primary; 0171 KScreen::OutputPtr candidate; 0172 for (const KScreen::OutputPtr &output : outputs) { 0173 if (output->isPrimary()) { 0174 if (!output->isEnabled()) { 0175 qCDebug(KSCREEN) << "can't be primary if disabled!!" << output; 0176 } else if (found) { 0177 qCDebug(KSCREEN) << "can only have 1 primary" << output; 0178 } else { 0179 found = true; 0180 primary = output; 0181 } 0182 } else if (output->isEnabled()) { 0183 candidate = output; 0184 } 0185 } 0186 0187 if (!found && candidate) { 0188 qCDebug(KSCREEN) << "setting primary instead" << candidate; 0189 config->setPrimaryOutput(candidate); 0190 } else if (primary) { 0191 // ensures all others are set to non-primary. It's OK if all outputs 0192 // are disabled and `primary` is essentially nullptr. 0193 config->setPrimaryOutput(primary); 0194 } 0195 } 0196 0197 #include "setconfigoperation.moc"