diff --git a/backends/kwayland/waylandconfig.cpp b/backends/kwayland/waylandconfig.cpp --- a/backends/kwayland/waylandconfig.cpp +++ b/backends/kwayland/waylandconfig.cpp @@ -226,7 +226,9 @@ // TODO: do this setScreen call less clunky m_kscreenConfig->setScreen(m_screen->toKScreenScreen(m_kscreenConfig)); - auto features = Config::Feature::Writable | Config::Feature::PerOutputScaling; + const Config::Features features = Config::Feature::Writable | + Config::Feature::PerOutputScaling | + Config::Feature::OutputReplication; m_kscreenConfig->setSupportedFeatures(features); m_kscreenConfig->setValid(m_connection->display()); diff --git a/backends/kwayland/waylandoutput.h b/backends/kwayland/waylandoutput.h --- a/backends/kwayland/waylandoutput.h +++ b/backends/kwayland/waylandoutput.h @@ -71,10 +71,12 @@ private: void showOutput(); QString modeName(const KWayland::Client::OutputDevice::Mode &m) const; + int getReplicationSource() const; quint32 m_id; KWayland::Client::OutputDevice *m_device; KWayland::Client::Registry *m_registry; + WaylandConfig *m_config; // left-hand-side: KScreen::Mode, right-hand-side: KWayland's mode.id QMap m_modeIdMap; diff --git a/backends/kwayland/waylandoutput.cpp b/backends/kwayland/waylandoutput.cpp --- a/backends/kwayland/waylandoutput.cpp +++ b/backends/kwayland/waylandoutput.cpp @@ -56,6 +56,7 @@ : QObject(parent) , m_id(id) , m_device(nullptr) + , m_config(parent) { } @@ -95,6 +96,19 @@ return output; } +int WaylandOutput::getReplicationSource() const +{ + const auto uuid = m_device->replicationSource(); + if (!uuid.isEmpty()) { + for (auto waylandOutput : m_config->outputMap()) { + if (uuid == waylandOutput->outputDevice()->uuid()) { + return waylandOutput->id(); + } + } + } + return 0; +} + void WaylandOutput::updateKScreenOutput(OutputPtr &output) { // Initialize primary output @@ -106,6 +120,7 @@ output->setSizeMm(m_device->physicalSize()); output->setPos(m_device->globalPosition()); output->setRotation(s_rotationMap[m_device->transform()]); + output->setReplicationSource(getReplicationSource()); ModeList modeList; QStringList preferredModeIds; @@ -189,6 +204,19 @@ wlConfig->setTransform(m_device, toKWaylandTransform(output->rotation())); } + // replication + const QByteArray oldSourceUuid = m_device->replicationSource(); + if (output->replicationSource() != 0) { + auto *newSource = m_config->outputMap()[output->replicationSource()]->outputDevice(); + if (oldSourceUuid != newSource->uuid()) { + changed = true; + wlConfig->setReplicationSource(m_device, newSource); + } + } else if (!oldSourceUuid.isEmpty()) { + changed = true; + wlConfig->setReplicationSource(m_device, nullptr); + } + // mode if (m_modeIdMap.contains(output->currentModeId())) { const int newModeId = m_modeIdMap.value(output->currentModeId(), -1); diff --git a/backends/kwayland/waylandscreen.cpp b/backends/kwayland/waylandscreen.cpp --- a/backends/kwayland/waylandscreen.cpp +++ b/backends/kwayland/waylandscreen.cpp @@ -45,8 +45,8 @@ QRect r; for (const auto *out : outputs) { - if (out->enabled()) { - const auto *dev = out->outputDevice(); + const auto *dev = out->outputDevice(); + if (out->enabled() && dev->replicationSource() == nullptr) { r |= QRect(dev->globalPosition(), dev->pixelSize() / dev->scale()); } } diff --git a/src/configserializer.cpp b/src/configserializer.cpp --- a/src/configserializer.cpp +++ b/src/configserializer.cpp @@ -92,6 +92,7 @@ obj[QLatin1String("clones")] = serializeList(output->clones()); //obj[QLatin1String("edid")] = output->edid()->raw(); obj[QLatin1String("sizeMM")] = serializeSize(output->sizeMm()); + obj[QLatin1String("replicationSource")] = output->replicationSource(); QJsonArray modes; Q_FOREACH (const ModePtr &mode, output->modes()) { @@ -251,6 +252,8 @@ output->setPrimary(value.toBool()); } else if (key == QLatin1String("clones")) { output->setClones(deserializeList(value.value())); + } else if (key == QLatin1String("replicationSource")) { + output->setReplicationSource(value.toInt()); } else if (key == QLatin1String("sizeMM")) { output->setSizeMm(deserializeSize(value.value())); } else if (key == QLatin1String("modes")) { diff --git a/src/output.h b/src/output.h --- a/src/output.h +++ b/src/output.h @@ -56,6 +56,8 @@ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY isEnabledChanged) Q_PROPERTY(bool primary READ isPrimary WRITE setPrimary NOTIFY isPrimaryChanged) Q_PROPERTY(QList clones READ clones WRITE setClones NOTIFY clonesChanged) + Q_PROPERTY(int replicationSource READ replicationSource + WRITE setReplicationSource NOTIFY replicationSourceChanged) Q_PROPERTY(KScreen::Edid* edid READ edid CONSTANT) Q_PROPERTY(QSize sizeMm READ sizeMm CONSTANT) Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) @@ -190,9 +192,41 @@ bool isPrimary() const; void setPrimary(bool primary); + /** + * @brief Immutable clones because of hardware restrictions + * + * Clones are set symmetcally on all outputs. The list contains ids + * for all other outputs being clones of this output. + * + * @return List of output ids being clones of each other. + */ QList clones() const; + /** + * @brief Set the clones list. + * + * When this output is part of a configuration this call is followed by + * similar calls on other outputs making the lists in all outputs + * symmetric. + * @param outputlist + */ void setClones(const QList &outputlist); + /** + * @brief Provides the source for an ongoing replication + * + * If the returned output id is non-null this output is a replica of the + * returned output. If null is returned the output is no replica of any + * other output. + * + * @return Replication source output id of this output + */ + int replicationSource() const; + /** + * @brief Set the replication source. + * @param source + */ + void setReplicationSource(int source); + void setEdid(const QByteArray &rawData); /** @@ -212,6 +246,16 @@ QSize sizeMm() const; void setSizeMm(const QSize &size); + /** + * Returns if the output needs to be taken account for in the overall compositor/screen + * space and if it should be depicted on its own in a graphical view for repositioning. + * + * @return true if the output is positionable in compositor/screen space. + * + * @since 5.17 + */ + bool isPositionable() const; + /** * Returns a rectangle containing the currently set output position and * size. @@ -269,6 +313,7 @@ void isEnabledChanged(); void isPrimaryChanged(); void clonesChanged(); + void replicationSourceChanged(); void scaleChanged(); void followPreferredModeChanged(bool followPreferredMode); diff --git a/src/output.cpp b/src/output.cpp --- a/src/output.cpp +++ b/src/output.cpp @@ -37,6 +37,7 @@ Private(): id(0), type(Unknown), + replicationSource(0), rotation(None), scale(1.0), connected(false), @@ -51,6 +52,7 @@ type(other.type), icon(other.icon), clones(other.clones), + replicationSource(other.replicationSource), currentMode(other.currentMode), preferredMode(other.preferredMode), preferredModes(other.preferredModes), @@ -81,6 +83,7 @@ QString icon; ModeList modeList; QList clones; + int replicationSource; QString currentMode; QString preferredMode; QStringList preferredModes; @@ -484,6 +487,22 @@ Q_EMIT clonesChanged(); } +int Output::replicationSource() const +{ + return d->replicationSource; +} + +void Output::setReplicationSource(int source) +{ + if (d->replicationSource == source) { + return; + } + + d->replicationSource = source; + + Q_EMIT replicationSourceChanged(); +} + void Output::setEdid(const QByteArray& rawData) { Q_ASSERT(d->edid.isNull()); @@ -518,6 +537,11 @@ } } +bool Output::isPositionable() const +{ + return isConnected() && isEnabled() && !replicationSource(); +} + QRect Output::geometry() const { if (!currentMode()) { @@ -590,6 +614,10 @@ changes << &Output::clonesChanged; setClones(other->d->clones);; } + if (d->replicationSource != other->d->replicationSource) { + changes << &Output::replicationSourceChanged; + setReplicationSource(other->d->replicationSource);; + } if (!d->compareModeList(d->modeList, other->d->modeList)) { changes << &Output::outputChanged; changes << &Output::modesChanged; @@ -627,6 +655,7 @@ << "pos:" << output->pos() << "res:" << output->size() << "modeId:" << output->currentModeId() << "scale:" << output->scale() + << "clone:" << (output->clones().isEmpty() ? "no" : "yes") << "followPreferredMode:" << output->followPreferredMode() << ")"; } else { diff --git a/src/setconfigoperation.cpp b/src/setconfigoperation.cpp --- a/src/setconfigoperation.cpp +++ b/src/setconfigoperation.cpp @@ -141,7 +141,7 @@ int offsetX = INT_MAX; int offsetY = INT_MAX; Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { - if (!output->isConnected() || !output->isEnabled()) { + if (!output->isPositionable()) { continue; } offsetX = qMin(output->pos().x(), offsetX);