[wayland] Add a framebuffer backend

The framebuffer backend currently only supports the QPainter backend
if the format matches RGB32.

It does not yet support any cursors.
This commit is contained in:
Martin Gräßlin 2015-03-31 11:00:46 +02:00
parent 30dd2ce466
commit de3788c094
9 changed files with 499 additions and 0 deletions

View file

@ -421,6 +421,8 @@ if(HAVE_WAYLAND)
set(kwin_KDEINIT_SRCS
${kwin_KDEINIT_SRCS}
abstract_backend.cpp
fb_backend.cpp
screens_fb.cpp
screens_wayland.cpp
screens_x11windowed.cpp
virtual_terminal.cpp

171
fb_backend.cpp Normal file
View file

@ -0,0 +1,171 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "fb_backend.h"
#include "composite.h"
#include "logind.h"
#include "scene_qpainter.h"
#include "screens_fb.h"
#include "virtual_terminal.h"
// Qt
#include <QDebug>
// system
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
// Linux
#include <linux/fb.h>
namespace KWin
{
FramebufferBackend::FramebufferBackend(QObject *parent)
: AbstractBackend(parent)
{
}
FramebufferBackend::~FramebufferBackend()
{
unmap();
if (m_fd >= 0) {
close(m_fd);
}
}
Screens *FramebufferBackend::createScreens(QObject *parent)
{
return new FramebufferScreens(this, parent);
}
QPainterBackend *FramebufferBackend::createQPainterBackend()
{
return new FramebufferQPainterBackend(this);
}
void FramebufferBackend::init()
{
LogindIntegration *logind = LogindIntegration::self();
auto takeControl = [logind, this]() {
if (logind->hasSessionControl()) {
openFrameBuffer();
} else {
logind->takeControl();
connect(logind, &LogindIntegration::hasSessionControlChanged, this, &FramebufferBackend::openFrameBuffer);
}
};
if (logind->isConnected()) {
takeControl();
} else {
connect(logind, &LogindIntegration::connectedChanged, this, takeControl);
}
auto v = VirtualTerminal::create(this);
v->init();
}
void FramebufferBackend::openFrameBuffer()
{
int fd = LogindIntegration::self()->takeDevice(m_device.toUtf8().constData());
if (fd < 0) {
qCWarning(KWIN_CORE) << "Failed to open frame buffer device through logind, trying without";
}
fd = open(m_device.toUtf8().constData(), O_RDWR | O_CLOEXEC);
if (fd < 0) {
qCWarning(KWIN_CORE) << "failed to open frame buffer device";
return;
}
m_fd = fd;
queryScreenInfo();
emit screensQueried();
}
bool FramebufferBackend::queryScreenInfo()
{
if (m_fd < 0) {
return false;
}
fb_var_screeninfo varinfo;
fb_fix_screeninfo fixinfo;
// Probe the device for screen information.
if (ioctl(m_fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 || ioctl(m_fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
return false;
}
m_resolution = QSize(varinfo.xres, varinfo.yres);
m_physicalSize = QSize(varinfo.width, varinfo.height);
m_id = QByteArray(fixinfo.id);
m_red = {varinfo.red.offset, varinfo.red.length};
m_green = {varinfo.green.offset, varinfo.green.length};
m_blue = {varinfo.blue.offset, varinfo.blue.length};
m_alpha = {varinfo.transp.offset, varinfo.transp.length};
m_bitsPerPixel = varinfo.bits_per_pixel;
m_bufferLength = fixinfo.smem_len;
m_bytesPerLine = fixinfo.line_length;
return true;
}
void FramebufferBackend::map()
{
if (m_memory) {
// already mapped;
return;
}
if (m_fd < 0) {
// not valid
return;
}
void *mem = mmap(nullptr, m_bufferLength, PROT_WRITE, MAP_SHARED, m_fd, 0);
if (mem == MAP_FAILED) {
qCWarning(KWIN_CORE) << "Failed to mmap frame buffer";
return;
}
m_memory = mem;
}
void FramebufferBackend::unmap()
{
if (!m_memory) {
return;
}
if (munmap(m_memory, m_bufferLength) < 0) {
qCWarning(KWIN_CORE) << "Failed to munmap frame buffer";
}
m_memory = nullptr;
}
QImage::Format FramebufferBackend::imageFormat() const
{
if (m_fd < 0) {
return QImage::Format_Invalid;
}
if (m_alpha.length == 0 &&
m_red.length == 8 &&
m_green.length == 8 &&
m_blue.length == 8 &&
m_blue.offset == 0 &&
m_green.offset == 8 &&
m_red.offset == 16) {
return QImage::Format_RGB32;
}
return QImage::Format_Invalid;
}
}

99
fb_backend.h Normal file
View file

@ -0,0 +1,99 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_FB_BACKEND_H
#define KWIN_FB_BACKEND_H
#include "abstract_backend.h"
#include <QImage>
#include <QSize>
namespace KWin
{
class KWIN_EXPORT FramebufferBackend : public AbstractBackend
{
Q_OBJECT
public:
explicit FramebufferBackend(QObject *parent = nullptr);
virtual ~FramebufferBackend();
Screens *createScreens(QObject *parent = nullptr) override;
QPainterBackend *createQPainterBackend() override;
void init();
void setDevice(const QString &device) {
m_device = device;
}
bool isValid() const {
return m_fd >= 0;
}
QSize size() const {
return m_resolution;
}
QSize physicalSize() const {
return m_physicalSize;
}
void map();
void unmap();
void *mappedMemory() const {
return m_memory;
}
int bytesPerLine() const {
return m_bytesPerLine;
}
int bufferSize() const {
return m_bufferLength;
}
quint32 bitsPerPixel() const {
return m_bitsPerPixel;
}
QImage::Format imageFormat() const;
Q_SIGNALS:
void screensQueried();
private:
void openFrameBuffer();
bool queryScreenInfo();
QString m_device = QStringLiteral("/dev/fb0");
QSize m_resolution;
QSize m_physicalSize;
QByteArray m_id;
struct Color {
quint32 offset;
quint32 length;
};
Color m_red;
Color m_green;
Color m_blue;
Color m_alpha;
quint32 m_bitsPerPixel = 0;
int m_fd = -1;
quint32 m_bufferLength = 0;
int m_bytesPerLine = 0;
void *m_memory = nullptr;
};
}
#endif

View file

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "workspace.h"
#include <config-kwin.h>
// kwin
#include "fb_backend.h"
#include "wayland_backend.h"
#include "wayland_server.h"
#include "xcbutils.h"
@ -119,6 +120,13 @@ void ApplicationWayland::createBackend()
}
}
}
if (!m_framebuffer.isEmpty()) {
FramebufferBackend *b = new FramebufferBackend(this);
connect(b, &FramebufferBackend::screensQueried, this, &ApplicationWayland::continueStartupWithScreens);
b->setDevice(m_framebuffer);
b->init();
backend = b;
}
if (!backend) {
std::cerr << "FATAL ERROR: could not create a backend, exiting now" << std::endl;
@ -378,6 +386,12 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
QStringLiteral("socket"));
QCommandLineOption windowedOption(QStringLiteral("windowed"),
i18n("Use a nested compositor in windowed mode."));
QCommandLineOption framebufferOption(QStringLiteral("framebuffer"),
i18n("Render to framebuffer."));
QCommandLineOption framebufferDeviceOption(QStringLiteral("fb-device"),
i18n("The framebuffer device to render to."),
QStringLiteral("fbdev"));
framebufferDeviceOption.setDefaultValue(QStringLiteral("/dev/fb0"));
QCommandLineOption x11DisplayOption(QStringLiteral("x11-display"),
i18n("The X11 Display to use in windowed mode on platform X11."),
QStringLiteral("display"));
@ -400,6 +414,8 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
parser.addOption(windowedOption);
parser.addOption(x11DisplayOption);
parser.addOption(waylandDisplayOption);
parser.addOption(framebufferOption);
parser.addOption(framebufferDeviceOption);
parser.addOption(widthOption);
parser.addOption(heightOption);
#if HAVE_INPUT
@ -415,6 +431,11 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
KWin::Application::setUseLibinput(parser.isSet(libinputOption));
#endif
if (parser.isSet(windowedOption) && parser.isSet(framebufferOption)) {
std::cerr << "FATAL ERROR Cannot have both --windowed and --framebuffer" << std::endl;
return 1;
}
a.setWindowed(parser.isSet(windowedOption));
if (parser.isSet(windowedOption)) {
bool ok = false;
@ -440,6 +461,9 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
a.setWaylandDisplay(qgetenv("WAYLAND_DISPLAY"));
}
}
if (parser.isSet(framebufferOption)) {
a.setFramebuffer(parser.value(framebufferDeviceOption));
}
a.setStartXwayland(parser.isSet(xwaylandOption));
a.start();

View file

@ -46,6 +46,9 @@ public:
void setWaylandDisplay(const QByteArray &display) {
m_waylandDisplay = display;
}
void setFramebuffer(const QString &fbdev) {
m_framebuffer = fbdev;
}
protected:
void performStartup() override;
@ -62,6 +65,7 @@ private:
bool m_windowed = false;
QByteArray m_x11Display;
QByteArray m_waylandDisplay;
QString m_framebuffer;
};
}

View file

@ -26,6 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include "toplevel.h"
#if HAVE_WAYLAND
#include "fb_backend.h"
#include "virtual_terminal.h"
#include "wayland_backend.h"
#include "wayland_server.h"
#include "x11windowed_backend.h"
@ -246,6 +248,69 @@ bool X11WindowedQPainterBackend::usesOverlayWindow() const
return false;
}
//****************************************
// FramebufferBackend
//****************************************
FramebufferQPainterBackend::FramebufferQPainterBackend(FramebufferBackend *backend)
: QObject()
, QPainterBackend()
, m_renderBuffer(backend->size(), QImage::Format_RGB32)
, m_backend(backend)
{
m_renderBuffer.fill(Qt::black);
m_backend->map();
m_backBuffer = QImage((uchar*)backend->mappedMemory(),
backend->bytesPerLine() / (backend->bitsPerPixel() / 8),
backend->bufferSize() / backend->bytesPerLine(),
backend->bytesPerLine(), backend->imageFormat());
m_backBuffer.fill(Qt::black);
connect(VirtualTerminal::self(), &VirtualTerminal::activeChanged, this,
[this] (bool active) {
if (active) {
Compositor::self()->bufferSwapComplete();
Compositor::self()->addRepaintFull();
} else {
Compositor::self()->aboutToSwapBuffers();
}
}
);
}
FramebufferQPainterBackend::~FramebufferQPainterBackend() = default;
QImage *FramebufferQPainterBackend::buffer()
{
return &m_renderBuffer;
}
bool FramebufferQPainterBackend::needsFullRepaint() const
{
return false;
}
void FramebufferQPainterBackend::prepareRenderingFrame()
{
}
void FramebufferQPainterBackend::present(int mask, const QRegion &damage)
{
Q_UNUSED(mask)
Q_UNUSED(damage)
if (!VirtualTerminal::self()->isActive()) {
return;
}
QPainter p(&m_backBuffer);
p.drawImage(QPoint(0, 0), m_renderBuffer);
}
bool FramebufferQPainterBackend::usesOverlayWindow() const
{
return false;
}
#endif
//****************************************

View file

@ -44,6 +44,7 @@ namespace Wayland
{
class WaylandBackend;
}
class FramebufferBackend;
class X11WindowedBackend;
class QPainterBackend
@ -152,6 +153,25 @@ private:
QImage m_backBuffer;
X11WindowedBackend *m_backend;
};
class FramebufferQPainterBackend : public QObject, public QPainterBackend
{
Q_OBJECT
public:
FramebufferQPainterBackend(FramebufferBackend *backend);
virtual ~FramebufferQPainterBackend();
QImage *buffer() override;
bool needsFullRepaint() const override;
bool usesOverlayWindow() const override;
void prepareRenderingFrame() override;
void present(int mask, const QRegion &damage) override;
private:
QImage m_renderBuffer;
QImage m_backBuffer;
FramebufferBackend *m_backend;
};
#endif
class SceneQPainter : public Scene

68
screens_fb.cpp Normal file
View file

@ -0,0 +1,68 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "screens_fb.h"
#include "fb_backend.h"
namespace KWin
{
FramebufferScreens::FramebufferScreens(FramebufferBackend *backend, QObject *parent)
: Screens(parent)
, m_backend(backend)
{
}
FramebufferScreens::~FramebufferScreens() = default;
void FramebufferScreens::init()
{
KWin::Screens::init();
updateCount();
emit changed();
}
QRect FramebufferScreens::geometry(int screen) const
{
if (screen == 0) {
return QRect(QPoint(0, 0), size(screen));
}
return QRect();
}
QSize FramebufferScreens::size(int screen) const
{
if (screen == 0) {
return m_backend->size();
}
return QSize();
}
void FramebufferScreens::updateCount()
{
setCount(1);
}
int FramebufferScreens::number(const QPoint &pos) const
{
Q_UNUSED(pos)
return 0;
}
}

46
screens_fb.h Normal file
View file

@ -0,0 +1,46 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_SCREENS_FRAMEBUFFER_H
#define KWIN_SCREENS_FRAMEBUFFER_H
#include "screens.h"
namespace KWin
{
class FramebufferBackend;
class FramebufferScreens : public Screens
{
Q_OBJECT
public:
FramebufferScreens(FramebufferBackend *backend, QObject *parent = nullptr);
virtual ~FramebufferScreens();
void init() override;
QRect geometry(int screen) const override;
int number(const QPoint &pos) const override;
QSize size(int screen) const override;
void updateCount() override;
private:
FramebufferBackend *m_backend;
};
}
#endif