File indexing completed on 2024-04-28 05:27:38
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 connect(m_outputModel, &OutputModel::dataChanged, this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) { 0049 Q_UNUSED(bottomRight) 0050 // Do not run checks during interactive reaarange 0051 if (!m_outputModel->isMoving()) { 0052 checkNeedsSave(); 0053 } 0054 Q_EMIT changed(); 0055 }); 0056 connect(m_config.data(), &KScreen::Config::outputAdded, this, [this]() { 0057 Q_EMIT outputConnect(true); 0058 }); 0059 connect(m_config.data(), &KScreen::Config::outputRemoved, this, [this]() { 0060 Q_EMIT outputConnect(false); 0061 }); 0062 connect(m_config.data(), &KScreen::Config::prioritiesChanged, this, &ConfigHandler::outputPrioritiesChanged); 0063 0064 Q_EMIT outputModelChanged(); 0065 } 0066 0067 void ConfigHandler::initOutput(const KScreen::OutputPtr &output) 0068 { 0069 output->setExplicitLogicalSize(config()->logicalSizeForOutput(*output)); 0070 0071 if (output->isConnected()) { 0072 m_outputModel->add(output); 0073 } 0074 connect(output.data(), &KScreen::Output::isConnectedChanged, this, [this, output]() { 0075 Q_EMIT outputConnect(output->isConnected()); 0076 }); 0077 } 0078 0079 void ConfigHandler::updateInitialData() 0080 { 0081 m_previousConfig = m_initialConfig->clone(); 0082 connect(new GetConfigOperation(), &GetConfigOperation::finished, this, [this](ConfigOperation *op) { 0083 if (op->hasError()) { 0084 return; 0085 } 0086 m_initialConfig = qobject_cast<GetConfigOperation *>(op)->config(); 0087 m_initialControl.reset(new ControlConfig(m_initialConfig)); 0088 checkNeedsSave(); 0089 }); 0090 } 0091 0092 bool ConfigHandler::shouldTestNewSettings() 0093 { 0094 return checkSaveandTestCommon(false); 0095 } 0096 0097 void ConfigHandler::checkNeedsSave() 0098 { 0099 if (checkPrioritiesNeedSave()) { 0100 Q_EMIT needsSaveChecked(true); 0101 return; 0102 } 0103 Q_EMIT needsSaveChecked(checkSaveandTestCommon(true)); 0104 } 0105 0106 bool ConfigHandler::checkPrioritiesNeedSave() 0107 { 0108 if (!(m_config->supportedFeatures() & KScreen::Config::Feature::PrimaryDisplay)) { 0109 return false; 0110 } 0111 // first item of pair is initial config, second is current 0112 QMap<QString, std::pair<std::optional<uint32_t>, std::optional<uint32_t>>> map; 0113 0114 // exploiting the fact that operator[] on a map is what's called get_or_insert_default in other languages 0115 const auto &initialList = m_initialConfig->outputs(); 0116 for (const OutputPtr &output : initialList) { 0117 map[output->hashMd5()].first = std::optional(output->priority()); 0118 } 0119 const auto ¤tList = m_config->outputs(); 0120 for (const OutputPtr &output : currentList) { 0121 map[output->hashMd5()].second = std::optional(output->priority()); 0122 } 0123 // so if we end up with items that are not both initialized to the same priority 0124 for (const auto &[left, right] : std::as_const(map)) { 0125 if (!(left.has_value() && right.has_value() && left.value() == right.value())) { 0126 // then configs must be different after all 0127 return true; 0128 } 0129 } 0130 return false; 0131 } 0132 0133 bool ConfigHandler::checkSaveandTestCommon(bool isSaveCheck) 0134 { 0135 const auto outputs = m_config->connectedOutputs(); 0136 for (const auto &output : outputs) { 0137 const QString hash = output->hashMd5(); 0138 const auto configs = m_initialConfig->outputs(); 0139 for (const auto &config : configs) { 0140 if (hash != config->hashMd5()) { 0141 continue; 0142 } 0143 0144 if (output->isEnabled() != config->isEnabled()) { 0145 return true; 0146 } 0147 0148 // clang-format off 0149 if (output->isEnabled()) { 0150 bool scaleChanged = false; 0151 if (isSaveCheck || m_config->supportedFeatures() & KScreen::Config::Feature::PerOutputScaling) { 0152 scaleChanged = output->scale() != config->scale(); 0153 } 0154 if ( output->currentModeId() != config->currentModeId() 0155 || output->pos() != config->pos() 0156 || scaleChanged 0157 || output->rotation() != config->rotation() 0158 || output->replicationSource() != config->replicationSource() 0159 || output->overscan() != config->overscan() 0160 || output->vrrPolicy() != config->vrrPolicy() 0161 || output->rgbRange() != config->rgbRange() 0162 || output->autoRotatePolicy() != config->autoRotatePolicy() 0163 || output->iccProfilePath() != config->iccProfilePath() 0164 || output->isHdrEnabled() != config->isHdrEnabled() 0165 || output->isWcgEnabled() != config->isWcgEnabled() 0166 || output->sdrBrightness() != config->sdrBrightness() 0167 || output->sdrGamutWideness() != config->sdrGamutWideness()) { 0168 return true; 0169 } 0170 } 0171 // clang-format on 0172 } 0173 } 0174 return false; 0175 } 0176 0177 QSize ConfigHandler::screenSize() const 0178 { 0179 int width = 0, height = 0; 0180 QSize size; 0181 0182 const auto connectedOutputs = m_config->connectedOutputs(); 0183 for (const auto &output : connectedOutputs) { 0184 if (!output->isPositionable()) { 0185 continue; 0186 } 0187 const int outputRight = output->geometry().right(); 0188 const int outputBottom = output->geometry().bottom(); 0189 0190 if (outputRight > width) { 0191 width = outputRight; 0192 } 0193 if (outputBottom > height) { 0194 height = outputBottom; 0195 } 0196 } 0197 if (width > 0 && height > 0) { 0198 size = QSize(width, height); 0199 } else { 0200 size = QSize(); 0201 } 0202 return size; 0203 } 0204 0205 QSize ConfigHandler::normalizeScreen() 0206 { 0207 if (!m_config) { 0208 return QSize(); 0209 } 0210 0211 m_outputModel->normalizePositions(); 0212 const auto currentScreenSize = screenSize(); 0213 m_lastNormalizedScreenSize = currentScreenSize; 0214 0215 Q_EMIT screenNormalizationUpdate(true); 0216 return currentScreenSize; 0217 } 0218 0219 void ConfigHandler::checkScreenNormalization() 0220 { 0221 const bool normalized = !m_config || (m_lastNormalizedScreenSize == screenSize() && m_outputModel->positionsNormalized()); 0222 0223 Q_EMIT screenNormalizationUpdate(normalized); 0224 } 0225 0226 void ConfigHandler::outputPrioritiesChanged() 0227 { 0228 checkNeedsSave(); 0229 Q_EMIT changed(); 0230 } 0231 0232 KScreen::OutputPtr ConfigHandler::replicationSource(const KScreen::OutputPtr &output) const 0233 { 0234 return m_control->getReplicationSource(output); 0235 } 0236 0237 void ConfigHandler::setReplicationSource(KScreen::OutputPtr &output, const KScreen::OutputPtr &source) 0238 { 0239 m_control->setReplicationSource(output, source); 0240 } 0241 0242 uint32_t ConfigHandler::overscan(const KScreen::OutputPtr &output) const 0243 { 0244 return m_control->getOverscan(output); 0245 } 0246 0247 void ConfigHandler::setOverscan(const KScreen::OutputPtr &output, uint32_t value) 0248 { 0249 m_control->setOverscan(output, value); 0250 } 0251 0252 KScreen::Output::VrrPolicy ConfigHandler::vrrPolicy(const KScreen::OutputPtr &output) const 0253 { 0254 return m_control->getVrrPolicy(output); 0255 } 0256 0257 void ConfigHandler::setVrrPolicy(const KScreen::OutputPtr &output, KScreen::Output::VrrPolicy value) 0258 { 0259 m_control->setVrrPolicy(output, value); 0260 } 0261 0262 KScreen::Output::RgbRange ConfigHandler::rgbRange(const KScreen::OutputPtr &output) const 0263 { 0264 return m_control->getRgbRange(output); 0265 } 0266 0267 void ConfigHandler::setRgbRange(const KScreen::OutputPtr &output, KScreen::Output::RgbRange value) 0268 { 0269 m_control->setRgbRange(output, value); 0270 } 0271 0272 void ConfigHandler::writeControl() 0273 { 0274 if (!m_control) { 0275 return; 0276 } 0277 m_control->writeFile(); 0278 } 0279 0280 #include "moc_config_handler.cpp"