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;