From 7db4df991542843c0bb22aa3abe2030a005dadda Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Fri, 13 Oct 2023 18:49:44 +0200 Subject: [PATCH] outputconfigurationstore: differentiate between outputs with their mst path The DisplayPort multi stream path should be more stable in comparison to connector names, so prefer that for differentiating between outputs with the same EDID. BUG: 470718 --- src/backends/drm/drm_connector.cpp | 24 +++++++++++++++++++++ src/backends/drm/drm_connector.h | 6 ++++++ src/backends/drm/drm_output.cpp | 1 + src/core/output.cpp | 5 +++++ src/core/output.h | 5 +++++ src/outputconfigurationstore.cpp | 34 ++++++++++++++++++++++++++---- src/outputconfigurationstore.h | 2 ++ 7 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/backends/drm/drm_connector.cpp b/src/backends/drm/drm_connector.cpp index ede1aabe78..1fd8cfaf1b 100644 --- a/src/backends/drm/drm_connector.cpp +++ b/src/backends/drm/drm_connector.cpp @@ -136,6 +136,7 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId) QByteArrayLiteral("BT2020_RGB"), QByteArrayLiteral("BT2020_YCC"), }) + , path(this, QByteArrayLiteral("PATH")) , m_conn(drmModeGetConnector(gpu->fd(), connectorId)) , m_pipeline(m_conn ? std::make_unique(this) : nullptr) { @@ -187,6 +188,11 @@ QSize DrmConnector::physicalSize() const return m_physicalSize; } +QByteArray DrmConnector::mstPath() const +{ + return m_mstPath; +} + QList> DrmConnector::modes() const { return m_modes; @@ -245,6 +251,7 @@ bool DrmConnector::updateProperties() hdrMetadata.update(props); scalingMode.update(props); colorspace.update(props); + path.update(props); if (gpu()->atomicModeSetting() && !crtcId.isValid()) { return false; @@ -298,6 +305,23 @@ bool DrmConnector::updateProperties() } } + m_mstPath.clear(); + if (auto blob = path.immutableBlob()) { + QByteArray value = QByteArray(static_cast(blob->data), blob->length); + if (value.startsWith("mst:")) { + // for backwards compatibility reasons the string also contains the drm connector id + // remove that to get a more stable identifier + const ssize_t firstHyphen = value.indexOf('-'); + if (firstHyphen > 0) { + m_mstPath = value.mid(firstHyphen); + } else { + qCWarning(KWIN_DRM) << "Unexpected format in path property:" << value; + } + } else { + qCWarning(KWIN_DRM) << "Unknown path type detected:" << value; + } + } + return true; } diff --git a/src/backends/drm/drm_connector.h b/src/backends/drm/drm_connector.h index d0b58db0eb..43355b7eb8 100644 --- a/src/backends/drm/drm_connector.h +++ b/src/backends/drm/drm_connector.h @@ -64,6 +64,10 @@ public: QString connectorName() const; QString modelName() const; QSize physicalSize() const; + /** + * @returns the mst path of the connector. Is empty if invalid + */ + QByteArray mstPath() const; QList> modes() const; std::shared_ptr findMode(const drmModeModeInfo &modeInfo) const; @@ -128,6 +132,7 @@ public: DrmProperty hdrMetadata; DrmEnumProperty scalingMode; DrmEnumProperty colorspace; + DrmProperty path; static DrmContentType kwinToDrmContentType(ContentType type); static OutputTransform toKWinTransform(PanelOrientation orientation); @@ -145,6 +150,7 @@ private: QList> m_driverModes; QList> m_modes; uint32_t m_possibleCrtcs = 0; + QByteArray m_mstPath; friend QDebug &operator<<(QDebug &s, const KWin::DrmConnector *obj); }; diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index aedfeb4139..55475d41f5 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -85,6 +85,7 @@ DrmOutput::DrmOutput(const std::shared_ptr &conn) .panelOrientation = conn->panelOrientation.isValid() ? DrmConnector::toKWinTransform(conn->panelOrientation.enumValue()) : OutputTransform::Normal, .internal = conn->isInternal(), .nonDesktop = conn->isNonDesktop(), + .mstPath = conn->mstPath(), }); initialState.modes = getModes(); diff --git a/src/core/output.cpp b/src/core/output.cpp index bf77a6c107..f95f979882 100644 --- a/src/core/output.cpp +++ b/src/core/output.cpp @@ -579,6 +579,11 @@ QString Output::iccProfilePath() const return m_state.iccProfilePath; } +QByteArray Output::mstPath() const +{ + return m_information.mstPath; +} + bool Output::updateCursorLayer() { return false; diff --git a/src/core/output.h b/src/core/output.h index 8fc4960063..c232d46943 100644 --- a/src/core/output.h +++ b/src/core/output.h @@ -317,6 +317,10 @@ public: AutoRotationPolicy autoRotationPolicy() const; std::shared_ptr iccProfile() const; QString iccProfilePath() const; + /** + * @returns the mst path of this output. Is empty if invalid + */ + QByteArray mstPath() const; virtual bool setGammaRamp(const std::shared_ptr &transformation); virtual bool setChannelFactors(const QVector3D &rgb); @@ -399,6 +403,7 @@ protected: bool internal = false; bool placeholder = false; bool nonDesktop = false; + QByteArray mstPath; }; struct State diff --git a/src/outputconfigurationstore.cpp b/src/outputconfigurationstore.cpp index 6052228724..83cd088908 100644 --- a/src/outputconfigurationstore.cpp +++ b/src/outputconfigurationstore.cpp @@ -128,12 +128,27 @@ std::optional OutputConfigurationStore::findOutput(Output *output, const QList &allOutputs) const { - const bool hasDuplicate = std::any_of(allOutputs.begin(), allOutputs.end(), [output](Output *otherOutput) { + const bool duplicateEdid = std::any_of(allOutputs.begin(), allOutputs.end(), [output](Output *otherOutput) { return otherOutput != output && otherOutput->edid().identifier() == output->edid().identifier(); }); - const auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [hasDuplicate, output](const auto &outputState) { - return outputState.edidIdentifier == output->edid().identifier() - && (!hasDuplicate || outputState.connectorName == output->name()); + const bool duplicateMst = std::any_of(allOutputs.begin(), allOutputs.end(), [output](Output *otherOutput) { + return otherOutput != output && otherOutput->edid().identifier() == output->edid().identifier() && otherOutput->mstPath() == output->mstPath(); + }); + const auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [duplicateEdid, duplicateMst, output](const auto &outputState) { + if (outputState.edidIdentifier != output->edid().identifier()) { + return false; + } + if (!duplicateEdid) { + return true; + } + if (!output->mstPath().isEmpty()) { + if (outputState.mstPath != output->mstPath()) { + return false; + } else if (!duplicateMst) { + return true; + } + } + return outputState.connectorName == output->name(); }); if (it != m_outputs.end()) { return std::distance(m_outputs.begin(), it); @@ -181,6 +196,7 @@ void OutputConfigurationStore::storeConfig(const QList &allOutputs, bo m_outputs[*outputIndex] = OutputState{ .edidIdentifier = output->edid().identifier(), .connectorName = output->name(), + .mstPath = output->mstPath(), .mode = ModeData{ .size = mode->size(), .refreshRate = mode->refreshRate(), @@ -208,6 +224,7 @@ void OutputConfigurationStore::storeConfig(const QList &allOutputs, bo m_outputs[*outputIndex] = OutputState{ .edidIdentifier = output->edid().identifier(), .connectorName = output->name(), + .mstPath = output->mstPath(), .mode = ModeData{ .size = mode->size(), .refreshRate = mode->refreshRate(), @@ -528,6 +545,12 @@ void OutputConfigurationStore::load() hasIdentifier = true; } } + if (const auto it = data.find("mstPath"); it != data.end()) { + if (const auto str = it->toString(); !str.isEmpty()) { + state.mstPath = str; + hasIdentifier = true; + } + } if (!hasIdentifier) { // without an identifier the settings are useless // we still have to push something into the list so that the indices stay correct @@ -740,6 +763,9 @@ void OutputConfigurationStore::save() if (output.connectorName) { o["connectorName"] = *output.connectorName; } + if (!output.mstPath.isEmpty()) { + o["mstPath"] = output.mstPath; + } if (output.mode) { QJsonObject mode; mode["width"] = output.mode->size.width(); diff --git a/src/outputconfigurationstore.h b/src/outputconfigurationstore.h index 408a34b767..0f100cdd9b 100644 --- a/src/outputconfigurationstore.h +++ b/src/outputconfigurationstore.h @@ -61,6 +61,8 @@ private: // identification data std::optional edidIdentifier; std::optional connectorName; + // empty if invalid + QString mstPath; // actual state std::optional mode; std::optional scale;