Commit fe63e21f authored by Roman Gilg's avatar Roman Gilg
Browse files

Introduce generic Output class

Summary:
In order to separate high-level properties of individual outputs from
hardware-specific ones and access these, introduce a new generic class Output.

Also make the DrmOutput class directly a child class of this generic class.

The long-term goal is to get rid of the Screens global object on Wayland and
instead directly work with Output objects on compositing level.

This should enable us long-term to do direct scanout to hardware planes, what
I predict needs this generic output representation at one point.

Test Plan: Manually.

Reviewers: #kwin

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D11781
parent 8cf5041e
......@@ -453,6 +453,7 @@ set(kwin_KDEINIT_SRCS
decorations/decorationrenderer.cpp
decorations/decorations_logging.cpp
platform.cpp
abstract_output.cpp
shell_client.cpp
wayland_server.cpp
wayland_cursor_theme.cpp
......
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright 2018 Roman Gilg <subdiff@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "abstract_output.h"
#include "wayland_server.h"
// KWayland
#include <KWayland/Server/output_interface.h>
#include <KWayland/Server/outputchangeset.h>
#include <KWayland/Server/outputdevice_interface.h>
#include <KWayland/Server/xdgoutput_interface.h>
// KF5
#include <KLocalizedString>
#include <cmath>
namespace KWin
{
AbstractOutput::AbstractOutput(QObject *parent)
: QObject(parent)
{
}
AbstractOutput::~AbstractOutput()
{
delete m_waylandOutputDevice.data();
delete m_xdgOutput.data();
delete m_waylandOutput.data();
}
QString AbstractOutput::name() const
{
if (!m_waylandOutput) {
return i18n("unknown");
}
return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model());
}
QRect AbstractOutput::geometry() const
{
return QRect(m_globalPos, pixelSize() / scale());
}
QSize AbstractOutput::physicalSize() const
{
if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) {
return m_physicalSize.transposed();
}
return m_physicalSize;
}
void AbstractOutput::setGlobalPos(const QPoint &pos)
{
m_globalPos = pos;
if (m_waylandOutput) {
m_waylandOutput->setGlobalPosition(pos);
}
if (m_waylandOutputDevice) {
m_waylandOutputDevice->setGlobalPosition(pos);
}
if (m_xdgOutput) {
m_xdgOutput->setLogicalPosition(pos);
m_xdgOutput->done();
}
}
void AbstractOutput::setScale(qreal scale)
{
m_scale = scale;
if (m_waylandOutput) {
// 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));
}
if (m_waylandOutputDevice) {
m_waylandOutputDevice->setScaleF(scale);
}
if (m_xdgOutput) {
m_xdgOutput->setLogicalSize(pixelSize() / m_scale);
m_xdgOutput->done();
}
}
void AbstractOutput::setChanges(KWayland::Server::OutputChangeSet *changes)
{
m_changeset = changes;
qCDebug(KWIN_CORE) << "set changes in AbstractOutput";
commitChanges();
}
void AbstractOutput::setWaylandOutput(KWayland::Server::OutputInterface *set)
{
m_waylandOutput = set;
}
void AbstractOutput::createXdgOutput()
{
if (!m_waylandOutput || m_xdgOutput) {
return;
}
m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput);
}
void AbstractOutput::setWaylandOutputDevice(KWayland::Server::OutputDeviceInterface *set)
{
m_waylandOutputDevice = set;
}
}
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright 2018 Roman Gilg <subdiff@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_OUTPUT_H
#define KWIN_OUTPUT_H
#include <utils.h>
#include <kwin_export.h>
#include <QObject>
#include <QPoint>
#include <QPointer>
#include <QRect>
#include <QSize>
#include <QVector>
namespace KWayland
{
namespace Server
{
class OutputInterface;
class OutputDeviceInterface;
class OutputChangeSet;
class OutputManagementInterface;
class XdgOutputInterface;
}
}
namespace KWin
{
/**
* Generic output representation in a Wayland session
**/
class KWIN_EXPORT AbstractOutput : public QObject
{
Q_OBJECT
public:
explicit AbstractOutput(QObject *parent = nullptr);
virtual ~AbstractOutput();
QString name() const;
bool isEnabled() const {
return !m_waylandOutput.isNull();
}
virtual QSize pixelSize() const = 0;
qreal scale() const {
return m_scale;
}
/*
* The geometry of this output in global compositor co-ordinates (i.e scaled)
*/
QRect geometry() const;
QSize physicalSize() const;
Qt::ScreenOrientation orientation() const {
return m_orientation;
}
bool isInternal() const {
return m_internal;
}
void setGlobalPos(const QPoint &pos);
void setScale(qreal scale);
/**
* This sets the changes and tests them against the specific output
*/
void setChanges(KWayland::Server::OutputChangeSet *changeset);
virtual bool commitChanges() { return false; }
QPointer<KWayland::Server::OutputInterface> waylandOutput() const {
return m_waylandOutput;
}
protected:
QPointer<KWayland::Server::OutputChangeSet> changes() const {
return m_changeset;
}
void setWaylandOutput(KWayland::Server::OutputInterface *set);
QPointer<KWayland::Server::XdgOutputInterface> xdgOutput() const {
return m_xdgOutput;
}
void createXdgOutput();
QPointer<KWayland::Server::OutputDeviceInterface> waylandOutputDevice() const {
return m_waylandOutputDevice;
}
void setWaylandOutputDevice(KWayland::Server::OutputDeviceInterface *set);
QPoint globalPos() const {
return m_globalPos;
}
QSize rawPhysicalSize() const {
return m_physicalSize;
}
void setRawPhysicalSize(const QSize &set) {
m_physicalSize = set;
}
void setOrientation(Qt::ScreenOrientation set) {
m_orientation = set;
}
bool internal() const {
return m_internal;
}
void setInternal(bool set) {
m_internal = set;
}
private:
QPointer<KWayland::Server::OutputChangeSet> m_changeset;
QPointer<KWayland::Server::OutputInterface> m_waylandOutput;
QPointer<KWayland::Server::XdgOutputInterface> m_xdgOutput;
QPointer<KWayland::Server::OutputDeviceInterface> m_waylandOutputDevice;
QPoint m_globalPos;
qreal m_scale = 1;
QSize m_physicalSize;
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
bool m_internal = false;
};
}
#endif // KWIN_OUTPUT_H
......@@ -45,6 +45,7 @@ class Manager;
struct GammaRamp;
}
class AbstractOutput;
class Edge;
class Compositor;
class OverlayWindow;
......@@ -64,6 +65,17 @@ class Renderer;
class DecoratedClientImpl;
}
class KWIN_EXPORT Outputs : public QVector<AbstractOutput*>
{
public:
Outputs(){};
template <typename T>
Outputs(const QVector<T> &other) {
resize(other.size());
std::copy(other.constBegin(), other.constEnd(), begin());
}
};
class KWIN_EXPORT Platform : public QObject
{
Q_OBJECT
......@@ -411,6 +423,15 @@ public:
return false;
}
// outputs with connections (org_kde_kwin_outputdevice)
virtual Outputs outputs() const {
return Outputs();
}
// actively compositing outputs (wl_output)
virtual Outputs enabledOutputs() const {
return Outputs();
}
/*
* A string of information to include in kwin debug output
* It should not be translated.
......
......@@ -49,6 +49,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QSocketNotifier>
#include <QPainter>
// system
#include <algorithm>
#include <unistd.h>
// drm
#include <xf86drm.h>
......@@ -90,7 +91,11 @@ DrmBackend::~DrmBackend()
while (m_pageFlipsPending != 0) {
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
// we need to first remove all outputs
qDeleteAll(m_outputs);
m_outputs.clear();
m_enabledOutputs.clear();
qDeleteAll(m_planes);
qDeleteAll(m_crtcs);
qDeleteAll(m_connectors);
......@@ -116,6 +121,16 @@ void DrmBackend::init()
}
}
Outputs DrmBackend::outputs() const
{
return m_outputs;
}
Outputs DrmBackend::enabledOutputs() const
{
return m_enabledOutputs;
}
void DrmBackend::outputWentOff()
{
if (!m_dpmsFilter.isNull()) {
......
......@@ -33,6 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QImage>
#include <QPointer>
#include <QSize>
#include <QVector>
#include <xf86drmMode.h>
#include <memory>
......@@ -93,12 +94,15 @@ public:
int fd() const {
return m_fd;
}
QVector<DrmOutput*> outputs() const {
Outputs outputs() const override;
Outputs enabledOutputs() const override;
QVector<DrmOutput*> drmOutputs() const {
return m_outputs;
}
QVector<DrmOutput*> enabledOutputs() const {
QVector<DrmOutput*> drmEnabledOutputs() const {
return m_enabledOutputs;
}
QVector<DrmPlane*> planes() const {
return m_planes;
}
......
......@@ -36,7 +36,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Server/display.h>
#include <KWayland/Server/output_interface.h>
#include <KWayland/Server/outputchangeset.h>
#include <KWayland/Server/outputdevice_interface.h>
#include <KWayland/Server/outputmanagement_interface.h>
#include <KWayland/Server/outputconfiguration_interface.h>
#include <KWayland/Server/xdgoutput_interface.h>
......@@ -53,13 +52,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <xf86drmMode.h>
#include <libdrm/drm_mode.h>
#include <cmath>
namespace KWin
{
DrmOutput::DrmOutput(DrmBackend *backend)
: QObject()
: AbstractOutput(backend)
, m_backend(backend)
{
}
......@@ -91,8 +88,6 @@ void DrmOutput::teardown()
m_crtc->setOutput(nullptr);
m_conn->setOutput(nullptr);
delete m_waylandOutput.data();
delete m_waylandOutputDevice.data();
delete m_cursor[0];
delete m_cursor[1];
if (!m_pageFlipPending) {
......@@ -146,11 +141,11 @@ void DrmOutput::updateCursor()
m_hasNewCursor = true;
QImage *c = m_cursor[m_cursorIndex]->image();
c->fill(Qt::transparent);
c->setDevicePixelRatio(m_scale);
c->setDevicePixelRatio(scale());
QPainter p;
p.begin(c);
if (m_orientation == Qt::InvertedLandscapeOrientation) {
if (orientation() == Qt::InvertedLandscapeOrientation) {
QMatrix4x4 matrix;
matrix.translate(cursorImage.width() / 2.0, cursorImage.height() / 2.0);
matrix.rotate(180.0f, 0.0f, 0.0f, 1.0f);
......@@ -165,7 +160,7 @@ void DrmOutput::moveCursor(const QPoint &globalPos)
{
QMatrix4x4 matrix;
QMatrix4x4 hotspotMatrix;
if (m_orientation == Qt::InvertedLandscapeOrientation) {
if (orientation() == Qt::InvertedLandscapeOrientation) {
matrix.translate(pixelSize().width() /2.0, pixelSize().height() / 2.0);
matrix.rotate(180.0f, 0.0f, 0.0f, 1.0f);
matrix.translate(-pixelSize().width() /2.0, -pixelSize().height() / 2.0);
......@@ -174,39 +169,23 @@ void DrmOutput::moveCursor(const QPoint &globalPos)
hotspotMatrix.rotate(180.0f, 0.0f, 0.0f, 1.0f);
hotspotMatrix.translate(-cursorSize.width()/2.0, -cursorSize.height()/2.0);
}
hotspotMatrix.scale(m_scale);
matrix.scale(m_scale);
matrix.translate(-m_globalPos.x(), -m_globalPos.y());
hotspotMatrix.scale(scale());
matrix.scale(scale());
const auto outputGlobalPos = AbstractOutput::globalPos();
matrix.translate(-outputGlobalPos.x(), -outputGlobalPos.y());
const QPoint p = matrix.map(globalPos) - hotspotMatrix.map(m_backend->softwareCursorHotspot());
drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y());
}
QSize DrmOutput::pixelSize() const
{
if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) {
auto orient = orientation();
if (orient == Qt::PortraitOrientation || orient == Qt::InvertedPortraitOrientation) {
return QSize(m_mode.vdisplay, m_mode.hdisplay);
}
return QSize(m_mode.hdisplay, m_mode.vdisplay);
}
QSize DrmOutput::physicalSize() const
{
if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) {
return QSize(m_physicalSize.height(), m_physicalSize.width());
}
return m_physicalSize;
}
QRect DrmOutput::geometry() const
{
return QRect(m_globalPos, pixelSize() / m_scale);
}
qreal DrmOutput::scale() const
{
return m_scale;
}
void DrmOutput::setEnabled(bool enabled)
{
if (enabled == isEnabled()) {
......@@ -217,17 +196,12 @@ void DrmOutput::setEnabled(bool enabled)
initOutput();
} else {
setDpms(DpmsMode::Off);
delete m_waylandOutput.data();
delete waylandOutput().data();
}
m_waylandOutputDevice->setEnabled(enabled ?
waylandOutputDevice()->setEnabled(enabled ?
KWayland::Server::OutputDeviceInterface::Enablement::Enabled : KWayland::Server::OutputDeviceInterface::Enablement::Disabled);
}
bool DrmOutput::isEnabled() const
{
return !m_waylandOutput.isNull();
}
static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode)
{
using namespace KWayland::Server;
......@@ -314,9 +288,9 @@ bool DrmOutput::init(drmModeConnector *connector)
return false;
}
m_internal = connector->connector_type == DRM_MODE_CONNECTOR_LVDS || connector->connector_type == DRM_MODE_CONNECTOR_eDP;
setInternal(connector->connector_type == DRM_MODE_CONNECTOR_LVDS || connector->connector_type == DRM_MODE_CONNECTOR_eDP);
if (m_internal) {
if (internal()) {
connect(kwinApp(), &Application::screensCreated, this,
[this] {
connect(screens()->orientationSensor(), &OrientationSensor::orientationChanged, this, &DrmOutput::automaticRotation);
......@@ -336,7 +310,7 @@ bool DrmOutput::init(drmModeConnector *connector)
qCWarning(KWIN_DRM) << "Overwriting monitor physical size for" << m_edid.eisaId << "/" << m_edid.monitorName << "/" << m_edid.serialNumber << " from " << physicalSize << "to " << overwriteSize;
physicalSize = overwriteSize;
}
m_physicalSize = physicalSize;
setRawPhysicalSize(physicalSize);
initOutputDevice(connector);
......@@ -356,41 +330,48 @@ void DrmOutput::initUuid()
void DrmOutput::initOutput()
{
Q_ASSERT(m_waylandOutputDevice);
if (!m_waylandOutput.isNull()) {
delete m_waylandOutput.data();
m_waylandOutput.clear();
auto wlOutputDevice = waylandOutputDevice();
Q_ASSERT(wlOutputDevice);
auto wlOutput = waylandOutput();
if (!wlOutput.isNull()) {
delete wlOutput.data();
wlOutput.clear();
}
m_waylandOutput = waylandServer()->display()->createOutput();
m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput);
wlOutput = waylandServer()->display()->createOutput();
setWaylandOutput(wlOutput.data());
createXdgOutput();
connect(this, &DrmOutput::modeChanged, this,
[this] {
if (m_waylandOutput.isNull()) {
auto wlOutput = waylandOutput();
if (wlOutput.isNull()) {
return;
}
m_waylandOutput->setCurrentMode(QSize(m_mode.hdisplay, m_mode.vdisplay), refreshRateForMode(&m_mode));
if (m_xdgOutput) {
m_xdgOutput->setLogicalSize(pixelSize() / m_scale);
m_xdgOutput->done();
wlOutput->setCurrentMode(QSize(m_mode.hdisplay, m_mode.vdisplay),
refreshRateForMode(&m_mode));
auto xdg = xdgOutput();
if (xdg) {
xdg->setLogicalSize(pixelSize() / scale());
xdg->done();
}
}
);
m_waylandOutput->setManufacturer(m_waylandOutputDevice->manufacturer());
m_waylandOutput->setModel(m_waylandOutputDevice->model());
m_waylandOutput->setPhysicalSize(m_physicalSize);
wlOutput->setManufacturer(wlOutputDevice->manufacturer());
wlOutput->setModel(wlOutputDevice->model());
wlOutput->setPhysicalSize(rawPhysicalSize());
// set dpms
if (!m_dpms.isNull()) {
m_waylandOutput->setDpmsSupported(true);
m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode));
connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this,
wlOutput->setDpmsSupported(true);
wlOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode));
connect(wlOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this,
[this] (KWayland::Server::OutputInterface::DpmsMode mode) {
setDpms(fromWaylandDpmsMode(mode));
}, Qt::QueuedConnection
);
}
for(const auto &mode: m_waylandOutputDevice->modes()) {
for(const auto &mode: wlOutputDevice->modes()) {
KWayland::Server::OutputInterface::ModeFlags flags;
if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Current) {
flags |= KWayland::Server::OutputInterface::ModeFlag::Current;
......@@ -398,25 +379,26 @@ void DrmOutput::initOutput()
if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred) {
flags |= KWayland::Server::OutputInterface::ModeFlag::Preferred;
}
m_waylandOutput->addMode(mode.size, flags, mode.refreshRate);
wlOutput->addMode(mode.size, flags, mode.refreshRate);
}
m_waylandOutput->create();
wlOutput->create();
}
void DrmOutput::initOutputDevice(drmModeConnector *connector)
{
if (!m_waylandOutputDevice.isNull()) {
delete m_waylandOutputDevice.data();
m_waylandOutputDevice.clear();
auto wlOutputDevice = waylandOutputDevice();
if (!wlOutputDevice.isNull()) {
delete wlOutputDevice.data();
wlOutputDevice.clear();
}
m_waylandOutputDevice = waylandServer()->display()->createOutputDevice();
m_waylandOutputDevice->setUuid(m_uuid);
wlOutputDevice = waylandServer()->display()->createOutputDevice();
wlOutputDevice->setUuid(m_uuid);