Index: abstract_output.h =================================================================== --- abstract_output.h +++ abstract_output.h @@ -130,7 +130,10 @@ virtual void applyChanges(const KWayland::Server::OutputChangeSet *changeSet); /** - * Returns geometry of this output in device independent pixels. + * Returns geometry of this output in global compositor space, that is the + * 2-dimensional vector space outputs are placed in relatively to each + * other. The x-axis is always set to be the most west edge of all outputs + * and the y-axis the most north edge. */ virtual QRect geometry() const = 0; @@ -148,7 +151,10 @@ virtual bool isInternal() const; /** - * Returns the ratio between physical pixels and logical pixels. + * Returns the scalar value with which output-relative vectors in global space are multiplied + * by to map into view space. + * + * This can also be interpreted as the ratio between physical and logical pixels. * * Default implementation returns 1. */ Index: abstract_wayland_output.h =================================================================== --- abstract_wayland_output.h +++ abstract_wayland_output.h @@ -62,13 +62,32 @@ QString name() const override; QByteArray uuid() const override; - QSize pixelSize() const; qreal scale() const override; /** * The geometry of this output in global compositor co-ordinates (i.e scaled) */ QRect geometry() const override; + + /** + * Describes the viewable rectangle on the output relative to the output's mode size. + * + * Per default the view spans the full output. + * + * @return viewable geometry on the output + */ + QRect viewGeometry() const; + + /** + * The mode size is the current hardware mode of the output in pixel + * and is dependent on hardware parameters but can often be adjusted. In most + * cases running the maximum resolution is preferred though since this has the + * best picture quality. + * + * @return output mode size + */ + QSize pixelSize() const; + QSize physicalSize() const override; Qt::ScreenOrientation orientation() const override { return m_orientation; @@ -141,6 +160,7 @@ virtual void updateMode(int modeIndex) { Q_UNUSED(modeIndex); } + virtual void resetMode() {} virtual void transform(KWayland::Server::OutputDeviceInterface::Transform transform) { Q_UNUSED(transform); } @@ -153,6 +173,12 @@ void createWaylandOutput(); void createXdgOutput(); + void updateClientsScale(int scale); + void updateLogicalSize(const QSize &size); + + QSize logicalSize() const; + KWayland::Server::OutputDeviceInterface *replicationSource() const; + QPointer m_waylandOutput; QPointer m_xdgOutput; QPointer m_waylandOutputDevice; Index: abstract_wayland_output.cpp =================================================================== --- abstract_wayland_output.cpp +++ abstract_wayland_output.cpp @@ -39,9 +39,9 @@ AbstractWaylandOutput::~AbstractWaylandOutput() { + delete m_waylandOutputDevice.data(); delete m_xdgOutput.data(); delete m_waylandOutput.data(); - delete m_waylandOutputDevice.data(); } QString AbstractWaylandOutput::name() const @@ -55,9 +55,19 @@ return m_waylandOutputDevice->uuid(); } +QSize AbstractWaylandOutput::logicalSize() const +{ + return m_waylandOutputDevice->logicalSize(); +} + QRect AbstractWaylandOutput::geometry() const { - return QRect(globalPos(), pixelSize() / scale()); + return QRect(globalPos(), logicalSize()); +} + +QRect AbstractWaylandOutput::viewGeometry() const +{ + return m_waylandOutputDevice->viewGeometry(); } QSize AbstractWaylandOutput::physicalSize() const @@ -72,18 +82,12 @@ QPoint AbstractWaylandOutput::globalPos() const { - return m_waylandOutputDevice->globalPosition(); + return m_waylandOutputDevice->logicalPosition(); } void AbstractWaylandOutput::setGlobalPos(const QPoint &pos) { m_waylandOutputDevice->setGlobalPosition(pos); - - if (isEnabled()) { - m_waylandOutput->setGlobalPosition(pos); - m_xdgOutput->setLogicalPosition(pos); - m_xdgOutput->done(); - } } QSize AbstractWaylandOutput::pixelSize() const @@ -93,32 +97,43 @@ qreal AbstractWaylandOutput::scale() const { - return m_waylandOutputDevice->scaleF(); + // TODO: do this in KWayland? + if (const auto *source = m_waylandOutputDevice->replicationSource()) { + return source->scaleF(); + } else { + return m_waylandOutputDevice->scaleF(); + } } void AbstractWaylandOutput::setScale(qreal scale) { m_waylandOutputDevice->setScaleF(scale); +} - if (isEnabled()) { - // this is the scale that clients will ideally use for their buffers - // this has to be an int which is fine - - // I don't know whether we want to round or ceil - // or maybe even set this to 3 when we're scaling to 1.5 - // don't treat this like it's chosen deliberately - m_waylandOutput->setScale(std::ceil(scale)); - m_xdgOutput->setLogicalSize(pixelSize() / scale); - m_xdgOutput->done(); +void AbstractWaylandOutput::updateLogicalSize(const QSize &size) +{ + if (!isEnabled()) { + return; } + m_xdgOutput->setLogicalSize(size); + m_xdgOutput->done(); + emit modeChanged(); } +using DeviceInterface = KWayland::Server::OutputDeviceInterface; + void AbstractWaylandOutput::applyChanges(const KWayland::Server::OutputChangeSet *changeSet) { - qCDebug(KWIN_CORE) << "Apply changes to the Wayland output."; + qCDebug(KWIN_CORE) << "Apply changes to output:" << name(); bool emitModeChanged = false; // Enablement changes are handled by platform. + if (changeSet->replicationSourceChanged()) { + qCDebug(KWIN_CORE) << "Setting replication source." << changeSet->replicationSource(); + m_waylandOutputDevice->setReplicationSource(changeSet->replicationSource(), + DeviceInterface::TriggerReason::Explicit); + emitModeChanged = true; + } if (changeSet->modeChanged()) { qCDebug(KWIN_CORE) << "Setting new mode:" << changeSet->mode(); m_waylandOutputDevice->setCurrentMode(changeSet->mode()); @@ -138,16 +153,16 @@ if (changeSet->scaleChanged()) { qCDebug(KWIN_CORE) << "Setting scale:" << changeSet->scale(); setScale(changeSet->scaleF()); - emitModeChanged = true; + if (!replicationSource()) { + emitModeChanged = true; + } } if (emitModeChanged) { emit modeChanged(); } } -using DeviceInterface = KWayland::Server::OutputDeviceInterface; - bool AbstractWaylandOutput::isEnabled() const { return m_waylandOutputDevice->enabled() == DeviceInterface::Enablement::Enabled; @@ -177,7 +192,7 @@ return; } m_waylandOutput->setCurrentMode(size, refreshRate); - m_xdgOutput->setLogicalSize(pixelSize() / scale()); + m_xdgOutput->setLogicalSize(logicalSize()); m_xdgOutput->done(); } @@ -187,7 +202,7 @@ Q_ASSERT(m_xdgOutput.isNull()); m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput); - m_xdgOutput->setLogicalSize(pixelSize() / scale()); + m_xdgOutput->setLogicalSize(logicalSize()); m_xdgOutput->setLogicalPosition(globalPos()); m_xdgOutput->done(); } @@ -218,6 +233,8 @@ } m_waylandOutput->addMode(mode.size, flags, mode.refreshRate); } + + m_waylandOutput->setScale(m_waylandOutputDevice->clientsScale()); m_waylandOutput->create(); /* @@ -256,6 +273,30 @@ m_waylandOutputDevice->addMode(mode); } + connect(m_waylandOutputDevice, &DeviceInterface::logicalPositionChanged, + this, [this](const QPoint &pos) { + if (isEnabled()) { + m_waylandOutput->setGlobalPosition(pos); + m_xdgOutput->setLogicalPosition(pos); + m_xdgOutput->done(); + } + }); + + connect(m_waylandOutputDevice, &DeviceInterface::clientsScaleChanged, + this, [this](int scale) { + if (isEnabled()) { + m_waylandOutput->setScale(scale); + } + }); + connect(m_waylandOutputDevice, &DeviceInterface::logicalSizeChanged, + this, &AbstractWaylandOutput::updateLogicalSize); + connect(m_waylandOutputDevice, &DeviceInterface::viewGeometryChanged, + this, [this](const QRect &geo) { + Q_UNUSED(geo) + resetMode(); + emit modeChanged(); + }); + m_waylandOutputDevice->create(); createWaylandOutput(); } @@ -268,4 +309,9 @@ return size; } +DeviceInterface *AbstractWaylandOutput::replicationSource() const +{ + return m_waylandOutputDevice->replicationSource(); +} + } Index: platform.cpp =================================================================== --- platform.cpp +++ platform.cpp @@ -133,6 +133,8 @@ const auto changes = config->changes(); bool countChanged = false; + // TODO: check values and fail if not correct + //process all non-disabling changes for (auto it = changes.begin(); it != changes.end(); it++) { const KWayland::Server::OutputChangeSet *changeset = it.value(); Index: plugins/platforms/drm/drm_output.h =================================================================== --- plugins/platforms/drm/drm_output.h +++ plugins/platforms/drm/drm_output.h @@ -121,6 +121,7 @@ bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) override; void updateMode(int modeIndex) override; + void resetMode() override; void setWaylandMode(); void transform(KWayland::Server::OutputDeviceInterface::Transform transform) override; Index: plugins/platforms/drm/drm_output.cpp =================================================================== --- plugins/platforms/drm/drm_output.cpp +++ plugins/platforms/drm/drm_output.cpp @@ -181,23 +181,24 @@ const QMatrix4x4 hotspotMatrix = matrixDisplay(m_backend->softwareCursor().size()); QPoint p = globalPos - AbstractWaylandOutput::globalPos(); + const QRect &viewGeo = viewGeometry(); switch (orientation()) { case Qt::PrimaryOrientation: case Qt::LandscapeOrientation: break; case Qt::PortraitOrientation: - p = QPoint(p.y(), pixelSize().height() - p.x()); + p = QPoint(p.y(), viewGeo.height() - p.x()); break; case Qt::InvertedPortraitOrientation: - p = QPoint(pixelSize().width() - p.y(), p.x()); + p = QPoint(viewGeo.width() - p.y(), p.x()); break; case Qt::InvertedLandscapeOrientation: - p = QPoint(pixelSize().width() - p.x(), pixelSize().height() - p.y()); + p = QPoint(viewGeo.width() - p.x(), viewGeo.height() - p.y()); break; } - p *= scale(); + p *= viewGeo.width() / (double)geometry().width(); p -= hotspotMatrix.map(m_backend->softwareCursorHotspot()); - drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); + drmModeMoveCursor(m_backend->fd(), m_crtc->id(), viewGeo.x() + p.x(), viewGeo.y() + p.y()); } static QHash s_connectorNames = { @@ -727,6 +728,11 @@ setWaylandMode(); } +void DrmOutput::resetMode() +{ + m_modesetRequested = true; +} + void DrmOutput::setWaylandMode() { AbstractWaylandOutput::setWaylandMode(QSize(m_mode.hdisplay, m_mode.vdisplay), @@ -1029,12 +1035,15 @@ bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) { if (enable) { + const QRect geo = viewGeometry(); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); - m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); - m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); - m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); - m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), geo.width() << 16); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), geo.height() << 16); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcX), geo.x()); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcY), geo.y()); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), geo.width()); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), geo.height()); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); } else { if (m_backend->deleteBufferAfterPageFlip()) { @@ -1048,6 +1057,8 @@ m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcX), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcY), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0); Index: plugins/platforms/drm/egl_gbm_backend.cpp =================================================================== --- plugins/platforms/drm/egl_gbm_backend.cpp +++ plugins/platforms/drm/egl_gbm_backend.cpp @@ -168,7 +168,7 @@ bool EglGbmBackend::resetOutput(Output &o, DrmOutput *drmOutput) { o.output = drmOutput; - auto size = drmOutput->pixelSize(); + auto size = drmOutput->viewGeometry().size(); auto gbmSurface = std::make_shared(m_backend->gbmDevice(), size.width(), size.height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); @@ -233,13 +233,16 @@ } // TODO: ensure the viewport is set correctly each time const QSize &overall = screens()->size(); - const QRect &v = output.output->geometry(); - // TODO: are the values correct? - + const QRect &geo = output.output->geometry(); + const QRect &view = output.output->viewGeometry(); qreal scale = output.output->scale(); - glViewport(-v.x() * scale, (v.height() - overall.height() + v.y()) * scale, - overall.width() * scale, overall.height() * scale); + glViewport(-geo.x() * scale, + (geo.height() - overall.height() + geo.y()) * scale, + overall.width() * view.width() / (double)geo.width(), + overall.height() * view.height() / (double)geo.height() + ); + return true; }