diff --git a/abstract_wayland_output.h b/abstract_wayland_output.h --- a/abstract_wayland_output.h +++ b/abstract_wayland_output.h @@ -73,7 +73,26 @@ QString name() const override; QByteArray uuid() const override; + /** + * 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; + + /** + * 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; + QSize logicalSize() const; + qreal scale() const override; /** @@ -152,6 +171,7 @@ void setWaylandMode(const QSize &size, int refreshRate); void setTransform(Transform transform); + void setLogicalSize(const QSizeF &size); QSize orientateSize(const QSize &size) const; @@ -171,12 +191,14 @@ void createXdgOutput(); void setTransform(KWayland::Server::OutputDeviceInterface::Transform transform); + void updateViewGeometry(); QPointer m_waylandOutput; QPointer m_xdgOutput; QPointer m_waylandOutputDevice; KWayland::Server::OutputInterface::DpmsMode m_dpms = KWayland::Server::OutputInterface::DpmsMode::On; + QRect m_viewGeometry; bool m_internal = false; bool m_supportsDpms = false; diff --git a/abstract_wayland_output.cpp b/abstract_wayland_output.cpp --- a/abstract_wayland_output.cpp +++ b/abstract_wayland_output.cpp @@ -57,7 +57,7 @@ QRect AbstractWaylandOutput::geometry() const { - return QRect(globalPos(), pixelSize() / scale()); + return QRect(globalPos(), logicalSize()); } QSize AbstractWaylandOutput::physicalSize() const @@ -91,6 +91,49 @@ return orientateSize(m_waylandOutputDevice->pixelSize()); } +QRect AbstractWaylandOutput::viewGeometry() const +{ + return m_viewGeometry; +} + +void AbstractWaylandOutput::updateViewGeometry() +{ + if (!m_waylandOutputDevice->logicalSize().isValid()) { + // No logical size explicitly set. In this case the view geometry is just the full mode. + m_viewGeometry = QRect(QPoint(0, 0), m_waylandOutputDevice->pixelSize()); + return; + } + + // Fit view into output mode keeping the aspect ratio. + const QSize modeSize = m_waylandOutputDevice->pixelSize(); + const QSize sourceSize = m_waylandOutputDevice->logicalSize().toSize(); + + QSize viewSize; + viewSize.setWidth(modeSize.width()); + viewSize.setHeight(viewSize.width() * sourceSize.height() / (double)sourceSize.width()); + + if (viewSize.height() > modeSize.height()) { + const QSize oldSize = viewSize; + viewSize.setHeight(modeSize.height()); + viewSize.setWidth(oldSize.width() * viewSize.height() / (double)oldSize.height()); + } + + Q_ASSERT(viewSize.height() <= modeSize.height()); + Q_ASSERT(viewSize.width() <= modeSize.width()); + + const QPoint pos((modeSize.width() - viewSize.width()) / 2, + (modeSize.height() - viewSize.height()) / 2); + m_viewGeometry = QRect(pos, viewSize); +} + +QSize AbstractWaylandOutput::logicalSize() const +{ + if (m_waylandOutputDevice->logicalSize().isValid()) { + return m_waylandOutputDevice->logicalSize().toSize(); + } + return pixelSize() / scale(); +} + qreal AbstractWaylandOutput::scale() const { return m_waylandOutputDevice->scaleF(); @@ -108,7 +151,7 @@ // 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->setLogicalSize(logicalSize()); m_xdgOutput->done(); } } @@ -146,7 +189,16 @@ if (isEnabled()) { m_waylandOutput->setTransform(toOutputTransform(transform)); - m_xdgOutput->setLogicalSize(pixelSize() / scale()); + m_xdgOutput->setLogicalSize(logicalSize()); + m_xdgOutput->done(); + } +} + +void AbstractWaylandOutput::setLogicalSize(const QSizeF &size) +{ + m_waylandOutputDevice->setLogicalSize(size); + if (isEnabled()) { + m_xdgOutput->setLogicalSize(logicalSize()); m_xdgOutput->done(); } } @@ -192,6 +244,15 @@ emitModeChanged = true; } + // Set logical size first such that calculations will be done according to that. + if (changeSet->logicalSizeChanged()) { + qCDebug(KWIN_CORE) << "Setting logical size:" << changeSet->logicalSize(); + setLogicalSize(changeSet->logicalSize()); + emitModeChanged = true; + } + + updateViewGeometry(); + if (emitModeChanged) { emit modeChanged(); } @@ -226,7 +287,7 @@ return; } m_waylandOutput->setCurrentMode(size, refreshRate); - m_xdgOutput->setLogicalSize(pixelSize() / scale()); + m_xdgOutput->setLogicalSize(logicalSize()); m_xdgOutput->done(); } @@ -236,7 +297,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(); } @@ -304,6 +365,7 @@ qCDebug(KWIN_CORE).nospace() << "Adding mode " << ++i << ": " << mode.size << " [" << mode.refreshRate << "]"; m_waylandOutputDevice->addMode(mode); } + updateViewGeometry(); m_waylandOutputDevice->create(); createWaylandOutput(); diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -54,6 +54,7 @@ : AbstractWaylandOutput(backend) , m_backend(backend) { + connect(this, &DrmOutput::modeChanged, this, [this] { m_modesetRequested = true; }); } DrmOutput::~DrmOutput() @@ -186,30 +187,31 @@ const QMatrix4x4 hotspotMatrix = matrixDisplay(m_backend->softwareCursor().size()); QPoint p = globalPos - AbstractWaylandOutput::globalPos(); + const QRect &viewGeo = viewGeometry(); // TODO: Do we need to handle the flipped cases differently? switch (transform()) { case Transform::Normal: case Transform::Flipped: break; case Transform::Rotated90: case Transform::Flipped90: - p = QPoint(p.y(), pixelSize().height() - p.x()); + p = QPoint(p.y(), viewGeo.height() - p.x()); break; case Transform::Rotated270: case Transform::Flipped270: - p = QPoint(pixelSize().width() - p.y(), p.x()); + p = QPoint(viewGeo.width() - p.y(), p.x()); break; case Transform::Rotated180: case Transform::Flipped180: - p = QPoint(pixelSize().width() - p.x(), pixelSize().height() - p.y()); + p = QPoint(viewGeo.width() - p.x(), viewGeo.height() - p.y()); break; default: Q_UNREACHABLE(); } - 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 = { @@ -1007,12 +1009,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()) { @@ -1026,6 +1031,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); diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -182,7 +182,7 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput) { output.output = drmOutput; - const QSize size = drmOutput->pixelSize(); + const QSize size = drmOutput->viewGeometry().size(); auto gbmSurface = createGbmSurface(size); if (!gbmSurface) { @@ -359,11 +359,15 @@ void EglGbmBackend::setViewport(const Output &output) const { const QSize &overall = screens()->size(); - const QRect &v = output.output->geometry(); + 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() + ); } QRegion EglGbmBackend::prepareRenderingForScreen(int screenId)