File indexing completed on 2024-04-28 16:45:06
0001 /* 0002 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "config_handler.h" 0007 0008 #include "kcm_screen_debug.h" 0009 #include "output_model.h" 0010 0011 #include <algorithm> 0012 #include <cstdint> 0013 #include <utility> 0014 0015 #include <kscreen/configmonitor.h> 0016 #include <kscreen/getconfigoperation.h> 0017 #include <kscreen/output.h> 0018 0019 #include <QGuiApplication> 0020 #include <QRect> 0021 0022 using namespace KScreen; 0023 0024 ConfigHandler::ConfigHandler(QObject *parent) 0025 : QObject(parent) 0026 { 0027 } 0028 0029 void ConfigHandler::setConfig(KScreen::ConfigPtr config) 0030 { 0031 m_config = config; 0032 m_initialConfig = m_config->clone(); 0033 m_initialControl.reset(new ControlConfig(m_initialConfig)); 0034 0035 KScreen::ConfigMonitor::instance()->addConfig(m_config); 0036 m_control.reset(new ControlConfig(config)); 0037 0038 m_outputModel = new OutputModel(this); 0039 connect(m_outputModel, &OutputModel::positionChanged, this, &ConfigHandler::checkScreenNormalization); 0040 connect(m_outputModel, &OutputModel::sizeChanged, this, &ConfigHandler::checkScreenNormalization); 0041 0042 const auto outputs = config->outputs(); 0043 for (const KScreen::OutputPtr &output : outputs) { 0044 initOutput(output); 0045 } 0046 m_lastNormalizedScreenSize = screenSize(); 0047 0048 // TODO: put this into m_initialControl 0049 m_initialRetention = getRetention(); 0050 Q_EMIT retentionChanged(); 0051 0052 connect(m_outputModel, &OutputModel::dataChanged, this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) { 0053 Q_UNUSED(bottomRight) 0054 // Do not run checks during interactive reaarange 0055 if (!m_outputModel->isMoving()) { 0056 checkNeedsSave(); 0057 } 0058 Q_EMIT changed(); 0059 }); 0060 connect(m_config.data(), &KScreen::Config::outputAdded, this, [this]() { 0061 Q_EMIT outputConnect(true); 0062 }); 0063 connect(m_config.data(), &KScreen::Config::outputRemoved, this, [this]() { 0064 Q_EMIT outputConnect(false); 0065 }); 0066 connect(m_config.data(), &KScreen::Config::prioritiesChanged, this, &ConfigHandler::outputPrioritiesChanged); 0067 0068 Q_EMIT outputModelChanged(); 0069 } 0070 0071 void ConfigHandler::initOutput(const KScreen::OutputPtr &output) 0072 { 0073 output->setExplicitLogicalSize(config()->logicalSizeForOutput(*output)); 0074 0075 if (output->isConnected()) { 0076 m_outputModel->add(output); 0077 } 0078 connect(output.data(), &KScreen::Output::isConnectedChanged, this, [this, output]() { 0079 Q_EMIT outputConnect(output->isConnected()); 0080 }); 0081 } 0082 0083 void ConfigHandler::updateInitialData() 0084 { 0085 m_previousConfig = m_initialConfig->clone(); 0086 m_initialRetention = getRetention(); 0087 connect(new GetConfigOperation(), &GetConfigOperation::finished, this, [this](ConfigOperation *op) { 0088 if (op->hasError()) { 0089 return; 0090 } 0091 m_initialConfig = qobject_cast<GetConfigOperation *>(op)->config(); 0092 m_initialControl.reset(new ControlConfig(m_initialConfig)); 0093 checkNeedsSave(); 0094 }); 0095 } 0096 0097 bool ConfigHandler::shouldTestNewSettings() 0098 { 0099 return checkSaveandTestCommon(false); 0100 } 0101 0102 void ConfigHandler::checkNeedsSave() 0103 { 0104 if (checkPrioritiesNeedSave()) { 0105 Q_EMIT needsSaveChecked(true); 0106 return; 0107 } 0108 if (m_initialRetention != getRetention()) { 0109 Q_EMIT needsSaveChecked(true); 0110 return; 0111 } 0112 Q_EMIT needsSaveChecked(checkSaveandTestCommon(true)); 0113 } 0114 0115 bool ConfigHandler::checkPrioritiesNeedSave() 0116 { 0117 if (!(m_config->supportedFeatures() & KScreen::Config::Feature::PrimaryDisplay)) { 0118 return false; 0119 } 0120 // first item of pair is initial config, second is current 0121 QMap<QString, std::pair<std::optional<uint32_t>, std::optional<uint32_t>>> map; 0122 0123 // exploiting the fact that operator[] on a map is what's called get_or_insert_default in other languages 0124 const auto &initialList = m_initialConfig->outputs(); 0125 for (const OutputPtr &output : initialList) { 0126 map[output->hashMd5()].first = std::optional(output->priority()); 0127 } 0128 const auto ¤tList = m_config->outputs(); 0129 for (const OutputPtr &output : currentList) { 0130 map[output->hashMd5()].second = std::optional(output->priority()); 0131 } 0132 // so if we end up with items that are not both initialized to the same priority 0133 for (const auto &[left, right] : std::as_const(map)) { 0134 if (!(left.has_value() && right.has_value() && left.value() == right.value())) { 0135 // then configs must be different after all 0136 return true; 0137 } 0138 } 0139 return false; 0140 } 0141 0142 bool ConfigHandler::checkSaveandTestCommon(bool isSaveCheck) 0143 { 0144 const auto outputs = m_config->connectedOutputs(); 0145 for (const auto &output : outputs) { 0146 const QString hash = output->hashMd5(); 0147 const auto configs = m_initialConfig->outputs(); 0148 for (const auto &config : configs) { 0149 if (hash != config->hashMd5()) { 0150 continue; 0151 } 0152 0153 if (output->isEnabled() != config->isEnabled()) { 0154 return true; 0155 } 0156 0157 // clang-format off 0158 if (output->isEnabled()) { 0159 bool scaleChanged = false; 0160 if (isSaveCheck || m_config->supportedFeatures() & KScreen::Config::Feature::PerOutputScaling) { 0161 scaleChanged = output->scale() != config->scale(); 0162 } 0163 if ( output->currentModeId() != config->currentModeId() 0164 || output->pos() != config->pos() 0165 || scaleChanged 0166 || output->rotation() != config->rotation() 0167 || output->replicationSource() != config->replicationSource() 0168 || autoRotate(output) != m_initialControl->getAutoRotate(output) 0169 || autoRotateOnlyInTabletMode(output) != m_initialControl->getAutoRotateOnlyInTabletMode(output) 0170 || output->overscan() != config->overscan() 0171 || output->vrrPolicy() != config->vrrPolicy() 0172 || output->rgbRange() != config->rgbRange()) { 0173 return true; 0174 } 0175 } 0176 // clang-format on 0177 } 0178 } 0179 return false; 0180 } 0181 0182 QSize ConfigHandler::screenSize() const 0183 { 0184 int width = 0, height = 0; 0185 QSize size; 0186 0187 const auto connectedOutputs = m_config->connectedOutputs(); 0188 for (const auto &output : connectedOutputs) { 0189 if (!output->isPositionable()) { 0190 continue; 0191 } 0192 const int outputRight = output->geometry().right(); 0193 const int outputBottom = output->geometry().bottom(); 0194 0195 if (outputRight > width) { 0196 width = outputRight; 0197 } 0198 if (outputBottom > height) { 0199 height = outputBottom; 0200 } 0201 } 0202 if (width > 0 && height > 0) { 0203 size = QSize(width, height); 0204 } else { 0205 size = QSize(); 0206 } 0207 return size; 0208 } 0209 0210 QSize ConfigHandler::normalizeScreen() 0211 { 0212 if (!m_config) { 0213 return QSize(); 0214 } 0215 0216 m_outputModel->normalizePositions(); 0217 const auto currentScreenSize = screenSize(); 0218 m_lastNormalizedScreenSize = currentScreenSize; 0219 0220 Q_EMIT screenNormalizationUpdate(true); 0221 return currentScreenSize; 0222 } 0223 0224 void ConfigHandler::checkScreenNormalization() 0225 { 0226 const bool normalized = !m_config || (m_lastNormalizedScreenSize == screenSize() && m_outputModel->positionsNormalized()); 0227 0228 Q_EMIT screenNormalizationUpdate(normalized); 0229 } 0230 0231 void ConfigHandler::outputPrioritiesChanged() 0232 { 0233 checkNeedsSave(); 0234 Q_EMIT changed(); 0235 } 0236 0237 Control::OutputRetention ConfigHandler::getRetention() const 0238 { 0239 using Retention = Control::OutputRetention; 0240 0241 auto ret = Retention::Undefined; 0242 if (!m_control) { 0243 return ret; 0244 } 0245 const auto outputs = m_config->connectedOutputs(); 0246 if (outputs.isEmpty()) { 0247 return ret; 0248 } 0249 ret = m_control->getOutputRetention(outputs.first()); 0250 0251 for (const auto &output : outputs) { 0252 const auto outputRet = m_control->getOutputRetention(output); 0253 if (ret != outputRet) { 0254 // Control file with different retention values per output. 0255 return Retention::Undefined; 0256 } 0257 } 0258 0259 if (ret == Retention::Undefined) { 0260 // If all outputs have undefined retention, 0261 // this should be displayed as global retention. 0262 return Retention::Global; 0263 } 0264 return ret; 0265 } 0266 0267 int ConfigHandler::retention() const 0268 { 0269 return static_cast<int>(getRetention()); 0270 } 0271 0272 void ConfigHandler::setRetention(int retention) 0273 { 0274 using Retention = Control::OutputRetention; 0275 0276 if (!m_control) { 0277 return; 0278 } 0279 if (retention != static_cast<int>(Retention::Global) && retention != static_cast<int>(Retention::Individual)) { 0280 // We only allow setting to global or individual retention. 0281 return; 0282 } 0283 if (retention == ConfigHandler::retention()) { 0284 return; 0285 } 0286 auto ret = static_cast<Retention>(retention); 0287 const auto connectedOutputs = m_config->connectedOutputs(); 0288 for (const auto &output : connectedOutputs) { 0289 m_control->setOutputRetention(output, ret); 0290 } 0291 checkNeedsSave(); 0292 Q_EMIT retentionChanged(); 0293 Q_EMIT changed(); 0294 } 0295 0296 KScreen::OutputPtr ConfigHandler::replicationSource(const KScreen::OutputPtr &output) const 0297 { 0298 return m_control->getReplicationSource(output); 0299 } 0300 0301 void ConfigHandler::setReplicationSource(KScreen::OutputPtr &output, const KScreen::OutputPtr &source) 0302 { 0303 m_control->setReplicationSource(output, source); 0304 } 0305 0306 bool ConfigHandler::autoRotate(const KScreen::OutputPtr &output) const 0307 { 0308 return m_control->getAutoRotate(output); 0309 } 0310 0311 void ConfigHandler::setAutoRotate(KScreen::OutputPtr &output, bool autoRotate) 0312 { 0313 m_control->setAutoRotate(output, autoRotate); 0314 } 0315 0316 bool ConfigHandler::autoRotateOnlyInTabletMode(const KScreen::OutputPtr &output) const 0317 { 0318 return m_control->getAutoRotateOnlyInTabletMode(output); 0319 } 0320 0321 void ConfigHandler::setAutoRotateOnlyInTabletMode(KScreen::OutputPtr &output, bool value) 0322 { 0323 m_control->setAutoRotateOnlyInTabletMode(output, value); 0324 } 0325 0326 uint32_t ConfigHandler::overscan(const KScreen::OutputPtr &output) const 0327 { 0328 return m_control->getOverscan(output); 0329 } 0330 0331 void ConfigHandler::setOverscan(const KScreen::OutputPtr &output, uint32_t value) 0332 { 0333 m_control->setOverscan(output, value); 0334 } 0335 0336 KScreen::Output::VrrPolicy ConfigHandler::vrrPolicy(const KScreen::OutputPtr &output) const 0337 { 0338 return m_control->getVrrPolicy(output); 0339 } 0340 0341 void ConfigHandler::setVrrPolicy(const KScreen::OutputPtr &output, KScreen::Output::VrrPolicy value) 0342 { 0343 m_control->setVrrPolicy(output, value); 0344 } 0345 0346 KScreen::Output::RgbRange ConfigHandler::rgbRange(const KScreen::OutputPtr &output) const 0347 { 0348 return m_control->getRgbRange(output); 0349 } 0350 0351 void ConfigHandler::setRgbRange(const KScreen::OutputPtr &output, KScreen::Output::RgbRange value) 0352 { 0353 m_control->setRgbRange(output, value); 0354 } 0355 0356 void ConfigHandler::writeControl() 0357 { 0358 if (!m_control) { 0359 return; 0360 } 0361 m_control->writeFile(); 0362 }