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