File indexing completed on 2024-11-10 04:56:39
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"