File indexing completed on 2024-05-19 05:31:35

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "output.h"
0011 #include "iccprofile.h"
0012 #include "outputconfiguration.h"
0013 
0014 #include <KConfigGroup>
0015 #include <KLocalizedString>
0016 #include <KSharedConfig>
0017 
0018 namespace KWin
0019 {
0020 
0021 QDebug operator<<(QDebug debug, const Output *output)
0022 {
0023     QDebugStateSaver saver(debug);
0024     debug.nospace();
0025     if (output) {
0026         debug << output->metaObject()->className() << '(' << static_cast<const void *>(output);
0027         debug << ", name=" << output->name();
0028         debug << ", geometry=" << output->geometry();
0029         debug << ", scale=" << output->scale();
0030         if (debug.verbosity() > 2) {
0031             debug << ", manufacturer=" << output->manufacturer();
0032             debug << ", model=" << output->model();
0033             debug << ", serialNumber=" << output->serialNumber();
0034         }
0035         debug << ')';
0036     } else {
0037         debug << "Output(0x0)";
0038     }
0039     return debug;
0040 }
0041 
0042 OutputMode::OutputMode(const QSize &size, uint32_t refreshRate, Flags flags)
0043     : m_size(size)
0044     , m_refreshRate(refreshRate)
0045     , m_flags(flags)
0046 {
0047 }
0048 
0049 QSize OutputMode::size() const
0050 {
0051     return m_size;
0052 }
0053 
0054 uint32_t OutputMode::refreshRate() const
0055 {
0056     return m_refreshRate;
0057 }
0058 
0059 OutputMode::Flags OutputMode::flags() const
0060 {
0061     return m_flags;
0062 }
0063 
0064 OutputTransform::Kind OutputTransform::kind() const
0065 {
0066     return m_kind;
0067 }
0068 
0069 OutputTransform OutputTransform::inverted() const
0070 {
0071     switch (m_kind) {
0072     case Kind::Normal:
0073         return Kind::Normal;
0074     case Kind::Rotate90:
0075         return Kind::Rotate270;
0076     case Kind::Rotate180:
0077         return Kind::Rotate180;
0078     case Kind::Rotate270:
0079         return Kind::Rotate90;
0080     case Kind::FlipX:
0081     case Kind::FlipX90:
0082     case Kind::FlipX180:
0083     case Kind::FlipX270:
0084         return m_kind; // inverse transform of a flip transform is itself
0085     }
0086 
0087     Q_UNREACHABLE();
0088 }
0089 
0090 QRectF OutputTransform::map(const QRectF &rect, const QSizeF &bounds) const
0091 {
0092     switch (m_kind) {
0093     case Kind::Normal:
0094         return rect;
0095     case Kind::Rotate90:
0096         return QRectF(rect.y(),
0097                       bounds.width() - (rect.x() + rect.width()),
0098                       rect.height(),
0099                       rect.width());
0100     case Kind::Rotate180:
0101         return QRectF(bounds.width() - (rect.x() + rect.width()),
0102                       bounds.height() - (rect.y() + rect.height()),
0103                       rect.width(),
0104                       rect.height());
0105     case Kind::Rotate270:
0106         return QRectF(bounds.height() - (rect.y() + rect.height()),
0107                       rect.x(),
0108                       rect.height(),
0109                       rect.width());
0110     case Kind::FlipX:
0111         return QRectF(bounds.width() - (rect.x() + rect.width()),
0112                       rect.y(),
0113                       rect.width(),
0114                       rect.height());
0115     case Kind::FlipX90:
0116         return QRectF(rect.y(),
0117                       rect.x(),
0118                       rect.height(),
0119                       rect.width());
0120     case Kind::FlipX180:
0121         return QRectF(rect.x(),
0122                       bounds.height() - (rect.y() + rect.height()),
0123                       rect.width(),
0124                       rect.height());
0125     case Kind::FlipX270:
0126         return QRectF(bounds.height() - (rect.y() + rect.height()),
0127                       bounds.width() - (rect.x() + rect.width()),
0128                       rect.height(),
0129                       rect.width());
0130     default:
0131         Q_UNREACHABLE();
0132     }
0133 }
0134 
0135 QRect OutputTransform::map(const QRect &rect, const QSize &bounds) const
0136 {
0137     switch (m_kind) {
0138     case Kind::Normal:
0139         return rect;
0140     case Kind::Rotate90:
0141         return QRect(rect.y(),
0142                      bounds.width() - (rect.x() + rect.width()),
0143                      rect.height(),
0144                      rect.width());
0145     case Kind::Rotate180:
0146         return QRect(bounds.width() - (rect.x() + rect.width()),
0147                      bounds.height() - (rect.y() + rect.height()),
0148                      rect.width(),
0149                      rect.height());
0150     case Kind::Rotate270:
0151         return QRect(bounds.height() - (rect.y() + rect.height()),
0152                      rect.x(),
0153                      rect.height(),
0154                      rect.width());
0155     case Kind::FlipX:
0156         return QRect(bounds.width() - (rect.x() + rect.width()),
0157                      rect.y(),
0158                      rect.width(),
0159                      rect.height());
0160     case Kind::FlipX90:
0161         return QRect(rect.y(),
0162                      rect.x(),
0163                      rect.height(),
0164                      rect.width());
0165     case Kind::FlipX180:
0166         return QRect(rect.x(),
0167                      bounds.height() - (rect.y() + rect.height()),
0168                      rect.width(),
0169                      rect.height());
0170     case Kind::FlipX270:
0171         return QRect(bounds.height() - (rect.y() + rect.height()),
0172                      bounds.width() - (rect.x() + rect.width()),
0173                      rect.height(),
0174                      rect.width());
0175     default:
0176         Q_UNREACHABLE();
0177     }
0178 }
0179 
0180 QPointF OutputTransform::map(const QPointF &point, const QSizeF &bounds) const
0181 {
0182     switch (m_kind) {
0183     case Kind::Normal:
0184         return point;
0185     case Kind::Rotate90:
0186         return QPointF(point.y(),
0187                        bounds.width() - point.x());
0188     case Kind::Rotate180:
0189         return QPointF(bounds.width() - point.x(),
0190                        bounds.height() - point.y());
0191     case Kind::Rotate270:
0192         return QPointF(bounds.height() - point.y(),
0193                        point.x());
0194     case Kind::FlipX:
0195         return QPointF(bounds.width() - point.x(),
0196                        point.y());
0197     case Kind::FlipX90:
0198         return QPointF(point.y(),
0199                        point.x());
0200     case Kind::FlipX180:
0201         return QPointF(point.x(),
0202                        bounds.height() - point.y());
0203     case Kind::FlipX270:
0204         return QPointF(bounds.height() - point.y(),
0205                        bounds.width() - point.x());
0206     default:
0207         Q_UNREACHABLE();
0208     }
0209 }
0210 
0211 QPoint OutputTransform::map(const QPoint &point, const QSize &bounds) const
0212 {
0213     switch (m_kind) {
0214     case Kind::Normal:
0215         return point;
0216     case Kind::Rotate90:
0217         return QPoint(point.y(),
0218                       bounds.width() - point.x());
0219     case Kind::Rotate180:
0220         return QPoint(bounds.width() - point.x(),
0221                       bounds.height() - point.y());
0222     case Kind::Rotate270:
0223         return QPoint(bounds.height() - point.y(),
0224                       point.x());
0225     case Kind::FlipX:
0226         return QPoint(bounds.width() - point.x(),
0227                       point.y());
0228     case Kind::FlipX90:
0229         return QPoint(point.y(),
0230                       point.x());
0231     case Kind::FlipX180:
0232         return QPoint(point.x(),
0233                       bounds.height() - point.y());
0234     case Kind::FlipX270:
0235         return QPoint(bounds.height() - point.y(),
0236                       bounds.width() - point.x());
0237     default:
0238         Q_UNREACHABLE();
0239     }
0240 }
0241 
0242 QSizeF OutputTransform::map(const QSizeF &size) const
0243 {
0244     switch (m_kind) {
0245     case Kind::Normal:
0246     case Kind::Rotate180:
0247     case Kind::FlipX:
0248     case Kind::FlipX180:
0249         return size;
0250     default:
0251         return size.transposed();
0252     }
0253 }
0254 
0255 QSize OutputTransform::map(const QSize &size) const
0256 {
0257     switch (m_kind) {
0258     case Kind::Normal:
0259     case Kind::Rotate180:
0260     case Kind::FlipX:
0261     case Kind::FlipX180:
0262         return size;
0263     default:
0264         return size.transposed();
0265     }
0266 }
0267 
0268 OutputTransform OutputTransform::combine(OutputTransform other) const
0269 {
0270     // Combining a rotate-N or flip-N (mirror-x | rotate-N) transform with a rotate-M
0271     // transform involves only adding rotation angles:
0272     //     rotate-N | rotate-M => rotate-(N + M)
0273     //     flip-N | rotate-M => mirror-x | rotate-N | rotate-M
0274     //         => mirror-x | rotate-(N + M)
0275     //         => flip-(N + M)
0276     //
0277     // rotate-N | mirror-x is the same as mirror-x | rotate-(360 - N). This can be used
0278     // to derive the resulting transform if the other transform flips the x axis
0279     //     rotate-N | flip-M => rotate-N | mirror-x | rotate-M
0280     //        => mirror-x | rotate-(360 - N + M)
0281     //        => flip-(M - N)
0282     //     flip-N | flip-M => mirror-x | rotate-N | mirror-x | rotate-M
0283     //         => mirror-x | mirror-x | rotate-(360 - N + M)
0284     //         => rotate-(360 - N + M)
0285     //         => rotate-(M - N)
0286     //
0287     // The remaining code here relies on the bit pattern of transform enums, i.e. the
0288     // lower two bits specify the rotation, the third bit indicates mirroring along the x axis.
0289 
0290     const int flip = (m_kind ^ other.m_kind) & 0x4;
0291     int rotate;
0292     if (other.m_kind & 0x4) {
0293         rotate = (other.m_kind - m_kind) & 0x3;
0294     } else {
0295         rotate = (m_kind + other.m_kind) & 0x3;
0296     }
0297     return OutputTransform(Kind(flip | rotate));
0298 }
0299 
0300 QMatrix4x4 OutputTransform::toMatrix() const
0301 {
0302     QMatrix4x4 matrix;
0303     switch (m_kind) {
0304     case Kind::Normal:
0305         break;
0306     case Kind::Rotate90:
0307         matrix.rotate(-90, 0, 0, 1);
0308         break;
0309     case Kind::Rotate180:
0310         matrix.rotate(-180, 0, 0, 1);
0311         break;
0312     case Kind::Rotate270:
0313         matrix.rotate(-270, 0, 0, 1);
0314         break;
0315     case Kind::FlipX:
0316         matrix.scale(-1, 1);
0317         break;
0318     case Kind::FlipX90:
0319         matrix.rotate(-90, 0, 0, 1);
0320         matrix.scale(-1, 1);
0321         break;
0322     case Kind::FlipX180:
0323         matrix.rotate(-180, 0, 0, 1);
0324         matrix.scale(-1, 1);
0325         break;
0326     case Kind::FlipX270:
0327         matrix.rotate(-270, 0, 0, 1);
0328         matrix.scale(-1, 1);
0329         break;
0330     default:
0331         Q_UNREACHABLE();
0332     }
0333     return matrix;
0334 }
0335 
0336 Output::Output(QObject *parent)
0337     : QObject(parent)
0338 {
0339 }
0340 
0341 Output::~Output()
0342 {
0343 }
0344 
0345 void Output::ref()
0346 {
0347     m_refCount++;
0348 }
0349 
0350 void Output::unref()
0351 {
0352     Q_ASSERT(m_refCount > 0);
0353     m_refCount--;
0354     if (m_refCount == 0) {
0355         delete this;
0356     }
0357 }
0358 
0359 QString Output::name() const
0360 {
0361     return m_information.name;
0362 }
0363 
0364 QUuid Output::uuid() const
0365 {
0366     return m_uuid;
0367 }
0368 
0369 OutputTransform Output::transform() const
0370 {
0371     return m_state.transform;
0372 }
0373 
0374 OutputTransform Output::manualTransform() const
0375 {
0376     return m_state.manualTransform;
0377 }
0378 
0379 QString Output::eisaId() const
0380 {
0381     return m_information.eisaId;
0382 }
0383 
0384 QString Output::manufacturer() const
0385 {
0386     return m_information.manufacturer;
0387 }
0388 
0389 QString Output::model() const
0390 {
0391     return m_information.model;
0392 }
0393 
0394 QString Output::serialNumber() const
0395 {
0396     return m_information.serialNumber;
0397 }
0398 
0399 bool Output::isInternal() const
0400 {
0401     return m_information.internal;
0402 }
0403 
0404 void Output::inhibitDirectScanout()
0405 {
0406     m_directScanoutCount++;
0407 }
0408 
0409 void Output::uninhibitDirectScanout()
0410 {
0411     m_directScanoutCount--;
0412 }
0413 
0414 bool Output::directScanoutInhibited() const
0415 {
0416     return m_directScanoutCount;
0417 }
0418 
0419 std::chrono::milliseconds Output::dimAnimationTime()
0420 {
0421     // See kscreen.kcfg
0422     return std::chrono::milliseconds(KSharedConfig::openConfig()->group(QStringLiteral("Effect-Kscreen")).readEntry("Duration", 250));
0423 }
0424 
0425 QRect Output::mapFromGlobal(const QRect &rect) const
0426 {
0427     return rect.translated(-geometry().topLeft());
0428 }
0429 
0430 QRectF Output::mapFromGlobal(const QRectF &rect) const
0431 {
0432     return rect.translated(-geometry().topLeft());
0433 }
0434 
0435 QRectF Output::mapToGlobal(const QRectF &rect) const
0436 {
0437     return rect.translated(geometry().topLeft());
0438 }
0439 
0440 QPointF Output::mapToGlobal(const QPointF &pos) const
0441 {
0442     return pos + geometry().topLeft();
0443 }
0444 
0445 QPointF Output::mapFromGlobal(const QPointF &pos) const
0446 {
0447     return pos - geometry().topLeft();
0448 }
0449 
0450 Output::Capabilities Output::capabilities() const
0451 {
0452     return m_information.capabilities;
0453 }
0454 
0455 qreal Output::scale() const
0456 {
0457     return m_state.scale;
0458 }
0459 
0460 QRect Output::geometry() const
0461 {
0462     return QRect(m_state.position, pixelSize() / scale());
0463 }
0464 
0465 QRectF Output::geometryF() const
0466 {
0467     return QRectF(m_state.position, QSizeF(pixelSize()) / scale());
0468 }
0469 
0470 QSize Output::physicalSize() const
0471 {
0472     return m_information.physicalSize;
0473 }
0474 
0475 uint32_t Output::refreshRate() const
0476 {
0477     return m_state.currentMode ? m_state.currentMode->refreshRate() : 0;
0478 }
0479 
0480 QSize Output::modeSize() const
0481 {
0482     return m_state.currentMode ? m_state.currentMode->size() : QSize();
0483 }
0484 
0485 QSize Output::pixelSize() const
0486 {
0487     return orientateSize(modeSize());
0488 }
0489 
0490 const Edid &Output::edid() const
0491 {
0492     return m_information.edid;
0493 }
0494 
0495 QList<std::shared_ptr<OutputMode>> Output::modes() const
0496 {
0497     return m_state.modes;
0498 }
0499 
0500 std::shared_ptr<OutputMode> Output::currentMode() const
0501 {
0502     return m_state.currentMode;
0503 }
0504 
0505 Output::SubPixel Output::subPixel() const
0506 {
0507     return m_information.subPixel;
0508 }
0509 
0510 void Output::applyChanges(const OutputConfiguration &config)
0511 {
0512     auto props = config.constChangeSet(this);
0513     if (!props) {
0514         return;
0515     }
0516     Q_EMIT aboutToChange(props.get());
0517 
0518     State next = m_state;
0519     next.enabled = props->enabled.value_or(m_state.enabled);
0520     next.transform = props->transform.value_or(m_state.transform);
0521     next.position = props->pos.value_or(m_state.position);
0522     next.scale = props->scale.value_or(m_state.scale);
0523     next.rgbRange = props->rgbRange.value_or(m_state.rgbRange);
0524     next.autoRotatePolicy = props->autoRotationPolicy.value_or(m_state.autoRotatePolicy);
0525     next.iccProfilePath = props->iccProfilePath.value_or(m_state.iccProfilePath);
0526     if (props->iccProfilePath) {
0527         next.iccProfile = IccProfile::load(*props->iccProfilePath);
0528     }
0529     next.vrrPolicy = props->vrrPolicy.value_or(m_state.vrrPolicy);
0530 
0531     setState(next);
0532 
0533     Q_EMIT changed();
0534 }
0535 
0536 bool Output::isEnabled() const
0537 {
0538     return m_state.enabled;
0539 }
0540 
0541 QString Output::description() const
0542 {
0543     return manufacturer() + ' ' + model();
0544 }
0545 
0546 static QUuid generateOutputId(const QString &eisaId, const QString &model,
0547                               const QString &serialNumber, const QString &name)
0548 {
0549     static const QUuid urlNs = QUuid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); // NameSpace_URL
0550     static const QUuid kwinNs = QUuid::createUuidV5(urlNs, QStringLiteral("https://kwin.kde.org/o/"));
0551 
0552     const QString payload = QStringList{name, eisaId, model, serialNumber}.join(':');
0553     return QUuid::createUuidV5(kwinNs, payload);
0554 }
0555 
0556 void Output::setInformation(const Information &information)
0557 {
0558     m_information = information;
0559     m_uuid = generateOutputId(eisaId(), model(), serialNumber(), name());
0560 }
0561 
0562 void Output::setState(const State &state)
0563 {
0564     const QRect oldGeometry = geometry();
0565     const State oldState = m_state;
0566 
0567     m_state = state;
0568 
0569     if (oldGeometry != geometry()) {
0570         Q_EMIT geometryChanged();
0571     }
0572     if (oldState.scale != state.scale) {
0573         Q_EMIT scaleChanged();
0574     }
0575     if (oldState.modes != state.modes) {
0576         Q_EMIT modesChanged();
0577     }
0578     if (oldState.currentMode != state.currentMode) {
0579         Q_EMIT currentModeChanged();
0580     }
0581     if (oldState.transform != state.transform) {
0582         Q_EMIT transformChanged();
0583     }
0584     if (oldState.overscan != state.overscan) {
0585         Q_EMIT overscanChanged();
0586     }
0587     if (oldState.dpmsMode != state.dpmsMode) {
0588         Q_EMIT dpmsModeChanged();
0589     }
0590     if (oldState.rgbRange != state.rgbRange) {
0591         Q_EMIT rgbRangeChanged();
0592     }
0593     if (oldState.highDynamicRange != state.highDynamicRange) {
0594         Q_EMIT highDynamicRangeChanged();
0595     }
0596     if (oldState.sdrBrightness != state.sdrBrightness) {
0597         Q_EMIT sdrBrightnessChanged();
0598     }
0599     if (oldState.wideColorGamut != state.wideColorGamut) {
0600         Q_EMIT wideColorGamutChanged();
0601     }
0602     if (oldState.autoRotatePolicy != state.autoRotatePolicy) {
0603         Q_EMIT autoRotationPolicyChanged();
0604     }
0605     if (oldState.iccProfile != state.iccProfile) {
0606         Q_EMIT iccProfileChanged();
0607     }
0608     if (oldState.iccProfilePath != state.iccProfilePath) {
0609         Q_EMIT iccProfilePathChanged();
0610     }
0611     if (oldState.maxPeakBrightnessOverride != state.maxPeakBrightnessOverride
0612         || oldState.maxAverageBrightnessOverride != state.maxAverageBrightnessOverride
0613         || oldState.minBrightnessOverride != state.minBrightnessOverride) {
0614         Q_EMIT brightnessMetadataChanged();
0615     }
0616     if (oldState.sdrGamutWideness != state.sdrGamutWideness) {
0617         Q_EMIT sdrGamutWidenessChanged();
0618     }
0619     if (oldState.vrrPolicy != state.vrrPolicy) {
0620         Q_EMIT vrrPolicyChanged();
0621     }
0622     if (oldState.colorDescription != state.colorDescription) {
0623         Q_EMIT colorDescriptionChanged();
0624     }
0625     if (oldState.enabled != state.enabled) {
0626         Q_EMIT enabledChanged();
0627     }
0628 }
0629 
0630 QSize Output::orientateSize(const QSize &size) const
0631 {
0632     switch (m_state.transform.kind()) {
0633     case OutputTransform::Rotate90:
0634     case OutputTransform::Rotate270:
0635     case OutputTransform::FlipX90:
0636     case OutputTransform::FlipX270:
0637         return size.transposed();
0638     default:
0639         return size;
0640     }
0641 }
0642 
0643 void Output::setDpmsMode(DpmsMode mode)
0644 {
0645 }
0646 
0647 Output::DpmsMode Output::dpmsMode() const
0648 {
0649     return m_state.dpmsMode;
0650 }
0651 
0652 uint32_t Output::overscan() const
0653 {
0654     return m_state.overscan;
0655 }
0656 
0657 VrrPolicy Output::vrrPolicy() const
0658 {
0659     return m_state.vrrPolicy;
0660 }
0661 
0662 bool Output::isPlaceholder() const
0663 {
0664     return m_information.placeholder;
0665 }
0666 
0667 bool Output::isNonDesktop() const
0668 {
0669     return m_information.nonDesktop;
0670 }
0671 
0672 Output::RgbRange Output::rgbRange() const
0673 {
0674     return m_state.rgbRange;
0675 }
0676 
0677 bool Output::setChannelFactors(const QVector3D &rgb)
0678 {
0679     return false;
0680 }
0681 
0682 bool Output::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
0683 {
0684     return false;
0685 }
0686 
0687 OutputTransform Output::panelOrientation() const
0688 {
0689     return m_information.panelOrientation;
0690 }
0691 
0692 bool Output::wideColorGamut() const
0693 {
0694     return m_state.wideColorGamut;
0695 }
0696 
0697 bool Output::highDynamicRange() const
0698 {
0699     return m_state.highDynamicRange;
0700 }
0701 
0702 uint32_t Output::sdrBrightness() const
0703 {
0704     return m_state.sdrBrightness;
0705 }
0706 
0707 Output::AutoRotationPolicy Output::autoRotationPolicy() const
0708 {
0709     return m_state.autoRotatePolicy;
0710 }
0711 
0712 std::shared_ptr<IccProfile> Output::iccProfile() const
0713 {
0714     return m_state.iccProfile;
0715 }
0716 
0717 QString Output::iccProfilePath() const
0718 {
0719     return m_state.iccProfilePath;
0720 }
0721 
0722 QByteArray Output::mstPath() const
0723 {
0724     return m_information.mstPath;
0725 }
0726 
0727 bool Output::updateCursorLayer()
0728 {
0729     return false;
0730 }
0731 
0732 const ColorDescription &Output::colorDescription() const
0733 {
0734     return m_state.colorDescription;
0735 }
0736 
0737 std::optional<double> Output::maxPeakBrightness() const
0738 {
0739     return m_state.maxPeakBrightnessOverride ? m_state.maxPeakBrightnessOverride : m_information.maxPeakBrightness;
0740 }
0741 
0742 std::optional<double> Output::maxAverageBrightness() const
0743 {
0744     return m_state.maxAverageBrightnessOverride ? *m_state.maxAverageBrightnessOverride : m_information.maxAverageBrightness;
0745 }
0746 
0747 double Output::minBrightness() const
0748 {
0749     return m_state.minBrightnessOverride.value_or(m_information.minBrightness);
0750 }
0751 
0752 std::optional<double> Output::maxPeakBrightnessOverride() const
0753 {
0754     return m_state.maxPeakBrightnessOverride;
0755 }
0756 
0757 std::optional<double> Output::maxAverageBrightnessOverride() const
0758 {
0759     return m_state.maxAverageBrightnessOverride;
0760 }
0761 
0762 std::optional<double> Output::minBrightnessOverride() const
0763 {
0764     return m_state.minBrightnessOverride;
0765 }
0766 
0767 double Output::sdrGamutWideness() const
0768 {
0769     return m_state.sdrGamutWideness;
0770 }
0771 } // namespace KWin
0772 
0773 #include "moc_output.cpp"