File indexing completed on 2024-05-12 05:33:54
0001 /* 0002 * SPDX-FileCopyrightText: 2014-2016 Sebastian Kügler <sebas@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "doctor.h" 0008 #include "mode.h" 0009 #include <dpms.h> 0010 0011 #include <QCollator> 0012 #include <QCoreApplication> 0013 #include <QDateTime> 0014 #include <QFile> 0015 #include <QGuiApplication> 0016 #include <QJsonArray> 0017 #include <QJsonDocument> 0018 #include <QJsonObject> 0019 #include <QLoggingCategory> 0020 #include <QRect> 0021 #include <QScreen> 0022 #include <QStandardPaths> 0023 0024 #include <utility> 0025 0026 #include "../backendmanager_p.h" 0027 #include "../config.h" 0028 #include "../configoperation.h" 0029 #include "../getconfigoperation.h" 0030 #include "../log.h" 0031 #include "../output.h" 0032 #include "../setconfigoperation.h" 0033 0034 Q_LOGGING_CATEGORY(KSCREEN_DOCTOR, "kscreen.doctor") 0035 0036 static QTextStream cout(stdout); 0037 static QTextStream cerr(stderr); 0038 0039 const static QString green = QStringLiteral("\033[01;32m"); 0040 const static QString red = QStringLiteral("\033[01;31m"); 0041 const static QString yellow = QStringLiteral("\033[01;33m"); 0042 const static QString blue = QStringLiteral("\033[01;34m"); 0043 const static QString bold = QStringLiteral("\033[01;39m"); 0044 const static QString cr = QStringLiteral("\033[0;0m"); 0045 0046 namespace KScreen 0047 { 0048 namespace ConfigSerializer 0049 { 0050 // Exported private symbol in configserializer_p.h in KScreen 0051 extern QJsonObject serializeConfig(const KScreen::ConfigPtr &config); 0052 } 0053 } 0054 0055 using namespace KScreen; 0056 0057 Doctor::Doctor(QObject *parent) 0058 : QObject(parent) 0059 , m_config(nullptr) 0060 , m_changed(false) 0061 , m_dpmsClient(nullptr) 0062 { 0063 } 0064 0065 Doctor::~Doctor() 0066 { 0067 } 0068 0069 void Doctor::start(QCommandLineParser *parser) 0070 { 0071 m_parser = parser; 0072 if (m_parser->isSet(QStringLiteral("info"))) { 0073 showBackends(); 0074 } 0075 if (parser->isSet(QStringLiteral("json")) || parser->isSet(QStringLiteral("outputs")) || !m_outputArgs.isEmpty()) { 0076 KScreen::GetConfigOperation *op = new KScreen::GetConfigOperation(); 0077 connect(op, &KScreen::GetConfigOperation::finished, this, [this](KScreen::ConfigOperation *op) { 0078 configReceived(op); 0079 }); 0080 return; 0081 } 0082 if (m_parser->isSet(QStringLiteral("dpms"))) { 0083 if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { 0084 cerr << "DPMS is only supported on Wayland." << Qt::endl; 0085 // We need to kick the event loop, otherwise .quit() hangs 0086 QTimer::singleShot(0, qApp->quit); 0087 return; 0088 } 0089 0090 m_dpmsClient = new Dpms(this); 0091 auto screens = qGuiApp->screens(); 0092 if (m_parser->isSet(QStringLiteral("dpms-excluded"))) { 0093 const auto excludedConnectors = m_parser->values(QStringLiteral("dpms-excluded")); 0094 auto it = std::remove_if(screens.begin(), screens.end(), [&excludedConnectors](QScreen *screen) { 0095 return excludedConnectors.contains(screen->name()); 0096 }); 0097 screens.erase(it, screens.end()); 0098 } 0099 0100 connect(m_dpmsClient, &Dpms::hasPendingChangesChanged, qGuiApp, [](bool hasChanges) { 0101 if (!hasChanges) { 0102 // We need to hit the event loop, otherwise .quit() hangs 0103 QTimer::singleShot(0, qApp->quit); 0104 } 0105 }); 0106 0107 const QString dpmsArg = m_parser->value(QStringLiteral("dpms")); 0108 if (dpmsArg == QLatin1String("show")) { 0109 } else { 0110 if (!m_dpmsClient->isSupported()) { 0111 cerr << "DPMS not supported in this system"; 0112 qGuiApp->quit(); 0113 return; 0114 } 0115 0116 if (dpmsArg == QLatin1String("off")) { 0117 m_dpmsClient->switchMode(KScreen::Dpms::Off, screens); 0118 } else if (dpmsArg == QLatin1String("on")) { 0119 m_dpmsClient->switchMode(KScreen::Dpms::On, screens); 0120 } else { 0121 cerr << "--dpms argument not understood (" << dpmsArg << ")"; 0122 } 0123 } 0124 return; 0125 } 0126 0127 if (m_parser->isSet(QStringLiteral("log"))) { 0128 const QString logmsg = m_parser->value(QStringLiteral("log")); 0129 if (!Log::instance()->enabled()) { 0130 qCWarning(KSCREEN_DOCTOR) << "Logging is disabled, unset KSCREEN_LOGGING in your environment."; 0131 } else { 0132 Log::log(logmsg); 0133 } 0134 } 0135 // We need to kick the event loop, otherwise .quit() hangs 0136 QTimer::singleShot(0, qApp->quit); 0137 } 0138 0139 void Doctor::showBackends() const 0140 { 0141 cout << "Environment: " << Qt::endl; 0142 auto env_kscreen_backend = qEnvironmentVariable("KSCREEN_BACKEND", QStringLiteral("[not set]")); 0143 cout << " * KSCREEN_BACKEND : " << env_kscreen_backend << Qt::endl; 0144 auto env_kscreen_backend_inprocess = qEnvironmentVariable("KSCREEN_BACKEND_INPROCESS", QStringLiteral("[not set]")); 0145 cout << " * KSCREEN_BACKEND_INPROCESS : " << env_kscreen_backend_inprocess << Qt::endl; 0146 auto env_kscreen_logging = qEnvironmentVariable("KSCREEN_LOGGING", QStringLiteral("[not set]")); 0147 cout << " * KSCREEN_LOGGING : " << env_kscreen_logging << Qt::endl; 0148 0149 cout << "Logging to : " << (Log::instance()->enabled() ? Log::instance()->logFile() : QStringLiteral("[logging disabled]")) << Qt::endl; 0150 const auto backends = BackendManager::instance()->listBackends(); 0151 auto preferred = BackendManager::instance()->preferredBackend(); 0152 cout << "Preferred KScreen backend : " << green << preferred.fileName() << cr << Qt::endl; 0153 cout << "Available KScreen backends:" << Qt::endl; 0154 for (const QFileInfo &f : backends) { 0155 auto c = blue; 0156 if (preferred == f) { 0157 c = green; 0158 } 0159 cout << " * " << c << f.fileName() << cr << ": " << f.absoluteFilePath() << Qt::endl; 0160 } 0161 cout << Qt::endl; 0162 } 0163 0164 void Doctor::setOptionList(const QStringList &outputArgs) 0165 { 0166 m_outputArgs = outputArgs; 0167 } 0168 0169 OutputPtr Doctor::findOutput(const QString &query) 0170 { 0171 // try as an output name or ID 0172 for (const auto &output : m_config->outputs()) { 0173 if (output->name() == query) { 0174 return output; 0175 } 0176 } 0177 bool ok; 0178 int id = query.toInt(&ok); 0179 if (!ok) { 0180 cerr << "Output with name " << query << " not found." << Qt::endl; 0181 return OutputPtr(); 0182 } 0183 0184 if (m_config->outputs().contains(id)) { 0185 return m_config->outputs()[id]; 0186 } else { 0187 cerr << "Output with id " << id << " not found." << Qt::endl; 0188 return OutputPtr(); 0189 } 0190 } 0191 0192 void Doctor::parseOutputArgs() 0193 { 0194 // qCDebug(KSCREEN_DOCTOR) << "POSARGS" << m_positionalArgs; 0195 for (const QString &op : std::as_const(m_outputArgs)) { 0196 auto ops = op.split(QLatin1Char('.')); 0197 if (ops.count() > 2) { 0198 bool ok; 0199 if (ops[0] == QLatin1String("output")) { 0200 OutputPtr output = findOutput(ops[1]); 0201 if (!output) { 0202 qApp->exit(3); 0203 return; 0204 } 0205 int output_id = output->id(); 0206 0207 const QString subcmd = ops.length() > 2 ? ops[2] : QString(); 0208 0209 if (ops.count() == 3 && subcmd == QLatin1String("primary")) { 0210 setPrimary(output); 0211 } else if (ops.count() == 4 && subcmd == QLatin1String("priority")) { 0212 uint32_t priority = ops[3].toUInt(&ok); 0213 if (!ok || priority > 100) { 0214 qCWarning(KSCREEN_DOCTOR) << "Wrong input: allowed values for priority are from 1 to 100"; 0215 qApp->exit(5); 0216 return; 0217 } 0218 setPriority(output, priority); 0219 } else if (ops.count() == 3 && subcmd == QLatin1String("enable")) { 0220 setEnabled(output, true); 0221 } else if (ops.count() == 3 && subcmd == QLatin1String("disable")) { 0222 setEnabled(output, false); 0223 } else if (ops.count() == 4 && subcmd == QLatin1String("mode")) { 0224 QString mode_id = ops[3]; 0225 // set mode 0226 if (!setMode(output, mode_id)) { 0227 qApp->exit(9); 0228 return; 0229 } 0230 qCDebug(KSCREEN_DOCTOR) << "Output" << output_id << "set mode" << mode_id; 0231 0232 } else if (ops.count() == 4 && subcmd == QLatin1String("position")) { 0233 QStringList _pos = ops[3].split(QLatin1Char(',')); 0234 if (_pos.count() != 2) { 0235 qCWarning(KSCREEN_DOCTOR) << "Invalid position:" << ops[3]; 0236 qApp->exit(5); 0237 return; 0238 } 0239 int x = _pos[0].toInt(&ok); 0240 int y = _pos[1].toInt(&ok); 0241 if (!ok) { 0242 cerr << "Unable to parse position: " << ops[3] << Qt::endl; 0243 qApp->exit(5); 0244 return; 0245 } 0246 0247 QPoint p(x, y); 0248 qCDebug(KSCREEN_DOCTOR) << "Output position" << p; 0249 setPosition(output, p); 0250 0251 } else if ((ops.count() == 4 || ops.count() == 5) && subcmd == QLatin1String("scale")) { 0252 // be lenient about . vs. comma as separator 0253 qreal scale = ops[3].replace(QLatin1Char(','), QLatin1Char('.')).toDouble(&ok); 0254 if (ops.count() == 5) { 0255 const QString dbl = ops[3] + QStringLiteral(".") + ops[4]; 0256 scale = dbl.toDouble(&ok); 0257 }; 0258 // set scale 0259 if (!ok || qFuzzyCompare(scale, 0.0)) { 0260 qCDebug(KSCREEN_DOCTOR) << "Could not set scale " << scale << " to output " << output_id; 0261 qApp->exit(9); 0262 return; 0263 } 0264 setScale(output, scale); 0265 } else if ((ops.count() == 4) && (subcmd == QLatin1String("orientation") || subcmd == QStringLiteral("rotation"))) { 0266 const QString _rotation = ops[3].toLower(); 0267 bool ok = false; 0268 const QHash<QString, KScreen::Output::Rotation> rotationMap({{QStringLiteral("none"), KScreen::Output::None}, 0269 {QStringLiteral("normal"), KScreen::Output::None}, 0270 {QStringLiteral("left"), KScreen::Output::Left}, 0271 {QStringLiteral("right"), KScreen::Output::Right}, 0272 {QStringLiteral("inverted"), KScreen::Output::Inverted}, 0273 {QStringLiteral("flipped"), KScreen::Output::Flipped}, 0274 {QStringLiteral("flipped90"), KScreen::Output::Flipped90}, 0275 {QStringLiteral("flipped180"), KScreen::Output::Flipped180}, 0276 {QStringLiteral("flipped270"), KScreen::Output::Flipped270}}); 0277 KScreen::Output::Rotation rot = KScreen::Output::None; 0278 // set orientation 0279 if (rotationMap.contains(_rotation)) { 0280 ok = true; 0281 rot = rotationMap[_rotation]; 0282 } 0283 if (!ok) { 0284 qCDebug(KSCREEN_DOCTOR) << "Could not set orientation " << _rotation << " to output " << output_id; 0285 qApp->exit(9); 0286 return; 0287 } 0288 setRotation(output, rot); 0289 } else if (ops.count() == 4 && subcmd == QLatin1String("overscan")) { 0290 const uint32_t overscan = ops[3].toInt(); 0291 if (overscan > 100) { 0292 qCWarning(KSCREEN_DOCTOR) << "Wrong input: allowed values for overscan are from 0 to 100"; 0293 qApp->exit(9); 0294 return; 0295 } 0296 setOverscan(output, overscan); 0297 } else if (ops.count() == 4 && subcmd == QLatin1String("vrrpolicy")) { 0298 const QString _policy = ops[3].toLower(); 0299 KScreen::Output::VrrPolicy policy; 0300 if (_policy == QStringLiteral("never")) { 0301 policy = KScreen::Output::VrrPolicy::Never; 0302 } else if (_policy == QStringLiteral("always")) { 0303 policy = KScreen::Output::VrrPolicy::Always; 0304 } else if (_policy == QStringLiteral("automatic")) { 0305 policy = KScreen::Output::VrrPolicy::Automatic; 0306 } else { 0307 qCDebug(KSCREEN_DOCTOR) << "Wrong input: Only allowed values are \"never\", \"always\" and \"automatic\""; 0308 qApp->exit(9); 0309 return; 0310 } 0311 setVrrPolicy(output, policy); 0312 } else if (ops.count() == 4 && subcmd == QLatin1String("rgbrange")) { 0313 const QString _range = ops[3].toLower(); 0314 KScreen::Output::RgbRange range; 0315 if (_range == QStringLiteral("automatic")) { 0316 range = KScreen::Output::RgbRange::Automatic; 0317 } else if (_range == QStringLiteral("full")) { 0318 range = KScreen::Output::RgbRange::Full; 0319 } else if (_range == QStringLiteral("limited")) { 0320 range = KScreen::Output::RgbRange::Limited; 0321 } else { 0322 qCDebug(KSCREEN_DOCTOR) << "Wrong input: Only allowed values for rgbrange are \"automatic\", \"full\" and \"limited\""; 0323 qApp->exit(9); 0324 return; 0325 } 0326 setRgbRange(output, range); 0327 } else if (ops.count() == 4 && subcmd == "hdr") { 0328 const QString _enable = ops[3].toLower(); 0329 if (_enable == "enable") { 0330 setHdrEnabled(output, true); 0331 } else if (_enable == "disable") { 0332 setHdrEnabled(output, false); 0333 } else { 0334 qCDebug(KSCREEN_DOCTOR) << "Wrong input: Only allowed values for hdr are \"enable\" and \"disable\""; 0335 qApp->exit(9); 0336 return; 0337 } 0338 } else if (ops.count() == 4 && subcmd == "sdr-brightness") { 0339 const uint32_t brightness = ops[3].toInt(); 0340 if (brightness < 100 || brightness > 10000) { 0341 qCDebug(KSCREEN_DOCTOR) << "Wrong input: Allowed range for sdr-brightness is 100 to 10000"; 0342 qApp->exit(9); 0343 return; 0344 } 0345 setSdrBrightness(output, brightness); 0346 } else if (ops.count() == 4 && subcmd == "wcg") { 0347 const QString _enable = ops[3].toLower(); 0348 if (_enable == "enable") { 0349 setWcgEnabled(output, true); 0350 } else if (_enable == "disable") { 0351 setWcgEnabled(output, false); 0352 } else { 0353 qCDebug(KSCREEN_DOCTOR) << "Wrong input: Only allowed values for wcg are \"enable\" and \"disable\""; 0354 qApp->exit(9); 0355 return; 0356 } 0357 } else if (ops.count() >= 4 && subcmd == "iccprofile") { 0358 QString profilePath = ops[3]; 0359 for (uint32_t i = 4; i < ops.size(); i++) { 0360 profilePath += "." + ops[i]; 0361 } 0362 output->setIccProfilePath(profilePath); 0363 m_changed = true; 0364 } else if (ops.count() >= 4 && subcmd == "sdrGamut") { 0365 const uint32_t wideness = ops[3].toUInt(); 0366 if (wideness > 100) { 0367 qCDebug(KSCREEN_DOCTOR) << "Wrong input: Allowed range for sdr wideness is 0 to 100"; 0368 qApp->exit(9); 0369 return; 0370 } 0371 output->setSdrGamutWideness(wideness / 100.0); 0372 m_changed = true; 0373 } else if (ops.count() >= 4 && subcmd == "maxBrightnessOverride") { 0374 if (ops[3] == "disable") { 0375 output->setMaxPeakBrightnessOverride(std::nullopt); 0376 } else if (const uint32_t nits = ops[3].toUInt(); nits != 0) { 0377 output->setMaxPeakBrightnessOverride(nits); 0378 } else { 0379 qCDebug(KSCREEN_DOCTOR) << "Wrong input: max brightness must be bigger than 0"; 0380 qApp->exit(9); 0381 return; 0382 } 0383 m_changed = true; 0384 } else if (ops.count() >= 4 && subcmd == "maxAverageBrightnessOverride") { 0385 if (ops[3] == "disable") { 0386 output->setMaxPeakBrightnessOverride(std::nullopt); 0387 } else if (const uint32_t nits = ops[3].toUInt(); nits != 0) { 0388 output->setMaxAverageBrightnessOverride(nits); 0389 } else { 0390 qCDebug(KSCREEN_DOCTOR) << "Wrong input: max average brightness must be bigger than 0"; 0391 qApp->exit(9); 0392 return; 0393 } 0394 m_changed = true; 0395 } else if (ops.count() >= 4 && subcmd == "minBrightnessOverride") { 0396 if (ops[3] == "disable") { 0397 output->setMinBrightnessOverride(std::nullopt); 0398 } else if (const uint32_t nits10k = ops[3].toUInt(); nits10k != 0) { 0399 output->setMinBrightnessOverride(nits10k / 10'000.0); 0400 } else { 0401 qCDebug(KSCREEN_DOCTOR) << "Wrong input: max average brightness must be bigger than 0"; 0402 qApp->exit(9); 0403 return; 0404 } 0405 m_changed = true; 0406 } else { 0407 cerr << "Unable to parse arguments: " << op << Qt::endl; 0408 qApp->exit(2); 0409 return; 0410 } 0411 } 0412 } 0413 } 0414 } 0415 0416 void Doctor::configReceived(KScreen::ConfigOperation *op) 0417 { 0418 m_config = op->config(); 0419 0420 if (!m_config) { 0421 qCWarning(KSCREEN_DOCTOR) << "Invalid config."; 0422 return; 0423 } 0424 0425 if (m_parser->isSet(QStringLiteral("json"))) { 0426 showJson(); 0427 qApp->quit(); 0428 } 0429 if (m_parser->isSet(QStringLiteral("outputs"))) { 0430 showOutputs(); 0431 qApp->quit(); 0432 } 0433 0434 parseOutputArgs(); 0435 0436 if (m_changed) { 0437 applyConfig(); 0438 m_changed = false; 0439 } 0440 } 0441 0442 void Doctor::showOutputs() const 0443 { 0444 QHash<KScreen::Output::Type, QString> typeString; 0445 typeString[KScreen::Output::Unknown] = QStringLiteral("Unknown"); 0446 typeString[KScreen::Output::VGA] = QStringLiteral("VGA"); 0447 typeString[KScreen::Output::DVI] = QStringLiteral("DVI"); 0448 typeString[KScreen::Output::DVII] = QStringLiteral("DVII"); 0449 typeString[KScreen::Output::DVIA] = QStringLiteral("DVIA"); 0450 typeString[KScreen::Output::DVID] = QStringLiteral("DVID"); 0451 typeString[KScreen::Output::HDMI] = QStringLiteral("HDMI"); 0452 typeString[KScreen::Output::Panel] = QStringLiteral("Panel"); 0453 typeString[KScreen::Output::TV] = QStringLiteral("TV"); 0454 typeString[KScreen::Output::TVComposite] = QStringLiteral("TVComposite"); 0455 typeString[KScreen::Output::TVSVideo] = QStringLiteral("TVSVideo"); 0456 typeString[KScreen::Output::TVComponent] = QStringLiteral("TVComponent"); 0457 typeString[KScreen::Output::TVSCART] = QStringLiteral("TVSCART"); 0458 typeString[KScreen::Output::TVC4] = QStringLiteral("TVC4"); 0459 typeString[KScreen::Output::DisplayPort] = QStringLiteral("DisplayPort"); 0460 0461 QCollator collator; 0462 collator.setNumericMode(true); 0463 0464 for (const auto &output : m_config->outputs()) { 0465 const auto endl = '\n'; 0466 cout << green << "Output: " << cr << output->id() << " " << output->name() << endl; 0467 cout << "\t" << (output->isEnabled() ? green + QStringLiteral("enabled") : red + QStringLiteral("disabled")) << cr << endl; 0468 cout << "\t" << (output->isConnected() ? green + QStringLiteral("connected") : red + QStringLiteral("disconnected")) << cr << endl; 0469 cout << "\t" << (output->isEnabled() ? green : red) + QStringLiteral("priority ") << output->priority() << cr << endl; 0470 auto _type = typeString[output->type()]; 0471 cout << "\t" << yellow << (_type.isEmpty() ? QStringLiteral("UnmappedOutputType") : _type) << endl; 0472 cout << blue << "\tModes: " << cr; 0473 0474 const auto modes = output->modes(); 0475 auto modeKeys = modes.keys(); 0476 std::sort(modeKeys.begin(), modeKeys.end(), collator); 0477 0478 for (const auto &key : modeKeys) { 0479 auto mode = *modes.find(key); 0480 0481 auto name = QStringLiteral("%1x%2@%3") 0482 .arg(QString::number(mode->size().width()), QString::number(mode->size().height()), QString::number(qRound(mode->refreshRate()))); 0483 if (mode == output->currentMode()) { 0484 name = green + name + QLatin1Char('*') + cr; 0485 } 0486 if (mode == output->preferredMode()) { 0487 name = name + QLatin1Char('!'); 0488 } 0489 cout << " " << mode->id() << ":" << name << " "; 0490 } 0491 cout << endl; 0492 const auto g = output->geometry(); 0493 cout << yellow << "\tGeometry: " << cr << g.x() << "," << g.y() << " " << g.width() << "x" << g.height() << endl; 0494 cout << yellow << "\tScale: " << cr << output->scale() << endl; 0495 cout << yellow << "\tRotation: " << cr << output->rotation() << endl; 0496 cout << yellow << "\tOverscan: " << cr << output->overscan() << endl; 0497 cout << yellow << "\tVrr: "; 0498 if (output->capabilities() & Output::Capability::Vrr) { 0499 switch (output->vrrPolicy()) { 0500 case Output::VrrPolicy::Never: 0501 cout << cr << "Never" << endl; 0502 break; 0503 case Output::VrrPolicy::Automatic: 0504 cout << cr << "Automatic" << endl; 0505 break; 0506 case Output::VrrPolicy::Always: 0507 cout << cr << "Always" << endl; 0508 } 0509 } else { 0510 cout << cr << "incapable" << endl; 0511 } 0512 cout << yellow << "\tRgbRange: "; 0513 if (output->capabilities() & Output::Capability::RgbRange) { 0514 switch (output->rgbRange()) { 0515 case Output::RgbRange::Automatic: 0516 cout << cr << "Automatic" << endl; 0517 break; 0518 case Output::RgbRange::Full: 0519 cout << cr << "Full" << endl; 0520 break; 0521 case Output::RgbRange::Limited: 0522 cout << cr << "Limited" << endl; 0523 } 0524 } else { 0525 cout << cr << "unknown" << endl; 0526 } 0527 cout << yellow << "\tHDR: "; 0528 if (output->capabilities() & Output::Capability::HighDynamicRange) { 0529 if (output->isHdrEnabled()) { 0530 cout << cr << "enabled" << endl; 0531 cout << yellow << "\t\tSDR brightness: " << cr << output->sdrBrightness() << " nits" << endl; 0532 cout << yellow << "\t\tSDR gamut wideness: " << cr << std::round(output->sdrGamutWideness() * 100) << "%" << endl; 0533 cout << yellow << "\t\tPeak brightness: " << cr << output->maxPeakBrightness() << " nits"; 0534 if (const auto used = output->maxPeakBrightnessOverride()) { 0535 cout << yellow << ", overridden with: " << cr << *used << " nits"; 0536 } 0537 cout << endl; 0538 cout << yellow << "\t\tMax average brightness: " << cr << output->maxAverageBrightness() << " nits"; 0539 if (const auto used = output->maxAverageBrightnessOverride()) { 0540 cout << yellow << ", overridden with: " << cr << *used << " nits"; 0541 } 0542 cout << endl; 0543 cout << yellow << "\t\tMin brightness: " << cr << output->minBrightness() << " nits"; 0544 if (const auto used = output->minBrightnessOverride()) { 0545 cout << yellow << ", overridden with: " << cr << (*used) / 10'000.0 << " nits"; 0546 } 0547 cout << endl; 0548 } else { 0549 cout << cr << "disabled" << endl; 0550 } 0551 } else { 0552 cout << cr << "incapable" << endl; 0553 } 0554 cout << yellow << "\tWide Color Gamut: "; 0555 if (output->capabilities() & Output::Capability::WideColorGamut) { 0556 if (output->isWcgEnabled()) { 0557 cout << cr << "enabled" << endl; 0558 } else { 0559 cout << cr << "disabled" << endl; 0560 } 0561 } else { 0562 cout << cr << "incapable" << endl; 0563 } 0564 cout << yellow << "\tICC profile: "; 0565 if (output->capabilities() & Output::Capability::IccProfile) { 0566 if (!output->iccProfilePath().isEmpty()) { 0567 cout << cr << output->iccProfilePath() << endl; 0568 } else { 0569 cout << cr << "none" << endl; 0570 } 0571 } else { 0572 cout << cr << "incapable" << endl; 0573 } 0574 } 0575 } 0576 0577 void Doctor::showJson() const 0578 { 0579 QJsonDocument doc(KScreen::ConfigSerializer::serializeConfig(m_config)); 0580 cout << doc.toJson(QJsonDocument::Indented); 0581 } 0582 0583 void Doctor::setEnabled(OutputPtr output, bool enable) 0584 { 0585 cout << (enable ? "Enabling " : "Disabling ") << "output " << output->id() << Qt::endl; 0586 output->setEnabled(enable); 0587 m_changed = true; 0588 } 0589 0590 void Doctor::setPosition(OutputPtr output, const QPoint &pos) 0591 { 0592 qCDebug(KSCREEN_DOCTOR) << "Set output position" << pos; 0593 output->setPos(pos); 0594 m_changed = true; 0595 } 0596 0597 KScreen::ModePtr Doctor::findMode(OutputPtr output, const QString &query) 0598 { 0599 for (const KScreen::ModePtr &mode : output->modes()) { 0600 auto name = QStringLiteral("%1x%2@%3") 0601 .arg(QString::number(mode->size().width()), QString::number(mode->size().height()), QString::number(qRound(mode->refreshRate()))); 0602 if (mode->id() == query || name == query) { 0603 qCDebug(KSCREEN_DOCTOR) << "Taddaaa! Found mode" << mode->id() << name; 0604 return mode; 0605 } 0606 } 0607 cout << "Output mode " << query << " not found." << Qt::endl; 0608 return ModePtr(); 0609 } 0610 0611 bool Doctor::setMode(OutputPtr output, const QString &query) 0612 { 0613 // find mode 0614 const KScreen::ModePtr mode = findMode(output, query); 0615 if (!mode) { 0616 return false; 0617 } 0618 output->setCurrentModeId(mode->id()); 0619 m_changed = true; 0620 return true; 0621 } 0622 0623 void Doctor::setScale(OutputPtr output, qreal scale) 0624 { 0625 output->setScale(scale); 0626 m_changed = true; 0627 } 0628 0629 void Doctor::setRotation(OutputPtr output, KScreen::Output::Rotation rot) 0630 { 0631 output->setRotation(rot); 0632 m_changed = true; 0633 } 0634 0635 void Doctor::setOverscan(OutputPtr output, uint32_t overscan) 0636 { 0637 output->setOverscan(overscan); 0638 m_changed = true; 0639 } 0640 0641 void Doctor::setVrrPolicy(OutputPtr output, KScreen::Output::VrrPolicy policy) 0642 { 0643 output->setVrrPolicy(policy); 0644 m_changed = true; 0645 } 0646 0647 void Doctor::setRgbRange(OutputPtr output, KScreen::Output::RgbRange rgbRange) 0648 { 0649 output->setRgbRange(rgbRange); 0650 m_changed = true; 0651 } 0652 0653 void KScreen::Doctor::setPrimary(OutputPtr output) 0654 { 0655 setPriority(output, 1); 0656 } 0657 0658 void KScreen::Doctor::setPriority(OutputPtr output, uint32_t priority) 0659 { 0660 m_config->setOutputPriority(output, priority); 0661 m_changed = true; 0662 } 0663 0664 void Doctor::setHdrEnabled(OutputPtr output, bool enable) 0665 { 0666 output->setHdrEnabled(enable); 0667 m_changed = true; 0668 } 0669 0670 void Doctor::setSdrBrightness(OutputPtr output, uint32_t brightness) 0671 { 0672 output->setSdrBrightness(brightness); 0673 m_changed = true; 0674 } 0675 0676 void Doctor::setWcgEnabled(OutputPtr output, bool enable) 0677 { 0678 output->setWcgEnabled(enable); 0679 m_changed = true; 0680 } 0681 0682 void Doctor::applyConfig() 0683 { 0684 if (!m_changed) { 0685 return; 0686 } 0687 auto setop = new SetConfigOperation(m_config, this); 0688 setop->exec(); 0689 qCDebug(KSCREEN_DOCTOR) << "setop exec returned" << m_config; 0690 qApp->exit(0); 0691 } 0692 0693 #include "moc_doctor.cpp"