Commit 6a5a180b authored by Roman Gilg's avatar Roman Gilg
Browse files

feat: replace replication source with logical size API

Summary:
Just overriding the logical size of an output that replicates another one is
simpler than trying to send a relation between both objects to the display
server and in case of X11 it is not possible.

Wires up support for that in the X11 backend.

Test Plan: Compiles. Wayland replication tested.

Reviewers: #kwin

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D26309
parent 08a82592
......@@ -155,7 +155,7 @@ void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config)
}
}
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 @@ void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config)
++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 @@ void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config)
}
}
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 @@ void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config)
//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 @@ void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config)
}
}
for (KScreen::OutputPtr &output : toReplicate) {
if (replicateOutput(output)) {
forceScreenSizeUpdate = true;
}
}
if (oldPrimaryOutput != primaryOutput) {
setPrimaryOutput(primaryOutput);
}
......@@ -566,8 +556,10 @@ bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const
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 @@ bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const
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 @@ bool XRandRConfig::changeOutput(const KScreen::OutputPtr &kscreenOutput) const
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 @@ bool XRandRConfig::changeOutput(const KScreen::OutputPtr &kscreenOutput) const
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<xcb_randr_output_t>(kscreenOutput->id()) };
......
......@@ -60,7 +60,6 @@ private:
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;
......
......@@ -40,7 +40,6 @@ XRandROutput::XRandROutput(xcb_randr_output_t id, XRandRConfig *config)
, m_id(id)
, m_primary(false)
, m_type(KScreen::Output::Unknown)
, m_replicationSource(XCB_NONE)
, m_crtc(nullptr)
{
init();
......@@ -129,11 +128,6 @@ XRandRCrtc* XRandROutput::crtc() const
return m_crtc;
}
xcb_randr_output_t XRandROutput::replicationSource() const
{
return m_replicationSource;
}
void XRandROutput::update()
{
init();
......@@ -327,20 +321,27 @@ bool isScaling(const xcb_render_transform_t &tr)
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;
......@@ -348,96 +349,38 @@ xcb_render_transform_t XRandROutput::currentTransform() const
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 @@ bool XRandROutput::updateReplication()
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 @@ KScreen::OutputPtr XRandROutput::toKScreenOutput() const
kscreenOutput->setRotation(rotation());
kscreenOutput->setCurrentModeId(currentModeId());
}
kscreenOutput->setReplicationSource(m_replicationSource);
// TODO: set logical size?
}
kscreenOutput->blockSignals(signalsBlocked);
......
......@@ -61,7 +61,7 @@ public:
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,12 +75,7 @@ public:
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();
......@@ -89,15 +84,7 @@ private:
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 @@ private:
QStringList m_preferredModes;
QList<xcb_randr_output_t> m_clones;
xcb_randr_output_t m_replicationSource;
unsigned int m_widthMm;
unsigned int m_heightMm;
......
......@@ -40,6 +40,7 @@ class Q_DECL_HIDDEN Output::Private
replicationSource(0),
rotation(None),
scale(1.0),
logicalSize(QSizeF()),
connected(false),
enabled(false),
primary(false),
......@@ -92,6 +93,7 @@ class Q_DECL_HIDDEN Output::Private
QSize size;
Rotation rotation;
qreal scale;
QSizeF logicalSize;
bool connected;
bool enabled;
bool primary;
......@@ -425,6 +427,43 @@ void Output::setScale(qreal factor)
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 @@ QSize Output::enforcedModeSize() const
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);
}
......
......@@ -62,6 +62,8 @@ class KSCREEN_EXPORT Output : public QObject
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 @@ class KSCREEN_EXPORT Output : public QObject
*/
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 @@ class KSCREEN_EXPORT Output : public QObject
void clonesChanged();
void replicationSourceChanged();
void scaleChanged();
void logicalSizeChanged();
void followPreferredModeChanged(bool followPreferredMode);
/** The mode list changed.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment