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
This commit is contained in:
Xaver Hugl 2023-10-13 18:49:44 +02:00
parent 0391b65628
commit 7db4df9915
7 changed files with 73 additions and 4 deletions

View file

@ -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<DrmPipeline>(this) : nullptr)
{
@ -187,6 +188,11 @@ QSize DrmConnector::physicalSize() const
return m_physicalSize;
}
QByteArray DrmConnector::mstPath() const
{
return m_mstPath;
}
QList<std::shared_ptr<DrmConnectorMode>> 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<const char *>(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;
}

View file

@ -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<std::shared_ptr<DrmConnectorMode>> modes() const;
std::shared_ptr<DrmConnectorMode> findMode(const drmModeModeInfo &modeInfo) const;
@ -128,6 +132,7 @@ public:
DrmProperty hdrMetadata;
DrmEnumProperty<ScalingMode> scalingMode;
DrmEnumProperty<Colorspace> colorspace;
DrmProperty path;
static DrmContentType kwinToDrmContentType(ContentType type);
static OutputTransform toKWinTransform(PanelOrientation orientation);
@ -145,6 +150,7 @@ private:
QList<std::shared_ptr<DrmConnectorMode>> m_driverModes;
QList<std::shared_ptr<DrmConnectorMode>> m_modes;
uint32_t m_possibleCrtcs = 0;
QByteArray m_mstPath;
friend QDebug &operator<<(QDebug &s, const KWin::DrmConnector *obj);
};

View file

@ -85,6 +85,7 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
.panelOrientation = conn->panelOrientation.isValid() ? DrmConnector::toKWinTransform(conn->panelOrientation.enumValue()) : OutputTransform::Normal,
.internal = conn->isInternal(),
.nonDesktop = conn->isNonDesktop(),
.mstPath = conn->mstPath(),
});
initialState.modes = getModes();

View file

@ -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;

View file

@ -317,6 +317,10 @@ public:
AutoRotationPolicy autoRotationPolicy() const;
std::shared_ptr<IccProfile> 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<ColorTransformation> &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

View file

@ -128,12 +128,27 @@ std::optional<std::pair<OutputConfigurationStore::Setup *, std::unordered_map<Ou
std::optional<size_t> OutputConfigurationStore::findOutput(Output *output, const QList<Output *> &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<Output *> &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<Output *> &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();

View file

@ -61,6 +61,8 @@ private:
// identification data
std::optional<QString> edidIdentifier;
std::optional<QString> connectorName;
// empty if invalid
QString mstPath;
// actual state
std::optional<ModeData> mode;
std::optional<double> scale;