summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/linuxfb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/linuxfb')
-rw-r--r--src/plugins/platforms/linuxfb/linuxfb.pro14
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp409
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.h68
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp13
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbintegration.h4
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbscreen.h6
6 files changed, 505 insertions, 9 deletions
diff --git a/src/plugins/platforms/linuxfb/linuxfb.pro b/src/plugins/platforms/linuxfb/linuxfb.pro
index e2fa31211d..d3a4476f80 100644
--- a/src/plugins/platforms/linuxfb/linuxfb.pro
+++ b/src/plugins/platforms/linuxfb/linuxfb.pro
@@ -10,8 +10,18 @@ QT += \
qtHaveModule(input_support-private): \
QT += input_support-private
-SOURCES = main.cpp qlinuxfbintegration.cpp qlinuxfbscreen.cpp
-HEADERS = qlinuxfbintegration.h qlinuxfbscreen.h
+SOURCES = main.cpp \
+ qlinuxfbintegration.cpp \
+ qlinuxfbscreen.cpp
+
+HEADERS = qlinuxfbintegration.h \
+ qlinuxfbscreen.h
+
+qtHaveModule(kms_support-private) {
+ QT += kms_support-private
+ SOURCES += qlinuxfbdrmscreen.cpp
+ HEADERS += qlinuxfbdrmscreen.h
+}
OTHER_FILES += linuxfb.json
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp
new file mode 100644
index 0000000000..bdf2634642
--- /dev/null
+++ b/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Experimental DRM dumb buffer backend.
+//
+// TODO:
+// Multiscreen: QWindow-QScreen(-output) association. Needs some reorg (device cannot be owned by screen)
+// Find card via devicediscovery like in eglfs_kms.
+// Mode restore like QEglFSKmsInterruptHandler.
+// Formats other then 32 bpp?
+// grabWindow
+
+#include "qlinuxfbdrmscreen.h"
+#include <QLoggingCategory>
+#include <QGuiApplication>
+#include <QPainter>
+#include <QtFbSupport/private/qfbcursor_p.h>
+#include <QtFbSupport/private/qfbwindow_p.h>
+#include <QtKmsSupport/private/qkmsdevice_p.h>
+#include <QtCore/private/qcore_unix_p.h>
+#include <sys/mman.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcFbDrm, "qt.qpa.fb")
+
+static const int BUFFER_COUNT = 2;
+
+class QLinuxFbDevice : public QKmsDevice
+{
+public:
+ struct Framebuffer {
+ Framebuffer() : handle(0), pitch(0), size(0), fb(0), p(MAP_FAILED) { }
+ uint32_t handle;
+ uint32_t pitch;
+ uint64_t size;
+ uint32_t fb;
+ void *p;
+ QImage wrapper;
+ };
+
+ struct Output {
+ Output() : backFb(0), flipped(false) { }
+ QKmsOutput kmsOutput;
+ Framebuffer fb[BUFFER_COUNT];
+ QRegion dirty[BUFFER_COUNT];
+ int backFb;
+ bool flipped;
+ QSize currentRes() const {
+ const drmModeModeInfo &modeInfo(kmsOutput.modes[kmsOutput.mode]);
+ return QSize(modeInfo.hdisplay, modeInfo.vdisplay);
+ }
+ };
+
+ QLinuxFbDevice(QKmsScreenConfig *screenConfig);
+
+ bool open() override;
+ void close() override;
+
+ void createFramebuffers();
+ void destroyFramebuffers();
+ void setMode();
+
+ void swapBuffers(Output *output);
+
+ int outputCount() const { return m_outputs.count(); }
+ Output *output(int idx) { return &m_outputs[idx]; }
+
+private:
+ void *nativeDisplay() const override;
+ QPlatformScreen *createScreen(const QKmsOutput &output) override;
+ void registerScreen(QPlatformScreen *screen,
+ const QPoint &virtualPos,
+ const QList<QPlatformScreen *> &virtualSiblings) override;
+
+ bool createFramebuffer(Output *output, int bufferIdx);
+ void destroyFramebuffer(Output *output, int bufferIdx);
+
+ static void pageFlipHandler(int fd, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec, void *user_data);
+
+ QVector<Output> m_outputs;
+};
+
+QLinuxFbDevice::QLinuxFbDevice(QKmsScreenConfig *screenConfig)
+ : QKmsDevice(screenConfig, QStringLiteral("/dev/dri/card0"))
+{
+}
+
+bool QLinuxFbDevice::open()
+{
+ int fd = qt_safe_open(devicePath().toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
+ if (fd == -1) {
+ qErrnoWarning("Could not open DRM device %s", qPrintable(devicePath()));
+ return false;
+ }
+
+ uint64_t hasDumbBuf = 0;
+ if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &hasDumbBuf) == -1 || !hasDumbBuf) {
+ qWarning("Dumb buffers not supported");
+ qt_safe_close(fd);
+ return false;
+ }
+
+ setFd(fd);
+
+ qCDebug(qLcFbDrm, "DRM device %s opened", qPrintable(devicePath()));
+
+ return true;
+}
+
+void QLinuxFbDevice::close()
+{
+ for (Output &output : m_outputs)
+ output.kmsOutput.cleanup(this); // restore mode
+
+ m_outputs.clear();
+
+ if (fd() != -1) {
+ qCDebug(qLcFbDrm, "Closing DRM device");
+ qt_safe_close(fd());
+ setFd(-1);
+ }
+}
+
+void *QLinuxFbDevice::nativeDisplay() const
+{
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QPlatformScreen *QLinuxFbDevice::createScreen(const QKmsOutput &output)
+{
+ qCDebug(qLcFbDrm, "Got a new output: %s", qPrintable(output.name));
+ Output o;
+ o.kmsOutput = output;
+ m_outputs.append(o);
+ return nullptr; // no platformscreen, we are not a platform plugin
+}
+
+void QLinuxFbDevice::registerScreen(QPlatformScreen *screen,
+ const QPoint &virtualPos,
+ const QList<QPlatformScreen *> &virtualSiblings)
+{
+ Q_UNUSED(screen);
+ Q_UNUSED(virtualPos);
+ Q_UNUSED(virtualSiblings);
+ Q_UNREACHABLE();
+}
+
+bool QLinuxFbDevice::createFramebuffer(QLinuxFbDevice::Output *output, int bufferIdx)
+{
+ const QSize size = output->currentRes();
+ const uint32_t w = size.width();
+ const uint32_t h = size.height();
+ drm_mode_create_dumb creq = {
+ h,
+ w,
+ 32,
+ 0, 0, 0, 0
+ };
+ if (drmIoctl(fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1) {
+ qErrnoWarning(errno, "Failed to create dumb buffer");
+ return false;
+ }
+
+ Framebuffer &fb(output->fb[bufferIdx]);
+ fb.handle = creq.handle;
+ fb.pitch = creq.pitch;
+ fb.size = creq.size;
+ qCDebug(qLcFbDrm, "Got a dumb buffer for size %dx%d, handle %u, pitch %u, size %u",
+ w, h, fb.handle, fb.pitch, (uint) fb.size);
+
+ if (drmModeAddFB(fd(), w, h, 24, 32, fb.pitch, fb.handle, &fb.fb) == -1) {
+ qErrnoWarning(errno, "Failed to add FB");
+ return false;
+ }
+
+ drm_mode_map_dumb mreq = {
+ fb.handle,
+ 0, 0
+ };
+ if (drmIoctl(fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq) == -1) {
+ qErrnoWarning(errno, "Failed to map dumb buffer");
+ return false;
+ }
+ fb.p = mmap(0, fb.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd(), mreq.offset);
+ if (fb.p == MAP_FAILED) {
+ qErrnoWarning(errno, "Failed to mmap dumb buffer");
+ return false;
+ }
+
+ qCDebug(qLcFbDrm, "FB is %u, mapped at %p", fb.fb, fb.p);
+ memset(fb.p, 0, fb.size);
+
+ fb.wrapper = QImage(static_cast<uchar *>(fb.p), w, h, fb.pitch, QImage::Format_ARGB32);
+
+ return true;
+}
+
+void QLinuxFbDevice::createFramebuffers()
+{
+ for (Output &output : m_outputs) {
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (!createFramebuffer(&output, i))
+ return;
+ }
+ output.backFb = 0;
+ output.flipped = false;
+ }
+}
+
+void QLinuxFbDevice::destroyFramebuffer(QLinuxFbDevice::Output *output, int bufferIdx)
+{
+ Framebuffer &fb(output->fb[bufferIdx]);
+ if (fb.p != MAP_FAILED)
+ munmap(fb.p, fb.size);
+ if (fb.fb) {
+ if (drmModeRmFB(fd(), fb.fb) == -1)
+ qErrnoWarning("Failed to remove fb");
+ }
+ if (fb.handle) {
+ drm_mode_destroy_dumb dreq = { fb.handle };
+ if (drmIoctl(fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &dreq) == -1)
+ qErrnoWarning(errno, "Failed to destroy dumb buffer %u", fb.handle);
+ }
+ fb = Framebuffer();
+}
+
+void QLinuxFbDevice::destroyFramebuffers()
+{
+ for (Output &output : m_outputs) {
+ for (int i = 0; i < BUFFER_COUNT; ++i)
+ destroyFramebuffer(&output, i);
+ }
+}
+
+void QLinuxFbDevice::setMode()
+{
+ for (Output &output : m_outputs) {
+ drmModeModeInfo &modeInfo(output.kmsOutput.modes[output.kmsOutput.mode]);
+ if (drmModeSetCrtc(fd(), output.kmsOutput.crtc_id, output.fb[0].fb, 0, 0,
+ &output.kmsOutput.connector_id, 1, &modeInfo) == -1) {
+ qErrnoWarning(errno, "Failed to set mode");
+ return;
+ }
+
+ output.kmsOutput.mode_set = true; // have cleanup() to restore the mode
+ output.kmsOutput.setPowerState(this, QPlatformScreen::PowerStateOn);
+ }
+}
+
+void QLinuxFbDevice::pageFlipHandler(int fd, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *user_data)
+{
+ Q_UNUSED(fd);
+ Q_UNUSED(sequence);
+ Q_UNUSED(tv_sec);
+ Q_UNUSED(tv_usec);
+
+ Output *output = static_cast<Output *>(user_data);
+ output->backFb = (output->backFb + 1) % BUFFER_COUNT;
+}
+
+void QLinuxFbDevice::swapBuffers(Output *output)
+{
+ Framebuffer &fb(output->fb[output->backFb]);
+ if (drmModePageFlip(fd(), output->kmsOutput.crtc_id, fb.fb, DRM_MODE_PAGE_FLIP_EVENT, output) == -1) {
+ qErrnoWarning(errno, "Page flip failed");
+ return;
+ }
+
+ const int fbIdx = output->backFb;
+ while (output->backFb == fbIdx) {
+ drmEventContext drmEvent = {
+ DRM_EVENT_CONTEXT_VERSION,
+ nullptr,
+ pageFlipHandler
+ };
+ // Blocks until there is something to read on the drm fd
+ // and calls back pageFlipHandler once the flip completes.
+ drmHandleEvent(fd(), &drmEvent);
+ }
+}
+
+QLinuxFbDrmScreen::QLinuxFbDrmScreen(const QStringList &args)
+ : m_screenConfig(nullptr),
+ m_device(nullptr)
+{
+ Q_UNUSED(args);
+}
+
+QLinuxFbDrmScreen::~QLinuxFbDrmScreen()
+{
+ if (m_device) {
+ m_device->destroyFramebuffers();
+ m_device->close();
+ delete m_device;
+ }
+ delete m_screenConfig;
+}
+
+bool QLinuxFbDrmScreen::initialize()
+{
+ m_screenConfig = new QKmsScreenConfig;
+ m_device = new QLinuxFbDevice(m_screenConfig);
+ if (!m_device->open())
+ return false;
+
+ // Discover outputs. Calls back Device::createScreen().
+ m_device->createScreens();
+ // Now off to dumb buffer specifics.
+ m_device->createFramebuffers();
+ // Do the modesetting.
+ m_device->setMode();
+
+ QLinuxFbDevice::Output *output(m_device->output(0));
+
+ mGeometry = QRect(QPoint(0, 0), output->currentRes());
+ mDepth = 32;
+ mFormat = QImage::Format_ARGB32;
+ mPhysicalSize = output->kmsOutput.physical_size;
+ qCDebug(qLcFbDrm) << mGeometry << mPhysicalSize;
+
+ QFbScreen::initializeCompositor();
+
+ mCursor = new QFbCursor(this);
+
+ return true;
+}
+
+QRegion QLinuxFbDrmScreen::doRedraw()
+{
+ const QRegion dirty = QFbScreen::doRedraw();
+ if (dirty.isEmpty())
+ return dirty;
+
+ QLinuxFbDevice::Output *output(m_device->output(0));
+
+ for (int i = 0; i < BUFFER_COUNT; ++i)
+ output->dirty[i] += dirty;
+
+ if (output->fb[output->backFb].wrapper.isNull())
+ return dirty;
+
+ QPainter pntr(&output->fb[output->backFb].wrapper);
+ // Image has alpha but no need for blending at this stage.
+ // Do not waste time with the default SourceOver.
+ pntr.setCompositionMode(QPainter::CompositionMode_Source);
+ for (const QRect &rect : qAsConst(output->dirty[output->backFb]))
+ pntr.drawImage(rect, mScreenImage, rect);
+ pntr.end();
+
+ output->dirty[output->backFb] = QRegion();
+
+ m_device->swapBuffers(output);
+
+ return dirty;
+}
+
+QPixmap QLinuxFbDrmScreen::grabWindow(WId wid, int x, int y, int width, int height) const
+{
+ Q_UNUSED(wid);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(width);
+ Q_UNUSED(height);
+
+ return QPixmap();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.h b/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.h
new file mode 100644
index 0000000000..50a9576798
--- /dev/null
+++ b/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLINUXFBDRMSCREEN_H
+#define QLINUXFBDRMSCREEN_H
+
+#include <QtFbSupport/private/qfbscreen_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QKmsScreenConfig;
+class QLinuxFbDevice;
+
+class QLinuxFbDrmScreen : public QFbScreen
+{
+ Q_OBJECT
+public:
+ QLinuxFbDrmScreen(const QStringList &args);
+ ~QLinuxFbDrmScreen();
+
+ bool initialize() override;
+ QRegion doRedraw() override;
+ QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override;
+
+private:
+ QKmsScreenConfig *m_screenConfig;
+ QLinuxFbDevice *m_device;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLINUXFBDRMSCREEN_H
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp
index c1c235588e..ce193bdf90 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp
+++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp
@@ -39,6 +39,9 @@
#include "qlinuxfbintegration.h"
#include "qlinuxfbscreen.h"
+#if QT_CONFIG(kms)
+#include "qlinuxfbdrmscreen.h"
+#endif
#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
#include <QtServiceSupport/private/qgenericunixservices_p.h>
@@ -69,10 +72,16 @@
QT_BEGIN_NAMESPACE
QLinuxFbIntegration::QLinuxFbIntegration(const QStringList &paramList)
- : m_fontDb(new QGenericUnixFontDatabase),
+ : m_primaryScreen(nullptr),
+ m_fontDb(new QGenericUnixFontDatabase),
m_services(new QGenericUnixServices)
{
- m_primaryScreen = new QLinuxFbScreen(paramList);
+#if QT_CONFIG(kms)
+ if (qEnvironmentVariableIntValue("QT_QPA_FB_DRM") != 0)
+ m_primaryScreen = new QLinuxFbDrmScreen(paramList);
+#endif
+ if (!m_primaryScreen)
+ m_primaryScreen = new QLinuxFbScreen(paramList);
}
QLinuxFbIntegration::~QLinuxFbIntegration()
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
index e48a1bae28..9934a8cd54 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
+++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
@@ -46,7 +46,7 @@
QT_BEGIN_NAMESPACE
class QAbstractEventDispatcher;
-class QLinuxFbScreen;
+class QFbScreen;
class QFbVtHandler;
class QLinuxFbIntegration : public QPlatformIntegration, public QPlatformNativeInterface
@@ -74,7 +74,7 @@ public:
private:
void createInputHandlers();
- QLinuxFbScreen *m_primaryScreen;
+ QFbScreen *m_primaryScreen;
QPlatformInputContext *m_inputContext;
QScopedPointer<QPlatformFontDatabase> m_fontDb;
QScopedPointer<QPlatformServices> m_services;
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.h b/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
index 1e98191569..c7ce455e6a 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
+++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
@@ -54,11 +54,11 @@ public:
QLinuxFbScreen(const QStringList &args);
~QLinuxFbScreen();
- bool initialize();
+ bool initialize() override;
- QPixmap grabWindow(WId wid, int x, int y, int width, int height) const Q_DECL_OVERRIDE;
+ QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override;
- QRegion doRedraw() Q_DECL_OVERRIDE;
+ QRegion doRedraw() override;
private:
QStringList mArgs;