e2a0863843
Hardware constraints limit the number of crtcs and which connector + crtc combinations can work together. The current code is searching for working combinations when a hotplug happens but that's not enough, it also needs to happen when the user enables or disables outputs and when modesets are done, and the configuration change needs to be applied with a single atomic commit. This commit removes the hard dependency of DrmPipeline on crtcs by moving the pending state of outputs from the drm objects to DrmPipeline itself, which ensures that it's independent from the set of drm objects currently used. It also changes requests from KScreen to be applied truly atomically.
380 lines
8.6 KiB
C++
380 lines
8.6 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
|
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
#include "abstract_wayland_output.h"
|
|
#include "screens.h"
|
|
#include "waylandoutputconfig.h"
|
|
|
|
// KWayland
|
|
#include <KWaylandServer/outputchangeset_v2.h>
|
|
// KF5
|
|
#include <KLocalizedString>
|
|
|
|
#include <QMatrix4x4>
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent)
|
|
: AbstractOutput(parent)
|
|
{
|
|
}
|
|
|
|
AbstractWaylandOutput::Capabilities AbstractWaylandOutput::capabilities() const
|
|
{
|
|
return m_capabilities;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setCapabilityInternal(Capability capability, bool on)
|
|
{
|
|
if (static_cast<bool>(m_capabilities & capability) != on) {
|
|
m_capabilities.setFlag(capability, on);
|
|
Q_EMIT capabilitiesChanged();
|
|
}
|
|
}
|
|
|
|
QString AbstractWaylandOutput::name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
QUuid AbstractWaylandOutput::uuid() const
|
|
{
|
|
return m_uuid;
|
|
}
|
|
|
|
QRect AbstractWaylandOutput::geometry() const
|
|
{
|
|
return QRect(m_position, pixelSize() / scale());
|
|
}
|
|
|
|
QSize AbstractWaylandOutput::physicalSize() const
|
|
{
|
|
return orientateSize(m_physicalSize);
|
|
}
|
|
|
|
int AbstractWaylandOutput::refreshRate() const
|
|
{
|
|
return m_refreshRate;
|
|
}
|
|
|
|
void AbstractWaylandOutput::moveTo(const QPoint &pos)
|
|
{
|
|
if (m_position != pos) {
|
|
m_position = pos;
|
|
Q_EMIT geometryChanged();
|
|
}
|
|
}
|
|
|
|
QString AbstractWaylandOutput::eisaId() const
|
|
{
|
|
return m_eisaId;
|
|
}
|
|
|
|
QString AbstractWaylandOutput::manufacturer() const
|
|
{
|
|
return m_manufacturer;
|
|
}
|
|
|
|
QString AbstractWaylandOutput::model() const
|
|
{
|
|
return m_model;
|
|
}
|
|
|
|
QString AbstractWaylandOutput::serialNumber() const
|
|
{
|
|
return m_serialNumber;
|
|
}
|
|
|
|
QSize AbstractWaylandOutput::modeSize() const
|
|
{
|
|
return m_modeSize;
|
|
}
|
|
|
|
QSize AbstractWaylandOutput::pixelSize() const
|
|
{
|
|
return orientateSize(m_modeSize);
|
|
}
|
|
|
|
QByteArray AbstractWaylandOutput::edid() const
|
|
{
|
|
return m_edid;
|
|
}
|
|
|
|
bool AbstractWaylandOutput::Mode::operator==(const Mode &other) const {
|
|
return id == other.id && other.flags == flags && size == other.size && refreshRate == other.refreshRate;
|
|
}
|
|
|
|
QVector<AbstractWaylandOutput::Mode> AbstractWaylandOutput::modes() const
|
|
{
|
|
return m_modes;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setModes(const QVector<Mode> &modes)
|
|
{
|
|
if (m_modes != modes) {
|
|
m_modes = modes;
|
|
Q_EMIT modesChanged();
|
|
}
|
|
}
|
|
|
|
qreal AbstractWaylandOutput::scale() const
|
|
{
|
|
return m_scale;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setScale(qreal scale)
|
|
{
|
|
if (m_scale != scale) {
|
|
m_scale = scale;
|
|
Q_EMIT scaleChanged();
|
|
Q_EMIT geometryChanged();
|
|
}
|
|
}
|
|
|
|
AbstractWaylandOutput::SubPixel AbstractWaylandOutput::subPixel() const
|
|
{
|
|
return m_subPixel;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setSubPixelInternal(SubPixel subPixel)
|
|
{
|
|
m_subPixel = subPixel;
|
|
}
|
|
|
|
void AbstractWaylandOutput::applyChanges(const WaylandOutputConfig &config)
|
|
{
|
|
auto props = config.constChangeSet(this);
|
|
Q_EMIT aboutToChange();
|
|
|
|
setEnabled(props->enabled);
|
|
updateTransform(props->transform);
|
|
moveTo(props->pos);
|
|
setScale(props->scale);
|
|
setVrrPolicy(props->vrrPolicy);
|
|
|
|
Q_EMIT changed();
|
|
}
|
|
|
|
bool AbstractWaylandOutput::isEnabled() const
|
|
{
|
|
return m_isEnabled;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setEnabled(bool enable)
|
|
{
|
|
if (m_isEnabled != enable) {
|
|
m_isEnabled = enable;
|
|
updateEnablement(enable);
|
|
Q_EMIT enabledChanged();
|
|
}
|
|
}
|
|
|
|
QString AbstractWaylandOutput::description() const
|
|
{
|
|
return m_manufacturer + ' ' + m_model;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setCurrentModeInternal(const QSize &size, int refreshRate)
|
|
{
|
|
const bool sizeChanged = m_modeSize != size;
|
|
if (sizeChanged || m_refreshRate != refreshRate) {
|
|
m_modeSize = size;
|
|
m_refreshRate = refreshRate;
|
|
|
|
Q_EMIT currentModeChanged();
|
|
if (sizeChanged) {
|
|
Q_EMIT geometryChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
static QUuid generateOutputId(const QString &eisaId, const QString &model,
|
|
const QString &serialNumber, const QString &name)
|
|
{
|
|
static const QUuid urlNs = QUuid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); // NameSpace_URL
|
|
static const QUuid kwinNs = QUuid::createUuidV5(urlNs, QStringLiteral("https://kwin.kde.org/o/"));
|
|
|
|
const QString payload = QStringList{name, eisaId, model, serialNumber}.join(':');
|
|
return QUuid::createUuidV5(kwinNs, payload);
|
|
}
|
|
|
|
void AbstractWaylandOutput::initialize(const QString &model, const QString &manufacturer,
|
|
const QString &eisaId, const QString &serialNumber,
|
|
const QSize &physicalSize,
|
|
const QVector<Mode> &modes, const QByteArray &edid)
|
|
{
|
|
m_serialNumber = serialNumber;
|
|
m_eisaId = eisaId;
|
|
m_manufacturer = manufacturer.isEmpty() ? i18n("unknown") : manufacturer;
|
|
m_model = model;
|
|
m_physicalSize = physicalSize;
|
|
m_edid = edid;
|
|
m_modes = modes;
|
|
m_uuid = generateOutputId(m_eisaId, m_model, m_serialNumber, m_name);
|
|
|
|
for (const Mode &mode : modes) {
|
|
if (mode.flags & ModeFlag::Current) {
|
|
m_modeSize = mode.size;
|
|
m_refreshRate = mode.refreshRate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
QSize AbstractWaylandOutput::orientateSize(const QSize &size) const
|
|
{
|
|
if (m_transform == Transform::Rotated90 || m_transform == Transform::Rotated270 ||
|
|
m_transform == Transform::Flipped90 || m_transform == Transform::Flipped270) {
|
|
return size.transposed();
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setTransformInternal(Transform transform)
|
|
{
|
|
if (m_transform != transform) {
|
|
m_transform = transform;
|
|
Q_EMIT transformChanged();
|
|
Q_EMIT currentModeChanged();
|
|
Q_EMIT geometryChanged();
|
|
}
|
|
}
|
|
|
|
AbstractWaylandOutput::Transform AbstractWaylandOutput::transform() const
|
|
{
|
|
return m_transform;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setDpmsModeInternal(DpmsMode dpmsMode)
|
|
{
|
|
if (m_dpmsMode != dpmsMode) {
|
|
m_dpmsMode = dpmsMode;
|
|
Q_EMIT dpmsModeChanged();
|
|
}
|
|
}
|
|
|
|
void AbstractWaylandOutput::setDpmsMode(DpmsMode mode)
|
|
{
|
|
Q_UNUSED(mode)
|
|
}
|
|
|
|
AbstractWaylandOutput::DpmsMode AbstractWaylandOutput::dpmsMode() const
|
|
{
|
|
return m_dpmsMode;
|
|
}
|
|
|
|
QMatrix4x4 AbstractWaylandOutput::logicalToNativeMatrix(const QRect &rect, qreal scale, Transform transform)
|
|
{
|
|
QMatrix4x4 matrix;
|
|
matrix.scale(scale);
|
|
|
|
switch (transform) {
|
|
case Transform::Normal:
|
|
case Transform::Flipped:
|
|
break;
|
|
case Transform::Rotated90:
|
|
case Transform::Flipped90:
|
|
matrix.translate(0, rect.width());
|
|
matrix.rotate(-90, 0, 0, 1);
|
|
break;
|
|
case Transform::Rotated180:
|
|
case Transform::Flipped180:
|
|
matrix.translate(rect.width(), rect.height());
|
|
matrix.rotate(-180, 0, 0, 1);
|
|
break;
|
|
case Transform::Rotated270:
|
|
case Transform::Flipped270:
|
|
matrix.translate(rect.height(), 0);
|
|
matrix.rotate(-270, 0, 0, 1);
|
|
break;
|
|
}
|
|
|
|
switch (transform) {
|
|
case Transform::Flipped:
|
|
case Transform::Flipped90:
|
|
case Transform::Flipped180:
|
|
case Transform::Flipped270:
|
|
matrix.translate(rect.width(), 0);
|
|
matrix.scale(-1, 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
matrix.translate(-rect.x(), -rect.y());
|
|
|
|
return matrix;
|
|
}
|
|
|
|
void AbstractWaylandOutput::recordingStarted()
|
|
{
|
|
m_recorders++;
|
|
}
|
|
|
|
void AbstractWaylandOutput::recordingStopped()
|
|
{
|
|
m_recorders--;
|
|
}
|
|
|
|
bool AbstractWaylandOutput::isBeingRecorded()
|
|
{
|
|
return m_recorders;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setOverscanInternal(uint32_t overscan)
|
|
{
|
|
if (m_overscan != overscan) {
|
|
m_overscan = overscan;
|
|
Q_EMIT overscanChanged();
|
|
}
|
|
}
|
|
|
|
uint32_t AbstractWaylandOutput::overscan() const
|
|
{
|
|
return m_overscan;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setVrrPolicy(RenderLoop::VrrPolicy policy)
|
|
{
|
|
if (renderLoop()->vrrPolicy() != policy && (m_capabilities & Capability::Vrr)) {
|
|
renderLoop()->setVrrPolicy(policy);
|
|
Q_EMIT vrrPolicyChanged();
|
|
}
|
|
}
|
|
|
|
RenderLoop::VrrPolicy AbstractWaylandOutput::vrrPolicy() const
|
|
{
|
|
return renderLoop()->vrrPolicy();
|
|
}
|
|
|
|
bool AbstractWaylandOutput::isPlaceholder() const
|
|
{
|
|
return m_isPlaceholder;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setPlaceholder(bool isPlaceholder)
|
|
{
|
|
m_isPlaceholder = isPlaceholder;
|
|
}
|
|
|
|
AbstractWaylandOutput::RgbRange AbstractWaylandOutput::rgbRange() const
|
|
{
|
|
return m_rgbRange;
|
|
}
|
|
|
|
void AbstractWaylandOutput::setRgbRangeInternal(RgbRange range)
|
|
{
|
|
if (m_rgbRange != range) {
|
|
m_rgbRange = range;
|
|
Q_EMIT rgbRangeChanged();
|
|
}
|
|
}
|
|
|
|
}
|