File indexing completed on 2024-04-28 05:31:29
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> 0003 * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "output.h" 0009 #include "edid.h" 0010 #include "kscreen_debug.h" 0011 #include "mode.h" 0012 0013 #include <QCryptographicHash> 0014 #include <QGuiApplication> 0015 #include <QRect> 0016 #include <QScopedPointer> 0017 0018 #include <cstdint> 0019 #include <qobjectdefs.h> 0020 #include <utility> 0021 0022 using namespace KScreen; 0023 0024 class Q_DECL_HIDDEN Output::Private 0025 { 0026 public: 0027 Private() 0028 : id(0) 0029 , type(Unknown) 0030 , rotation(None) 0031 , connected(false) 0032 , enabled(false) 0033 , priority(0) 0034 , replicationSource(0) 0035 , edid(nullptr) 0036 , scale(1.0) 0037 , explicitLogicalSize(QSizeF()) 0038 { 0039 } 0040 0041 Private(const Private &other) 0042 : id(other.id) 0043 , name(other.name) 0044 , type(other.type) 0045 , icon(other.icon) 0046 , pos(other.pos) 0047 , size(other.size) 0048 , rotation(other.rotation) 0049 , currentMode(other.currentMode) 0050 , preferredMode(other.preferredMode) 0051 , preferredModes(other.preferredModes) 0052 , connected(other.connected) 0053 , enabled(other.enabled) 0054 , priority(other.priority) 0055 , clones(other.clones) 0056 , replicationSource(other.replicationSource) 0057 , sizeMm(other.sizeMm) 0058 , scale(other.scale) 0059 , followPreferredMode(other.followPreferredMode) 0060 , explicitLogicalSize(other.explicitLogicalSize) 0061 , capabilities(other.capabilities) 0062 , overscan(other.overscan) 0063 , vrrPolicy(other.vrrPolicy) 0064 , rgbRange(other.rgbRange) 0065 , highDynamicRange(other.highDynamicRange) 0066 , sdrBrightness(other.sdrBrightness) 0067 , wideColorGamut(other.wideColorGamut) 0068 , autoRotatePolicy(other.autoRotatePolicy) 0069 , iccProfilePath(other.iccProfilePath) 0070 , sdrGamutWideness(other.sdrGamutWideness) 0071 , maxPeakBrightness(other.maxPeakBrightness) 0072 , maxAverageBrightness(other.maxAverageBrightness) 0073 , minBrightness(other.minBrightness) 0074 , maxPeakBrightnessOverride(other.maxPeakBrightnessOverride) 0075 , maxAverageBrightnessOverride(other.maxAverageBrightnessOverride) 0076 , minBrightnessOverride(other.minBrightnessOverride) 0077 { 0078 const auto otherModeList = other.modeList; 0079 for (const ModePtr &otherMode : otherModeList) { 0080 modeList.insert(otherMode->id(), otherMode->clone()); 0081 } 0082 if (other.edid) { 0083 edid.reset(other.edid->clone()); 0084 } 0085 } 0086 0087 QString biggestMode(const ModeList &modes) const; 0088 bool compareModeList(const ModeList &before, const ModeList &after); 0089 0090 // please keep them consistent with order of Q_PROPERTY declarations 0091 int id; 0092 QString name; 0093 Type type; 0094 QString icon; 0095 ModeList modeList; 0096 QPoint pos; 0097 QSize size; 0098 Rotation rotation; 0099 // next three don't exactly match properties by name, but keep them close to each other anyway 0100 QString currentMode; 0101 QString preferredMode; 0102 QStringList preferredModes; 0103 // 0104 bool connected; 0105 bool enabled; 0106 uint32_t priority; 0107 QList<int> clones; 0108 int replicationSource; 0109 QScopedPointer<Edid> edid; 0110 QSize sizeMm; 0111 qreal scale; 0112 bool followPreferredMode = false; 0113 QSizeF explicitLogicalSize; 0114 Capabilities capabilities; 0115 uint32_t overscan = 0; 0116 VrrPolicy vrrPolicy = VrrPolicy::Automatic; 0117 RgbRange rgbRange = RgbRange::Automatic; 0118 bool highDynamicRange = false; 0119 uint32_t sdrBrightness = 200; 0120 bool wideColorGamut = false; 0121 AutoRotatePolicy autoRotatePolicy = AutoRotatePolicy::InTabletMode; 0122 QString iccProfilePath; 0123 double sdrGamutWideness = 0; 0124 double maxPeakBrightness = 0; 0125 double maxAverageBrightness = 0; 0126 double minBrightness = 0; 0127 std::optional<double> maxPeakBrightnessOverride; 0128 std::optional<double> maxAverageBrightnessOverride; 0129 std::optional<double> minBrightnessOverride; 0130 }; 0131 0132 bool Output::Private::compareModeList(const ModeList &before, const ModeList &after) 0133 { 0134 if (before.count() != after.count()) { 0135 return false; 0136 } 0137 0138 for (auto itb = before.constBegin(); itb != before.constEnd(); ++itb) { 0139 auto ita = after.constFind(itb.key()); 0140 if (ita == after.constEnd()) { 0141 return false; 0142 } 0143 const auto &mb = itb.value(); 0144 const auto &ma = ita.value(); 0145 if (mb->id() != ma->id()) { 0146 return false; 0147 } 0148 if (mb->size() != ma->size()) { 0149 return false; 0150 } 0151 if (!qFuzzyCompare(mb->refreshRate(), ma->refreshRate())) { 0152 return false; 0153 } 0154 if (mb->name() != ma->name()) { 0155 return false; 0156 } 0157 } 0158 // They're the same 0159 return true; 0160 } 0161 0162 QString Output::Private::biggestMode(const ModeList &modes) const 0163 { 0164 int area, total = 0; 0165 KScreen::ModePtr biggest; 0166 for (const KScreen::ModePtr &mode : modes) { 0167 area = mode->size().width() * mode->size().height(); 0168 if (area < total) { 0169 continue; 0170 } 0171 if (area == total && mode->refreshRate() < biggest->refreshRate()) { 0172 continue; 0173 } 0174 if (area == total && mode->refreshRate() > biggest->refreshRate()) { 0175 biggest = mode; 0176 continue; 0177 } 0178 0179 total = area; 0180 biggest = mode; 0181 } 0182 0183 if (!biggest) { 0184 return QString(); 0185 } 0186 0187 return biggest->id(); 0188 } 0189 0190 Output::Output() 0191 : QObject(nullptr) 0192 , d(new Private()) 0193 { 0194 } 0195 0196 Output::Output(Output::Private *dd) 0197 : QObject() 0198 , d(dd) 0199 { 0200 } 0201 0202 Output::~Output() 0203 { 0204 delete d; 0205 } 0206 0207 OutputPtr Output::clone() const 0208 { 0209 return OutputPtr(new Output(new Private(*d))); 0210 } 0211 0212 int Output::id() const 0213 { 0214 return d->id; 0215 } 0216 0217 void Output::setId(int id) 0218 { 0219 if (d->id == id) { 0220 return; 0221 } 0222 d->id = id; 0223 Q_EMIT outputChanged(); 0224 } 0225 0226 QString Output::name() const 0227 { 0228 return d->name; 0229 } 0230 0231 void Output::setName(const QString &name) 0232 { 0233 if (d->name == name) { 0234 return; 0235 } 0236 d->name = name; 0237 Q_EMIT outputChanged(); 0238 } 0239 0240 // TODO KF6: remove this deprecated method 0241 QString Output::hash() const 0242 { 0243 if (edid() && edid()->isValid()) { 0244 return edid()->hash(); 0245 } 0246 return name(); 0247 } 0248 0249 QString Output::hashMd5() const 0250 { 0251 if (edid() && edid()->isValid()) { 0252 return edid()->hash(); 0253 } 0254 const auto hash = QCryptographicHash::hash(name().toLatin1(), QCryptographicHash::Md5); 0255 return QString::fromLatin1(hash.toHex()); 0256 } 0257 0258 Output::Type Output::type() const 0259 { 0260 return d->type; 0261 } 0262 0263 void Output::setType(Type type) 0264 { 0265 if (d->type == type) { 0266 return; 0267 } 0268 d->type = type; 0269 Q_EMIT outputChanged(); 0270 } 0271 0272 QString Output::typeName() const 0273 { 0274 switch (d->type) { 0275 case Output::Unknown: 0276 return QStringLiteral("Unknown"); 0277 case Output::Panel: 0278 return QStringLiteral("Panel (Laptop)"); 0279 case Output::VGA: 0280 return QStringLiteral("VGA"); 0281 case Output::DVI: 0282 return QStringLiteral("DVI"); 0283 case Output::DVII: 0284 return QStringLiteral("DVI-I"); 0285 case Output::DVIA: 0286 return QStringLiteral("DVI-A"); 0287 case Output::DVID: 0288 return QStringLiteral("DVI-D"); 0289 case Output::HDMI: 0290 return QStringLiteral("HDMI"); 0291 case Output::TV: 0292 return QStringLiteral("TV"); 0293 case Output::TVComposite: 0294 return QStringLiteral("TV-Composite"); 0295 case Output::TVSVideo: 0296 return QStringLiteral("TV-SVideo"); 0297 case Output::TVComponent: 0298 return QStringLiteral("TV-Component"); 0299 case Output::TVSCART: 0300 return QStringLiteral("TV-SCART"); 0301 case Output::TVC4: 0302 return QStringLiteral("TV-C4"); 0303 case Output::DisplayPort: 0304 return QStringLiteral("DisplayPort"); 0305 }; 0306 return QStringLiteral("Invalid Type") + QString::number(d->type); 0307 } 0308 0309 QString Output::icon() const 0310 { 0311 return d->icon; 0312 } 0313 0314 void Output::setIcon(const QString &icon) 0315 { 0316 if (d->icon == icon) { 0317 return; 0318 } 0319 d->icon = icon; 0320 Q_EMIT outputChanged(); 0321 } 0322 0323 ModePtr Output::mode(const QString &id) const 0324 { 0325 if (!d->modeList.contains(id)) { 0326 return ModePtr(); 0327 } 0328 0329 return d->modeList[id]; 0330 } 0331 0332 ModeList Output::modes() const 0333 { 0334 return d->modeList; 0335 } 0336 0337 void Output::setModes(const ModeList &modes) 0338 { 0339 bool changed = !d->compareModeList(d->modeList, modes); 0340 d->modeList = modes; 0341 if (changed) { 0342 Q_EMIT modesChanged(); 0343 Q_EMIT outputChanged(); 0344 } 0345 } 0346 0347 QString Output::currentModeId() const 0348 { 0349 return d->currentMode; 0350 } 0351 0352 void Output::setCurrentModeId(const QString &mode) 0353 { 0354 if (d->currentMode == mode) { 0355 return; 0356 } 0357 d->currentMode = mode; 0358 Q_EMIT currentModeIdChanged(); 0359 } 0360 0361 ModePtr Output::currentMode() const 0362 { 0363 return d->modeList.value(d->currentMode); 0364 } 0365 0366 void Output::setPreferredModes(const QStringList &modes) 0367 { 0368 d->preferredMode = QString(); 0369 d->preferredModes = modes; 0370 } 0371 0372 QStringList Output::preferredModes() const 0373 { 0374 return d->preferredModes; 0375 } 0376 0377 QString Output::preferredModeId() const 0378 { 0379 if (!d->preferredMode.isEmpty()) { 0380 return d->preferredMode; 0381 } 0382 if (d->preferredModes.isEmpty()) { 0383 return d->biggestMode(modes()); 0384 } 0385 0386 int total = 0; 0387 KScreen::ModePtr biggest; 0388 KScreen::ModePtr candidateMode; 0389 for (const QString &modeId : std::as_const(d->preferredModes)) { 0390 candidateMode = mode(modeId); 0391 const int area = candidateMode->size().width() * candidateMode->size().height(); 0392 if (area < total) { 0393 continue; 0394 } 0395 if (area == total && biggest && candidateMode->refreshRate() < biggest->refreshRate()) { 0396 continue; 0397 } 0398 if (area == total && biggest && candidateMode->refreshRate() > biggest->refreshRate()) { 0399 biggest = candidateMode; 0400 continue; 0401 } 0402 0403 total = area; 0404 biggest = candidateMode; 0405 } 0406 0407 Q_ASSERT_X(biggest, "preferredModeId", "biggest mode must exist"); 0408 0409 d->preferredMode = biggest->id(); 0410 return d->preferredMode; 0411 } 0412 0413 ModePtr Output::preferredMode() const 0414 { 0415 return d->modeList.value(preferredModeId()); 0416 } 0417 0418 QPoint Output::pos() const 0419 { 0420 return d->pos; 0421 } 0422 0423 void Output::setPos(const QPoint &pos) 0424 { 0425 if (d->pos == pos) { 0426 return; 0427 } 0428 d->pos = pos; 0429 Q_EMIT posChanged(); 0430 } 0431 0432 QSize Output::size() const 0433 { 0434 return d->size; 0435 } 0436 0437 void Output::setSize(const QSize &size) 0438 { 0439 if (d->size == size) { 0440 return; 0441 } 0442 d->size = size; 0443 Q_EMIT sizeChanged(); 0444 } 0445 0446 // TODO KF6: make the Rotation enum an enum class and align values with Wayland transformation property 0447 Output::Rotation Output::rotation() const 0448 { 0449 return d->rotation; 0450 } 0451 0452 void Output::setRotation(Output::Rotation rotation) 0453 { 0454 if (d->rotation == rotation) { 0455 return; 0456 } 0457 d->rotation = rotation; 0458 Q_EMIT rotationChanged(); 0459 } 0460 0461 qreal Output::scale() const 0462 { 0463 return d->scale; 0464 } 0465 0466 void Output::setScale(qreal factor) 0467 { 0468 if (qFuzzyCompare(d->scale, factor)) { 0469 return; 0470 } 0471 d->scale = factor; 0472 Q_EMIT scaleChanged(); 0473 } 0474 0475 QSizeF Output::explicitLogicalSize() const 0476 { 0477 return d->explicitLogicalSize; 0478 } 0479 0480 QSize Output::explicitLogicalSizeInt() const 0481 { 0482 const QSizeF sizeF = explicitLogicalSize(); 0483 return QSize(std::ceil(sizeF.width()), std::ceil(sizeF.height())); 0484 } 0485 0486 void Output::setExplicitLogicalSize(const QSizeF &size) 0487 { 0488 if (qFuzzyCompare(d->explicitLogicalSize.width(), size.width()) && qFuzzyCompare(d->explicitLogicalSize.height(), size.height())) { 0489 return; 0490 } 0491 d->explicitLogicalSize = size; 0492 Q_EMIT explicitLogicalSizeChanged(); 0493 } 0494 0495 bool Output::isConnected() const 0496 { 0497 return d->connected; 0498 } 0499 0500 void Output::setConnected(bool connected) 0501 { 0502 if (d->connected == connected) { 0503 return; 0504 } 0505 d->connected = connected; 0506 Q_EMIT isConnectedChanged(); 0507 } 0508 0509 bool Output::isEnabled() const 0510 { 0511 return d->enabled; 0512 } 0513 0514 void Output::setEnabled(bool enabled) 0515 { 0516 if (d->enabled == enabled) { 0517 return; 0518 } 0519 d->enabled = enabled; 0520 Q_EMIT isEnabledChanged(); 0521 } 0522 0523 bool Output::isPrimary() const 0524 { 0525 return d->enabled && (d->priority == 1); 0526 } 0527 0528 void Output::setPrimary(bool primary) 0529 { 0530 if (primary) { 0531 setPriority(1); 0532 } else { 0533 qCWarning(KSCREEN) << "Calling Output::setPrimary(false) is not supported. Port your code to Config::setPrimaryOutput"; 0534 } 0535 } 0536 0537 uint32_t Output::priority() const 0538 { 0539 return d->priority; 0540 } 0541 0542 void Output::setPriority(uint32_t priority) 0543 { 0544 if (d->priority == priority) { 0545 return; 0546 } 0547 d->priority = priority; 0548 Q_EMIT priorityChanged(); 0549 } 0550 0551 QList<int> Output::clones() const 0552 { 0553 return d->clones; 0554 } 0555 0556 void Output::setClones(const QList<int> &outputlist) 0557 { 0558 if (d->clones == outputlist) { 0559 return; 0560 } 0561 d->clones = outputlist; 0562 Q_EMIT clonesChanged(); 0563 } 0564 0565 int Output::replicationSource() const 0566 { 0567 return d->replicationSource; 0568 } 0569 0570 void Output::setReplicationSource(int source) 0571 { 0572 if (d->replicationSource == source) { 0573 return; 0574 } 0575 d->replicationSource = source; 0576 Q_EMIT replicationSourceChanged(); 0577 } 0578 0579 void Output::setEdid(const QByteArray &rawData) 0580 { 0581 Q_ASSERT(d->edid.isNull()); 0582 d->edid.reset(new Edid(rawData)); 0583 } 0584 0585 Edid *Output::edid() const 0586 { 0587 return d->edid.data(); 0588 } 0589 0590 QSize Output::sizeMm() const 0591 { 0592 return d->sizeMm; 0593 } 0594 0595 void Output::setSizeMm(const QSize &size) 0596 { 0597 d->sizeMm = size; 0598 } 0599 0600 bool KScreen::Output::followPreferredMode() const 0601 { 0602 return d->followPreferredMode; 0603 } 0604 0605 void KScreen::Output::setFollowPreferredMode(bool follow) 0606 { 0607 if (follow == d->followPreferredMode) { 0608 return; 0609 } 0610 d->followPreferredMode = follow; 0611 Q_EMIT followPreferredModeChanged(follow); 0612 } 0613 0614 bool Output::isPositionable() const 0615 { 0616 return isConnected() && isEnabled() && !replicationSource(); 0617 } 0618 0619 QSize Output::enforcedModeSize() const 0620 { 0621 if (const auto mode = currentMode()) { 0622 return mode->size(); 0623 } else if (const auto mode = preferredMode()) { 0624 return mode->size(); 0625 } else if (d->modeList.count() > 0) { 0626 return d->modeList.first()->size(); 0627 } 0628 return QSize(); 0629 } 0630 0631 QRect Output::geometry() const 0632 { 0633 QSize size = explicitLogicalSizeInt(); 0634 if (!size.isValid()) { 0635 return QRect(); 0636 } 0637 0638 return QRect(d->pos, size); 0639 } 0640 0641 Output::Capabilities Output::capabilities() const 0642 { 0643 return d->capabilities; 0644 } 0645 0646 void Output::setCapabilities(Capabilities capabilities) 0647 { 0648 if (d->capabilities == capabilities) { 0649 return; 0650 } 0651 d->capabilities = capabilities; 0652 Q_EMIT capabilitiesChanged(); 0653 } 0654 0655 uint32_t Output::overscan() const 0656 { 0657 return d->overscan; 0658 } 0659 0660 void Output::setOverscan(uint32_t overscan) 0661 { 0662 if (d->overscan == overscan) { 0663 return; 0664 } 0665 d->overscan = overscan; 0666 Q_EMIT overscanChanged(); 0667 } 0668 0669 Output::VrrPolicy Output::vrrPolicy() const 0670 { 0671 return d->vrrPolicy; 0672 } 0673 0674 void Output::setVrrPolicy(VrrPolicy policy) 0675 { 0676 if (d->vrrPolicy == policy) { 0677 return; 0678 } 0679 d->vrrPolicy = policy; 0680 Q_EMIT vrrPolicyChanged(); 0681 } 0682 0683 Output::RgbRange Output::rgbRange() const 0684 { 0685 return d->rgbRange; 0686 } 0687 0688 void Output::setRgbRange(Output::RgbRange rgbRange) 0689 { 0690 if (d->rgbRange == rgbRange) { 0691 return; 0692 } 0693 d->rgbRange = rgbRange; 0694 Q_EMIT rgbRangeChanged(); 0695 } 0696 0697 bool Output::isHdrEnabled() const 0698 { 0699 return d->highDynamicRange; 0700 } 0701 0702 void Output::setHdrEnabled(bool enable) 0703 { 0704 if (d->highDynamicRange != enable) { 0705 d->highDynamicRange = enable; 0706 Q_EMIT hdrEnabledChanged(); 0707 } 0708 } 0709 0710 uint32_t Output::sdrBrightness() const 0711 { 0712 return d->sdrBrightness; 0713 } 0714 0715 void Output::setSdrBrightness(uint32_t brightness) 0716 { 0717 if (d->sdrBrightness != brightness) { 0718 d->sdrBrightness = brightness; 0719 Q_EMIT sdrBrightnessChanged(); 0720 } 0721 } 0722 0723 bool Output::isWcgEnabled() const 0724 { 0725 return d->wideColorGamut; 0726 } 0727 0728 void Output::setWcgEnabled(bool enable) 0729 { 0730 if (d->wideColorGamut != enable) { 0731 d->wideColorGamut = enable; 0732 Q_EMIT wcgEnabledChanged(); 0733 } 0734 } 0735 0736 Output::AutoRotatePolicy Output::autoRotatePolicy() const 0737 { 0738 return d->autoRotatePolicy; 0739 } 0740 0741 void Output::setAutoRotatePolicy(AutoRotatePolicy policy) 0742 { 0743 if (d->autoRotatePolicy != policy) { 0744 d->autoRotatePolicy = policy; 0745 Q_EMIT autoRotatePolicyChanged(); 0746 } 0747 } 0748 0749 QString Output::iccProfilePath() const 0750 { 0751 return d->iccProfilePath; 0752 } 0753 0754 void Output::setIccProfilePath(const QString &path) 0755 { 0756 if (d->iccProfilePath != path) { 0757 d->iccProfilePath = path; 0758 Q_EMIT iccProfilePathChanged(); 0759 } 0760 } 0761 0762 double Output::sdrGamutWideness() const 0763 { 0764 return d->sdrGamutWideness; 0765 } 0766 0767 void Output::setSdrGamutWideness(double value) 0768 { 0769 if (d->sdrGamutWideness != value) { 0770 d->sdrGamutWideness = value; 0771 Q_EMIT sdrGamutWidenessChanged(); 0772 } 0773 } 0774 0775 double Output::maxPeakBrightness() const 0776 { 0777 return d->maxPeakBrightness; 0778 } 0779 0780 void Output::setMaxPeakBrightness(double value) 0781 { 0782 if (d->maxPeakBrightness != value) { 0783 d->maxPeakBrightness = value; 0784 Q_EMIT maxPeakBrightnessChanged(); 0785 } 0786 } 0787 0788 double Output::maxAverageBrightness() const 0789 { 0790 return d->maxAverageBrightness; 0791 } 0792 0793 void Output::setMaxAverageBrightness(double value) 0794 { 0795 if (d->maxAverageBrightness != value) { 0796 d->maxAverageBrightness = value; 0797 Q_EMIT maxAverageBrightnessChanged(); 0798 } 0799 } 0800 0801 double Output::minBrightness() const 0802 { 0803 return d->minBrightness; 0804 } 0805 0806 void Output::setMinBrightness(double value) 0807 { 0808 if (d->minBrightness != value) { 0809 d->minBrightness = value; 0810 Q_EMIT minBrightnessChanged(); 0811 } 0812 } 0813 0814 std::optional<double> Output::maxPeakBrightnessOverride() const 0815 { 0816 return d->maxPeakBrightnessOverride; 0817 } 0818 0819 void Output::setMaxPeakBrightnessOverride(std::optional<double> value) 0820 { 0821 if (d->maxPeakBrightnessOverride != value) { 0822 d->maxPeakBrightnessOverride = value; 0823 Q_EMIT maxPeakBrightnessOverrideChanged(); 0824 } 0825 } 0826 0827 std::optional<double> Output::maxAverageBrightnessOverride() const 0828 { 0829 return d->maxAverageBrightnessOverride; 0830 } 0831 0832 void Output::setMaxAverageBrightnessOverride(std::optional<double> value) 0833 { 0834 if (d->maxAverageBrightnessOverride != value) { 0835 d->maxAverageBrightnessOverride = value; 0836 Q_EMIT maxAverageBrightnessOverrideChanged(); 0837 } 0838 } 0839 0840 std::optional<double> Output::minBrightnessOverride() const 0841 { 0842 return d->minBrightnessOverride; 0843 } 0844 0845 void Output::setMinBrightnessOverride(std::optional<double> value) 0846 { 0847 if (d->minBrightnessOverride != value) { 0848 d->minBrightnessOverride = value; 0849 Q_EMIT minBrightnessOverrideChanged(); 0850 } 0851 } 0852 0853 void Output::apply(const OutputPtr &other) 0854 { 0855 typedef void (KScreen::Output::*ChangeSignal)(); 0856 QList<ChangeSignal> changes; 0857 0858 // We block all signals, and emit them only after we have set up everything 0859 // This is necessary in order to prevent clients from accessing inconsistent 0860 // outputs from intermediate change signals 0861 const bool keepBlocked = blockSignals(true); 0862 if (d->name != other->d->name) { 0863 changes << &Output::outputChanged; 0864 setName(other->d->name); 0865 } 0866 if (d->type != other->d->type) { 0867 changes << &Output::outputChanged; 0868 setType(other->d->type); 0869 } 0870 if (d->icon != other->d->icon) { 0871 changes << &Output::outputChanged; 0872 setIcon(other->d->icon); 0873 } 0874 if (d->pos != other->d->pos) { 0875 changes << &Output::posChanged; 0876 setPos(other->pos()); 0877 } 0878 if (d->rotation != other->d->rotation) { 0879 changes << &Output::rotationChanged; 0880 setRotation(other->d->rotation); 0881 } 0882 if (!qFuzzyCompare(d->scale, other->d->scale)) { 0883 changes << &Output::scaleChanged; 0884 setScale(other->d->scale); 0885 } 0886 if (d->currentMode != other->d->currentMode) { 0887 changes << &Output::currentModeIdChanged; 0888 setCurrentModeId(other->d->currentMode); 0889 } 0890 if (d->connected != other->d->connected) { 0891 changes << &Output::isConnectedChanged; 0892 setConnected(other->d->connected); 0893 } 0894 if (d->enabled != other->d->enabled) { 0895 changes << &Output::isEnabledChanged; 0896 setEnabled(other->d->enabled); 0897 } 0898 if (d->priority != other->d->priority) { 0899 changes << &Output::priorityChanged; 0900 setPriority(other->d->priority); 0901 } 0902 if (d->clones != other->d->clones) { 0903 changes << &Output::clonesChanged; 0904 setClones(other->d->clones); 0905 } 0906 if (d->replicationSource != other->d->replicationSource) { 0907 changes << &Output::replicationSourceChanged; 0908 setReplicationSource(other->d->replicationSource); 0909 } 0910 if (!d->compareModeList(d->modeList, other->d->modeList)) { 0911 changes << &Output::outputChanged; 0912 changes << &Output::modesChanged; 0913 } 0914 0915 setPreferredModes(other->d->preferredModes); 0916 ModeList modes; 0917 for (const ModePtr &otherMode : other->modes()) { 0918 modes.insert(otherMode->id(), otherMode->clone()); 0919 } 0920 setModes(modes); 0921 0922 if (d->capabilities != other->d->capabilities) { 0923 changes << &Output::capabilitiesChanged; 0924 setCapabilities(other->d->capabilities); 0925 } 0926 if (d->vrrPolicy != other->d->vrrPolicy) { 0927 changes << &Output::vrrPolicyChanged; 0928 setVrrPolicy(other->d->vrrPolicy); 0929 } 0930 if (d->overscan != other->d->overscan) { 0931 changes << &Output::overscanChanged; 0932 setOverscan(other->d->overscan); 0933 } 0934 if (d->rgbRange != other->d->rgbRange) { 0935 changes << &Output::rgbRangeChanged; 0936 setRgbRange(other->d->rgbRange); 0937 } 0938 if (d->highDynamicRange != other->d->highDynamicRange) { 0939 changes << &Output::hdrEnabledChanged; 0940 setHdrEnabled(other->d->highDynamicRange); 0941 } 0942 if (d->sdrBrightness != other->d->sdrBrightness) { 0943 changes << &Output::sdrBrightnessChanged; 0944 setSdrBrightness(other->d->sdrBrightness); 0945 } 0946 if (d->wideColorGamut != other->d->wideColorGamut) { 0947 changes << &Output::wcgEnabledChanged; 0948 setWcgEnabled(other->d->wideColorGamut); 0949 } 0950 if (d->autoRotatePolicy != other->d->autoRotatePolicy) { 0951 changes << &Output::autoRotatePolicyChanged; 0952 setAutoRotatePolicy(other->d->autoRotatePolicy); 0953 } 0954 if (d->iccProfilePath != other->d->iccProfilePath) { 0955 changes << &Output::iccProfilePathChanged; 0956 setIccProfilePath(other->d->iccProfilePath); 0957 } 0958 if (d->sdrGamutWideness != other->d->sdrGamutWideness) { 0959 changes << &Output::sdrGamutWidenessChanged; 0960 setSdrGamutWideness(other->d->sdrGamutWideness); 0961 } 0962 if (d->maxPeakBrightness != other->d->maxPeakBrightness) { 0963 changes << &Output::maxPeakBrightnessChanged; 0964 setMaxPeakBrightness(other->d->maxPeakBrightness); 0965 } 0966 if (d->maxAverageBrightness != other->d->maxAverageBrightness) { 0967 changes << &Output::maxAverageBrightnessChanged; 0968 setMaxAverageBrightness(other->d->maxAverageBrightness); 0969 } 0970 if (d->minBrightness != other->d->minBrightness) { 0971 changes << &Output::minBrightnessChanged; 0972 setMinBrightness(other->d->minBrightness); 0973 } 0974 0975 if (d->maxPeakBrightnessOverride != other->d->maxPeakBrightnessOverride) { 0976 changes << &Output::maxPeakBrightnessOverrideChanged; 0977 setMaxPeakBrightnessOverride(other->d->maxPeakBrightnessOverride); 0978 } 0979 if (d->maxAverageBrightnessOverride != other->d->maxAverageBrightnessOverride) { 0980 changes << &Output::maxAverageBrightnessOverrideChanged; 0981 setMaxAverageBrightnessOverride(other->d->maxAverageBrightnessOverride); 0982 } 0983 if (d->minBrightnessOverride != other->d->minBrightnessOverride) { 0984 changes << &Output::minBrightnessOverrideChanged; 0985 setMinBrightnessOverride(other->d->minBrightnessOverride); 0986 } 0987 0988 // Non-notifyable changes 0989 if (other->d->edid) { 0990 d->edid.reset(other->d->edid->clone()); 0991 } 0992 0993 blockSignals(keepBlocked); 0994 0995 while (!changes.isEmpty()) { 0996 const ChangeSignal &sig = changes.first(); 0997 Q_EMIT(this->*sig)(); 0998 changes.removeAll(sig); 0999 } 1000 } 1001 1002 QDebug operator<<(QDebug dbg, const KScreen::OutputPtr &output) 1003 { 1004 QDebugStateSaver saver(dbg); 1005 if (!output) { 1006 dbg << "KScreen::Output(NULL)"; 1007 return dbg; 1008 } 1009 1010 // clang-format off 1011 dbg.nospace() 1012 << "KScreen::Output(" 1013 << output->id() << ", " 1014 << output->name() << ", " 1015 << (output->isConnected() ? "connected " : "disconnected ") 1016 << (output->isEnabled() ? "enabled" : "disabled") 1017 << " priority " << output->priority() 1018 << ", pos: " << output->pos() 1019 << ", res: " << output->size() 1020 << ", modeId: " << output->currentModeId() 1021 << ", scale: " << output->scale() 1022 << ", clone: " << (output->clones().isEmpty() ? "no" : "yes") 1023 << ", rotation: " << output->rotation() 1024 << ", followPreferredMode: " << output->followPreferredMode() 1025 << ")"; 1026 // clang-format on 1027 return dbg; 1028 } 1029 1030 #include "moc_output.cpp"