Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
target_sources(kwin PRIVATE
|
||||
wayland_backend.cpp
|
||||
wayland_display.cpp
|
||||
wayland_egl_backend.cpp
|
||||
wayland_logging.cpp
|
||||
wayland_output.cpp
|
||||
wayland_qpainter_backend.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(kwin PRIVATE Plasma::KWaylandClient Wayland::Client gbm::gbm)
|
||||
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "wayland_backend.h"
|
||||
#include "core/drmdevice.h"
|
||||
#include "input.h"
|
||||
#include "wayland_display.h"
|
||||
#include "wayland_egl_backend.h"
|
||||
#include "wayland_logging.h"
|
||||
#include "wayland_output.h"
|
||||
#include "wayland_qpainter_backend.h"
|
||||
|
||||
#include <KWayland/Client/keyboard.h>
|
||||
#include <KWayland/Client/pointer.h>
|
||||
#include <KWayland/Client/pointergestures.h>
|
||||
#include <KWayland/Client/relativepointer.h>
|
||||
#include <KWayland/Client/seat.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <KWayland/Client/touch.h>
|
||||
|
||||
#include <QAbstractEventDispatcher>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <fcntl.h>
|
||||
#include <gbm.h>
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client-core.h>
|
||||
|
||||
#include "wayland-linux-dmabuf-unstable-v1-client-protocol.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
using namespace KWayland::Client;
|
||||
|
||||
inline static QPointF sizeToPoint(const QSizeF &size)
|
||||
{
|
||||
return QPointF(size.width(), size.height());
|
||||
}
|
||||
|
||||
WaylandInputDevice::WaylandInputDevice(KWayland::Client::Keyboard *keyboard, WaylandSeat *seat)
|
||||
: m_seat(seat)
|
||||
, m_keyboard(keyboard)
|
||||
{
|
||||
connect(keyboard, &Keyboard::left, this, [this](quint32 time) {
|
||||
for (quint32 key : std::as_const(m_pressedKeys)) {
|
||||
Q_EMIT keyChanged(key, KeyboardKeyState::Released, std::chrono::milliseconds(time), this);
|
||||
}
|
||||
m_pressedKeys.clear();
|
||||
});
|
||||
connect(keyboard, &Keyboard::keyChanged, this, [this](quint32 key, Keyboard::KeyState nativeState, quint32 time) {
|
||||
KeyboardKeyState state;
|
||||
switch (nativeState) {
|
||||
case Keyboard::KeyState::Pressed:
|
||||
if (key == KEY_RIGHTCTRL) {
|
||||
m_seat->backend()->togglePointerLock();
|
||||
}
|
||||
state = KeyboardKeyState::Pressed;
|
||||
m_pressedKeys.insert(key);
|
||||
break;
|
||||
case Keyboard::KeyState::Released:
|
||||
m_pressedKeys.remove(key);
|
||||
state = KeyboardKeyState::Released;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
Q_EMIT keyChanged(key, state, std::chrono::milliseconds(time), this);
|
||||
});
|
||||
}
|
||||
|
||||
WaylandInputDevice::WaylandInputDevice(KWayland::Client::Pointer *pointer, WaylandSeat *seat)
|
||||
: m_seat(seat)
|
||||
, m_pointer(pointer)
|
||||
{
|
||||
connect(pointer, &Pointer::entered, this, [this](quint32 serial, const QPointF &relativeToSurface) {
|
||||
WaylandOutput *output = m_seat->backend()->findOutput(m_pointer->enteredSurface());
|
||||
Q_ASSERT(output);
|
||||
output->cursor()->setPointer(m_pointer.get());
|
||||
});
|
||||
connect(pointer, &Pointer::left, this, [this]() {
|
||||
// wl_pointer.leave carries the wl_surface, but KWayland::Client::Pointer::left does not.
|
||||
const auto outputs = m_seat->backend()->outputs();
|
||||
for (Output *output : outputs) {
|
||||
WaylandOutput *waylandOutput = static_cast<WaylandOutput *>(output);
|
||||
if (waylandOutput->cursor()->pointer()) {
|
||||
waylandOutput->cursor()->setPointer(nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(pointer, &Pointer::motion, this, [this](const QPointF &relativeToSurface, quint32 time) {
|
||||
WaylandOutput *output = m_seat->backend()->findOutput(m_pointer->enteredSurface());
|
||||
Q_ASSERT(output);
|
||||
const QPointF absolutePos = output->geometry().topLeft() + relativeToSurface;
|
||||
Q_EMIT pointerMotionAbsolute(absolutePos, std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(pointer, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState nativeState) {
|
||||
PointerButtonState state;
|
||||
switch (nativeState) {
|
||||
case Pointer::ButtonState::Pressed:
|
||||
state = PointerButtonState::Pressed;
|
||||
break;
|
||||
case Pointer::ButtonState::Released:
|
||||
state = PointerButtonState::Released;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
Q_EMIT pointerButtonChanged(button, state, std::chrono::milliseconds(time), this);
|
||||
});
|
||||
// TODO: Send discreteDelta and source as well.
|
||||
connect(pointer, &Pointer::axisChanged, this, [this](quint32 time, Pointer::Axis nativeAxis, qreal delta) {
|
||||
PointerAxis axis;
|
||||
switch (nativeAxis) {
|
||||
case Pointer::Axis::Horizontal:
|
||||
axis = PointerAxis::Horizontal;
|
||||
break;
|
||||
case Pointer::Axis::Vertical:
|
||||
axis = PointerAxis::Vertical;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
Q_EMIT pointerAxisChanged(axis, delta, 0, PointerAxisSource::Unknown, false, std::chrono::milliseconds(time), this);
|
||||
});
|
||||
|
||||
connect(pointer, &Pointer::frame, this, [this]() {
|
||||
Q_EMIT pointerFrame(this);
|
||||
});
|
||||
|
||||
KWayland::Client::PointerGestures *pointerGestures = m_seat->backend()->display()->pointerGestures();
|
||||
if (pointerGestures) {
|
||||
m_pinchGesture.reset(pointerGestures->createPinchGesture(m_pointer.get(), this));
|
||||
connect(m_pinchGesture.get(), &PointerPinchGesture::started, this, [this](quint32 serial, quint32 time) {
|
||||
Q_EMIT pinchGestureBegin(m_pinchGesture->fingerCount(), std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(m_pinchGesture.get(), &PointerPinchGesture::updated, this, [this](const QSizeF &delta, qreal scale, qreal rotation, quint32 time) {
|
||||
Q_EMIT pinchGestureUpdate(scale, rotation, sizeToPoint(delta), std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(m_pinchGesture.get(), &PointerPinchGesture::ended, this, [this](quint32 serial, quint32 time) {
|
||||
Q_EMIT pinchGestureEnd(std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(m_pinchGesture.get(), &PointerPinchGesture::cancelled, this, [this](quint32 serial, quint32 time) {
|
||||
Q_EMIT pinchGestureCancelled(std::chrono::milliseconds(time), this);
|
||||
});
|
||||
|
||||
m_swipeGesture.reset(pointerGestures->createSwipeGesture(m_pointer.get(), this));
|
||||
connect(m_swipeGesture.get(), &PointerSwipeGesture::started, this, [this](quint32 serial, quint32 time) {
|
||||
Q_EMIT swipeGestureBegin(m_swipeGesture->fingerCount(), std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(m_swipeGesture.get(), &PointerSwipeGesture::updated, this, [this](const QSizeF &delta, quint32 time) {
|
||||
Q_EMIT swipeGestureUpdate(sizeToPoint(delta), std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(m_swipeGesture.get(), &PointerSwipeGesture::ended, this, [this](quint32 serial, quint32 time) {
|
||||
Q_EMIT swipeGestureEnd(std::chrono::milliseconds(time), this);
|
||||
});
|
||||
connect(m_swipeGesture.get(), &PointerSwipeGesture::cancelled, this, [this](quint32 serial, quint32 time) {
|
||||
Q_EMIT swipeGestureCancelled(std::chrono::milliseconds(time), this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
WaylandInputDevice::WaylandInputDevice(KWayland::Client::RelativePointer *relativePointer, WaylandSeat *seat)
|
||||
: m_seat(seat)
|
||||
, m_relativePointer(relativePointer)
|
||||
{
|
||||
connect(relativePointer, &RelativePointer::relativeMotion, this, [this](const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp) {
|
||||
Q_EMIT pointerMotion(sizeToPoint(delta), sizeToPoint(deltaNonAccelerated), std::chrono::microseconds(timestamp), this);
|
||||
});
|
||||
}
|
||||
|
||||
WaylandInputDevice::WaylandInputDevice(KWayland::Client::Touch *touch, WaylandSeat *seat)
|
||||
: m_seat(seat)
|
||||
, m_touch(touch)
|
||||
{
|
||||
connect(touch, &Touch::sequenceCanceled, this, [this]() {
|
||||
Q_EMIT touchCanceled(this);
|
||||
});
|
||||
connect(touch, &Touch::frameEnded, this, [this]() {
|
||||
Q_EMIT touchFrame(this);
|
||||
});
|
||||
connect(touch, &Touch::sequenceStarted, this, [this](TouchPoint *tp) {
|
||||
auto o = m_seat->backend()->findOutput(tp->surface());
|
||||
Q_ASSERT(o);
|
||||
const QPointF position = o->geometry().topLeft() + tp->position();
|
||||
Q_EMIT touchDown(tp->id(), position, std::chrono::milliseconds(tp->time()), this);
|
||||
});
|
||||
connect(touch, &Touch::pointAdded, this, [this](TouchPoint *tp) {
|
||||
auto o = m_seat->backend()->findOutput(tp->surface());
|
||||
Q_ASSERT(o);
|
||||
const QPointF position = o->geometry().topLeft() + tp->position();
|
||||
Q_EMIT touchDown(tp->id(), position, std::chrono::milliseconds(tp->time()), this);
|
||||
});
|
||||
connect(touch, &Touch::pointRemoved, this, [this](TouchPoint *tp) {
|
||||
Q_EMIT touchUp(tp->id(), std::chrono::milliseconds(tp->time()), this);
|
||||
});
|
||||
connect(touch, &Touch::pointMoved, this, [this](TouchPoint *tp) {
|
||||
auto o = m_seat->backend()->findOutput(tp->surface());
|
||||
Q_ASSERT(o);
|
||||
const QPointF position = o->geometry().topLeft() + tp->position();
|
||||
Q_EMIT touchMotion(tp->id(), position, std::chrono::milliseconds(tp->time()), this);
|
||||
});
|
||||
}
|
||||
|
||||
WaylandInputDevice::~WaylandInputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
QString WaylandInputDevice::name() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaylandInputDevice::setEnabled(bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isKeyboard() const
|
||||
{
|
||||
return m_keyboard != nullptr;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isPointer() const
|
||||
{
|
||||
return m_pointer || m_relativePointer;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isTouchpad() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isTouch() const
|
||||
{
|
||||
return m_touch != nullptr;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isTabletTool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isTabletPad() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isTabletModeSwitch() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WaylandInputDevice::isLidSwitch() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
KWayland::Client::Pointer *WaylandInputDevice::nativePointer() const
|
||||
{
|
||||
return m_pointer.get();
|
||||
}
|
||||
|
||||
WaylandInputBackend::WaylandInputBackend(WaylandBackend *backend, QObject *parent)
|
||||
: InputBackend(parent)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
void WaylandInputBackend::initialize()
|
||||
{
|
||||
WaylandSeat *seat = m_backend->seat();
|
||||
if (seat->relativePointerDevice()) {
|
||||
Q_EMIT deviceAdded(seat->relativePointerDevice());
|
||||
}
|
||||
if (seat->pointerDevice()) {
|
||||
Q_EMIT deviceAdded(seat->pointerDevice());
|
||||
}
|
||||
if (seat->keyboardDevice()) {
|
||||
Q_EMIT deviceAdded(seat->keyboardDevice());
|
||||
}
|
||||
if (seat->touchDevice()) {
|
||||
Q_EMIT deviceAdded(seat->touchDevice());
|
||||
}
|
||||
|
||||
connect(seat, &WaylandSeat::deviceAdded, this, &InputBackend::deviceAdded);
|
||||
connect(seat, &WaylandSeat::deviceRemoved, this, &InputBackend::deviceRemoved);
|
||||
}
|
||||
|
||||
WaylandSeat::WaylandSeat(KWayland::Client::Seat *nativeSeat, WaylandBackend *backend)
|
||||
: QObject(nullptr)
|
||||
, m_seat(nativeSeat)
|
||||
, m_backend(backend)
|
||||
{
|
||||
auto updateKeyboardDevice = [this](){
|
||||
if (m_seat->hasKeyboard()) {
|
||||
createKeyboardDevice();
|
||||
} else {
|
||||
destroyKeyboardDevice();
|
||||
}
|
||||
};
|
||||
|
||||
updateKeyboardDevice();
|
||||
connect(m_seat, &Seat::hasKeyboardChanged, this, updateKeyboardDevice);
|
||||
|
||||
auto updatePointerDevice = [this]() {
|
||||
if (m_seat->hasPointer()) {
|
||||
createPointerDevice();
|
||||
} else {
|
||||
destroyPointerDevice();
|
||||
}
|
||||
};
|
||||
|
||||
updatePointerDevice();
|
||||
connect(m_seat, &Seat::hasPointerChanged, this, updatePointerDevice);
|
||||
|
||||
auto updateTouchDevice = [this]() {
|
||||
if (m_seat->hasTouch()) {
|
||||
createTouchDevice();
|
||||
} else {
|
||||
destroyTouchDevice();
|
||||
}
|
||||
};
|
||||
|
||||
updateTouchDevice();
|
||||
connect(m_seat, &Seat::hasTouchChanged, this, updateTouchDevice);
|
||||
}
|
||||
|
||||
WaylandSeat::~WaylandSeat()
|
||||
{
|
||||
destroyPointerDevice();
|
||||
destroyKeyboardDevice();
|
||||
destroyTouchDevice();
|
||||
}
|
||||
|
||||
void WaylandSeat::createPointerDevice()
|
||||
{
|
||||
m_pointerDevice = std::make_unique<WaylandInputDevice>(m_seat->createPointer(), this);
|
||||
Q_EMIT deviceAdded(m_pointerDevice.get());
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyPointerDevice()
|
||||
{
|
||||
if (m_pointerDevice) {
|
||||
Q_EMIT deviceRemoved(m_pointerDevice.get());
|
||||
destroyRelativePointer();
|
||||
m_pointerDevice.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::createRelativePointer()
|
||||
{
|
||||
KWayland::Client::RelativePointerManager *manager = m_backend->display()->relativePointerManager();
|
||||
if (manager) {
|
||||
m_relativePointerDevice = std::make_unique<WaylandInputDevice>(manager->createRelativePointer(m_pointerDevice->nativePointer()), this);
|
||||
Q_EMIT deviceAdded(m_relativePointerDevice.get());
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyRelativePointer()
|
||||
{
|
||||
if (m_relativePointerDevice) {
|
||||
Q_EMIT deviceRemoved(m_relativePointerDevice.get());
|
||||
m_relativePointerDevice.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::createKeyboardDevice()
|
||||
{
|
||||
m_keyboardDevice = std::make_unique<WaylandInputDevice>(m_seat->createKeyboard(), this);
|
||||
Q_EMIT deviceAdded(m_keyboardDevice.get());
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyKeyboardDevice()
|
||||
{
|
||||
if (m_keyboardDevice) {
|
||||
Q_EMIT deviceRemoved(m_keyboardDevice.get());
|
||||
m_keyboardDevice.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::createTouchDevice()
|
||||
{
|
||||
m_touchDevice = std::make_unique<WaylandInputDevice>(m_seat->createTouch(), this);
|
||||
Q_EMIT deviceAdded(m_touchDevice.get());
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyTouchDevice()
|
||||
{
|
||||
if (m_touchDevice) {
|
||||
Q_EMIT deviceRemoved(m_touchDevice.get());
|
||||
m_touchDevice.reset();
|
||||
}
|
||||
}
|
||||
|
||||
WaylandBackend::WaylandBackend(const WaylandBackendOptions &options, QObject *parent)
|
||||
: OutputBackend(parent)
|
||||
, m_options(options)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandBackend::~WaylandBackend()
|
||||
{
|
||||
m_eglDisplay.reset();
|
||||
destroyOutputs();
|
||||
|
||||
m_buffers.clear();
|
||||
|
||||
m_seat.reset();
|
||||
m_display.reset();
|
||||
qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display";
|
||||
}
|
||||
|
||||
bool WaylandBackend::initialize()
|
||||
{
|
||||
m_display = std::make_unique<WaylandDisplay>();
|
||||
if (!m_display->initialize(m_options.socketName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WaylandLinuxDmabufV1 *dmabuf = m_display->linuxDmabuf()) {
|
||||
m_drmDevice = DrmDevice::open(dmabuf->mainDevice());
|
||||
if (!m_drmDevice) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "Failed to open drm render node" << dmabuf->mainDevice();
|
||||
}
|
||||
}
|
||||
|
||||
createOutputs();
|
||||
|
||||
m_seat = std::make_unique<WaylandSeat>(m_display->seat(), this);
|
||||
|
||||
QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance();
|
||||
QObject::connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, m_display.get(), &WaylandDisplay::flush);
|
||||
QObject::connect(dispatcher, &QAbstractEventDispatcher::awake, m_display.get(), &WaylandDisplay::flush);
|
||||
|
||||
connect(this, &WaylandBackend::pointerLockChanged, this, [this](bool locked) {
|
||||
if (locked) {
|
||||
m_seat->createRelativePointer();
|
||||
} else {
|
||||
m_seat->destroyRelativePointer();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaylandBackend::createOutputs()
|
||||
{
|
||||
// we need to multiply the initial window size with the scale in order to
|
||||
// create an output window of this size in the end
|
||||
const QSize pixelSize = m_options.outputSize * m_options.outputScale;
|
||||
for (int i = 0; i < m_options.outputCount; i++) {
|
||||
WaylandOutput *output = createOutput(QStringLiteral("WL-%1").arg(i), pixelSize, m_options.outputScale);
|
||||
m_outputs << output;
|
||||
Q_EMIT outputAdded(output);
|
||||
output->updateEnabled(true);
|
||||
}
|
||||
|
||||
Q_EMIT outputsQueried();
|
||||
}
|
||||
|
||||
WaylandOutput *WaylandBackend::createOutput(const QString &name, const QSize &size, qreal scale)
|
||||
{
|
||||
WaylandOutput *waylandOutput = new WaylandOutput(name, this);
|
||||
waylandOutput->init(size, scale);
|
||||
|
||||
// Wait until the output window is configured by the host compositor.
|
||||
while (!waylandOutput->isReady()) {
|
||||
wl_display_roundtrip(m_display->nativeDisplay());
|
||||
}
|
||||
|
||||
return waylandOutput;
|
||||
}
|
||||
|
||||
void WaylandBackend::destroyOutputs()
|
||||
{
|
||||
while (!m_outputs.isEmpty()) {
|
||||
WaylandOutput *output = m_outputs.takeLast();
|
||||
output->updateEnabled(false);
|
||||
Q_EMIT outputRemoved(output);
|
||||
delete output;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<InputBackend> WaylandBackend::createInputBackend()
|
||||
{
|
||||
return std::make_unique<WaylandInputBackend>(this);
|
||||
}
|
||||
|
||||
std::unique_ptr<OpenGLBackend> WaylandBackend::createOpenGLBackend()
|
||||
{
|
||||
return std::make_unique<WaylandEglBackend>(this);
|
||||
}
|
||||
|
||||
std::unique_ptr<QPainterBackend> WaylandBackend::createQPainterBackend()
|
||||
{
|
||||
return std::make_unique<WaylandQPainterBackend>(this);
|
||||
}
|
||||
|
||||
WaylandOutput *WaylandBackend::findOutput(KWayland::Client::Surface *nativeSurface) const
|
||||
{
|
||||
for (WaylandOutput *output : m_outputs) {
|
||||
if (output->surface() == nativeSurface) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WaylandBackend::supportsPointerLock()
|
||||
{
|
||||
return m_display->pointerConstraints() && m_display->relativePointerManager();
|
||||
}
|
||||
|
||||
void WaylandBackend::togglePointerLock()
|
||||
{
|
||||
if (!supportsPointerLock()) {
|
||||
return;
|
||||
}
|
||||
if (!m_seat) {
|
||||
return;
|
||||
}
|
||||
auto pointer = m_seat->pointerDevice()->nativePointer();
|
||||
if (!pointer) {
|
||||
return;
|
||||
}
|
||||
if (m_outputs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto output : std::as_const(m_outputs)) {
|
||||
output->lockPointer(m_seat->pointerDevice()->nativePointer(), !m_pointerLockRequested);
|
||||
}
|
||||
m_pointerLockRequested = !m_pointerLockRequested;
|
||||
}
|
||||
|
||||
QList<CompositingType> WaylandBackend::supportedCompositors() const
|
||||
{
|
||||
QList<CompositingType> ret;
|
||||
if (m_display->linuxDmabuf() && m_drmDevice) {
|
||||
ret.append(OpenGLCompositing);
|
||||
}
|
||||
ret.append(QPainterCompositing);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Outputs WaylandBackend::outputs() const
|
||||
{
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
Output *WaylandBackend::createVirtualOutput(const QString &name, const QString &description, const QSize &size, double scale)
|
||||
{
|
||||
return createOutput(name, size * scale, scale);
|
||||
}
|
||||
|
||||
void WaylandBackend::removeVirtualOutput(Output *output)
|
||||
{
|
||||
WaylandOutput *waylandOutput = dynamic_cast<WaylandOutput *>(output);
|
||||
if (waylandOutput && m_outputs.removeAll(waylandOutput)) {
|
||||
waylandOutput->updateEnabled(false);
|
||||
Q_EMIT outputRemoved(waylandOutput);
|
||||
Q_EMIT outputsQueried();
|
||||
waylandOutput->unref();
|
||||
}
|
||||
}
|
||||
|
||||
static wl_buffer *importDmaBufBuffer(WaylandDisplay *display, const DmaBufAttributes *attributes)
|
||||
{
|
||||
zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(display->linuxDmabuf()->handle());
|
||||
for (int i = 0; i < attributes->planeCount; ++i) {
|
||||
zwp_linux_buffer_params_v1_add(params,
|
||||
attributes->fd[i].get(),
|
||||
i,
|
||||
attributes->offset[i],
|
||||
attributes->pitch[i],
|
||||
attributes->modifier >> 32,
|
||||
attributes->modifier & 0xffffffff);
|
||||
}
|
||||
|
||||
wl_buffer *buffer = zwp_linux_buffer_params_v1_create_immed(params, attributes->width, attributes->height, attributes->format, 0);
|
||||
zwp_linux_buffer_params_v1_destroy(params);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static wl_buffer *importShmBuffer(WaylandDisplay *display, const ShmAttributes *attributes)
|
||||
{
|
||||
wl_shm_format format;
|
||||
switch (attributes->format) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
format = WL_SHM_FORMAT_ARGB8888;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
format = WL_SHM_FORMAT_XRGB8888;
|
||||
break;
|
||||
default:
|
||||
format = static_cast<wl_shm_format>(attributes->format);
|
||||
break;
|
||||
}
|
||||
|
||||
wl_shm_pool *pool = wl_shm_create_pool(display->shm(), attributes->fd.get(), attributes->size.height() * attributes->stride);
|
||||
wl_buffer *buffer = wl_shm_pool_create_buffer(pool,
|
||||
attributes->offset,
|
||||
attributes->size.width(),
|
||||
attributes->size.height(),
|
||||
attributes->stride,
|
||||
format);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
wl_buffer *WaylandBackend::importBuffer(GraphicsBuffer *graphicsBuffer)
|
||||
{
|
||||
auto &buffer = m_buffers[graphicsBuffer];
|
||||
if (!buffer) {
|
||||
wl_buffer *handle = nullptr;
|
||||
if (const DmaBufAttributes *attributes = graphicsBuffer->dmabufAttributes()) {
|
||||
handle = importDmaBufBuffer(m_display.get(), attributes);
|
||||
} else if (const ShmAttributes *attributes = graphicsBuffer->shmAttributes()) {
|
||||
handle = importShmBuffer(m_display.get(), attributes);
|
||||
} else {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << graphicsBuffer << "has unknown type";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buffer = std::make_unique<WaylandBuffer>(handle, graphicsBuffer);
|
||||
connect(buffer.get(), &WaylandBuffer::defunct, this, [this, graphicsBuffer]() {
|
||||
m_buffers.erase(graphicsBuffer);
|
||||
});
|
||||
|
||||
static const wl_buffer_listener listener = {
|
||||
.release = [](void *userData, wl_buffer *buffer) {
|
||||
WaylandBuffer *slot = static_cast<WaylandBuffer *>(userData);
|
||||
slot->unlock();
|
||||
},
|
||||
};
|
||||
wl_buffer_add_listener(handle, &listener, buffer.get());
|
||||
}
|
||||
|
||||
buffer->lock();
|
||||
return buffer->handle();
|
||||
}
|
||||
|
||||
void WaylandBackend::setEglDisplay(std::unique_ptr<EglDisplay> &&display)
|
||||
{
|
||||
m_eglDisplay = std::move(display);
|
||||
}
|
||||
|
||||
EglDisplay *WaylandBackend::sceneEglDisplayObject() const
|
||||
{
|
||||
return m_eglDisplay.get();
|
||||
}
|
||||
|
||||
DrmDevice *WaylandBackend::drmDevice() const
|
||||
{
|
||||
return m_drmDevice.get();
|
||||
}
|
||||
|
||||
WaylandBuffer::WaylandBuffer(wl_buffer *handle, GraphicsBuffer *graphicsBuffer)
|
||||
: m_graphicsBuffer(graphicsBuffer)
|
||||
, m_handle(handle)
|
||||
{
|
||||
connect(graphicsBuffer, &GraphicsBuffer::destroyed, this, &WaylandBuffer::defunct);
|
||||
}
|
||||
|
||||
WaylandBuffer::~WaylandBuffer()
|
||||
{
|
||||
m_graphicsBuffer->disconnect(this);
|
||||
if (m_locked) {
|
||||
m_graphicsBuffer->unref();
|
||||
}
|
||||
wl_buffer_destroy(m_handle);
|
||||
}
|
||||
|
||||
wl_buffer *WaylandBuffer::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
void WaylandBuffer::lock()
|
||||
{
|
||||
if (!m_locked) {
|
||||
m_locked = true;
|
||||
m_graphicsBuffer->ref();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandBuffer::unlock()
|
||||
{
|
||||
if (m_locked) {
|
||||
m_locked = false;
|
||||
m_graphicsBuffer->unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // KWin
|
||||
|
||||
#include "moc_wayland_backend.cpp"
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "config-kwin.h"
|
||||
|
||||
// KWin
|
||||
#include "core/inputbackend.h"
|
||||
#include "core/inputdevice.h"
|
||||
#include "core/outputbackend.h"
|
||||
#include "effect/globals.h"
|
||||
#include "utils/filedescriptor.h"
|
||||
// Qt
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
|
||||
struct wl_buffer;
|
||||
struct wl_display;
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Client
|
||||
{
|
||||
class Keyboard;
|
||||
class Pointer;
|
||||
class PointerSwipeGesture;
|
||||
class PointerPinchGesture;
|
||||
class RelativePointer;
|
||||
class Seat;
|
||||
class Surface;
|
||||
class Touch;
|
||||
}
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class GraphicsBuffer;
|
||||
class DrmDevice;
|
||||
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
class WaylandBackend;
|
||||
class WaylandSeat;
|
||||
class WaylandOutput;
|
||||
class WaylandEglBackend;
|
||||
class WaylandDisplay;
|
||||
|
||||
class WaylandInputDevice : public InputDevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WaylandInputDevice(KWayland::Client::Touch *touch, WaylandSeat *seat);
|
||||
WaylandInputDevice(KWayland::Client::Keyboard *keyboard, WaylandSeat *seat);
|
||||
WaylandInputDevice(KWayland::Client::RelativePointer *relativePointer, WaylandSeat *seat);
|
||||
WaylandInputDevice(KWayland::Client::Pointer *pointer, WaylandSeat *seat);
|
||||
~WaylandInputDevice() override;
|
||||
|
||||
QString name() const override;
|
||||
|
||||
bool isEnabled() const override;
|
||||
void setEnabled(bool enabled) override;
|
||||
|
||||
bool isKeyboard() const override;
|
||||
bool isPointer() const override;
|
||||
bool isTouchpad() const override;
|
||||
bool isTouch() const override;
|
||||
bool isTabletTool() const override;
|
||||
bool isTabletPad() const override;
|
||||
bool isTabletModeSwitch() const override;
|
||||
bool isLidSwitch() const override;
|
||||
|
||||
KWayland::Client::Pointer *nativePointer() const;
|
||||
|
||||
private:
|
||||
WaylandSeat *const m_seat;
|
||||
|
||||
std::unique_ptr<KWayland::Client::Keyboard> m_keyboard;
|
||||
std::unique_ptr<KWayland::Client::Touch> m_touch;
|
||||
std::unique_ptr<KWayland::Client::RelativePointer> m_relativePointer;
|
||||
std::unique_ptr<KWayland::Client::Pointer> m_pointer;
|
||||
std::unique_ptr<KWayland::Client::PointerPinchGesture> m_pinchGesture;
|
||||
std::unique_ptr<KWayland::Client::PointerSwipeGesture> m_swipeGesture;
|
||||
|
||||
QSet<quint32> m_pressedKeys;
|
||||
};
|
||||
|
||||
class WaylandInputBackend : public InputBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WaylandInputBackend(WaylandBackend *backend, QObject *parent = nullptr);
|
||||
|
||||
void initialize() override;
|
||||
|
||||
private:
|
||||
WaylandBackend *m_backend;
|
||||
};
|
||||
|
||||
class WaylandSeat : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaylandSeat(KWayland::Client::Seat *nativeSeat, WaylandBackend *backend);
|
||||
~WaylandSeat() override;
|
||||
|
||||
WaylandBackend *backend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
WaylandInputDevice *pointerDevice() const
|
||||
{
|
||||
return m_pointerDevice.get();
|
||||
}
|
||||
WaylandInputDevice *relativePointerDevice() const
|
||||
{
|
||||
return m_relativePointerDevice.get();
|
||||
}
|
||||
WaylandInputDevice *keyboardDevice() const
|
||||
{
|
||||
return m_keyboardDevice.get();
|
||||
}
|
||||
WaylandInputDevice *touchDevice() const
|
||||
{
|
||||
return m_touchDevice.get();
|
||||
}
|
||||
|
||||
void createRelativePointer();
|
||||
void destroyRelativePointer();
|
||||
|
||||
Q_SIGNALS:
|
||||
void deviceAdded(WaylandInputDevice *device);
|
||||
void deviceRemoved(WaylandInputDevice *device);
|
||||
|
||||
private:
|
||||
void createPointerDevice();
|
||||
void destroyPointerDevice();
|
||||
void createKeyboardDevice();
|
||||
void destroyKeyboardDevice();
|
||||
void createTouchDevice();
|
||||
void destroyTouchDevice();
|
||||
|
||||
KWayland::Client::Seat *m_seat;
|
||||
WaylandBackend *m_backend;
|
||||
|
||||
std::unique_ptr<WaylandInputDevice> m_pointerDevice;
|
||||
std::unique_ptr<WaylandInputDevice> m_relativePointerDevice;
|
||||
std::unique_ptr<WaylandInputDevice> m_keyboardDevice;
|
||||
std::unique_ptr<WaylandInputDevice> m_touchDevice;
|
||||
};
|
||||
|
||||
class WaylandBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WaylandBuffer(wl_buffer *handle, GraphicsBuffer *graphicsBuffer);
|
||||
~WaylandBuffer() override;
|
||||
|
||||
wl_buffer *handle() const;
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
Q_SIGNALS:
|
||||
void defunct();
|
||||
|
||||
private:
|
||||
GraphicsBuffer *m_graphicsBuffer;
|
||||
wl_buffer *m_handle;
|
||||
bool m_locked = false;
|
||||
};
|
||||
|
||||
struct WaylandBackendOptions
|
||||
{
|
||||
QString socketName;
|
||||
int outputCount = 1;
|
||||
qreal outputScale = 1;
|
||||
QSize outputSize = QSize(1024, 768);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class encapsulating all Wayland data structures needed by the Egl backend.
|
||||
*
|
||||
* It creates the connection to the Wayland Compositor, sets up the registry and creates
|
||||
* the Wayland output surfaces and its shell mappings.
|
||||
*/
|
||||
class KWIN_EXPORT WaylandBackend : public OutputBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WaylandBackend(const WaylandBackendOptions &options, QObject *parent = nullptr);
|
||||
~WaylandBackend() override;
|
||||
bool initialize() override;
|
||||
|
||||
std::unique_ptr<InputBackend> createInputBackend() override;
|
||||
std::unique_ptr<OpenGLBackend> createOpenGLBackend() override;
|
||||
std::unique_ptr<QPainterBackend> createQPainterBackend() override;
|
||||
|
||||
WaylandDisplay *display() const
|
||||
{
|
||||
return m_display.get();
|
||||
}
|
||||
WaylandSeat *seat() const
|
||||
{
|
||||
return m_seat.get();
|
||||
}
|
||||
|
||||
bool supportsPointerLock();
|
||||
void togglePointerLock();
|
||||
|
||||
QList<CompositingType> supportedCompositors() const override;
|
||||
|
||||
WaylandOutput *findOutput(KWayland::Client::Surface *nativeSurface) const;
|
||||
Outputs outputs() const override;
|
||||
QList<WaylandOutput *> waylandOutputs() const
|
||||
{
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
Output *createVirtualOutput(const QString &name, const QString &description, const QSize &size, double scale) override;
|
||||
void removeVirtualOutput(Output *output) override;
|
||||
|
||||
wl_buffer *importBuffer(GraphicsBuffer *graphicsBuffer);
|
||||
|
||||
DrmDevice *drmDevice() const;
|
||||
|
||||
void setEglBackend(WaylandEglBackend *eglBackend)
|
||||
{
|
||||
m_eglBackend = eglBackend;
|
||||
}
|
||||
void setEglDisplay(std::unique_ptr<EglDisplay> &&display);
|
||||
EglDisplay *sceneEglDisplayObject() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void pointerLockChanged(bool locked);
|
||||
|
||||
private:
|
||||
void createOutputs();
|
||||
void destroyOutputs();
|
||||
WaylandOutput *createOutput(const QString &name, const QSize &size, qreal scale);
|
||||
|
||||
WaylandBackendOptions m_options;
|
||||
std::unique_ptr<WaylandDisplay> m_display;
|
||||
std::unique_ptr<WaylandSeat> m_seat;
|
||||
WaylandEglBackend *m_eglBackend = nullptr;
|
||||
QList<WaylandOutput *> m_outputs;
|
||||
bool m_pointerLockRequested = false;
|
||||
std::unique_ptr<DrmDevice> m_drmDevice;
|
||||
std::unique_ptr<EglDisplay> m_eglDisplay;
|
||||
std::map<GraphicsBuffer *, std::unique_ptr<WaylandBuffer>> m_buffers;
|
||||
};
|
||||
|
||||
} // namespace Wayland
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "wayland_display.h"
|
||||
#include "utils/memorymap.h"
|
||||
#include "wayland_logging.h"
|
||||
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/pointerconstraints.h>
|
||||
#include <KWayland/Client/pointergestures.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include <KWayland/Client/relativepointer.h>
|
||||
#include <KWayland/Client/seat.h>
|
||||
#include <KWayland/Client/subcompositor.h>
|
||||
#include <KWayland/Client/xdgdecoration.h>
|
||||
#include <KWayland/Client/xdgshell.h>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <span>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
// Generated in src/wayland.
|
||||
#include "wayland-linux-dmabuf-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-gestures-unstable-v1-server-protocol.h"
|
||||
#include "wayland-presentation-time-client-protocol.h"
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
class WaylandEventThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WaylandEventThread(wl_display *display)
|
||||
: m_display(display)
|
||||
, m_fd(wl_display_get_fd(display))
|
||||
, m_quitPipe{-1, -1}
|
||||
, m_reading(true)
|
||||
, m_quitting(false)
|
||||
{
|
||||
if (pipe2(m_quitPipe, O_CLOEXEC) == -1) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "Failed to create quite pipe in WaylandEventThread";
|
||||
}
|
||||
}
|
||||
|
||||
~WaylandEventThread() override
|
||||
{
|
||||
if (m_quitPipe[0] != -1) {
|
||||
close(m_quitPipe[0]);
|
||||
close(m_quitPipe[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch()
|
||||
{
|
||||
while (true) {
|
||||
if (wl_display_dispatch_pending(m_display) < 0) {
|
||||
qFatal("Wayland connection broke");
|
||||
}
|
||||
|
||||
wl_display_flush(m_display);
|
||||
|
||||
if (m_reading.loadAcquire()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (wl_display_prepare_read(m_display) == 0) {
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_reading.storeRelease(true);
|
||||
m_cond.wakeOne();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (m_quitPipe[1] != -1) {
|
||||
write(m_quitPipe[1], "\0", 1);
|
||||
}
|
||||
|
||||
m_mutex.lock();
|
||||
m_quitting = true;
|
||||
m_cond.wakeOne();
|
||||
m_mutex.unlock();
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void available();
|
||||
|
||||
protected:
|
||||
void run() override
|
||||
{
|
||||
while (true) {
|
||||
m_reading.storeRelease(false);
|
||||
|
||||
Q_EMIT available();
|
||||
|
||||
m_mutex.lock();
|
||||
while (!m_reading.loadRelaxed() && !m_quitting) {
|
||||
m_cond.wait(&m_mutex);
|
||||
}
|
||||
m_mutex.unlock();
|
||||
|
||||
if (m_quitting) {
|
||||
break;
|
||||
}
|
||||
|
||||
pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_quitPipe[0], POLLIN, 0 } };
|
||||
poll(fds, 2, -1);
|
||||
|
||||
if (fds[1].revents & POLLIN) {
|
||||
wl_display_cancel_read(m_display);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fds[0].revents & POLLIN) {
|
||||
wl_display_read_events(m_display);
|
||||
} else {
|
||||
wl_display_cancel_read(m_display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
wl_display *const m_display;
|
||||
int m_fd;
|
||||
int m_quitPipe[2];
|
||||
QAtomicInteger<bool> m_reading;
|
||||
QMutex m_mutex;
|
||||
QWaitCondition m_cond;
|
||||
bool m_quitting;
|
||||
};
|
||||
|
||||
static dev_t deserializeDeviceId(wl_array *data)
|
||||
{
|
||||
Q_ASSERT(sizeof(dev_t) == data->size);
|
||||
dev_t ret;
|
||||
std::memcpy(&ret, data->data, data->size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
class WaylandLinuxDmabufFeedbackV1
|
||||
{
|
||||
public:
|
||||
WaylandLinuxDmabufFeedbackV1(zwp_linux_dmabuf_feedback_v1 *feedback)
|
||||
: feedback(feedback)
|
||||
{
|
||||
static const struct zwp_linux_dmabuf_feedback_v1_listener feedbackListener = {
|
||||
.done = done,
|
||||
.format_table = format_table,
|
||||
.main_device = main_device,
|
||||
.tranche_done = tranche_done,
|
||||
.tranche_target_device = tranche_target_device,
|
||||
.tranche_formats = tranche_formats,
|
||||
.tranche_flags = tranche_flags,
|
||||
};
|
||||
zwp_linux_dmabuf_feedback_v1_add_listener(feedback, &feedbackListener, this);
|
||||
}
|
||||
|
||||
~WaylandLinuxDmabufFeedbackV1()
|
||||
{
|
||||
zwp_linux_dmabuf_feedback_v1_destroy(feedback);
|
||||
}
|
||||
|
||||
zwp_linux_dmabuf_feedback_v1 *feedback;
|
||||
QByteArray mainDevice;
|
||||
dev_t mainDeviceId = 0;
|
||||
dev_t trancheDeviceId = 0;
|
||||
MemoryMap formatTable;
|
||||
QHash<uint32_t, QList<uint64_t>> formats;
|
||||
|
||||
private:
|
||||
static void done(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void format_table(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int32_t fd, uint32_t size)
|
||||
{
|
||||
WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
|
||||
|
||||
feedback->formatTable = MemoryMap(size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void main_device(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *deviceId)
|
||||
{
|
||||
WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
|
||||
|
||||
feedback->mainDeviceId = deserializeDeviceId(deviceId);
|
||||
|
||||
drmDevice *device = nullptr;
|
||||
if (drmGetDeviceFromDevId(feedback->mainDeviceId, 0, &device) != 0) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "drmGetDeviceFromDevId() failed";
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
|
||||
feedback->mainDevice = QByteArray(device->nodes[DRM_NODE_RENDER]);
|
||||
} else if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) {
|
||||
// We can't reliably find the render node from the primary node if the display and
|
||||
// render devices are split, so just fallback to the primary node.
|
||||
feedback->mainDevice = QByteArray(device->nodes[DRM_NODE_PRIMARY]);
|
||||
}
|
||||
|
||||
drmFreeDevice(&device);
|
||||
}
|
||||
|
||||
static void tranche_done(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
|
||||
{
|
||||
WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
|
||||
|
||||
feedback->trancheDeviceId = 0;
|
||||
}
|
||||
|
||||
static void tranche_target_device(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *deviceId)
|
||||
{
|
||||
WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
|
||||
|
||||
feedback->trancheDeviceId = deserializeDeviceId(deviceId);
|
||||
}
|
||||
|
||||
static void tranche_formats(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *indices)
|
||||
{
|
||||
WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
|
||||
if (!feedback->formatTable.isValid()) {
|
||||
return;
|
||||
}
|
||||
if (feedback->mainDeviceId != feedback->trancheDeviceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct linux_dmabuf_feedback_v1_table_entry
|
||||
{
|
||||
uint32_t format;
|
||||
uint32_t pad; // unused
|
||||
uint64_t modifier;
|
||||
};
|
||||
|
||||
const auto entries = static_cast<linux_dmabuf_feedback_v1_table_entry *>(feedback->formatTable.data());
|
||||
for (const uint16_t &index : std::span(static_cast<uint16_t *>(indices->data), indices->size / sizeof(uint16_t))) {
|
||||
const linux_dmabuf_feedback_v1_table_entry &entry = entries[index];
|
||||
feedback->formats[entry.format].append(entry.modifier);
|
||||
}
|
||||
}
|
||||
|
||||
static void tranche_flags(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
};
|
||||
|
||||
WaylandLinuxDmabufV1::WaylandLinuxDmabufV1(wl_registry *registry, uint32_t name, uint32_t version)
|
||||
{
|
||||
m_dmabuf = static_cast<zwp_linux_dmabuf_v1 *>(wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version));
|
||||
|
||||
static const struct zwp_linux_dmabuf_v1_listener dmabufListener = {
|
||||
.format = format,
|
||||
.modifier = modifier,
|
||||
};
|
||||
zwp_linux_dmabuf_v1_add_listener(m_dmabuf, &dmabufListener, this);
|
||||
|
||||
m_defaultFeedback = std::make_unique<WaylandLinuxDmabufFeedbackV1>(zwp_linux_dmabuf_v1_get_default_feedback(m_dmabuf));
|
||||
}
|
||||
|
||||
WaylandLinuxDmabufV1::~WaylandLinuxDmabufV1()
|
||||
{
|
||||
zwp_linux_dmabuf_v1_destroy(m_dmabuf);
|
||||
}
|
||||
|
||||
zwp_linux_dmabuf_v1 *WaylandLinuxDmabufV1::handle() const
|
||||
{
|
||||
return m_dmabuf;
|
||||
}
|
||||
|
||||
QByteArray WaylandLinuxDmabufV1::mainDevice() const
|
||||
{
|
||||
return m_defaultFeedback->mainDevice;
|
||||
}
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> WaylandLinuxDmabufV1::formats() const
|
||||
{
|
||||
return m_defaultFeedback->formats;
|
||||
}
|
||||
|
||||
void WaylandLinuxDmabufV1::format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format)
|
||||
{
|
||||
// Not sent in v4 and onward.
|
||||
}
|
||||
|
||||
void WaylandLinuxDmabufV1::modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
|
||||
{
|
||||
// Not sent in v4 and onward.
|
||||
}
|
||||
|
||||
WaylandDisplay::WaylandDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
WaylandDisplay::~WaylandDisplay()
|
||||
{
|
||||
m_eventThread->stop();
|
||||
m_eventThread.reset();
|
||||
|
||||
m_compositor.reset();
|
||||
m_pointerConstraints.reset();
|
||||
m_pointerGestures.reset();
|
||||
m_relativePointerManager.reset();
|
||||
m_seat.reset();
|
||||
m_xdgDecorationManager.reset();
|
||||
m_xdgShell.reset();
|
||||
m_linuxDmabuf.reset();
|
||||
|
||||
if (m_shm) {
|
||||
wl_shm_destroy(m_shm);
|
||||
}
|
||||
if (m_presentationTime) {
|
||||
wp_presentation_destroy(m_presentationTime);
|
||||
}
|
||||
if (m_registry) {
|
||||
wl_registry_destroy(m_registry);
|
||||
}
|
||||
if (m_display) {
|
||||
wl_display_disconnect(m_display);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDisplay::flush()
|
||||
{
|
||||
m_eventThread->dispatch();
|
||||
}
|
||||
|
||||
bool WaylandDisplay::initialize(const QString &socketName)
|
||||
{
|
||||
m_display = wl_display_connect(socketName.toUtf8());
|
||||
if (!m_display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_eventThread = std::make_unique<WaylandEventThread>(m_display);
|
||||
connect(m_eventThread.get(), &WaylandEventThread::available, this, &WaylandDisplay::flush, Qt::QueuedConnection);
|
||||
m_eventThread->start();
|
||||
|
||||
static wl_registry_listener registryListener {
|
||||
.global = registry_global,
|
||||
.global_remove = registry_global_remove,
|
||||
};
|
||||
m_registry = wl_display_get_registry(m_display);
|
||||
wl_registry_add_listener(m_registry, ®istryListener, this);
|
||||
wl_display_roundtrip(m_display);
|
||||
wl_display_roundtrip(m_display); // get dmabuf formats
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wl_display *WaylandDisplay::nativeDisplay() const
|
||||
{
|
||||
return m_display;
|
||||
}
|
||||
|
||||
KWayland::Client::Compositor *WaylandDisplay::compositor() const
|
||||
{
|
||||
return m_compositor.get();
|
||||
}
|
||||
|
||||
KWayland::Client::PointerConstraints *WaylandDisplay::pointerConstraints() const
|
||||
{
|
||||
return m_pointerConstraints.get();
|
||||
}
|
||||
|
||||
KWayland::Client::PointerGestures *WaylandDisplay::pointerGestures() const
|
||||
{
|
||||
return m_pointerGestures.get();
|
||||
}
|
||||
|
||||
KWayland::Client::RelativePointerManager *WaylandDisplay::relativePointerManager() const
|
||||
{
|
||||
return m_relativePointerManager.get();
|
||||
}
|
||||
|
||||
wl_shm *WaylandDisplay::shm() const
|
||||
{
|
||||
return m_shm;
|
||||
}
|
||||
|
||||
KWayland::Client::Seat *WaylandDisplay::seat() const
|
||||
{
|
||||
return m_seat.get();
|
||||
}
|
||||
|
||||
KWayland::Client::XdgShell *WaylandDisplay::xdgShell() const
|
||||
{
|
||||
return m_xdgShell.get();
|
||||
}
|
||||
|
||||
KWayland::Client::XdgDecorationManager *WaylandDisplay::xdgDecorationManager() const
|
||||
{
|
||||
return m_xdgDecorationManager.get();
|
||||
}
|
||||
|
||||
WaylandLinuxDmabufV1 *WaylandDisplay::linuxDmabuf() const
|
||||
{
|
||||
return m_linuxDmabuf.get();
|
||||
}
|
||||
|
||||
wp_presentation *WaylandDisplay::presentationTime() const
|
||||
{
|
||||
return m_presentationTime;
|
||||
}
|
||||
|
||||
void WaylandDisplay::registry_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
WaylandDisplay *display = static_cast<WaylandDisplay *>(data);
|
||||
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
if (version < 4) {
|
||||
qFatal("wl_compositor version 4 or later is required");
|
||||
}
|
||||
display->m_compositor = std::make_unique<KWayland::Client::Compositor>();
|
||||
display->m_compositor->setup(static_cast<wl_compositor *>(wl_registry_bind(registry, name, &wl_compositor_interface, std::min(version, 4u))));
|
||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
display->m_shm = static_cast<wl_shm *>(wl_registry_bind(registry, name, &wl_shm_interface, std::min(version, 1u)));
|
||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||
display->m_seat = std::make_unique<KWayland::Client::Seat>();
|
||||
display->m_seat->setup(static_cast<wl_seat *>(wl_registry_bind(registry, name, &wl_seat_interface, std::min(version, 5u))));
|
||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||
display->m_xdgShell = std::make_unique<KWayland::Client::XdgShellStable>();
|
||||
display->m_xdgShell->setup(static_cast<xdg_wm_base *>(wl_registry_bind(registry, name, &xdg_wm_base_interface, std::min(version, 1u))));
|
||||
} else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) {
|
||||
display->m_pointerConstraints = std::make_unique<KWayland::Client::PointerConstraints>();
|
||||
display->m_pointerConstraints->setup(static_cast<zwp_pointer_constraints_v1 *>(wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, std::min(version, 1u))));
|
||||
} else if (strcmp(interface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
||||
display->m_pointerGestures = std::make_unique<KWayland::Client::PointerGestures>();
|
||||
display->m_pointerGestures->setup(static_cast<zwp_pointer_gestures_v1 *>(wl_registry_bind(registry, name, &zwp_pointer_gestures_v1_interface, std::min(version, 1u))));
|
||||
} else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
|
||||
display->m_relativePointerManager = std::make_unique<KWayland::Client::RelativePointerManager>();
|
||||
display->m_relativePointerManager->setup(static_cast<zwp_relative_pointer_manager_v1 *>(wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, std::min(version, 1u))));
|
||||
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
||||
display->m_xdgDecorationManager = std::make_unique<KWayland::Client::XdgDecorationManager>();
|
||||
display->m_xdgDecorationManager->setup(static_cast<zxdg_decoration_manager_v1 *>(wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, std::min(version, 1u))));
|
||||
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
|
||||
if (version < 4) {
|
||||
qWarning("zwp_linux_dmabuf_v1 v4 or newer is needed");
|
||||
return;
|
||||
}
|
||||
display->m_linuxDmabuf = std::make_unique<WaylandLinuxDmabufV1>(registry, name, std::min(version, 4u));
|
||||
} else if (strcmp(interface, wp_presentation_interface.name) == 0) {
|
||||
display->m_presentationTime = reinterpret_cast<wp_presentation *>(wl_registry_bind(registry, name, &wp_presentation_interface, std::min(version, 2u)));
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDisplay::registry_global_remove(void *data, wl_registry *registry, uint32_t name)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "wayland_display.moc"
|
||||
|
||||
#include "moc_wayland_display.cpp"
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct wl_display;
|
||||
struct wl_registry;
|
||||
struct wl_shm;
|
||||
struct zwp_linux_dmabuf_v1;
|
||||
struct wp_presentation;
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Client
|
||||
{
|
||||
class Compositor;
|
||||
class PointerConstraints;
|
||||
class PointerGestures;
|
||||
class RelativePointerManager;
|
||||
class Seat;
|
||||
class XdgDecorationManager;
|
||||
class XdgShell;
|
||||
}
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
class WaylandEventThread;
|
||||
class WaylandLinuxDmabufFeedbackV1;
|
||||
|
||||
class WaylandLinuxDmabufV1
|
||||
{
|
||||
public:
|
||||
WaylandLinuxDmabufV1(wl_registry *registry, uint32_t name, uint32_t version);
|
||||
~WaylandLinuxDmabufV1();
|
||||
|
||||
zwp_linux_dmabuf_v1 *handle() const;
|
||||
QByteArray mainDevice() const;
|
||||
QHash<uint32_t, QList<uint64_t>> formats() const;
|
||||
|
||||
private:
|
||||
static void format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format);
|
||||
static void modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo);
|
||||
|
||||
zwp_linux_dmabuf_v1 *m_dmabuf;
|
||||
std::unique_ptr<WaylandLinuxDmabufFeedbackV1> m_defaultFeedback;
|
||||
};
|
||||
|
||||
class WaylandDisplay : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WaylandDisplay();
|
||||
~WaylandDisplay() override;
|
||||
|
||||
bool initialize(const QString &socketName);
|
||||
|
||||
wl_display *nativeDisplay() const;
|
||||
KWayland::Client::Compositor *compositor() const;
|
||||
KWayland::Client::PointerConstraints *pointerConstraints() const;
|
||||
KWayland::Client::PointerGestures *pointerGestures() const;
|
||||
KWayland::Client::RelativePointerManager *relativePointerManager() const;
|
||||
KWayland::Client::Seat *seat() const;
|
||||
KWayland::Client::XdgDecorationManager *xdgDecorationManager() const;
|
||||
wl_shm *shm() const;
|
||||
KWayland::Client::XdgShell *xdgShell() const;
|
||||
WaylandLinuxDmabufV1 *linuxDmabuf() const;
|
||||
wp_presentation *presentationTime() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void flush();
|
||||
|
||||
private:
|
||||
static void registry_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version);
|
||||
static void registry_global_remove(void *data, wl_registry *registry, uint32_t name);
|
||||
|
||||
wl_display *m_display = nullptr;
|
||||
wl_registry *m_registry = nullptr;
|
||||
wl_shm *m_shm = nullptr;
|
||||
wp_presentation *m_presentationTime = nullptr;
|
||||
std::unique_ptr<WaylandEventThread> m_eventThread;
|
||||
std::unique_ptr<WaylandLinuxDmabufV1> m_linuxDmabuf;
|
||||
std::unique_ptr<KWayland::Client::Compositor> m_compositor;
|
||||
std::unique_ptr<KWayland::Client::PointerConstraints> m_pointerConstraints;
|
||||
std::unique_ptr<KWayland::Client::PointerGestures> m_pointerGestures;
|
||||
std::unique_ptr<KWayland::Client::RelativePointerManager> m_relativePointerManager;
|
||||
std::unique_ptr<KWayland::Client::Seat> m_seat;
|
||||
std::unique_ptr<KWayland::Client::XdgDecorationManager> m_xdgDecorationManager;
|
||||
std::unique_ptr<KWayland::Client::XdgShell> m_xdgShell;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "wayland_egl_backend.h"
|
||||
#include "core/drmdevice.h"
|
||||
#include "core/gbmgraphicsbufferallocator.h"
|
||||
#include "opengl/eglswapchain.h"
|
||||
#include "opengl/glrendertimequery.h"
|
||||
#include "opengl/glutils.h"
|
||||
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
||||
#include "scene/surfaceitem_wayland.h"
|
||||
#include "wayland/surface.h"
|
||||
#include "wayland_backend.h"
|
||||
#include "wayland_display.h"
|
||||
#include "wayland_logging.h"
|
||||
#include "wayland_output.h"
|
||||
|
||||
#include <KWayland/Client/surface.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <drm_fourcc.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0");
|
||||
|
||||
WaylandEglPrimaryLayer::WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend)
|
||||
: OutputLayer(output)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandEglPrimaryLayer::~WaylandEglPrimaryLayer()
|
||||
{
|
||||
}
|
||||
|
||||
GLFramebuffer *WaylandEglPrimaryLayer::fbo() const
|
||||
{
|
||||
return m_buffer->framebuffer();
|
||||
}
|
||||
|
||||
std::shared_ptr<GLTexture> WaylandEglPrimaryLayer::texture() const
|
||||
{
|
||||
return m_buffer->texture();
|
||||
}
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> WaylandEglPrimaryLayer::doBeginFrame()
|
||||
{
|
||||
if (!m_backend->openglContext()->makeCurrent()) {
|
||||
qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const QSize nativeSize = m_output->modeSize();
|
||||
if (!m_swapchain || m_swapchain->size() != nativeSize) {
|
||||
const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats();
|
||||
for (const uint32_t &candidateFormat : {DRM_FORMAT_XRGB2101010, DRM_FORMAT_XRGB8888}) {
|
||||
auto it = formatTable.constFind(candidateFormat);
|
||||
if (it == formatTable.constEnd()) {
|
||||
continue;
|
||||
}
|
||||
m_swapchain = EglSwapchain::create(m_backend->drmDevice()->allocator(), m_backend->openglContext(), nativeSize, it.key(), it.value());
|
||||
if (m_swapchain) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!m_swapchain) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format";
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer = m_swapchain->acquire();
|
||||
if (!m_buffer) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const QRegion repair = bufferAgeEnabled ? m_damageJournal.accumulate(m_buffer->age(), infiniteRegion()) : infiniteRegion();
|
||||
m_query = std::make_unique<GLRenderTimeQuery>(m_backend->openglContextRef());
|
||||
m_query->begin();
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(m_buffer->framebuffer()),
|
||||
.repaint = repair,
|
||||
};
|
||||
}
|
||||
|
||||
bool WaylandEglPrimaryLayer::doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame)
|
||||
{
|
||||
m_query->end();
|
||||
frame->addRenderTimeQuery(std::move(m_query));
|
||||
// Flush rendering commands to the dmabuf.
|
||||
glFlush();
|
||||
EGLNativeFence releaseFence{m_backend->eglDisplayObject()};
|
||||
|
||||
static_cast<WaylandOutput *>(m_output)->setPrimaryBuffer(m_backend->backend()->importBuffer(m_buffer->buffer()));
|
||||
m_swapchain->release(m_buffer, releaseFence.takeFileDescriptor());
|
||||
|
||||
m_damageJournal.add(damagedRegion);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaylandEglPrimaryLayer::doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame)
|
||||
{
|
||||
// TODO use viewporter to relax this check
|
||||
if (sourceRect() != targetRect() || targetRect() != QRectF(QPointF(0, 0), m_output->modeSize())) {
|
||||
return false;
|
||||
}
|
||||
if (offloadTransform() != OutputTransform::Kind::Normal || color != ColorDescription::sRGB) {
|
||||
return false;
|
||||
}
|
||||
auto presentationBuffer = m_backend->backend()->importBuffer(buffer);
|
||||
if (presentationBuffer) {
|
||||
static_cast<WaylandOutput *>(m_output)->setPrimaryBuffer(presentationBuffer);
|
||||
}
|
||||
return presentationBuffer;
|
||||
}
|
||||
|
||||
DrmDevice *WaylandEglPrimaryLayer::scanoutDevice() const
|
||||
{
|
||||
return m_backend->drmDevice();
|
||||
}
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> WaylandEglPrimaryLayer::supportedDrmFormats() const
|
||||
{
|
||||
return m_backend->backend()->display()->linuxDmabuf()->formats();
|
||||
}
|
||||
|
||||
WaylandEglCursorLayer::WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend)
|
||||
: OutputLayer(output)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandEglCursorLayer::~WaylandEglCursorLayer()
|
||||
{
|
||||
m_backend->openglContext()->makeCurrent();
|
||||
}
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> WaylandEglCursorLayer::doBeginFrame()
|
||||
{
|
||||
if (!m_backend->openglContext()->makeCurrent()) {
|
||||
qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto tmp = targetRect().size().expandedTo(QSize(64, 64));
|
||||
const QSize bufferSize(std::ceil(tmp.width()), std::ceil(tmp.height()));
|
||||
if (!m_swapchain || m_swapchain->size() != bufferSize) {
|
||||
const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats();
|
||||
uint32_t format = DRM_FORMAT_INVALID;
|
||||
QList<uint64_t> modifiers;
|
||||
for (const uint32_t &candidateFormat : {DRM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB8888}) {
|
||||
auto it = formatTable.constFind(candidateFormat);
|
||||
if (it != formatTable.constEnd()) {
|
||||
format = it.key();
|
||||
modifiers = it.value();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (format == DRM_FORMAT_INVALID) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format";
|
||||
return std::nullopt;
|
||||
}
|
||||
m_swapchain = EglSwapchain::create(m_backend->drmDevice()->allocator(), m_backend->openglContext(), bufferSize, format, modifiers);
|
||||
if (!m_swapchain) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer = m_swapchain->acquire();
|
||||
if (!m_buffer) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
m_query = std::make_unique<GLRenderTimeQuery>(m_backend->openglContextRef());
|
||||
m_query->begin();
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(m_buffer->framebuffer()),
|
||||
.repaint = infiniteRegion(),
|
||||
};
|
||||
}
|
||||
|
||||
bool WaylandEglCursorLayer::doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame)
|
||||
{
|
||||
m_query->end();
|
||||
if (frame) {
|
||||
frame->addRenderTimeQuery(std::move(m_query));
|
||||
}
|
||||
// Flush rendering commands to the dmabuf.
|
||||
glFlush();
|
||||
|
||||
wl_buffer *buffer = m_backend->backend()->importBuffer(m_buffer->buffer());
|
||||
Q_ASSERT(buffer);
|
||||
|
||||
static_cast<WaylandOutput *>(m_output)->cursor()->update(buffer, scale(), hotspot().toPoint());
|
||||
|
||||
EGLNativeFence releaseFence{m_backend->eglDisplayObject()};
|
||||
m_swapchain->release(m_buffer, releaseFence.takeFileDescriptor());
|
||||
return true;
|
||||
}
|
||||
|
||||
DrmDevice *WaylandEglCursorLayer::scanoutDevice() const
|
||||
{
|
||||
return m_backend->drmDevice();
|
||||
}
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> WaylandEglCursorLayer::supportedDrmFormats() const
|
||||
{
|
||||
return m_backend->supportedFormats();
|
||||
}
|
||||
|
||||
WaylandEglBackend::WaylandEglBackend(WaylandBackend *b)
|
||||
: AbstractEglBackend()
|
||||
, m_backend(b)
|
||||
{
|
||||
connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput);
|
||||
connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) {
|
||||
m_outputs.erase(output);
|
||||
});
|
||||
|
||||
b->setEglBackend(this);
|
||||
}
|
||||
|
||||
WaylandEglBackend::~WaylandEglBackend()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
WaylandBackend *WaylandEglBackend::backend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
DrmDevice *WaylandEglBackend::drmDevice() const
|
||||
{
|
||||
return m_backend->drmDevice();
|
||||
}
|
||||
|
||||
void WaylandEglBackend::cleanupSurfaces()
|
||||
{
|
||||
m_outputs.clear();
|
||||
}
|
||||
|
||||
bool WaylandEglBackend::createEglWaylandOutput(Output *waylandOutput)
|
||||
{
|
||||
m_outputs[waylandOutput] = Layers{
|
||||
.primaryLayer = std::make_unique<WaylandEglPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
|
||||
.cursorLayer = std::make_unique<WaylandEglCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaylandEglBackend::initializeEgl()
|
||||
{
|
||||
initClientExtensions();
|
||||
|
||||
if (!m_backend->sceneEglDisplayObject()) {
|
||||
for (const QByteArray &extension : {QByteArrayLiteral("EGL_EXT_platform_base"), QByteArrayLiteral("EGL_KHR_platform_gbm")}) {
|
||||
if (!hasClientExtension(extension)) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << extension << "client extension is not supported by the platform";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_backend->drmDevice()->gbmDevice(), nullptr)));
|
||||
}
|
||||
|
||||
const auto display = m_backend->sceneEglDisplayObject();
|
||||
if (!display) {
|
||||
return false;
|
||||
}
|
||||
setEglDisplay(display);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaylandEglBackend::init()
|
||||
{
|
||||
if (!initializeEgl()) {
|
||||
setFailed("Could not initialize egl");
|
||||
return;
|
||||
}
|
||||
if (!initRenderingContext()) {
|
||||
setFailed("Could not initialize rendering context");
|
||||
return;
|
||||
}
|
||||
|
||||
initWayland();
|
||||
}
|
||||
|
||||
bool WaylandEglBackend::initRenderingContext()
|
||||
{
|
||||
if (!createContext(EGL_NO_CONFIG_KHR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto waylandOutputs = m_backend->waylandOutputs();
|
||||
|
||||
// we only allow to start with at least one output
|
||||
if (waylandOutputs.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto *out : waylandOutputs) {
|
||||
if (!createEglWaylandOutput(out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_outputs.empty()) {
|
||||
qCCritical(KWIN_WAYLAND_BACKEND) << "Create Window Surfaces failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
return makeCurrent();
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> WaylandEglBackend::textureForOutput(KWin::Output *output) const
|
||||
{
|
||||
return std::make_pair(m_outputs.at(output).primaryLayer->texture(), ColorDescription::sRGB);
|
||||
}
|
||||
|
||||
std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap)
|
||||
{
|
||||
return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
|
||||
}
|
||||
|
||||
bool WaylandEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
|
||||
{
|
||||
static_cast<WaylandOutput *>(output)->present(frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
OutputLayer *WaylandEglBackend::primaryLayer(Output *output)
|
||||
{
|
||||
return m_outputs[output].primaryLayer.get();
|
||||
}
|
||||
|
||||
OutputLayer *WaylandEglBackend::cursorLayer(Output *output)
|
||||
{
|
||||
return m_outputs[output].cursorLayer.get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_wayland_egl_backend.cpp"
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/outputlayer.h"
|
||||
#include "opengl/eglnativefence.h"
|
||||
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
||||
#include "utils/damagejournal.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct wl_buffer;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class EglSwapchainSlot;
|
||||
class EglSwapchain;
|
||||
class GLFramebuffer;
|
||||
class GraphicsBufferAllocator;
|
||||
class GLRenderTimeQuery;
|
||||
|
||||
namespace Wayland
|
||||
{
|
||||
class WaylandBackend;
|
||||
class WaylandOutput;
|
||||
class WaylandEglBackend;
|
||||
|
||||
class WaylandEglPrimaryLayer : public OutputLayer
|
||||
{
|
||||
public:
|
||||
WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend);
|
||||
~WaylandEglPrimaryLayer() override;
|
||||
|
||||
GLFramebuffer *fbo() const;
|
||||
std::shared_ptr<GLTexture> texture() const;
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> doBeginFrame() override;
|
||||
bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override;
|
||||
bool doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override;
|
||||
DrmDevice *scanoutDevice() const override;
|
||||
QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override;
|
||||
|
||||
private:
|
||||
DamageJournal m_damageJournal;
|
||||
std::shared_ptr<EglSwapchain> m_swapchain;
|
||||
std::shared_ptr<EglSwapchainSlot> m_buffer;
|
||||
std::unique_ptr<GLRenderTimeQuery> m_query;
|
||||
WaylandEglBackend *const m_backend;
|
||||
|
||||
friend class WaylandEglBackend;
|
||||
};
|
||||
|
||||
class WaylandEglCursorLayer : public OutputLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend);
|
||||
~WaylandEglCursorLayer() override;
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> doBeginFrame() override;
|
||||
bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override;
|
||||
DrmDevice *scanoutDevice() const override;
|
||||
QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override;
|
||||
|
||||
private:
|
||||
WaylandEglBackend *m_backend;
|
||||
std::shared_ptr<EglSwapchain> m_swapchain;
|
||||
std::shared_ptr<EglSwapchainSlot> m_buffer;
|
||||
std::unique_ptr<GLRenderTimeQuery> m_query;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL Backend using Egl on a Wayland surface.
|
||||
*
|
||||
* This Backend is the basis for a session compositor running on top of a Wayland system compositor.
|
||||
* It creates a Surface as large as the screen and maps it as a fullscreen shell surface on the
|
||||
* system compositor. The OpenGL context is created on the Wayland surface, so for rendering X11 is
|
||||
* not involved.
|
||||
*
|
||||
* Also in repainting the backend is currently still rather limited. Only supported mode is fullscreen
|
||||
* repaints, which is obviously not optimal. Best solution is probably to go for buffer_age extension
|
||||
* and make it the only available solution next to fullscreen repaints.
|
||||
*/
|
||||
class WaylandEglBackend : public AbstractEglBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaylandEglBackend(WaylandBackend *b);
|
||||
~WaylandEglBackend() override;
|
||||
|
||||
WaylandBackend *backend() const;
|
||||
DrmDevice *drmDevice() const override;
|
||||
|
||||
std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap) override;
|
||||
|
||||
void init() override;
|
||||
bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
|
||||
OutputLayer *primaryLayer(Output *output) override;
|
||||
OutputLayer *cursorLayer(Output *output) override;
|
||||
|
||||
std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> textureForOutput(KWin::Output *output) const override;
|
||||
|
||||
private:
|
||||
bool initializeEgl();
|
||||
bool initRenderingContext();
|
||||
bool createEglWaylandOutput(Output *output);
|
||||
void cleanupSurfaces() override;
|
||||
|
||||
struct Layers
|
||||
{
|
||||
std::unique_ptr<WaylandEglPrimaryLayer> primaryLayer;
|
||||
std::unique_ptr<WaylandEglCursorLayer> cursorLayer;
|
||||
};
|
||||
|
||||
WaylandBackend *m_backend;
|
||||
std::map<Output *, Layers> m_outputs;
|
||||
};
|
||||
|
||||
} // namespace Wayland
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "wayland_logging.h"
|
||||
Q_LOGGING_CATEGORY(KWIN_WAYLAND_BACKEND, "kwin_wayland_backend", QtWarningMsg)
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(KWIN_WAYLAND_BACKEND)
|
||||
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "wayland_output.h"
|
||||
#include "compositor.h"
|
||||
#include "core/outputlayer.h"
|
||||
#include "core/renderbackend.h"
|
||||
#include "core/renderloop_p.h"
|
||||
#include "wayland_backend.h"
|
||||
#include "wayland_display.h"
|
||||
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/pointer.h>
|
||||
#include <KWayland/Client/pointerconstraints.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <KWayland/Client/xdgdecoration.h>
|
||||
|
||||
#include "wayland-presentation-time-client-protocol.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
using namespace KWayland::Client;
|
||||
static const int s_refreshRate = 60000; // TODO: can we get refresh rate data from Wayland host?
|
||||
|
||||
WaylandCursor::WaylandCursor(WaylandBackend *backend)
|
||||
: m_surface(backend->display()->compositor()->createSurface())
|
||||
{
|
||||
}
|
||||
|
||||
WaylandCursor::~WaylandCursor() = default;
|
||||
|
||||
KWayland::Client::Pointer *WaylandCursor::pointer() const
|
||||
{
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
void WaylandCursor::setPointer(KWayland::Client::Pointer *pointer)
|
||||
{
|
||||
if (m_pointer == pointer) {
|
||||
return;
|
||||
}
|
||||
m_pointer = pointer;
|
||||
if (m_pointer) {
|
||||
m_pointer->setCursor(m_surface.get(), m_hotspot);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandCursor::setEnabled(bool enable)
|
||||
{
|
||||
if (m_enabled != enable) {
|
||||
m_enabled = enable;
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandCursor::update(wl_buffer *buffer, qreal scale, const QPoint &hotspot)
|
||||
{
|
||||
if (m_buffer != buffer || m_scale != scale || m_hotspot != hotspot) {
|
||||
m_buffer = buffer;
|
||||
m_scale = scale;
|
||||
m_hotspot = hotspot;
|
||||
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandCursor::sync()
|
||||
{
|
||||
if (!m_enabled) {
|
||||
m_surface->attachBuffer(KWayland::Client::Buffer::Ptr());
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
} else {
|
||||
m_surface->attachBuffer(m_buffer);
|
||||
m_surface->setScale(std::ceil(m_scale));
|
||||
m_surface->damageBuffer(QRect(0, 0, INT32_MAX, INT32_MAX));
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
}
|
||||
|
||||
if (m_pointer) {
|
||||
m_pointer->setCursor(m_surface.get(), m_hotspot);
|
||||
}
|
||||
}
|
||||
|
||||
WaylandOutput::WaylandOutput(const QString &name, WaylandBackend *backend)
|
||||
: Output(backend)
|
||||
, m_renderLoop(std::make_unique<RenderLoop>(this))
|
||||
, m_surface(backend->display()->compositor()->createSurface())
|
||||
, m_xdgShellSurface(backend->display()->xdgShell()->createSurface(m_surface.get()))
|
||||
, m_backend(backend)
|
||||
, m_cursor(std::make_unique<WaylandCursor>(backend))
|
||||
{
|
||||
if (KWayland::Client::XdgDecorationManager *manager = m_backend->display()->xdgDecorationManager()) {
|
||||
m_xdgDecoration.reset(manager->getToplevelDecoration(m_xdgShellSurface.get()));
|
||||
m_xdgDecoration->setMode(KWayland::Client::XdgDecoration::Mode::ServerSide);
|
||||
}
|
||||
|
||||
setInformation(Information{
|
||||
.name = name,
|
||||
.model = name,
|
||||
.capabilities = Capability::Dpms,
|
||||
});
|
||||
|
||||
m_turnOffTimer.setSingleShot(true);
|
||||
m_turnOffTimer.setInterval(dimAnimationTime());
|
||||
connect(&m_turnOffTimer, &QTimer::timeout, this, [this] {
|
||||
updateDpmsMode(DpmsMode::Off);
|
||||
});
|
||||
|
||||
m_configureThrottleTimer.setSingleShot(true);
|
||||
connect(&m_configureThrottleTimer, &QTimer::timeout, this, [this]() {
|
||||
applyConfigure(m_pendingConfigureSize, m_pendingConfigureSerial);
|
||||
});
|
||||
|
||||
connect(m_surface.get(), &KWayland::Client::Surface::frameRendered, this, [this]() {
|
||||
Q_ASSERT(m_frame);
|
||||
m_frame->presented(std::chrono::steady_clock::now().time_since_epoch(), PresentationMode::VSync);
|
||||
m_frame.reset();
|
||||
});
|
||||
|
||||
updateWindowTitle();
|
||||
|
||||
connect(m_xdgShellSurface.get(), &XdgShellSurface::configureRequested, this, &WaylandOutput::handleConfigure);
|
||||
connect(m_xdgShellSurface.get(), &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit);
|
||||
connect(this, &WaylandOutput::enabledChanged, this, &WaylandOutput::updateWindowTitle);
|
||||
connect(this, &WaylandOutput::dpmsModeChanged, this, &WaylandOutput::updateWindowTitle);
|
||||
}
|
||||
|
||||
WaylandOutput::~WaylandOutput()
|
||||
{
|
||||
if (m_presentationFeedback) {
|
||||
wp_presentation_feedback_destroy(m_presentationFeedback);
|
||||
m_presentationFeedback = nullptr;
|
||||
}
|
||||
m_xdgDecoration.reset();
|
||||
m_xdgShellSurface.reset();
|
||||
m_surface.reset();
|
||||
}
|
||||
|
||||
void WaylandOutput::setPrimaryBuffer(wl_buffer *buffer)
|
||||
{
|
||||
m_presentationBuffer = buffer;
|
||||
}
|
||||
|
||||
static void handleDiscarded(void *data,
|
||||
struct wp_presentation_feedback *wp_presentation_feedback)
|
||||
{
|
||||
reinterpret_cast<WaylandOutput *>(data)->frameDiscarded();
|
||||
}
|
||||
|
||||
static void handlePresented(void *data,
|
||||
struct wp_presentation_feedback *wp_presentation_feedback,
|
||||
uint32_t tv_sec_hi,
|
||||
uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec,
|
||||
uint32_t refresh,
|
||||
uint32_t seq_hi,
|
||||
uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
const auto timestamp = std::chrono::seconds((uint64_t(tv_sec_hi) << 32) | tv_sec_lo) + std::chrono::nanoseconds(tv_nsec);
|
||||
uint32_t refreshRate = 60'000;
|
||||
if (refresh != 0) {
|
||||
refreshRate = 1'000'000'000'000 / refresh;
|
||||
}
|
||||
reinterpret_cast<WaylandOutput *>(data)->framePresented(timestamp, refreshRate);
|
||||
}
|
||||
|
||||
static void handleSyncOutput(void *data, struct wp_presentation_feedback *, struct wl_output *)
|
||||
{
|
||||
// intentionally ignored
|
||||
}
|
||||
|
||||
static constexpr struct wp_presentation_feedback_listener s_presentationListener{
|
||||
.sync_output = handleSyncOutput,
|
||||
.presented = handlePresented,
|
||||
.discarded = handleDiscarded,
|
||||
};
|
||||
|
||||
void WaylandOutput::present(const std::shared_ptr<OutputFrame> &frame)
|
||||
{
|
||||
if (!m_presentationBuffer) {
|
||||
return;
|
||||
}
|
||||
m_surface->attachBuffer(m_presentationBuffer);
|
||||
m_surface->damage(frame->damage());
|
||||
m_surface->setScale(std::ceil(scale()));
|
||||
m_presentationBuffer = nullptr;
|
||||
if (auto presentationTime = m_backend->display()->presentationTime()) {
|
||||
m_presentationFeedback = wp_presentation_feedback(presentationTime, *m_surface);
|
||||
wp_presentation_feedback_add_listener(m_presentationFeedback, &s_presentationListener, this);
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
} else {
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::FrameCallback);
|
||||
}
|
||||
m_frame = frame;
|
||||
Q_EMIT outputChange(frame->damage());
|
||||
}
|
||||
|
||||
void WaylandOutput::frameDiscarded()
|
||||
{
|
||||
m_frame.reset();
|
||||
if (m_presentationFeedback) {
|
||||
wp_presentation_feedback_destroy(m_presentationFeedback);
|
||||
m_presentationFeedback = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandOutput::framePresented(std::chrono::nanoseconds timestamp, uint32_t refreshRate)
|
||||
{
|
||||
if (refreshRate != this->refreshRate()) {
|
||||
m_refreshRate = refreshRate;
|
||||
const auto mode = std::make_shared<OutputMode>(pixelSize(), m_refreshRate);
|
||||
State next = m_state;
|
||||
next.modes = {mode};
|
||||
next.currentMode = mode;
|
||||
setState(next);
|
||||
m_renderLoop->setRefreshRate(m_refreshRate);
|
||||
}
|
||||
m_frame->presented(timestamp, PresentationMode::VSync);
|
||||
m_frame.reset();
|
||||
if (m_presentationFeedback) {
|
||||
wp_presentation_feedback_destroy(m_presentationFeedback);
|
||||
m_presentationFeedback = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool WaylandOutput::isReady() const
|
||||
{
|
||||
return m_ready;
|
||||
}
|
||||
|
||||
KWayland::Client::Surface *WaylandOutput::surface() const
|
||||
{
|
||||
return m_surface.get();
|
||||
}
|
||||
|
||||
WaylandCursor *WaylandOutput::cursor() const
|
||||
{
|
||||
return m_cursor.get();
|
||||
}
|
||||
|
||||
WaylandBackend *WaylandOutput::backend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
RenderLoop *WaylandOutput::renderLoop() const
|
||||
{
|
||||
return m_renderLoop.get();
|
||||
}
|
||||
|
||||
bool WaylandOutput::updateCursorLayer(std::optional<std::chrono::nanoseconds> allowedVrrDelay)
|
||||
{
|
||||
if (m_hasPointerLock) {
|
||||
m_cursor->setEnabled(false);
|
||||
return false;
|
||||
} else {
|
||||
m_cursor->setEnabled(Compositor::self()->backend()->cursorLayer(this)->isEnabled());
|
||||
// the layer already takes care of updating the image
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandOutput::init(const QSize &pixelSize, qreal scale)
|
||||
{
|
||||
m_renderLoop->setRefreshRate(m_refreshRate);
|
||||
|
||||
auto mode = std::make_shared<OutputMode>(pixelSize, m_refreshRate);
|
||||
|
||||
State initialState;
|
||||
initialState.modes = {mode};
|
||||
initialState.currentMode = mode;
|
||||
initialState.scale = scale;
|
||||
setState(initialState);
|
||||
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
}
|
||||
|
||||
void WaylandOutput::resize(const QSize &pixelSize)
|
||||
{
|
||||
auto mode = std::make_shared<OutputMode>(pixelSize, m_refreshRate);
|
||||
|
||||
State next = m_state;
|
||||
next.modes = {mode};
|
||||
next.currentMode = mode;
|
||||
setState(next);
|
||||
|
||||
Q_EMIT m_backend->outputsQueried();
|
||||
}
|
||||
|
||||
void WaylandOutput::setDpmsMode(DpmsMode mode)
|
||||
{
|
||||
if (mode == DpmsMode::Off) {
|
||||
if (!m_turnOffTimer.isActive()) {
|
||||
Q_EMIT aboutToTurnOff(std::chrono::milliseconds(m_turnOffTimer.interval()));
|
||||
m_turnOffTimer.start();
|
||||
}
|
||||
} else {
|
||||
m_turnOffTimer.stop();
|
||||
if (mode != dpmsMode()) {
|
||||
updateDpmsMode(mode);
|
||||
Q_EMIT wakeUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandOutput::updateDpmsMode(DpmsMode dpmsMode)
|
||||
{
|
||||
State next = m_state;
|
||||
next.dpmsMode = dpmsMode;
|
||||
setState(next);
|
||||
}
|
||||
|
||||
void WaylandOutput::updateEnabled(bool enabled)
|
||||
{
|
||||
State next = m_state;
|
||||
next.enabled = enabled;
|
||||
setState(next);
|
||||
}
|
||||
|
||||
void WaylandOutput::handleConfigure(const QSize &size, XdgShellSurface::States states, quint32 serial)
|
||||
{
|
||||
if (!m_ready) {
|
||||
m_ready = true;
|
||||
|
||||
applyConfigure(size, serial);
|
||||
} else {
|
||||
// Output resizing is a resource intensive task, so the configure events are throttled.
|
||||
m_pendingConfigureSerial = serial;
|
||||
m_pendingConfigureSize = size;
|
||||
|
||||
if (!m_configureThrottleTimer.isActive()) {
|
||||
m_configureThrottleTimer.start(1000000 / m_state.currentMode->refreshRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandOutput::applyConfigure(const QSize &size, quint32 serial)
|
||||
{
|
||||
m_xdgShellSurface->ackConfigure(serial);
|
||||
if (!size.isEmpty()) {
|
||||
resize(size * scale());
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandOutput::updateWindowTitle()
|
||||
{
|
||||
QString grab;
|
||||
if (m_hasPointerLock) {
|
||||
grab = i18n("Press right control to ungrab pointer");
|
||||
} else if (m_backend->display()->pointerConstraints()) {
|
||||
grab = i18n("Press right control key to grab pointer");
|
||||
}
|
||||
|
||||
QString title = i18nc("Title of nested KWin Wayland with Wayland socket identifier as argument",
|
||||
"KDE Wayland Compositor %1", name());
|
||||
|
||||
if (!isEnabled()) {
|
||||
title += i18n("- Output disabled");
|
||||
} else if (dpmsMode() != DpmsMode::On) {
|
||||
title += i18n("- Output dimmed");
|
||||
} else if (!grab.isEmpty()) {
|
||||
title += QStringLiteral(" — ") + grab;
|
||||
}
|
||||
m_xdgShellSurface->setTitle(title);
|
||||
}
|
||||
|
||||
void WaylandOutput::lockPointer(Pointer *pointer, bool lock)
|
||||
{
|
||||
if (!lock) {
|
||||
const bool surfaceWasLocked = m_pointerLock && m_hasPointerLock;
|
||||
m_pointerLock.reset();
|
||||
m_hasPointerLock = false;
|
||||
if (surfaceWasLocked) {
|
||||
updateWindowTitle();
|
||||
updateCursorLayer(std::nullopt);
|
||||
Q_EMIT m_backend->pointerLockChanged(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_pointerLock);
|
||||
m_pointerLock.reset(m_backend->display()->pointerConstraints()->lockPointer(surface(), pointer, nullptr, PointerConstraints::LifeTime::OneShot));
|
||||
if (!m_pointerLock->isValid()) {
|
||||
m_pointerLock.reset();
|
||||
return;
|
||||
}
|
||||
connect(m_pointerLock.get(), &LockedPointer::locked, this, [this]() {
|
||||
m_hasPointerLock = true;
|
||||
updateWindowTitle();
|
||||
updateCursorLayer(std::nullopt);
|
||||
Q_EMIT m_backend->pointerLockChanged(true);
|
||||
});
|
||||
connect(m_pointerLock.get(), &LockedPointer::unlocked, this, [this]() {
|
||||
m_pointerLock.reset();
|
||||
m_hasPointerLock = false;
|
||||
updateWindowTitle();
|
||||
updateCursorLayer(std::nullopt);
|
||||
Q_EMIT m_backend->pointerLockChanged(false);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_wayland_output.cpp"
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/output.h"
|
||||
|
||||
#include <KWayland/Client/xdgshell.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Client
|
||||
{
|
||||
class Surface;
|
||||
class Pointer;
|
||||
class LockedPointer;
|
||||
class XdgDecoration;
|
||||
}
|
||||
}
|
||||
|
||||
struct wl_buffer;
|
||||
struct wp_presentation_feedback;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class OutputFrame;
|
||||
|
||||
namespace Wayland
|
||||
{
|
||||
class WaylandBackend;
|
||||
|
||||
class WaylandCursor
|
||||
{
|
||||
public:
|
||||
explicit WaylandCursor(WaylandBackend *backend);
|
||||
~WaylandCursor();
|
||||
|
||||
KWayland::Client::Pointer *pointer() const;
|
||||
void setPointer(KWayland::Client::Pointer *pointer);
|
||||
|
||||
void setEnabled(bool enable);
|
||||
void update(wl_buffer *buffer, qreal scale, const QPoint &hotspot);
|
||||
|
||||
private:
|
||||
void sync();
|
||||
|
||||
KWayland::Client::Pointer *m_pointer = nullptr;
|
||||
std::unique_ptr<KWayland::Client::Surface> m_surface;
|
||||
wl_buffer *m_buffer = nullptr;
|
||||
QPoint m_hotspot;
|
||||
qreal m_scale = 1;
|
||||
bool m_enabled = true;
|
||||
};
|
||||
|
||||
class WaylandOutput : public Output
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaylandOutput(const QString &name, WaylandBackend *backend);
|
||||
~WaylandOutput() override;
|
||||
|
||||
RenderLoop *renderLoop() const override;
|
||||
bool updateCursorLayer(std::optional<std::chrono::nanoseconds> allowedVrrDelay) override;
|
||||
|
||||
void init(const QSize &pixelSize, qreal scale);
|
||||
|
||||
bool isReady() const;
|
||||
KWayland::Client::Surface *surface() const;
|
||||
WaylandCursor *cursor() const;
|
||||
WaylandBackend *backend() const;
|
||||
|
||||
void lockPointer(KWayland::Client::Pointer *pointer, bool lock);
|
||||
void resize(const QSize &pixelSize);
|
||||
void setDpmsMode(DpmsMode mode) override;
|
||||
void updateDpmsMode(DpmsMode dpmsMode);
|
||||
void updateEnabled(bool enabled);
|
||||
|
||||
void present(const std::shared_ptr<OutputFrame> &frame);
|
||||
void setPrimaryBuffer(wl_buffer *buffer);
|
||||
|
||||
void frameDiscarded();
|
||||
void framePresented(std::chrono::nanoseconds timestamp, uint32_t refreshRate);
|
||||
|
||||
private:
|
||||
void handleConfigure(const QSize &size, KWayland::Client::XdgShellSurface::States states, quint32 serial);
|
||||
void updateWindowTitle();
|
||||
void applyConfigure(const QSize &size, quint32 serial);
|
||||
|
||||
std::unique_ptr<RenderLoop> m_renderLoop;
|
||||
std::unique_ptr<KWayland::Client::Surface> m_surface;
|
||||
std::unique_ptr<KWayland::Client::XdgShellSurface> m_xdgShellSurface;
|
||||
std::unique_ptr<KWayland::Client::LockedPointer> m_pointerLock;
|
||||
std::unique_ptr<KWayland::Client::XdgDecoration> m_xdgDecoration;
|
||||
WaylandBackend *const m_backend;
|
||||
std::unique_ptr<WaylandCursor> m_cursor;
|
||||
QTimer m_turnOffTimer;
|
||||
bool m_hasPointerLock = false;
|
||||
bool m_ready = false;
|
||||
std::shared_ptr<OutputFrame> m_frame;
|
||||
wl_buffer *m_presentationBuffer = nullptr;
|
||||
quint32 m_pendingConfigureSerial = 0;
|
||||
QSize m_pendingConfigureSize;
|
||||
QTimer m_configureThrottleTimer;
|
||||
wp_presentation_feedback *m_presentationFeedback = nullptr;
|
||||
uint32_t m_refreshRate = 60'000;
|
||||
};
|
||||
|
||||
} // namespace Wayland
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2013, 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "wayland_qpainter_backend.h"
|
||||
#include "core/graphicsbufferview.h"
|
||||
#include "core/shmgraphicsbufferallocator.h"
|
||||
#include "platformsupport/scenes/qpainter/qpainterswapchain.h"
|
||||
#include "wayland_backend.h"
|
||||
#include "wayland_output.h"
|
||||
|
||||
#include <KWayland/Client/surface.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <drm_fourcc.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
|
||||
WaylandQPainterPrimaryLayer::WaylandQPainterPrimaryLayer(WaylandOutput *output, WaylandQPainterBackend *backend)
|
||||
: OutputLayer(output)
|
||||
, m_waylandOutput(output)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandQPainterPrimaryLayer::~WaylandQPainterPrimaryLayer()
|
||||
{
|
||||
}
|
||||
|
||||
QRegion WaylandQPainterPrimaryLayer::accumulateDamage(int bufferAge) const
|
||||
{
|
||||
return m_damageJournal.accumulate(bufferAge, infiniteRegion());
|
||||
}
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> WaylandQPainterPrimaryLayer::doBeginFrame()
|
||||
{
|
||||
const QSize nativeSize(m_waylandOutput->modeSize());
|
||||
if (!m_swapchain || m_swapchain->size() != nativeSize) {
|
||||
m_swapchain = std::make_unique<QPainterSwapchain>(m_backend->graphicsBufferAllocator(), nativeSize, DRM_FORMAT_XRGB8888);
|
||||
}
|
||||
|
||||
m_back = m_swapchain->acquire();
|
||||
if (!m_back) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
m_renderTime = std::make_unique<CpuRenderTimeQuery>();
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(m_back->view()->image()),
|
||||
.repaint = accumulateDamage(m_back->age()),
|
||||
};
|
||||
}
|
||||
|
||||
bool WaylandQPainterPrimaryLayer::doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame)
|
||||
{
|
||||
m_renderTime->end();
|
||||
frame->addRenderTimeQuery(std::move(m_renderTime));
|
||||
m_damageJournal.add(damagedRegion);
|
||||
m_waylandOutput->setPrimaryBuffer(m_waylandOutput->backend()->importBuffer(m_back->buffer()));
|
||||
m_swapchain->release(m_back);
|
||||
return true;
|
||||
}
|
||||
|
||||
DrmDevice *WaylandQPainterPrimaryLayer::scanoutDevice() const
|
||||
{
|
||||
return m_backend->drmDevice();
|
||||
}
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> WaylandQPainterPrimaryLayer::supportedDrmFormats() const
|
||||
{
|
||||
return {{DRM_FORMAT_ARGB8888, {DRM_FORMAT_MOD_LINEAR}}};
|
||||
}
|
||||
|
||||
WaylandQPainterCursorLayer::WaylandQPainterCursorLayer(WaylandOutput *output, WaylandQPainterBackend *backend)
|
||||
: OutputLayer(output)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandQPainterCursorLayer::~WaylandQPainterCursorLayer()
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> WaylandQPainterCursorLayer::doBeginFrame()
|
||||
{
|
||||
const auto tmp = targetRect().size().expandedTo(QSize(64, 64));
|
||||
const QSize bufferSize(std::ceil(tmp.width()), std::ceil(tmp.height()));
|
||||
if (!m_swapchain || m_swapchain->size() != bufferSize) {
|
||||
m_swapchain = std::make_unique<QPainterSwapchain>(m_backend->graphicsBufferAllocator(), bufferSize, DRM_FORMAT_ARGB8888);
|
||||
}
|
||||
|
||||
m_back = m_swapchain->acquire();
|
||||
if (!m_back) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
m_renderTime = std::make_unique<CpuRenderTimeQuery>();
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(m_back->view()->image()),
|
||||
.repaint = infiniteRegion(),
|
||||
};
|
||||
}
|
||||
|
||||
bool WaylandQPainterCursorLayer::doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame)
|
||||
{
|
||||
if (frame) {
|
||||
frame->addRenderTimeQuery(std::move(m_renderTime));
|
||||
}
|
||||
wl_buffer *buffer = static_cast<WaylandOutput *>(m_output)->backend()->importBuffer(m_back->buffer());
|
||||
Q_ASSERT(buffer);
|
||||
|
||||
static_cast<WaylandOutput *>(m_output)->cursor()->update(buffer, scale(), hotspot().toPoint());
|
||||
m_swapchain->release(m_back);
|
||||
return true;
|
||||
}
|
||||
|
||||
DrmDevice *WaylandQPainterCursorLayer::scanoutDevice() const
|
||||
{
|
||||
return m_backend->drmDevice();
|
||||
}
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> WaylandQPainterCursorLayer::supportedDrmFormats() const
|
||||
{
|
||||
return {{DRM_FORMAT_ARGB8888, {DRM_FORMAT_MOD_LINEAR}}};
|
||||
}
|
||||
|
||||
WaylandQPainterBackend::WaylandQPainterBackend(Wayland::WaylandBackend *b)
|
||||
: QPainterBackend()
|
||||
, m_backend(b)
|
||||
, m_allocator(std::make_unique<ShmGraphicsBufferAllocator>())
|
||||
{
|
||||
|
||||
const auto waylandOutputs = m_backend->waylandOutputs();
|
||||
for (auto *output : waylandOutputs) {
|
||||
createOutput(output);
|
||||
}
|
||||
connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandQPainterBackend::createOutput);
|
||||
connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *waylandOutput) {
|
||||
m_outputs.erase(waylandOutput);
|
||||
});
|
||||
}
|
||||
|
||||
WaylandQPainterBackend::~WaylandQPainterBackend()
|
||||
{
|
||||
}
|
||||
|
||||
void WaylandQPainterBackend::createOutput(Output *waylandOutput)
|
||||
{
|
||||
m_outputs[waylandOutput] = Layers{
|
||||
.primaryLayer = std::make_unique<WaylandQPainterPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
|
||||
.cursorLayer = std::make_unique<WaylandQPainterCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
|
||||
};
|
||||
}
|
||||
|
||||
GraphicsBufferAllocator *WaylandQPainterBackend::graphicsBufferAllocator() const
|
||||
{
|
||||
return m_allocator.get();
|
||||
}
|
||||
|
||||
bool WaylandQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
|
||||
{
|
||||
static_cast<WaylandOutput *>(output)->present(frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
OutputLayer *WaylandQPainterBackend::primaryLayer(Output *output)
|
||||
{
|
||||
return m_outputs[output].primaryLayer.get();
|
||||
}
|
||||
|
||||
OutputLayer *WaylandQPainterBackend::cursorLayer(Output *output)
|
||||
{
|
||||
return m_outputs[output].cursorLayer.get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_wayland_qpainter_backend.cpp"
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2013, 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/outputlayer.h"
|
||||
#include "platformsupport/scenes/qpainter/qpainterbackend.h"
|
||||
#include "utils/damagejournal.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
#include <chrono>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Output;
|
||||
class GraphicsBufferAllocator;
|
||||
class QPainterSwapchainSlot;
|
||||
class QPainterSwapchain;
|
||||
|
||||
namespace Wayland
|
||||
{
|
||||
class WaylandBackend;
|
||||
class WaylandDisplay;
|
||||
class WaylandOutput;
|
||||
class WaylandQPainterBackend;
|
||||
|
||||
class WaylandQPainterPrimaryLayer : public OutputLayer
|
||||
{
|
||||
public:
|
||||
WaylandQPainterPrimaryLayer(WaylandOutput *output, WaylandQPainterBackend *backend);
|
||||
~WaylandQPainterPrimaryLayer() override;
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> doBeginFrame() override;
|
||||
bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override;
|
||||
DrmDevice *scanoutDevice() const override;
|
||||
QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override;
|
||||
|
||||
QRegion accumulateDamage(int bufferAge) const;
|
||||
|
||||
private:
|
||||
WaylandOutput *m_waylandOutput;
|
||||
WaylandQPainterBackend *m_backend;
|
||||
DamageJournal m_damageJournal;
|
||||
|
||||
std::unique_ptr<QPainterSwapchain> m_swapchain;
|
||||
std::shared_ptr<QPainterSwapchainSlot> m_back;
|
||||
std::unique_ptr<CpuRenderTimeQuery> m_renderTime;
|
||||
|
||||
friend class WaylandQPainterBackend;
|
||||
};
|
||||
|
||||
class WaylandQPainterCursorLayer : public OutputLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WaylandQPainterCursorLayer(WaylandOutput *output, WaylandQPainterBackend *backend);
|
||||
~WaylandQPainterCursorLayer() override;
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> doBeginFrame() override;
|
||||
bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override;
|
||||
DrmDevice *scanoutDevice() const override;
|
||||
QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override;
|
||||
|
||||
private:
|
||||
WaylandQPainterBackend *m_backend;
|
||||
std::unique_ptr<QPainterSwapchain> m_swapchain;
|
||||
std::shared_ptr<QPainterSwapchainSlot> m_back;
|
||||
std::unique_ptr<CpuRenderTimeQuery> m_renderTime;
|
||||
};
|
||||
|
||||
class WaylandQPainterBackend : public QPainterBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaylandQPainterBackend(WaylandBackend *b);
|
||||
~WaylandQPainterBackend() override;
|
||||
|
||||
GraphicsBufferAllocator *graphicsBufferAllocator() const;
|
||||
|
||||
bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
|
||||
OutputLayer *primaryLayer(Output *output) override;
|
||||
OutputLayer *cursorLayer(Output *output) override;
|
||||
|
||||
private:
|
||||
void createOutput(Output *waylandOutput);
|
||||
|
||||
struct Layers
|
||||
{
|
||||
std::unique_ptr<WaylandQPainterPrimaryLayer> primaryLayer;
|
||||
std::unique_ptr<WaylandQPainterCursorLayer> cursorLayer;
|
||||
};
|
||||
|
||||
WaylandBackend *m_backend;
|
||||
std::unique_ptr<GraphicsBufferAllocator> m_allocator;
|
||||
std::map<Output *, Layers> m_outputs;
|
||||
};
|
||||
|
||||
} // namespace Wayland
|
||||
} // namespace KWin
|
||||
Reference in New Issue
Block a user