summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2016-11-10 12:32:28 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2016-11-24 12:08:21 +0000
commit1f81ba45eeb1a0d233bb2515c048da8743c91f6f (patch)
treeb87d2319b77e6b281d5d4a0eb8f24a3ecacfe5d5 /src/plugins
parent705e3f68df83dca165e3cddb914d3816155323f8 (diff)
linuxfb: add experimental support for DRM dumb buffers
Add a QFbScreen implementation that uses DRM dumb buffers and page flipping, thus implementing a vsynced, double (triple) buffered plain software platform plugin. Besides better supporting systems that by now only do fbdev via (possibly buggy) emulation, this can also be an enabler for implementing certain types of 2D accelerated compositors e.g. for Wayland. Enable with QT_QPA_FB_DRM=1. Right now there is no good way to autodetect the support for dumb buffers early enough so the default is always the traditional fbdev path. Long-term this will also enable proper multi-display support like with eglfs' DRM backends. Multiple QScreens are not yet reported currently due to the single-screen limitations of fbconvenience. The generic DRM code is shared with eglfs, meaning configuration happens via the JSON file specified in QT_QPA_(EGLFS_)KMS_CONFIG. This allows changing the screen resolution, disabling outputs, etc. [ChangeLog][Platform Specific Changes][linuxfb] Added experimental support for DRM dumb buffers. Enable by setting the environment variable QT_QPA_FB_DRM to 1. Task-number: QTBUG-56306 Change-Id: I4bdcf16fff67412183220dffa0c4ea235c5cad3d Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/plugins')
-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;