[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:
parent
30dd2ce466
commit
de3788c094
9 changed files with 499 additions and 0 deletions
|
@ -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
171
fb_backend.cpp
Normal 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
99
fb_backend.h
Normal 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
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
//****************************************
|
||||
|
|
|
@ -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
68
screens_fb.cpp
Normal 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
46
screens_fb.h
Normal 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
|
Loading…
Reference in a new issue