diff --git a/backends/xrandr/xrandrconfig.h b/backends/xrandr/xrandrconfig.h --- a/backends/xrandr/xrandrconfig.h +++ b/backends/xrandr/xrandrconfig.h @@ -60,7 +60,6 @@ bool disableOutput(const KScreen::OutputPtr &output) const; bool enableOutput(const KScreen::OutputPtr &output) const; bool changeOutput(const KScreen::OutputPtr &output) const; - bool replicateOutput(const KScreen::OutputPtr &output) const; bool sendConfig(const KScreen::OutputPtr &kscreenOutput, XRandRCrtc *crtc) const; diff --git a/backends/xrandr/xrandrconfig.cpp b/backends/xrandr/xrandrconfig.cpp --- a/backends/xrandr/xrandrconfig.cpp +++ b/backends/xrandr/xrandrconfig.cpp @@ -155,7 +155,7 @@ } } - KScreen::OutputList toDisable, toEnable, toChange, toReplicate; + KScreen::OutputList toDisable, toEnable, toChange; for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) { xcb_randr_output_t outputId = kscreenOutput->id(); @@ -180,16 +180,6 @@ ++neededCrtcs; - // Update replication when it is supposed to be a replica or changes not to be anymore. - if (kscreenOutput->replicationSource() || currentOutput->replicationSource()) { - if (int sourceId = kscreenOutput->replicationSource()) { - kscreenOutput->setPos(config->output(sourceId)->pos()); - } - if (!toReplicate.contains(outputId)) { - toReplicate.insert(outputId, kscreenOutput); - } - } - if (kscreenOutput->currentModeId() != currentOutput->currentModeId()) { if (!toChange.contains(outputId)) { toChange.insert(outputId, kscreenOutput); @@ -208,6 +198,12 @@ } } + if (kscreenOutput->explicitLogicalSize() != currentOutput->logicalSize()) { + if (!toChange.contains(outputId)) { + toChange.insert(outputId, kscreenOutput); + } + } + XRandRMode *currentMode = currentOutput->modes().value( kscreenOutput->currentModeId().toInt()); // For some reason, in some environments currentMode is null @@ -291,7 +287,7 @@ //If there is nothing to do, not even bother if (oldPrimaryOutput == primaryOutput && toDisable.isEmpty() && - toEnable.isEmpty() && toChange.isEmpty() && toReplicate.isEmpty()) { + toEnable.isEmpty() && toChange.isEmpty()) { if (newScreenSize != currentScreenSize) { setScreenSize(newScreenSize); } @@ -327,12 +323,6 @@ } } - for (KScreen::OutputPtr &output : toReplicate) { - if (replicateOutput(output)) { - forceScreenSizeUpdate = true; - } - } - if (oldPrimaryOutput != primaryOutput) { setPrimaryOutput(primaryOutput); } @@ -566,8 +556,10 @@ return false; } + XRandROutput *xOutput = output(kscreenOutput->id()); const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt(); + xOutput->updateLogicalSize(kscreenOutput); qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (enable output)" << "\n" << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() @@ -582,8 +574,8 @@ return false; } - output(kscreenOutput->id())->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED, - kscreenOutput->isPrimary()); + xOutput->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED, + kscreenOutput->isPrimary()); return true; } @@ -600,6 +592,7 @@ int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt(); + xOutput->updateLogicalSize(kscreenOutput); qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)" << "\n" << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() @@ -618,40 +611,6 @@ return true; } -bool XRandRConfig::replicateOutput(const KScreen::OutputPtr &kscreenOutput) const -{ - XRandROutput *xOutput = output(kscreenOutput->id()); - Q_ASSERT(xOutput); - - if (!xOutput->crtc()) { - qCDebug(KSCREEN_XRANDR) << "Output" << kscreenOutput->id() - << "has no CRTC, falling back to enableOutput()"; - enableOutput(kscreenOutput); - } - - int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : - kscreenOutput->preferredModeId().toInt(); - - qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)" << "\n" - << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() - << ")" << "\n" - << "\tCRTC:" << xOutput->crtc()->crtc() << "\n" - << "\tPos:" << kscreenOutput->pos() << "\n" - << "\tMode:" << modeId << kscreenOutput->currentMode() << "\n" - << "\tRotation:" << kscreenOutput->rotation(); - - if (!xOutput->setReplicationSource(kscreenOutput->replicationSource())) { - return false; - } - if (!sendConfig(kscreenOutput, xOutput->crtc())) { - return false; - } - - xOutput->update(xOutput->crtc()->crtc(), modeId, - XCB_RANDR_CONNECTION_CONNECTED, kscreenOutput->isPrimary()); - return true; -} - bool XRandRConfig::sendConfig(const KScreen::OutputPtr &kscreenOutput, XRandRCrtc *crtc) const { xcb_randr_output_t outputs[1] { static_cast(kscreenOutput->id()) }; diff --git a/backends/xrandr/xrandroutput.h b/backends/xrandr/xrandroutput.h --- a/backends/xrandr/xrandroutput.h +++ b/backends/xrandr/xrandroutput.h @@ -61,7 +61,7 @@ QPoint position() const; QSize size() const; - QSizeF scaledSize(xcb_render_transform_t transform) const; + QSizeF logicalSize() const; QString currentModeId() const; XRandRMode::Map modes() const; @@ -75,29 +75,16 @@ KScreen::OutputPtr toKScreenOutput() const; - bool updateReplication(); - bool setReplicationSource(xcb_randr_output_t source); - - xcb_randr_output_t replicationSource() const; - - xcb_render_transform_t currentTransform() const; + void updateLogicalSize(const KScreen::OutputPtr &output); private: void init(); void updateModes(const XCB::OutputInfo &outputInfo); static KScreen::Output::Type fetchOutputType(xcb_randr_output_t outputId, const QString &name); static QByteArray typeFromProperty(xcb_randr_output_t outputId); - xcb_render_transform_t getReplicationTransform(XRandROutput *source); - - /** - * This makes an educated guess based on position, size and scale if @param output is a - * replication source for this output. - * - * @return true if this output can be seen as a replica of @param output - */ - bool isReplicaOf(XRandROutput *output, xcb_render_transform_t ownTransform) const; + xcb_render_transform_t currentTransform() const; XRandRConfig *m_config; xcb_randr_output_t m_id; @@ -113,7 +100,6 @@ QStringList m_preferredModes; QList m_clones; - xcb_randr_output_t m_replicationSource; unsigned int m_widthMm; unsigned int m_heightMm; diff --git a/backends/xrandr/xrandroutput.cpp b/backends/xrandr/xrandroutput.cpp --- a/backends/xrandr/xrandroutput.cpp +++ b/backends/xrandr/xrandroutput.cpp @@ -40,7 +40,6 @@ , m_id(id) , m_primary(false) , m_type(KScreen::Output::Unknown) - , m_replicationSource(XCB_NONE) , m_crtc(nullptr) { init(); @@ -129,11 +128,6 @@ return m_crtc; } -xcb_randr_output_t XRandROutput::replicationSource() const -{ - return m_replicationSource; -} - void XRandROutput::update() { init(); @@ -327,117 +321,66 @@ tr.matrix31 == fZero && tr.matrix32 == fZero && tr.matrix33 == fOne; } -xcb_render_transform_t zeroMatrix() +xcb_render_transform_t zeroTransform() { return { DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0) }; } +xcb_render_transform_t unityTransform() +{ + return { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), + DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), + DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; +} + xcb_render_transform_t XRandROutput::currentTransform() const { auto cookie = xcb_randr_get_crtc_transform(XCB::connection(), m_crtc->crtc()); xcb_generic_error_t *error = nullptr; auto *reply = xcb_randr_get_crtc_transform_reply(XCB::connection(), cookie, &error); if (error) { - return zeroMatrix(); + return zeroTransform(); } const xcb_render_transform_t transform = reply->pending_transform; free(reply); return transform; } -QSizeF XRandROutput::scaledSize(xcb_render_transform_t transform) const +QSizeF XRandROutput::logicalSize() const { - const QSize ownSize = size(); - if (!ownSize.isValid()) { + const QSize modeSize = size(); + if (!modeSize.isValid()) { return QSize(); } - const qreal width = FIXED_TO_DOUBLE(transform.matrix11) * ownSize.width(); - const qreal height = FIXED_TO_DOUBLE(transform.matrix22) * ownSize.height(); + const xcb_render_transform_t transform = currentTransform(); + const qreal width = FIXED_TO_DOUBLE(transform.matrix11) * modeSize.width(); + const qreal height = FIXED_TO_DOUBLE(transform.matrix22) * modeSize.height(); return QSizeF(width, height); } -bool XRandROutput::isReplicaOf(XRandROutput *output, xcb_render_transform_t ownTransform) const -{ - if (output->id() == m_id) { - return false; - } - if (output->position() != position()) { - return false; - } - if (output->replicationSource() != XCB_NONE) { - return false; - } - - const QSizeF sSize = scaledSize(ownTransform); - if (!sSize.isValid()) { - return false; - } - - const auto outputTransform = output->currentTransform(); - if (!isScaling(outputTransform)) { - return false; - } - - if (sSize != output->scaledSize(outputTransform)) { - return false; - } - - return true; -} - -xcb_render_transform_t unityTransform() -{ - return { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), - DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), - DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; -} - -xcb_render_transform_t XRandROutput::getReplicationTransform(XRandROutput *source) +void XRandROutput::updateLogicalSize(const KScreen::OutputPtr &output) { - if (!source) { - return unityTransform(); - } - - const auto *sourceMode = source->currentMode(); - const auto *ownMode = currentMode(); - if (!sourceMode || !ownMode) { - return unityTransform(); - } - - QSize sourceSize = sourceMode->size(); - QSize size = ownMode->size(); - - if (isHorizontal()) { - if (!source->isHorizontal()) { - sourceSize.transpose(); - } - } else if (source->isHorizontal()) { - size.transpose(); - } - - const qreal widthFactor = sourceSize.width() / (qreal)size.width(); - const qreal heightFactor = sourceSize.height() / (qreal)size.height(); - + const QSizeF logicalSize = output->explicitLogicalSize(); xcb_render_transform_t transform = unityTransform(); - transform.matrix11 = DOUBLE_TO_FIXED(widthFactor); - transform.matrix22 = DOUBLE_TO_FIXED(heightFactor); - return transform; -} + KScreen::ModePtr mode = output->currentMode() ? output->currentMode() : output->preferredMode(); + if(mode && logicalSize.isValid()) { + QSize modeSize = mode->size(); + if (!output->isHorizontal()) { + modeSize.transpose(); + } -bool XRandROutput::updateReplication() -{ - XRandROutput *source = m_config->output(m_replicationSource); - if (source && (!source->isEnabled() || !source->isConnected())) { - return false; + const qreal widthFactor = logicalSize.width() / (qreal)modeSize.width(); + const qreal heightFactor = logicalSize.height() / (qreal)modeSize.height(); + transform.matrix11 = DOUBLE_TO_FIXED(widthFactor); + transform.matrix22 = DOUBLE_TO_FIXED(heightFactor); } - xcb_render_transform_t transform = getReplicationTransform(source); QByteArray filterName(isScaling(transform) ? "bilinear" : "nearest"); auto cookie = xcb_randr_set_crtc_transform_checked(XCB::connection(), @@ -447,29 +390,9 @@ 0, nullptr); xcb_generic_error_t *error = xcb_request_check(XCB::connection(), cookie); if (error) { - qCDebug(KSCREEN_XRANDR) << "Error on replication transformation!"; + qCDebug(KSCREEN_XRANDR) << "Error on logical size transformation!"; free(error); - return false; - } - free(error); - return true; -} - -bool XRandROutput::setReplicationSource(xcb_randr_output_t source) -{ - if (!m_crtc) { - return false; - } - if (m_replicationSource == source) { - return true; - } - xcb_randr_output_t oldSource = m_replicationSource; - m_replicationSource = source; - if (!updateReplication()) { - m_replicationSource = oldSource; - return false; } - return true; } KScreen::OutputPtr XRandROutput::toKScreenOutput() const @@ -513,7 +436,7 @@ kscreenOutput->setRotation(rotation()); kscreenOutput->setCurrentModeId(currentModeId()); } - kscreenOutput->setReplicationSource(m_replicationSource); + // TODO: set logical size? } kscreenOutput->blockSignals(signalsBlocked); diff --git a/src/output.h b/src/output.h --- a/src/output.h +++ b/src/output.h @@ -62,6 +62,8 @@ Q_PROPERTY(QSize sizeMm READ sizeMm CONSTANT) Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) Q_PROPERTY(bool followPreferredMode READ followPreferredMode WRITE setFollowPreferredMode NOTIFY followPreferredModeChanged) + Q_PROPERTY(QSizeF logicalSize READ logicalSize WRITE setLogicalSize + NOTIFY logicalSizeChanged) enum Type { @@ -295,6 +297,37 @@ */ void setScale(qreal factor); + /** + * The logical size is the output's representation internal to the display server and its + * overall screen geometry. + * + * returns the logical size of this output + * + * @since 5.18 + */ + QSizeF logicalSize() const; + + /** + * The logical size is the output's representation internal to the display server and its + * overall screen geometry. + * + * returns the explicitly set logical size of this output, is an invalid size if not set + * + * @since 5.18 + */ + QSizeF explicitLogicalSize() const; + + /** + * Specifies explicitly the logical size of this output and by that overrides any other + * logical size calculation through mode and scale. To enable this instead again call this + * function with an invalid size as argument. + * + * @param size of this output in logical space + * + * @since 5.18 + */ + void setLogicalSize(const QSizeF &size); + /** * @returns whether the mode should be changed to the new preferred mode * once it changes @@ -323,6 +356,7 @@ void clonesChanged(); void replicationSourceChanged(); void scaleChanged(); + void logicalSizeChanged(); void followPreferredModeChanged(bool followPreferredMode); /** The mode list changed. diff --git a/src/output.cpp b/src/output.cpp --- a/src/output.cpp +++ b/src/output.cpp @@ -40,6 +40,7 @@ replicationSource(0), rotation(None), scale(1.0), + logicalSize(QSizeF()), connected(false), enabled(false), primary(false), @@ -92,6 +93,7 @@ QSize size; Rotation rotation; qreal scale; + QSizeF logicalSize; bool connected; bool enabled; bool primary; @@ -425,6 +427,43 @@ emit scaleChanged(); } +QSizeF Output::logicalSize() const +{ + if (d->logicalSize.isValid()) { + return d->logicalSize; + } + + QSizeF size = enforcedModeSize(); + if (!size.isValid()) { + return QSizeF(); + } + size = size / d->scale; + + // We can't use d->size, because d->size does not reflect the actual rotation() set by caller. + // It is only updated when we get update from KScreen, but not when user changes mode or + // rotation manually. + + if (!isHorizontal()) { + size = size.transposed(); + } + return size; +} + +QSizeF Output::explicitLogicalSize() const +{ + return d->logicalSize; +} + +void Output::setLogicalSize(const QSizeF &size) +{ + if (qFuzzyCompare(d->logicalSize.width(), size.width()) + && qFuzzyCompare(d->logicalSize.height(), size.height())) { + return; + } + d->logicalSize = size; + Q_EMIT logicalSizeChanged(); +} + bool Output::isConnected() const { return d->connected; @@ -558,19 +597,10 @@ QRect Output::geometry() const { - QSize size = enforcedModeSize(); + QSize size = logicalSize().toSize(); if (!size.isValid()) { return QRect(); } - size = size / d->scale; - - // We can't use QRect(d->pos, d->size), because d->size does not reflect the - // actual rotation() set by caller, it's only updated when we get update from - // KScreen, but not when user changes mode or rotation manually - - if (!isHorizontal()) { - size = size.transposed(); - } return QRect(d->pos, size); }