From b7f0583f3157da5cb8a6d86af5a4b4f410847556 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 10 Oct 2014 16:03:36 +0200 Subject: Pluginize the eglfs hooks Following the principle of device integrations in QtWayland and soon xcb, a plugin interface is being introduced to gradually replace the statically compiled-in hooks. The interface is same as before for the time being, for compatibility with the existing device-specific hooks. QEglFSHooks is now just a dummy subclass for QEGLDeviceIntegration to support the legacy, compiled-in, device-specific hooks. When -device is not used with configure and so there is no hook active, the new plugin-based approach kicks in. The environment variable QT_QPA_EGLFS_INTEGRATION can be set to indicate the preferred integration name (e.g. eglfs_x11, eglfs_kms). It can also be set to "none", indicating that no plugins should be considered and the default, non-specialized integration is to be used. (this is for devices, like Beagleboard|bone, that do not need any special code to set up EGL) Device makespecs can set EGLFS_DEVICE_INTEGRATION. The value is then used as the default, preferred plugin name when QT_QPA_EGLFS_INTEGRATION is not set. In the future device makespecs are expected to set a plugin name instead of relying on the traditional EGLFS_PLATFORM_HOOKS_*. When neither the QT_QPA_EGLFS_INTEGRATION nor EGLFS_DEVICE_INTEGRATION are set, all plugins will be tried in an unspecified order. The first one that succeeds to load is used. If all fails or there are no plugins, the built-in, non-specialized integration is used. To debug what integration is being used, enable the logging category qt.qpa.egldeviceintegration. There is some built-in logic for desktop/Mesa based systems: Under X, eglfs_x11 is preferred, otherwise eglfs_kms is prioritized. This, assuming sufficient permissions to video and input devices, allows simply launching apps with -platform eglfs. No more editing of eglfs.pri. [ChangeLog][QtGui] Added support for device-specific backend plugins in eglfs. Change-Id: Ia2ddcddac014c25817171dc140cd8cf913784ac6 Reviewed-by: Louai Al-Khanji --- src/gui/gui.pro | 3 +- .../eglfs/deviceintegration/deviceintegration.pro | 4 + .../deviceintegration/eglfs_kms/eglfs_kms.json | 3 + .../deviceintegration/eglfs_kms/eglfs_kms.pro | 26 + .../eglfs_kms/qeglfskmsintegration.cpp | 1155 +++++++++++++++++++ .../eglfs_kms/qeglfskmsintegration.h | 90 ++ .../deviceintegration/eglfs_kms/qeglfskmsmain.cpp | 58 + .../deviceintegration/eglfs_x11/eglfs_x11.json | 3 + .../deviceintegration/eglfs_x11/eglfs_x11.pro | 24 + .../eglfs_x11/qeglfsx11integration.cpp | 294 +++++ .../eglfs_x11/qeglfsx11integration.h | 95 ++ .../deviceintegration/eglfs_x11/qeglfsx11main.cpp | 58 + src/plugins/platforms/eglfs/eglfs-plugin.pro | 12 + src/plugins/platforms/eglfs/eglfs.pri | 46 - src/plugins/platforms/eglfs/eglfs.pro | 14 +- src/plugins/platforms/eglfs/eglfs_device_lib.pro | 59 + src/plugins/platforms/eglfs/main.cpp | 58 - src/plugins/platforms/eglfs/qeglfscontext.cpp | 12 +- src/plugins/platforms/eglfs/qeglfscontext.h | 3 +- .../platforms/eglfs/qeglfsdeviceintegration.cpp | 306 +++++ .../platforms/eglfs/qeglfsdeviceintegration.h | 113 ++ src/plugins/platforms/eglfs/qeglfsglobal.h | 45 + src/plugins/platforms/eglfs/qeglfshooks.cpp | 130 +++ src/plugins/platforms/eglfs/qeglfshooks.h | 56 +- src/plugins/platforms/eglfs/qeglfshooks_kms.cpp | 1176 -------------------- src/plugins/platforms/eglfs/qeglfshooks_stub.cpp | 233 ---- src/plugins/platforms/eglfs/qeglfshooks_x11.cpp | 342 ------ src/plugins/platforms/eglfs/qeglfsintegration.cpp | 41 +- src/plugins/platforms/eglfs/qeglfsintegration.h | 3 +- src/plugins/platforms/eglfs/qeglfsmain.cpp | 58 + .../platforms/eglfs/qeglfsoffscreenwindow.cpp | 4 +- .../platforms/eglfs/qeglfsoffscreenwindow.h | 3 +- src/plugins/platforms/eglfs/qeglfsscreen.cpp | 16 +- src/plugins/platforms/eglfs/qeglfsscreen.h | 6 +- src/plugins/platforms/eglfs/qeglfswindow.cpp | 6 +- src/plugins/platforms/eglfs/qeglfswindow.h | 4 +- 36 files changed, 2592 insertions(+), 1967 deletions(-) create mode 100644 src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.json create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsmain.cpp create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.pro create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp create mode 100644 src/plugins/platforms/eglfs/eglfs-plugin.pro delete mode 100644 src/plugins/platforms/eglfs/eglfs.pri create mode 100644 src/plugins/platforms/eglfs/eglfs_device_lib.pro delete mode 100644 src/plugins/platforms/eglfs/main.cpp create mode 100644 src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp create mode 100644 src/plugins/platforms/eglfs/qeglfsdeviceintegration.h create mode 100644 src/plugins/platforms/eglfs/qeglfsglobal.h create mode 100644 src/plugins/platforms/eglfs/qeglfshooks.cpp delete mode 100644 src/plugins/platforms/eglfs/qeglfshooks_kms.cpp delete mode 100644 src/plugins/platforms/eglfs/qeglfshooks_stub.cpp delete mode 100644 src/plugins/platforms/eglfs/qeglfshooks_x11.cpp create mode 100644 src/plugins/platforms/eglfs/qeglfsmain.cpp (limited to 'src') diff --git a/src/gui/gui.pro b/src/gui/gui.pro index af84eb7342..94fa5331d6 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -13,7 +13,8 @@ MODULE_PLUGIN_TYPES = \ platforminputcontexts \ generic \ iconengines \ - imageformats + imageformats \ + egldeviceintegrations # This is here only because the platform plugin is no module, obviously. win32:contains(QT_CONFIG, angle)|contains(QT_CONFIG, dynamicgl) { diff --git a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro new file mode 100644 index 0000000000..d8ce385636 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +contains(QT_CONFIG, egl_x11): SUBDIRS += eglfs_x11 +contains(QT_CONFIG, kms): SUBDIRS += eglfs_kms diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.json new file mode 100644 index 0000000000..70fec1cacf --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "eglfs_kms" ] +} diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro new file mode 100644 index 0000000000..2eb46f8427 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro @@ -0,0 +1,26 @@ +TARGET = qeglfs-kms-integration + +PLUGIN_CLASS_NAME=QEglFSKmsIntegrationPlugin +PLUGIN_TYPE=egldeviceintegrations + +load(qt_plugin) + +QT += core-private gui-private platformsupport-private eglfs_device_lib-private + +INCLUDEPATH += $$PWD/../.. + +# Avoid X11 header collision +DEFINES += MESA_EGL_NO_X11_HEADERS + +CONFIG += link_pkgconfig +PKGCONFIG += libdrm gbm + +CONFIG += egl +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +SOURCES += $$PWD/qeglfskmsmain.cpp \ + $$PWD/qeglfskmsintegration.cpp + +HEADERS += $$PWD/qeglfskmsintegration.h + +OTHER_FILES += $$PWD/eglfs_kms.json diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp new file mode 100644 index 0000000000..5a9d3db236 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp @@ -0,0 +1,1155 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmsintegration.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef DRM_CAP_CURSOR_WIDTH +#define DRM_CAP_CURSOR_WIDTH 0x8 +#endif + +#ifndef DRM_CAP_CURSOR_HEIGHT +#define DRM_CAP_CURSOR_HEIGHT 0x9 +#endif + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms") + +class QEglFSKmsCursor; +class QEglFSKmsScreen; + +enum OutputConfiguration { + OutputConfigOff, + OutputConfigPreferred, + OutputConfigCurrent, + OutputConfigMode, + OutputConfigModeline +}; + +struct QEglFSKmsOutput { + QString name; + uint32_t connector_id; + uint32_t crtc_id; + QSizeF physical_size; + int mode; // index of selected mode in list below + bool mode_set; + drmModeCrtcPtr saved_crtc; + QList modes; +}; + +class QEglFSKmsDevice +{ + Q_DISABLE_COPY(QEglFSKmsDevice) + + QEglFSKmsIntegration *m_integration; + QString m_path; + int m_dri_fd; + gbm_device *m_gbm_device; + + quint32 m_crtc_allocator; + quint32 m_connector_allocator; + + int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); + QEglFSKmsScreen *screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos); + + static void pageFlipHandler(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); +public: + QEglFSKmsDevice(QEglFSKmsIntegration *integration, const QString &path); + + bool open(); + void close(); + + void createScreens(); + + gbm_device *device() const; + int fd() const; + + void handleDrmEvent(); +}; + +class QEglFSKmsScreen : public QEglFSScreen +{ + QEglFSKmsIntegration *m_integration; + QEglFSKmsDevice *m_device; + gbm_surface *m_gbm_surface; + + gbm_bo *m_gbm_bo_current; + gbm_bo *m_gbm_bo_next; + + QEglFSKmsOutput m_output; + QPoint m_pos; + QScopedPointer m_cursor; + + struct FrameBuffer { + FrameBuffer() : fb(0) {} + uint32_t fb; + }; + static void bufferDestroyedHandler(gbm_bo *bo, void *data); + FrameBuffer *framebufferForBufferObject(gbm_bo *bo); + + static QMutex m_waitForFlipMutex; + +public: + QEglFSKmsScreen(QEglFSKmsIntegration *integration, + QEglFSKmsDevice *device, + QEglFSKmsOutput output, + QPoint position); + ~QEglFSKmsScreen(); + + QRect geometry() const Q_DECL_OVERRIDE; + int depth() const Q_DECL_OVERRIDE; + QImage::Format format() const Q_DECL_OVERRIDE; + + QSizeF physicalSize() const Q_DECL_OVERRIDE; + QDpi logicalDpi() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; + + QString name() const Q_DECL_OVERRIDE; + + QPlatformCursor *cursor() const Q_DECL_OVERRIDE; + + QEglFSKmsDevice *device() const { return m_device; } + + gbm_surface *surface() const { return m_gbm_surface; } + gbm_surface *createSurface(); + void destroySurface(); + + void waitForFlip(); + void flip(); + void flipFinished(); + + QEglFSKmsOutput &output() { return m_output; } + void restoreMode(); +}; + +QMutex QEglFSKmsScreen::m_waitForFlipMutex; + +class QEglFSKmsCursor : public QPlatformCursor +{ + Q_OBJECT +public: + QEglFSKmsCursor(QEglFSKmsScreen *screen); + ~QEglFSKmsCursor(); + + // input methods + void pointerEvent(const QMouseEvent & event) Q_DECL_OVERRIDE; +#ifndef QT_NO_CURSOR + void changeCursor(QCursor * windowCursor, QWindow * window) Q_DECL_OVERRIDE; +#endif + QPoint pos() const Q_DECL_OVERRIDE; + void setPos(const QPoint &pos) Q_DECL_OVERRIDE; + +private: + void initCursorAtlas(); + + QEglFSKmsScreen *m_screen; + QSize m_cursorSize; + gbm_bo *m_bo; + QPoint m_pos; + QPlatformCursorImage m_cursorImage; + bool m_visible; + + // cursor atlas information + struct CursorAtlas { + CursorAtlas() : cursorsPerRow(0), cursorWidth(0), cursorHeight(0) { } + int cursorsPerRow; + int width, height; // width and height of the atlas + int cursorWidth, cursorHeight; // width and height of cursors inside the atlas + QList hotSpots; + QImage image; + } m_cursorAtlas; +}; + +QEglFSKmsIntegration::QEglFSKmsIntegration() + : m_device(Q_NULLPTR) + , m_hwCursor(true) + , m_pbuffers(false) +{} + +void QEglFSKmsIntegration::platformInit() +{ + loadConfig(); + + if (!m_devicePath.isEmpty()) { + qCDebug(qLcEglfsKmsDebug) << "Using DRM device" << m_devicePath << "specified in config file"; + } else { + + QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask); + QStringList devices = d->scanConnectedDevices(); + qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices; + d->deleteLater(); + + if (devices.isEmpty()) + qFatal("Could not find DRM device!"); + + m_devicePath = devices.first(); + qCDebug(qLcEglfsKmsDebug) << "Using" << m_devicePath; + } + + m_device = new QEglFSKmsDevice(this, m_devicePath); + if (!m_device->open()) + qFatal("Could not open device %s - aborting!", qPrintable(m_devicePath)); +} + +void QEglFSKmsIntegration::platformDestroy() +{ + m_device->close(); + delete m_device; + m_device = Q_NULLPTR; +} + +EGLNativeDisplayType QEglFSKmsIntegration::platformDisplay() const +{ + Q_ASSERT(m_device); + return reinterpret_cast(m_device->device()); +} + +bool QEglFSKmsIntegration::usesDefaultScreen() +{ + return false; +} + +void QEglFSKmsIntegration::screenInit() +{ + m_device->createScreens(); +} + +QSurfaceFormat QEglFSKmsIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + QSurfaceFormat format(inputFormat); + format.setRenderableType(QSurfaceFormat::OpenGLES); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + return format; +} + +EGLNativeWindowType QEglFSKmsIntegration::createNativeWindow(QPlatformWindow *platformWindow, + const QSize &size, + const QSurfaceFormat &format) +{ + Q_UNUSED(size); + Q_UNUSED(format); + + QEglFSKmsScreen *screen = static_cast(platformWindow->screen()); + if (screen->surface()) { + qWarning("Only single window per screen supported!"); + return 0; + } + + return reinterpret_cast(screen->createSurface()); +} + +EGLNativeWindowType QEglFSKmsIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format) +{ + Q_UNUSED(format); + Q_ASSERT(m_device); + + qCDebug(qLcEglfsKmsDebug) << "Creating native off screen window"; + gbm_surface *surface = gbm_surface_create(m_device->device(), + 1, 1, + GBM_FORMAT_XRGB8888, + GBM_BO_USE_RENDERING); + + return reinterpret_cast(surface); +} + +void QEglFSKmsIntegration::destroyNativeWindow(EGLNativeWindowType window) +{ + gbm_surface *surface = reinterpret_cast(window); + gbm_surface_destroy(surface); +} + +bool QEglFSKmsIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case QPlatformIntegration::ThreadedPixmaps: + case QPlatformIntegration::OpenGL: + case QPlatformIntegration::ThreadedOpenGL: + return true; + default: + return false; + } +} + +QPlatformCursor *QEglFSKmsIntegration::createCursor(QPlatformScreen *screen) const +{ + if (m_hwCursor) + return Q_NULLPTR; + else + return new QEGLPlatformCursor(screen); +} + +void QEglFSKmsIntegration::waitForVSync(QPlatformSurface *surface) const +{ + QWindow *window = static_cast(surface->surface()); + QEglFSKmsScreen *screen = static_cast(window->screen()->handle()); + + screen->waitForFlip(); +} + +void QEglFSKmsIntegration::presentBuffer(QPlatformSurface *surface) +{ + QWindow *window = static_cast(surface->surface()); + QEglFSKmsScreen *screen = static_cast(window->screen()->handle()); + + screen->flip(); +} + +bool QEglFSKmsIntegration::supportsPBuffers() const +{ + return m_pbuffers; +} + +bool QEglFSKmsIntegration::hwCursor() const +{ + return m_hwCursor; +} + +QMap QEglFSKmsIntegration::outputSettings() const +{ + return m_outputSettings; +} + +void QEglFSKmsIntegration::loadConfig() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); + if (json.isEmpty()) + return; + + qCDebug(qLcEglfsKmsDebug) << "Loading KMS setup from" << json; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + qCDebug(qLcEglfsKmsDebug) << "Could not open config file" + << json << "for reading"; + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + if (!doc.isObject()) { + qCDebug(qLcEglfsKmsDebug) << "Invalid config file" << json + << "- no top-level JSON object"; + return; + } + + QJsonObject object = doc.object(); + + m_hwCursor = object.value("hwcursor").toBool(m_hwCursor); + m_pbuffers = object.value("pbuffers").toBool(m_pbuffers); + m_devicePath = object.value("device").toString(); + + QJsonArray outputs = object.value("outputs").toArray(); + for (int i = 0; i < outputs.size(); i++) { + QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); + + if (outputSettings.contains("name")) { + QString name = outputSettings.value("name").toString(); + + if (m_outputSettings.contains(name)) { + qCDebug(qLcEglfsKmsDebug) << "Output" << name << "configured multiple times!"; + } + + m_outputSettings.insert(name, outputSettings); + } + } + + qCDebug(qLcEglfsKmsDebug) << "Configuration:\n" + << "\thwcursor:" << m_hwCursor << "\n" + << "\tpbuffers:" << m_pbuffers << "\n" + << "\toutputs:" << m_outputSettings; +} + +QEglFSKmsCursor::QEglFSKmsCursor(QEglFSKmsScreen *screen) + : m_screen(screen) + , m_cursorSize(64, 64) // 64x64 is the old standard size, we now try to query the real size below + , m_bo(Q_NULLPTR) + , m_cursorImage(0, 0, 0, 0, 0, 0) + , m_visible(true) +{ + uint64_t width, height; + if ((drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_WIDTH, &width) == 0) + && (drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_HEIGHT, &height) == 0)) { + m_cursorSize.setWidth(width); + m_cursorSize.setHeight(height); + } + + m_bo = gbm_bo_create(m_screen->device()->device(), m_cursorSize.width(), m_cursorSize.height(), + GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); + if (!m_bo) { + qWarning("Could not create buffer for cursor!"); + } else { + initCursorAtlas(); + } + + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); +} + +QEglFSKmsCursor::~QEglFSKmsCursor() +{ + drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); + + gbm_bo_destroy(m_bo); + m_bo = Q_NULLPTR; +} + +void QEglFSKmsCursor::pointerEvent(const QMouseEvent &event) +{ + setPos(event.screenPos().toPoint()); +} + +#ifndef QT_NO_CURSOR +void QEglFSKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) +{ + Q_UNUSED(window); + + if (!m_visible) + return; + + const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; + if (newShape == Qt::BitmapCursor) { + m_cursorImage.set(windowCursor->pixmap().toImage(), + windowCursor->hotSpot().x(), + windowCursor->hotSpot().y()); + } else { + // Standard cursor, look up in atlas + const int width = m_cursorAtlas.cursorWidth; + const int height = m_cursorAtlas.cursorHeight; + const qreal ws = (qreal)m_cursorAtlas.cursorWidth / m_cursorAtlas.width; + const qreal hs = (qreal)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; + + QRect textureRect(ws * (newShape % m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.width, + hs * (newShape / m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.height, + width, + height); + QPoint hotSpot = m_cursorAtlas.hotSpots[newShape]; + m_cursorImage.set(m_cursorAtlas.image.copy(textureRect), + hotSpot.x(), + hotSpot.y()); + } + + if (m_cursorImage.image()->width() > m_cursorSize.width() || m_cursorImage.image()->height() > m_cursorSize.height()) + qWarning("Cursor larger than %dx%d, cursor will be clipped.", m_cursorSize.width(), m_cursorSize.height()); + + QImage cursorImage(m_cursorSize, QImage::Format_ARGB32); + cursorImage.fill(Qt::transparent); + + QPainter painter; + painter.begin(&cursorImage); + painter.drawImage(0, 0, *m_cursorImage.image()); + painter.end(); + + gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount()); + + uint32_t handle = gbm_bo_get_handle(m_bo).u32; + int status = drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, handle, + m_cursorSize.width(), m_cursorSize.height()); + if (status != 0) + qWarning("Could not set cursor: %d", status); +} +#endif // QT_NO_CURSOR + +QPoint QEglFSKmsCursor::pos() const +{ + return m_pos; +} + +void QEglFSKmsCursor::setPos(const QPoint &pos) +{ + QPoint adjustedPos = pos - m_cursorImage.hotspot(); + int ret = drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, adjustedPos.x(), adjustedPos.y()); + if (ret == 0) { + m_pos = pos; + } else { + qWarning("Failed to move cursor: %d", ret); + } +} + +void QEglFSKmsCursor::initCursorAtlas() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); + if (json.isEmpty()) + json = ":/cursor.json"; + + qCDebug(qLcEglfsKmsDebug) << "Initializing cursor atlas from" << json; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); + m_visible = false; + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + QJsonObject object = doc.object(); + + QString atlas = object.value(QLatin1String("image")).toString(); + Q_ASSERT(!atlas.isEmpty()); + + const int cursorsPerRow = object.value(QLatin1String("cursorsPerRow")).toDouble(); + Q_ASSERT(cursorsPerRow); + m_cursorAtlas.cursorsPerRow = cursorsPerRow; + + const QJsonArray hotSpots = object.value(QLatin1String("hotSpots")).toArray(); + Q_ASSERT(hotSpots.count() == Qt::LastCursor + 1); + for (int i = 0; i < hotSpots.count(); i++) { + QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble()); + m_cursorAtlas.hotSpots << hotSpot; + } + + QImage image = QImage(atlas).convertToFormat(QImage::Format_ARGB32); + m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow; + m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow) / cursorsPerRow); + m_cursorAtlas.width = image.width(); + m_cursorAtlas.height = image.height(); + m_cursorAtlas.image = image; +} + +void QEglFSKmsScreen::bufferDestroyedHandler(gbm_bo *bo, void *data) +{ + FrameBuffer *fb = static_cast(data); + + if (fb->fb) { + gbm_device *device = gbm_bo_get_device(bo); + drmModeRmFB(gbm_device_get_fd(device), fb->fb); + } + + delete fb; +} + +QEglFSKmsScreen::FrameBuffer *QEglFSKmsScreen::framebufferForBufferObject(gbm_bo *bo) +{ + { + FrameBuffer *fb = static_cast(gbm_bo_get_user_data(bo)); + if (fb) + return fb; + } + + uint32_t width = gbm_bo_get_width(bo); + uint32_t height = gbm_bo_get_height(bo); + uint32_t stride = gbm_bo_get_stride(bo); + uint32_t handle = gbm_bo_get_handle(bo).u32; + + QScopedPointer fb(new FrameBuffer); + + int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, + stride, handle, &fb->fb); + + if (ret) { + qWarning("Failed to create KMS FB!"); + return Q_NULLPTR; + } + + gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler); + return fb.take(); + +} + +QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration, + QEglFSKmsDevice *device, + QEglFSKmsOutput output, + QPoint position) + : QEglFSScreen(eglGetDisplay(reinterpret_cast(device->device()))) + , m_integration(integration) + , m_device(device) + , m_gbm_surface(Q_NULLPTR) + , m_gbm_bo_current(Q_NULLPTR) + , m_gbm_bo_next(Q_NULLPTR) + , m_output(output) + , m_pos(position) + , m_cursor(Q_NULLPTR) +{ +} + +QEglFSKmsScreen::~QEglFSKmsScreen() +{ + restoreMode(); +} + +QRect QEglFSKmsScreen::geometry() const +{ + const int mode = m_output.mode; + return QRect(m_pos.x(), m_pos.y(), + m_output.modes[mode].hdisplay, + m_output.modes[mode].vdisplay); +} + +int QEglFSKmsScreen::depth() const +{ + return 32; +} + +QImage::Format QEglFSKmsScreen::format() const +{ + return QImage::Format_RGB32; +} + +QSizeF QEglFSKmsScreen::physicalSize() const +{ + return m_output.physical_size; +} + +QDpi QEglFSKmsScreen::logicalDpi() const +{ + QSizeF ps = physicalSize(); + QSize s = geometry().size(); + + if (ps.isValid() && s.isValid()) + return QDpi(25.4 * s.width() / ps.width(), + 25.4 * s.height() / ps.height()); + else + return QDpi(100, 100); +} + +Qt::ScreenOrientation QEglFSKmsScreen::nativeOrientation() const +{ + return Qt::PrimaryOrientation; +} + +Qt::ScreenOrientation QEglFSKmsScreen::orientation() const +{ + return Qt::PrimaryOrientation; +} + +QString QEglFSKmsScreen::name() const +{ + return m_output.name; +} + +QPlatformCursor *QEglFSKmsScreen::cursor() const +{ + if (m_integration->hwCursor()) { + if (m_cursor.isNull()) { + QEglFSKmsScreen *that = const_cast(this); + that->m_cursor.reset(new QEglFSKmsCursor(that)); + } + + return m_cursor.data(); + } else { + return QEglFSScreen::cursor(); + } +} + +gbm_surface *QEglFSKmsScreen::createSurface() +{ + if (!m_gbm_surface) { + qCDebug(qLcEglfsKmsDebug) << "Creating window for screen" << name(); + m_gbm_surface = gbm_surface_create(m_device->device(), + geometry().width(), + geometry().height(), + GBM_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + } + return m_gbm_surface; +} + +void QEglFSKmsScreen::destroySurface() +{ + if (m_gbm_bo_current) { + gbm_bo_destroy(m_gbm_bo_current); + m_gbm_bo_current = Q_NULLPTR; + } + + if (m_gbm_bo_next) { + gbm_bo_destroy(m_gbm_bo_next); + m_gbm_bo_next = Q_NULLPTR; + } + + if (m_gbm_surface) { + gbm_surface_destroy(m_gbm_surface); + m_gbm_surface = Q_NULLPTR; + } +} + +void QEglFSKmsScreen::waitForFlip() +{ + // Don't lock the mutex unless we actually need to + if (!m_gbm_bo_next) + return; + + QMutexLocker lock(&m_waitForFlipMutex); + while (m_gbm_bo_next) + m_device->handleDrmEvent(); +} + +void QEglFSKmsScreen::flip() +{ + if (!m_gbm_surface) { + qWarning("Cannot sync before platform init!"); + return; + } + + m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); + if (!m_gbm_bo_next) { + qWarning("Could not lock GBM surface front buffer!"); + return; + } + + FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); + + if (!m_output.mode_set) { + int ret = drmModeSetCrtc(m_device->fd(), + m_output.crtc_id, + fb->fb, + 0, 0, + &m_output.connector_id, 1, + &m_output.modes[m_output.mode]); + + if (ret) + qErrnoWarning("Could not set DRM mode!"); + else + m_output.mode_set = true; + } + + int ret = drmModePageFlip(m_device->fd(), + m_output.crtc_id, + fb->fb, + DRM_MODE_PAGE_FLIP_EVENT, + this); + if (ret) { + qErrnoWarning("Could not queue DRM page flip!"); + gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); + m_gbm_bo_next = Q_NULLPTR; + } +} + +void QEglFSKmsScreen::flipFinished() +{ + if (m_gbm_bo_current) + gbm_surface_release_buffer(m_gbm_surface, + m_gbm_bo_current); + + m_gbm_bo_current = m_gbm_bo_next; + m_gbm_bo_next = Q_NULLPTR; +} + +void QEglFSKmsScreen::restoreMode() +{ + if (m_output.mode_set && m_output.saved_crtc) { + drmModeSetCrtc(m_device->fd(), + m_output.saved_crtc->crtc_id, + m_output.saved_crtc->buffer_id, + 0, 0, + &m_output.connector_id, 1, + &m_output.saved_crtc->mode); + + drmModeFreeCrtc(m_output.saved_crtc); + m_output.saved_crtc = Q_NULLPTR; + + m_output.mode_set = false; + } +} + +int QEglFSKmsDevice::crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector) +{ + for (int i = 0; i < connector->count_encoders; i++) { + drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoders[i]); + if (!encoder) { + qWarning("Failed to get encoder"); + continue; + } + + quint32 possibleCrtcs = encoder->possible_crtcs; + drmModeFreeEncoder(encoder); + + for (int j = 0; j < resources->count_crtcs; j++) { + bool isPossible = possibleCrtcs & (1 << j); + bool isAvailable = !(m_crtc_allocator & 1 << resources->crtcs[j]); + + if (isPossible && isAvailable) + return j; + } + } + + return -1; +} + +static const char * const connector_type_names[] = { + "None", + "VGA", + "DVI", + "DVI", + "DVI", + "Composite", + "TV", + "LVDS", + "CTV", + "DIN", + "DP", + "HDMI", + "HDMI", + "TV", + "eDP", +}; + +static QString nameForConnector(const drmModeConnectorPtr connector) +{ + QString connectorName = "UNKNOWN"; + + if (connector->connector_type < ARRAY_LENGTH(connector_type_names)) + connectorName = connector_type_names[connector->connector_type]; + + connectorName += QString::number(connector->connector_type_id); + + return connectorName; +} + +static bool parseModeline(const QString &s, drmModeModeInfoPtr mode) +{ + char hsync[16]; + char vsync[16]; + float fclock; + + mode->type = DRM_MODE_TYPE_USERDEF; + mode->hskew = 0; + mode->vscan = 0; + mode->vrefresh = 0; + mode->flags = 0; + + if (sscanf(qPrintable(s), "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s", + &fclock, + &mode->hdisplay, + &mode->hsync_start, + &mode->hsync_end, + &mode->htotal, + &mode->vdisplay, + &mode->vsync_start, + &mode->vsync_end, + &mode->vtotal, hsync, vsync) != 11) + return false; + + mode->clock = fclock * 1000; + + if (strcmp(hsync, "+hsync") == 0) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else if (strcmp(hsync, "-hsync") == 0) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + else + return false; + + if (strcmp(vsync, "+vsync") == 0) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else if (strcmp(vsync, "-vsync") == 0) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + else + return false; + + return true; +} + +QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos) +{ + const QString connectorName = nameForConnector(connector); + + const int crtc = crtcForConnector(resources, connector); + if (crtc < 0) { + qWarning() << "No usable crtc/encoder pair for connector" << connectorName; + return Q_NULLPTR; + } + + OutputConfiguration configuration; + QSize configurationSize; + drmModeModeInfo configurationModeline; + + const QString mode = m_integration->outputSettings().value(connectorName).value("mode", "preferred").toString().toLower(); + if (mode == "off") { + configuration = OutputConfigOff; + } else if (mode == "preferred") { + configuration = OutputConfigPreferred; + } else if (mode == "current") { + configuration = OutputConfigCurrent; + } else if (sscanf(qPrintable(mode), "%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) { + configuration = OutputConfigMode; + } else if (parseModeline(mode, &configurationModeline)) { + configuration = OutputConfigModeline; + } else { + qWarning("Invalid mode \"%s\" for output %s", qPrintable(mode), qPrintable(connectorName)); + configuration = OutputConfigPreferred; + } + + const uint32_t crtc_id = resources->crtcs[crtc]; + + if (configuration == OutputConfigOff) { + qCDebug(qLcEglfsKmsDebug) << "Turning off output" << connectorName; + drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, Q_NULLPTR); + return Q_NULLPTR; + } + + // Get the current mode on the current crtc + drmModeModeInfo crtc_mode; + memset(&crtc_mode, 0, sizeof crtc_mode); + if (drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->connector_id)) { + drmModeCrtcPtr crtc = drmModeGetCrtc(m_dri_fd, encoder->crtc_id); + drmModeFreeEncoder(encoder); + + if (!crtc) + return Q_NULLPTR; + + if (crtc->mode_valid) + crtc_mode = crtc->mode; + + drmModeFreeCrtc(crtc); + } + + QList modes; + qCDebug(qLcEglfsKmsDebug) << connectorName << "mode count:" << connector->count_modes; + for (int i = 0; i < connector->count_modes; i++) { + const drmModeModeInfo &mode = connector->modes[i]; + qCDebug(qLcEglfsKmsDebug) << "mode" << i << mode.hdisplay << "x" << mode.vdisplay + << "@" << mode.vrefresh << "hz"; + modes << connector->modes[i]; + } + + int preferred = -1; + int current = -1; + int configured = -1; + int best = -1; + + for (int i = modes.size() - 1; i >= 0; i--) { + const drmModeModeInfo &m = modes.at(i); + + if (configuration == OutputConfigMode && + m.hdisplay == configurationSize.width() && + m.vdisplay == configurationSize.height()) { + configured = i; + } + + if (!memcmp(&crtc_mode, &m, sizeof m)) + current = i; + + if (m.type & DRM_MODE_TYPE_PREFERRED) + preferred = i; + + best = i; + } + + if (configuration == OutputConfigModeline) { + modes << configurationModeline; + configured = modes.size() - 1; + } + + if (current < 0 && crtc_mode.clock != 0) { + modes << crtc_mode; + current = mode.size() - 1; + } + + if (configuration == OutputConfigCurrent) + configured = current; + + int selected_mode = -1; + + if (configured >= 0) + selected_mode = configured; + else if (preferred >= 0) + selected_mode = preferred; + else if (current >= 0) + selected_mode = current; + else if (best >= 0) + selected_mode = best; + + if (selected_mode < 0) { + qWarning() << "No modes available for output" << connectorName; + return Q_NULLPTR; + } else { + int width = modes[selected_mode].hdisplay; + int height = modes[selected_mode].vdisplay; + int refresh = modes[selected_mode].vrefresh; + qCDebug(qLcEglfsKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height + << "@" << refresh << "hz for output" << connectorName; + } + + QEglFSKmsOutput output = { + connectorName, + connector->connector_id, + crtc_id, + QSizeF(connector->mmWidth, connector->mmHeight), + selected_mode, + false, + drmModeGetCrtc(m_dri_fd, crtc_id), + modes + }; + + m_crtc_allocator |= (1 << output.crtc_id); + m_connector_allocator |= (1 << output.connector_id); + + return new QEglFSKmsScreen(m_integration, this, output, pos); +} + +void QEglFSKmsDevice::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); + + QEglFSKmsScreen *screen = static_cast(user_data); + screen->flipFinished(); +} + +QEglFSKmsDevice::QEglFSKmsDevice(QEglFSKmsIntegration *integration, const QString &path) + : m_integration(integration) + , m_path(path) + , m_dri_fd(-1) + , m_gbm_device(Q_NULLPTR) + , m_crtc_allocator(0) + , m_connector_allocator(0) +{ +} + +bool QEglFSKmsDevice::open() +{ + Q_ASSERT(m_dri_fd == -1); + Q_ASSERT(m_gbm_device == Q_NULLPTR); + + qCDebug(qLcEglfsKmsDebug) << "Opening device" << m_path; + m_dri_fd = qt_safe_open(m_path.toLocal8Bit().constData(), O_RDWR | O_CLOEXEC); + if (m_dri_fd == -1) { + qErrnoWarning("Could not open DRM device %s", qPrintable(m_path)); + return false; + } + + qCDebug(qLcEglfsKmsDebug) << "Creating GBM device for file descriptor" << m_dri_fd + << "obtained from" << m_path; + m_gbm_device = gbm_create_device(m_dri_fd); + if (!m_gbm_device) { + qErrnoWarning("Could not create GBM device"); + qt_safe_close(m_dri_fd); + m_dri_fd = -1; + return false; + } + + return true; +} + +void QEglFSKmsDevice::close() +{ + if (m_gbm_device) { + gbm_device_destroy(m_gbm_device); + m_gbm_device = Q_NULLPTR; + } + + if (m_dri_fd != -1) { + qt_safe_close(m_dri_fd); + m_dri_fd = -1; + } +} + +void QEglFSKmsDevice::createScreens() +{ + drmModeResPtr resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qWarning("drmModeGetResources failed"); + return; + } + + QPoint pos(0, 0); + QEglFSIntegration *integration = static_cast(QGuiApplicationPrivate::platformIntegration()); + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) + continue; + + QEglFSKmsScreen *screen = screenForConnector(resources, connector, pos); + if (screen) { + integration->addScreen(screen); + pos.rx() += screen->geometry().width(); + } + + drmModeFreeConnector(connector); + } + + drmModeFreeResources(resources); +} + +gbm_device *QEglFSKmsDevice::device() const +{ + return m_gbm_device; +} + +int QEglFSKmsDevice::fd() const +{ + return m_dri_fd; +} + +void QEglFSKmsDevice::handleDrmEvent() +{ + drmEventContext drmEvent = { + DRM_EVENT_CONTEXT_VERSION, + Q_NULLPTR, // vblank handler + pageFlipHandler // page flip handler + }; + + drmHandleEvent(m_dri_fd, &drmEvent); +} + +QT_END_NAMESPACE + +#include "qeglfskmsintegration.moc" diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h new file mode 100644 index 0000000000..44414a2eed --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKMSINTEGRATION_H +#define QEGLFSKMSINTEGRATION_H + +#include "qeglfsdeviceintegration.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QEglFSKmsDevice; + +class QEglFSKmsIntegration : public QEGLDeviceIntegration +{ +public: + QEglFSKmsIntegration(); + + void platformInit() Q_DECL_OVERRIDE; + void platformDestroy() Q_DECL_OVERRIDE; + EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; + bool usesDefaultScreen() Q_DECL_OVERRIDE; + void screenInit() Q_DECL_OVERRIDE; + QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const Q_DECL_OVERRIDE; + EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, + const QSize &size, + const QSurfaceFormat &format) Q_DECL_OVERRIDE; + EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) Q_DECL_OVERRIDE; + void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + QPlatformCursor *createCursor(QPlatformScreen *screen) const Q_DECL_OVERRIDE; + void waitForVSync(QPlatformSurface *surface) const Q_DECL_OVERRIDE; + void presentBuffer(QPlatformSurface *surface) Q_DECL_OVERRIDE; + bool supportsPBuffers() const Q_DECL_OVERRIDE; + + bool hwCursor() const; + QMap outputSettings() const; + +private: + void loadConfig(); + + QEglFSKmsDevice *m_device; + bool m_hwCursor; + bool m_pbuffers; + QString m_devicePath; + QMap m_outputSettings; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsmain.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsmain.cpp new file mode 100644 index 0000000000..8d75ff797e --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsmain.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the qmake spec 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsdeviceintegration.h" +#include "qeglfskmsintegration.h" + +QT_BEGIN_NAMESPACE + +class QEglFSKmsIntegrationPlugin : public QEGLDeviceIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QEGLDeviceIntegrationFactoryInterface_iid FILE "eglfs_kms.json") + +public: + QEGLDeviceIntegration *create() Q_DECL_OVERRIDE { return new QEglFSKmsIntegration; } +}; + +QT_END_NAMESPACE + +#include "qeglfskmsmain.moc" diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json new file mode 100644 index 0000000000..846252789a --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "eglfs_x11" ] +} diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.pro new file mode 100644 index 0000000000..a1d08248ff --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.pro @@ -0,0 +1,24 @@ +TARGET = qeglfs-x11-integration + +PLUGIN_CLASS_NAME=QEglFSX11IntegrationPlugin +PLUGIN_TYPE=egldeviceintegrations + +load(qt_plugin) + +QT += core-private gui-private platformsupport-private eglfs_device_lib-private + +# Avoid X11 header collision +DEFINES += MESA_EGL_NO_X11_HEADERS + +INCLUDEPATH += $$PWD/../.. + +CONFIG += egl +LIBS += -lX11 -lX11-xcb -lxcb +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +SOURCES += $$PWD/qeglfsx11main.cpp \ + $$PWD/qeglfsx11integration.cpp + +HEADERS += $$PWD/qeglfsx11integration.h + +OTHER_FILES += $$PWD/eglfs_x11.json diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp new file mode 100644 index 0000000000..0ce5e341bf --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsx11integration.h" +#include + +#include +#include + +/* Make no mistake: This is not a replacement for the xcb platform plugin. + This here is barely an extremely useful tool for developing eglfs itself because + it allows to do so without any requirements for devices or drivers. */ + +QT_BEGIN_NAMESPACE + +class EventReader : public QThread +{ +public: + EventReader(QEglFSX11Integration *integration) + : m_integration(integration) { } + + void run(); + +private: + QEglFSX11Integration *m_integration; +}; + +QAtomicInt running; + +static Qt::MouseButtons translateMouseButtons(int s) +{ + Qt::MouseButtons ret = 0; + if (s & XCB_BUTTON_MASK_1) + ret |= Qt::LeftButton; + if (s & XCB_BUTTON_MASK_2) + ret |= Qt::MidButton; + if (s & XCB_BUTTON_MASK_3) + ret |= Qt::RightButton; + return ret; +} + +static Qt::MouseButton translateMouseButton(xcb_button_t s) +{ + switch (s) { + case 1: return Qt::LeftButton; + case 2: return Qt::MidButton; + case 3: return Qt::RightButton; + // Button values 4-7 were already handled as Wheel events, and won't occur here. + case 8: return Qt::BackButton; // Also known as Qt::ExtraButton1 + case 9: return Qt::ForwardButton; // Also known as Qt::ExtraButton2 + case 10: return Qt::ExtraButton3; + case 11: return Qt::ExtraButton4; + case 12: return Qt::ExtraButton5; + case 13: return Qt::ExtraButton6; + case 14: return Qt::ExtraButton7; + case 15: return Qt::ExtraButton8; + case 16: return Qt::ExtraButton9; + case 17: return Qt::ExtraButton10; + case 18: return Qt::ExtraButton11; + case 19: return Qt::ExtraButton12; + case 20: return Qt::ExtraButton13; + case 21: return Qt::ExtraButton14; + case 22: return Qt::ExtraButton15; + case 23: return Qt::ExtraButton16; + case 24: return Qt::ExtraButton17; + case 25: return Qt::ExtraButton18; + case 26: return Qt::ExtraButton19; + case 27: return Qt::ExtraButton20; + case 28: return Qt::ExtraButton21; + case 29: return Qt::ExtraButton22; + case 30: return Qt::ExtraButton23; + case 31: return Qt::ExtraButton24; + default: return Qt::NoButton; + } +} + +void EventReader::run() +{ + Qt::MouseButtons buttons; + + xcb_generic_event_t *event; + while (running.load() && (event = xcb_wait_for_event(m_integration->connection()))) { + uint response_type = event->response_type & ~0x80; + switch (response_type) { + case XCB_BUTTON_PRESS: { + xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; + QPoint p(press->event_x, press->event_y); + buttons = (buttons & ~0x7) | translateMouseButtons(press->state); + buttons |= translateMouseButton(press->detail); + QWindowSystemInterface::handleMouseEvent(0, press->time, p, p, buttons); + break; + } + case XCB_BUTTON_RELEASE: { + xcb_button_release_event_t *release = (xcb_button_release_event_t *)event; + QPoint p(release->event_x, release->event_y); + buttons = (buttons & ~0x7) | translateMouseButtons(release->state); + buttons &= ~translateMouseButton(release->detail); + QWindowSystemInterface::handleMouseEvent(0, release->time, p, p, buttons); + break; + } + case XCB_MOTION_NOTIFY: { + xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; + QPoint p(motion->event_x, motion->event_y); + QWindowSystemInterface::handleMouseEvent(0, motion->time, p, p, buttons); + break; + } + case XCB_CLIENT_MESSAGE: { + xcb_client_message_event_t *client = (xcb_client_message_event_t *) event; + const xcb_atom_t *atoms = m_integration->atoms(); + if (client->format == 32 + && client->type == atoms[Atoms::WM_PROTOCOLS] + && client->data.data32[0] == atoms[Atoms::WM_DELETE_WINDOW]) { + QWindow *window = m_integration->platformWindow() ? m_integration->platformWindow()->window() : 0; + if (window) + QWindowSystemInterface::handleCloseEvent(window); + } + break; + } + default: + break; + } + } +} + +void QEglFSX11Integration::sendConnectionEvent(xcb_atom_t a) +{ + xcb_client_message_event_t event; + memset(&event, 0, sizeof(event)); + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.sequence = 0; + event.window = m_connectionEventListener; + event.type = a; + + xcb_send_event(m_connection, false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + xcb_flush(m_connection); +} + +#define DISPLAY ((Display *) m_display) + +void QEglFSX11Integration::platformInit() +{ + m_display = XOpenDisplay(0); + if (!m_display) + qFatal("Could not open display"); + + XSetEventQueueOwner(DISPLAY, XCBOwnsEventQueue); + m_connection = XGetXCBConnection(DISPLAY); + + running.ref(); + + xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); + + m_connectionEventListener = xcb_generate_id(m_connection); + xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, + m_connectionEventListener, it.data->root, + 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, + it.data->root_visual, 0, 0); + + m_eventReader = new EventReader(this); + m_eventReader->start(); +} + +void QEglFSX11Integration::platformDestroy() +{ + running.deref(); + + sendConnectionEvent(XCB_ATOM_NONE); + + m_eventReader->wait(); + delete m_eventReader; + m_eventReader = 0; + + XCloseDisplay(DISPLAY); + m_display = 0; + m_connection = 0; +} + +EGLNativeDisplayType QEglFSX11Integration::platformDisplay() const +{ + return DISPLAY; +} + +QSize QEglFSX11Integration::screenSize() const +{ + if (m_screenSize.isEmpty()) { + QList env = qgetenv("EGLFS_X11_SIZE").split('x'); + if (env.length() == 2) { + m_screenSize = QSize(env.at(0).toInt(), env.at(1).toInt()); + } else { + m_screenSize = QSize(640, 480); + qWarning("EGLFS_X11_SIZE not set, falling back to 640x480"); + } + } + return m_screenSize; +} + +EGLNativeWindowType QEglFSX11Integration::createNativeWindow(QPlatformWindow *platformWindow, + const QSize &size, + const QSurfaceFormat &format) +{ + Q_UNUSED(format); + + m_platformWindow = platformWindow; + + xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); + m_window = xcb_generate_id(m_connection); + xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, it.data->root, + 0, 0, size.width(), size.height(), 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, it.data->root_visual, + 0, 0); + + xcb_map_window(m_connection, m_window); + + xcb_intern_atom_cookie_t cookies[Atoms::N_ATOMS]; + static const char *atomNames[Atoms::N_ATOMS] = { + "_NET_WM_NAME", + "UTF8_STRING", + "WM_PROTOCOLS", + "WM_DELETE_WINDOW", + "_NET_WM_STATE", + "_NET_WM_STATE_FULLSCREEN" + }; + + for (int i = 0; i < Atoms::N_ATOMS; ++i) { + cookies[i] = xcb_intern_atom(m_connection, false, strlen(atomNames[i]), atomNames[i]); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(m_connection, cookies[i], 0); + m_atoms[i] = reply->atom; + free(reply); + } + + // Set window title + xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, + m_atoms[Atoms::_NET_WM_NAME], m_atoms[Atoms::UTF8_STRING], 8, 5, "EGLFS"); + + // Enable WM_DELETE_WINDOW + xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, + m_atoms[Atoms::WM_PROTOCOLS], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::WM_DELETE_WINDOW]); + + if (qEnvironmentVariableIntValue("EGLFS_X11_FULLSCREEN")) { + // Go fullscreen. The QScreen and QWindow size is controlled by EGLFS_X11_SIZE regardless, + // this is just the native window. + xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, + m_atoms[Atoms::_NET_WM_STATE], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::_NET_WM_STATE_FULLSCREEN]); + } + + xcb_flush(m_connection); + + return m_window; +} + +void QEglFSX11Integration::destroyNativeWindow(EGLNativeWindowType window) +{ + xcb_destroy_window(m_connection, window); +} + +bool QEglFSX11Integration::hasCapability(QPlatformIntegration::Capability cap) const +{ + Q_UNUSED(cap); + return false; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h new file mode 100644 index 0000000000..78db504f3b --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSX11INTEGRATION_H +#define QEGLFSX11INTEGRATION_H + +#include "qeglfsdeviceintegration.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace Atoms { + enum { + _NET_WM_NAME = 0, + UTF8_STRING, + WM_PROTOCOLS, + WM_DELETE_WINDOW, + _NET_WM_STATE, + _NET_WM_STATE_FULLSCREEN, + + N_ATOMS + }; +} + +class EventReader; + +class QEglFSX11Integration : public QEGLDeviceIntegration +{ +public: + QEglFSX11Integration() : m_connection(0), m_window(0), m_eventReader(0) {} + + void platformInit() Q_DECL_OVERRIDE; + void platformDestroy() Q_DECL_OVERRIDE; + EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; + QSize screenSize() const Q_DECL_OVERRIDE; + EGLNativeWindowType createNativeWindow(QPlatformWindow *window, + const QSize &size, + const QSurfaceFormat &format) Q_DECL_OVERRIDE; + void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + + xcb_connection_t *connection() { return m_connection; } + const xcb_atom_t *atoms() const { return m_atoms; } + QPlatformWindow *platformWindow() { return m_platformWindow; } + +private: + void sendConnectionEvent(xcb_atom_t a); + + void *m_display; + xcb_connection_t *m_connection; + xcb_atom_t m_atoms[Atoms::N_ATOMS]; + xcb_window_t m_window; + EventReader *m_eventReader; + xcb_window_t m_connectionEventListener; + QPlatformWindow *m_platformWindow; + mutable QSize m_screenSize; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp new file mode 100644 index 0000000000..2eee03f595 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the qmake spec 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsdeviceintegration.h" +#include "qeglfsx11integration.h" + +QT_BEGIN_NAMESPACE + +class QEglFSX11IntegrationPlugin : public QEGLDeviceIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QEGLDeviceIntegrationFactoryInterface_iid FILE "eglfs_x11.json") + +public: + QEGLDeviceIntegration *create() Q_DECL_OVERRIDE { return new QEglFSX11Integration; } +}; + +QT_END_NAMESPACE + +#include "qeglfsx11main.moc" diff --git a/src/plugins/platforms/eglfs/eglfs-plugin.pro b/src/plugins/platforms/eglfs/eglfs-plugin.pro new file mode 100644 index 0000000000..0f493fdc01 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfs-plugin.pro @@ -0,0 +1,12 @@ +TARGET = qeglfs + +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = QEglFSIntegrationPlugin +!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - +load(qt_plugin) + +QT += platformsupport-private eglfs_device_lib-private + +SOURCES += $$PWD/qeglfsmain.cpp + +OTHER_FILES += $$PWD/eglfs.json diff --git a/src/plugins/platforms/eglfs/eglfs.pri b/src/plugins/platforms/eglfs/eglfs.pri deleted file mode 100644 index 182539d8fe..0000000000 --- a/src/plugins/platforms/eglfs/eglfs.pri +++ /dev/null @@ -1,46 +0,0 @@ -QT += core-private gui-private platformsupport-private - -# Avoid X11 header collision -DEFINES += MESA_EGL_NO_X11_HEADERS - -# Uncomment these to enable the X hooks, allowing to test the platform -# plugin in a regular X11 environment (as long as EGL is available). -# EGLFS_PLATFORM_HOOKS_SOURCES += qeglfshooks_x11.cpp -# LIBS += -lX11 -lX11-xcb -lxcb - -# Uncomment these to enable the KMS hooks. -# EGLFS_PLATFORM_HOOKS_SOURCES += qeglfshooks_kms.cpp -# CONFIG += link_pkgconfig -# PKGCONFIG += libdrm gbm - -SOURCES += $$PWD/qeglfsintegration.cpp \ - $$PWD/qeglfswindow.cpp \ - $$PWD/qeglfsscreen.cpp \ - $$PWD/qeglfshooks_stub.cpp \ - $$PWD/qeglfscontext.cpp \ - $$PWD/qeglfsoffscreenwindow.cpp - -HEADERS += $$PWD/qeglfsintegration.h \ - $$PWD/qeglfswindow.h \ - $$PWD/qeglfsscreen.h \ - $$PWD/qeglfshooks.h \ - $$PWD/qeglfscontext.h \ - $$PWD/qeglfsoffscreenwindow.h - -QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF - -INCLUDEPATH += $$PWD - -!isEmpty(EGLFS_PLATFORM_HOOKS_SOURCES) { - HEADERS += $$EGLFS_PLATFORM_HOOKS_HEADERS - SOURCES += $$EGLFS_PLATFORM_HOOKS_SOURCES - LIBS += $$EGLFS_PLATFORM_HOOKS_LIBS - DEFINES += EGLFS_PLATFORM_HOOKS -} - -CONFIG += egl qpa/genericunixfontdatabase - -RESOURCES += $$PWD/cursor.qrc - -OTHER_FILES += \ - $$PWD/eglfs.json diff --git a/src/plugins/platforms/eglfs/eglfs.pro b/src/plugins/platforms/eglfs/eglfs.pro index 3ebe05b35e..03c96ca1d9 100644 --- a/src/plugins/platforms/eglfs/eglfs.pro +++ b/src/plugins/platforms/eglfs/eglfs.pro @@ -1,10 +1,6 @@ -TARGET = qeglfs +TEMPLATE = subdirs +CONFIG += ordered -PLUGIN_TYPE = platforms -PLUGIN_CLASS_NAME = QEglFSIntegrationPlugin -!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -load(qt_plugin) - -SOURCES += $$PWD/main.cpp - -include(eglfs.pri) +SUBDIRS += eglfs_device_lib.pro +SUBDIRS += eglfs-plugin.pro +SUBDIRS += deviceintegration diff --git a/src/plugins/platforms/eglfs/eglfs_device_lib.pro b/src/plugins/platforms/eglfs/eglfs_device_lib.pro new file mode 100644 index 0000000000..d8ce421a69 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfs_device_lib.pro @@ -0,0 +1,59 @@ +# The device integration plugin base class has to live in a shared library, +# placing it into a static lib like platformsupport is not sufficient since we +# have to keep the QObject magic like qobject_cast working. +# Hence this header-less, private-only module. + +TARGET = QtEglDeviceIntegration +CONFIG += no_module_headers internal_module + +MODULE_INCLUDES = \ + \$\$QT_MODULE_INCLUDE_BASE \ + \$\$QT_MODULE_INCLUDE_BASE/QtQGui +MODULE_PRIVATE_INCLUDES = \ + \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION \ + \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION/QtGui + +load(qt_module) + +QT += core-private gui-private platformsupport-private +LIBS += $$QMAKE_LIBS_DYNLOAD + +# Avoid X11 header collision +DEFINES += MESA_EGL_NO_X11_HEADERS + +DEFINES += QT_BUILD_EGL_DEVICE_LIB + +SOURCES += $$PWD/qeglfsintegration.cpp \ + $$PWD/qeglfswindow.cpp \ + $$PWD/qeglfsscreen.cpp \ + $$PWD/qeglfshooks.cpp \ + $$PWD/qeglfscontext.cpp \ + $$PWD/qeglfsoffscreenwindow.cpp \ + $$PWD/qeglfsdeviceintegration.cpp + +HEADERS += $$PWD/qeglfsintegration.h \ + $$PWD/qeglfswindow.h \ + $$PWD/qeglfsscreen.h \ + $$PWD/qeglfshooks.h \ + $$PWD/qeglfscontext.h \ + $$PWD/qeglfsoffscreenwindow.h \ + $$PWD/qeglfsdeviceintegration.h + +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +INCLUDEPATH += $$PWD + +!isEmpty(EGLFS_PLATFORM_HOOKS_SOURCES) { + HEADERS += $$EGLFS_PLATFORM_HOOKS_HEADERS + SOURCES += $$EGLFS_PLATFORM_HOOKS_SOURCES + LIBS += $$EGLFS_PLATFORM_HOOKS_LIBS + DEFINES += EGLFS_PLATFORM_HOOKS +} + +!isEmpty(EGLFS_DEVICE_INTEGRATION) { + DEFINES += EGLFS_PREFERRED_PLUGIN=$$EGLFS_DEVICE_INTEGRATION +} + +CONFIG += egl qpa/genericunixfontdatabase + +RESOURCES += $$PWD/cursor.qrc diff --git a/src/plugins/platforms/eglfs/main.cpp b/src/plugins/platforms/eglfs/main.cpp deleted file mode 100644 index 985fccb7af..0000000000 --- a/src/plugins/platforms/eglfs/main.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "qeglfsintegration.h" - -QT_BEGIN_NAMESPACE - -class QEglFSIntegrationPlugin : public QPlatformIntegrationPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "eglfs.json") -public: - QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE; -}; - -QPlatformIntegration* QEglFSIntegrationPlugin::create(const QString& system, const QStringList& paramList) -{ - Q_UNUSED(paramList); - if (!system.compare(QLatin1String("eglfs"), Qt::CaseInsensitive)) - return new QEglFSIntegration; - - return 0; -} - -QT_END_NAMESPACE - -#include "main.moc" diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 00778373ae..7054c90521 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -60,11 +60,11 @@ EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface EGLSurface QEglFSContext::createTemporaryOffscreenSurface() { - if (QEglFSHooks::hooks()->supportsPBuffers()) + if (qt_egl_device_integration()->supportsPBuffers()) return QEGLPlatformContext::createTemporaryOffscreenSurface(); if (!m_tempWindow) { - m_tempWindow = QEglFSHooks::hooks()->createNativeOffscreenWindow(format()); + m_tempWindow = qt_egl_device_integration()->createNativeOffscreenWindow(format()); if (!m_tempWindow) { qWarning("QEglFSContext: Failed to create temporary native window"); return EGL_NO_SURFACE; @@ -76,11 +76,11 @@ EGLSurface QEglFSContext::createTemporaryOffscreenSurface() void QEglFSContext::destroyTemporaryOffscreenSurface(EGLSurface surface) { - if (QEglFSHooks::hooks()->supportsPBuffers()) { + if (qt_egl_device_integration()->supportsPBuffers()) { QEGLPlatformContext::destroyTemporaryOffscreenSurface(surface); } else { eglDestroySurface(eglDisplay(), surface); - QEglFSHooks::hooks()->destroyNativeWindow(m_tempWindow); + qt_egl_device_integration()->destroyNativeWindow(m_tempWindow); m_tempWindow = 0; } } @@ -94,9 +94,9 @@ void QEglFSContext::swapBuffers(QPlatformSurface *surface) cursor->paintOnScreen(); } - QEglFSHooks::hooks()->waitForVSync(surface); + qt_egl_device_integration()->waitForVSync(surface); QEGLPlatformContext::swapBuffers(surface); - QEglFSHooks::hooks()->presentBuffer(surface); + qt_egl_device_integration()->presentBuffer(surface); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfscontext.h b/src/plugins/platforms/eglfs/qeglfscontext.h index a656a50e2f..245f690822 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.h +++ b/src/plugins/platforms/eglfs/qeglfscontext.h @@ -36,10 +36,11 @@ #include #include +#include "qeglfsglobal.h" QT_BEGIN_NAMESPACE -class QEglFSContext : public QEGLPlatformContext +class Q_EGLFS_EXPORT QEglFSContext : public QEGLPlatformContext { public: QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, diff --git a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp new file mode 100644 index 0000000000..a2e1ac3832 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsdeviceintegration.h" +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_OS_LINUX) +#include +#include +#include +#include +#endif + +#include +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcEglDevDebug, "qt.qpa.egldeviceintegration") + +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QEGLDeviceIntegrationFactoryInterface_iid, QLatin1String("/egldeviceintegrations"), Qt::CaseInsensitive)) + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, + (QEGLDeviceIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) + +static inline QEGLDeviceIntegration *loadIntegration(QFactoryLoader *loader, const QString &key) +{ + const int index = loader->indexOf(key); + if (index != -1) { + QObject *plugin = loader->instance(index); + if (QEGLDeviceIntegrationPlugin *factory = qobject_cast(plugin)) { + if (QEGLDeviceIntegration *result = factory->create()) + return result; + } + } + return Q_NULLPTR; +} + +#endif // QT_NO_LIBRARY + +QStringList QEGLDeviceIntegrationFactory::keys(const QString &pluginPath) +{ +#ifndef QT_NO_LIBRARY + QStringList list; + if (!pluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(pluginPath); + list = directLoader()->keyMap().values(); + if (!list.isEmpty()) { + const QString postFix = QStringLiteral(" (from ") + + QDir::toNativeSeparators(pluginPath) + + QLatin1Char(')'); + const QStringList::iterator end = list.end(); + for (QStringList::iterator it = list.begin(); it != end; ++it) + (*it).append(postFix); + } + } + list.append(loader()->keyMap().values()); + qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys:" << list; + return list; +#else + return QStringList(); +#endif +} + +QEGLDeviceIntegration *QEGLDeviceIntegrationFactory::create(const QString &key, const QString &pluginPath) +{ + QEGLDeviceIntegration *integration = Q_NULLPTR; +#ifndef QT_NO_LIBRARY + if (!pluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(pluginPath); + integration = loadIntegration(directLoader(), key); + } + if (!integration) + integration = loadIntegration(loader(), key); + if (integration) + qCDebug(qLcEglDevDebug) << "Using EGL device integration" << key; + else + qCWarning(qLcEglDevDebug) << "Failed to load EGL device integration" << key; +#endif + return integration; +} + +static int framebuffer = -1; + +QByteArray QEGLDeviceIntegration::fbDeviceName() const +{ + QByteArray fbDev = qgetenv("QT_QPA_EGLFS_FB"); + if (fbDev.isEmpty()) + fbDev = QByteArrayLiteral("/dev/fb0"); + + return fbDev; +} + +int QEGLDeviceIntegration::framebufferIndex() const +{ + int fbIndex = 0; +#ifndef QT_NO_REGULAREXPRESSION + QRegularExpression fbIndexRx(QLatin1String("fb(\\d+)")); + QRegularExpressionMatch match = fbIndexRx.match(QString::fromLocal8Bit(fbDeviceName())); + if (match.hasMatch()) + fbIndex = match.captured(1).toInt(); +#endif + return fbIndex; +} + +void QEGLDeviceIntegration::platformInit() +{ + QByteArray fbDev = fbDeviceName(); + + framebuffer = qt_safe_open(fbDev, O_RDONLY); + + if (framebuffer == -1) { + qWarning("EGLFS: Failed to open %s", fbDev.constData()); + qFatal("EGLFS: Can't continue without a display"); + } +} + +void QEGLDeviceIntegration::platformDestroy() +{ + if (framebuffer != -1) + close(framebuffer); +} + +EGLNativeDisplayType QEGLDeviceIntegration::platformDisplay() const +{ + return EGL_DEFAULT_DISPLAY; +} + +bool QEGLDeviceIntegration::usesDefaultScreen() +{ + return true; +} + +void QEGLDeviceIntegration::screenInit() +{ + // Nothing to do here. Called only when usesDefaultScreen is false. +} + +void QEGLDeviceIntegration::screenDestroy() +{ + while (!qApp->screens().isEmpty()) + delete qApp->screens().last()->handle(); +} + +QSizeF QEGLDeviceIntegration::physicalScreenSize() const +{ + return q_physicalScreenSizeFromFb(framebuffer, screenSize()); +} + +QSize QEGLDeviceIntegration::screenSize() const +{ + return q_screenSizeFromFb(framebuffer); +} + +QDpi QEGLDeviceIntegration::logicalDpi() const +{ + QSizeF ps = physicalScreenSize(); + QSize s = screenSize(); + + return QDpi(25.4 * s.width() / ps.width(), + 25.4 * s.height() / ps.height()); +} + +Qt::ScreenOrientation QEGLDeviceIntegration::nativeOrientation() const +{ + return Qt::PrimaryOrientation; +} + +Qt::ScreenOrientation QEGLDeviceIntegration::orientation() const +{ + return Qt::PrimaryOrientation; +} + +int QEGLDeviceIntegration::screenDepth() const +{ + return q_screenDepthFromFb(framebuffer); +} + +QImage::Format QEGLDeviceIntegration::screenFormat() const +{ + return screenDepth() == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; +} + +QSurfaceFormat QEGLDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + QSurfaceFormat format = inputFormat; + + static const bool force888 = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCE888"); + if (force888) { + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + } + + return format; +} + +bool QEGLDeviceIntegration::filterConfig(EGLDisplay, EGLConfig) const +{ + return true; +} + +EGLNativeWindowType QEGLDeviceIntegration::createNativeWindow(QPlatformWindow *platformWindow, + const QSize &size, + const QSurfaceFormat &format) +{ + Q_UNUSED(platformWindow); + Q_UNUSED(size); + Q_UNUSED(format); + return 0; +} + +EGLNativeWindowType QEGLDeviceIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format) +{ + Q_UNUSED(format); + return 0; +} + +void QEGLDeviceIntegration::destroyNativeWindow(EGLNativeWindowType window) +{ + Q_UNUSED(window); +} + +bool QEGLDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + Q_UNUSED(cap); + return false; +} + +QPlatformCursor *QEGLDeviceIntegration::createCursor(QPlatformScreen *screen) const +{ + return new QEGLPlatformCursor(screen); +} + +void QEGLDeviceIntegration::waitForVSync(QPlatformSurface *surface) const +{ + Q_UNUSED(surface); + +#if defined(FBIO_WAITFORVSYNC) + static const bool forceSync = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCEVSYNC"); + if (forceSync && framebuffer != -1) { + int arg = 0; + if (ioctl(framebuffer, FBIO_WAITFORVSYNC, &arg) == -1) + qWarning("Could not wait for vsync."); + } +#endif +} + +void QEGLDeviceIntegration::presentBuffer(QPlatformSurface *surface) +{ + Q_UNUSED(surface); +} + +bool QEGLDeviceIntegration::supportsPBuffers() const +{ + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h new file mode 100644 index 0000000000..42abc23c31 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSDEVICEINTEGRATION_H +#define QEGLFSDEVICEINTEGRATION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include "qeglfsglobal.h" + +QT_BEGIN_NAMESPACE + +class QPlatformSurface; + +#define QEGLDeviceIntegrationFactoryInterface_iid "org.qt-project.qt.qpa.egl.QEGLDeviceIntegrationFactoryInterface.5.5" + +class Q_EGLFS_EXPORT QEGLDeviceIntegration +{ +public: + virtual ~QEGLDeviceIntegration() { } + + virtual void platformInit(); + virtual void platformDestroy(); + virtual EGLNativeDisplayType platformDisplay() const; + virtual bool usesDefaultScreen(); + virtual void screenInit(); + virtual void screenDestroy(); + virtual QSizeF physicalScreenSize() const; + virtual QSize screenSize() const; + virtual QDpi logicalDpi() const; + virtual Qt::ScreenOrientation nativeOrientation() const; + virtual Qt::ScreenOrientation orientation() const; + virtual int screenDepth() const; + virtual QImage::Format screenFormat() const; + virtual QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const; + virtual EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, + const QSize &size, + const QSurfaceFormat &format); + virtual EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format); + virtual void destroyNativeWindow(EGLNativeWindowType window); + virtual bool hasCapability(QPlatformIntegration::Capability cap) const; + virtual QPlatformCursor *createCursor(QPlatformScreen *screen) const; + virtual bool filterConfig(EGLDisplay display, EGLConfig config) const; + virtual void waitForVSync(QPlatformSurface *surface) const; + virtual void presentBuffer(QPlatformSurface *surface); + virtual QByteArray fbDeviceName() const; + virtual int framebufferIndex() const; + virtual bool supportsPBuffers() const; +}; + +class Q_EGLFS_EXPORT QEGLDeviceIntegrationPlugin : public QObject +{ + Q_OBJECT + +public: + virtual QEGLDeviceIntegration *create() = 0; +}; + +class Q_EGLFS_EXPORT QEGLDeviceIntegrationFactory +{ +public: + static QStringList keys(const QString &pluginPath = QString()); + static QEGLDeviceIntegration *create(const QString &name, const QString &platformPluginPath = QString()); +}; + +QT_END_NAMESPACE + +#endif // QEGLDEVICEINTEGRATION_H diff --git a/src/plugins/platforms/eglfs/qeglfsglobal.h b/src/plugins/platforms/eglfs/qeglfsglobal.h new file mode 100644 index 0000000000..db9a76f899 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfsglobal.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSGLOBAL_H +#define QEGLFSGLOBAL_H + +#include + +#ifdef QT_BUILD_EGL_DEVICE_LIB +#define Q_EGLFS_EXPORT Q_DECL_EXPORT +#else +#define Q_EGLFS_EXPORT Q_DECL_IMPORT +#endif + +#endif diff --git a/src/plugins/platforms/eglfs/qeglfshooks.cpp b/src/plugins/platforms/eglfs/qeglfshooks.cpp new file mode 100644 index 0000000000..2f12c61f99 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfshooks.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the qmake spec of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfshooks.h" +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglDevDebug) + +#ifdef EGLFS_PLATFORM_HOOKS + +QEGLDeviceIntegration *qt_egl_device_integration() +{ + extern QEglFSHooks *platformHooks; + return platformHooks; +} + +#else + +class DeviceIntegration +{ +public: + DeviceIntegration(); + ~DeviceIntegration() { delete m_integration; } + QEGLDeviceIntegration *integration() { return m_integration; } +private: + QEGLDeviceIntegration *m_integration; +}; + +Q_GLOBAL_STATIC(DeviceIntegration, deviceIntegration) + +DeviceIntegration::DeviceIntegration() +{ + QStringList pluginKeys = QEGLDeviceIntegrationFactory::keys(); + if (!pluginKeys.isEmpty()) { + // Some built-in logic: Prioritize either X11 or KMS/DRM. + if (qEnvironmentVariableIsSet("DISPLAY")) { + const QString x11key = QStringLiteral("eglfs_x11"); + if (pluginKeys.contains(x11key)) { + pluginKeys.removeOne(x11key); + pluginKeys.prepend(x11key); + } + } else { + const QString kmskey = QStringLiteral("eglfs_kms"); + if (pluginKeys.contains(kmskey)) { + pluginKeys.removeOne(kmskey); + pluginKeys.prepend(kmskey); + } + } + + QByteArray requested; + + // The environment variable can override everything. + if (qEnvironmentVariableIsSet("QT_QPA_EGLFS_INTEGRATION")) { + requested = qgetenv("QT_QPA_EGLFS_INTEGRATION"); + } else { + // Device-specific makespecs may define a preferred plugin. +#ifdef EGLFS_PREFERRED_PLUGIN +#define DEFAULT_PLUGIN EGLFS_PREFERRED_PLUGIN +#define STR(s) #s +#define STRQ(s) STR(s) + requested = STRQ(DEFAULT_PLUGIN); +#endif + } + + // Treat "none" as special. There has to be a way to indicate + // that plugins must be ignored when the device is known to be + // functional with the default, non-specialized integration. + if (requested != QByteArrayLiteral("none")) { + if (!requested.isEmpty()) { + QString reqStr = QString::fromLocal8Bit(requested); + pluginKeys.removeOne(reqStr); + pluginKeys.prepend(reqStr); + } + qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys (sorted):" << pluginKeys; + while (!m_integration && !pluginKeys.isEmpty()) { + QString key = pluginKeys.takeFirst(); + qCDebug(qLcEglDevDebug) << "Trying to load device EGL integration" << key; + m_integration = QEGLDeviceIntegrationFactory::create(key); + } + } + } + + if (!m_integration) { + // Use a default, non-specialized device integration when no plugin is available. + // For some systems this is sufficient. + qCDebug(qLcEglDevDebug) << "Using base device integration"; + m_integration = new QEGLDeviceIntegration; + } +} + +QEGLDeviceIntegration *qt_egl_device_integration() +{ + return deviceIntegration()->integration(); +} + +#endif // EGLFS_PLATFORM_HOOKS + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfshooks.h b/src/plugins/platforms/eglfs/qeglfshooks.h index b2b8d46741..1fcfc5bd08 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks.h +++ b/src/plugins/platforms/eglfs/qeglfshooks.h @@ -34,63 +34,17 @@ #ifndef QEGLFSHOOKS_H #define QEGLFSHOOKS_H -#include -#include -#include -#include -#include +#include "qeglfsdeviceintegration.h" +#include "qeglfsglobal.h" QT_BEGIN_NAMESPACE -class QEglFSScreen; -class QEglFSIntegration; -class QPlatformSurface; - -class QEglFSHooks +class QEglFSHooks : public QEGLDeviceIntegration { -public: - virtual ~QEglFSHooks() {} - virtual void platformInit(); - virtual void platformDestroy(); - virtual EGLNativeDisplayType platformDisplay() const; - virtual void screenInit(); - virtual void screenDestroy(); - virtual QSizeF physicalScreenSize() const; - virtual QSize screenSize() const; - virtual QDpi logicalDpi() const; - virtual Qt::ScreenOrientation nativeOrientation() const; - virtual Qt::ScreenOrientation orientation() const; - virtual int screenDepth() const; - virtual QImage::Format screenFormat() const; - virtual QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const; - virtual EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, - const QSize &size, - const QSurfaceFormat &format); - virtual EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format); - virtual void destroyNativeWindow(EGLNativeWindowType window); - virtual bool hasCapability(QPlatformIntegration::Capability cap) const; - virtual QPlatformCursor *createCursor(QPlatformScreen *screen) const; - virtual bool filterConfig(EGLDisplay display, EGLConfig config) const; - virtual void waitForVSync(QPlatformSurface *surface) const; - virtual void presentBuffer(QPlatformSurface *surface); - - virtual QByteArray fbDeviceName() const; - virtual int framebufferIndex() const; - - virtual bool supportsPBuffers() const; - - static QEglFSHooks *hooks() - { -#ifdef EGLFS_PLATFORM_HOOKS - extern QEglFSHooks *platformHooks; - return platformHooks; -#else - extern QEglFSHooks stubHooks; - return &stubHooks; -#endif - } }; +Q_EGLFS_EXPORT QEGLDeviceIntegration *qt_egl_device_integration(); + QT_END_NAMESPACE #endif // QEGLFSHOOKS_H diff --git a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp deleted file mode 100644 index 4c430964a3..0000000000 --- a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp +++ /dev/null @@ -1,1176 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the qmake spec 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qeglfshooks.h" -#include "qeglfsintegration.h" -#include "qeglfsscreen.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifndef DRM_CAP_CURSOR_WIDTH -#define DRM_CAP_CURSOR_WIDTH 0x8 -#endif - -#ifndef DRM_CAP_CURSOR_HEIGHT -#define DRM_CAP_CURSOR_HEIGHT 0x9 -#endif - -#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) - -QT_USE_NAMESPACE - -Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms") - -class QEglFSKmsCursor; -class QEglFSKmsScreen; - -enum OutputConfiguration { - OutputConfigOff, - OutputConfigPreferred, - OutputConfigCurrent, - OutputConfigMode, - OutputConfigModeline -}; - -struct QEglFSKmsOutput { - QString name; - uint32_t connector_id; - uint32_t crtc_id; - QSizeF physical_size; - int mode; // index of selected mode in list below - bool mode_set; - drmModeCrtcPtr saved_crtc; - QList modes; -}; - -class QEglFSKmsDevice -{ - Q_DISABLE_COPY(QEglFSKmsDevice) - - QString m_path; - int m_dri_fd; - gbm_device *m_gbm_device; - - quint32 m_crtc_allocator; - quint32 m_connector_allocator; - - int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); - QEglFSKmsScreen *screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos); - - static void pageFlipHandler(int fd, - unsigned int sequence, - unsigned int tv_sec, - unsigned int tv_usec, - void *user_data); -public: - QEglFSKmsDevice(const QString &path); - - bool open(); - void close(); - - void createScreens(); - - gbm_device *device() const; - int fd() const; - - void handleDrmEvent(); -}; - -class QEglFSKmsScreen : public QEglFSScreen -{ - QEglFSKmsDevice *m_device; - gbm_surface *m_gbm_surface; - - gbm_bo *m_gbm_bo_current; - gbm_bo *m_gbm_bo_next; - - QEglFSKmsOutput m_output; - QPoint m_pos; - QScopedPointer m_cursor; - - struct FrameBuffer { - FrameBuffer() : fb(0) {} - uint32_t fb; - }; - static void bufferDestroyedHandler(gbm_bo *bo, void *data); - FrameBuffer *framebufferForBufferObject(gbm_bo *bo); - - static QMutex m_waitForFlipMutex; - -public: - QEglFSKmsScreen(QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position); - ~QEglFSKmsScreen(); - - QRect geometry() const Q_DECL_OVERRIDE; - int depth() const Q_DECL_OVERRIDE; - QImage::Format format() const Q_DECL_OVERRIDE; - - QSizeF physicalSize() const Q_DECL_OVERRIDE; - QDpi logicalDpi() const Q_DECL_OVERRIDE; - Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; - Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; - - QString name() const Q_DECL_OVERRIDE; - - QPlatformCursor *cursor() const Q_DECL_OVERRIDE; - - QEglFSKmsDevice *device() const { return m_device; } - - gbm_surface *surface() const { return m_gbm_surface; } - gbm_surface *createSurface(); - void destroySurface(); - - void waitForFlip(); - void flip(); - void flipFinished(); - - QEglFSKmsOutput &output() { return m_output; } - void restoreMode(); -}; - -QMutex QEglFSKmsScreen::m_waitForFlipMutex; - -class QEglFSKmsCursor : public QPlatformCursor -{ - Q_OBJECT -public: - QEglFSKmsCursor(QEglFSKmsScreen *screen); - ~QEglFSKmsCursor(); - - // input methods - void pointerEvent(const QMouseEvent & event) Q_DECL_OVERRIDE; -#ifndef QT_NO_CURSOR - void changeCursor(QCursor * windowCursor, QWindow * window) Q_DECL_OVERRIDE; -#endif - QPoint pos() const Q_DECL_OVERRIDE; - void setPos(const QPoint &pos) Q_DECL_OVERRIDE; - -private: - void initCursorAtlas(); - - QEglFSKmsScreen *m_screen; - QSize m_cursorSize; - gbm_bo *m_bo; - QPoint m_pos; - QPlatformCursorImage m_cursorImage; - bool m_visible; - - // cursor atlas information - struct CursorAtlas { - CursorAtlas() : cursorsPerRow(0), cursorWidth(0), cursorHeight(0) { } - int cursorsPerRow; - int width, height; // width and height of the atlas - int cursorWidth, cursorHeight; // width and height of cursors inside the atlas - QList hotSpots; - QImage image; - } m_cursorAtlas; -}; - -class QEglKmsHooks : public QEglFSHooks -{ -public: - QEglKmsHooks(); - - void platformInit() Q_DECL_OVERRIDE; - void platformDestroy() Q_DECL_OVERRIDE; - EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; - void screenInit() Q_DECL_OVERRIDE; - QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const Q_DECL_OVERRIDE; - EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, - const QSize &size, - const QSurfaceFormat &format) Q_DECL_OVERRIDE; - EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) Q_DECL_OVERRIDE; - void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; - bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - QPlatformCursor *createCursor(QPlatformScreen *screen) const Q_DECL_OVERRIDE; - void waitForVSync(QPlatformSurface *surface) const Q_DECL_OVERRIDE; - void presentBuffer(QPlatformSurface *surface) Q_DECL_OVERRIDE; - bool supportsPBuffers() const Q_DECL_OVERRIDE; - - bool hwCursor() const; - QMap outputSettings() const; - -private: - void loadConfig(); - - QEglFSKmsDevice *m_device; - - bool m_hwCursor; - bool m_pbuffers; - QString m_devicePath; - QMap m_outputSettings; -}; - -static QEglKmsHooks kms_hooks; -QEglFSHooks *platformHooks = &kms_hooks; - -QEglKmsHooks::QEglKmsHooks() - : m_device(Q_NULLPTR) - , m_hwCursor(true) - , m_pbuffers(false) -{} - -void QEglKmsHooks::platformInit() -{ - loadConfig(); - - if (!m_devicePath.isEmpty()) { - qCDebug(qLcEglfsKmsDebug) << "Using DRM device" << m_devicePath << "specified in config file"; - } else { - - QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask); - QStringList devices = d->scanConnectedDevices(); - qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices; - d->deleteLater(); - - if (devices.isEmpty()) - qFatal("Could not find DRM device!"); - - m_devicePath = devices.first(); - qCDebug(qLcEglfsKmsDebug) << "Using" << m_devicePath; - } - - m_device = new QEglFSKmsDevice(m_devicePath); - if (!m_device->open()) - qFatal("Could not open device %s - aborting!", qPrintable(m_devicePath)); -} - -void QEglKmsHooks::platformDestroy() -{ - m_device->close(); - delete m_device; - m_device = Q_NULLPTR; -} - -EGLNativeDisplayType QEglKmsHooks::platformDisplay() const -{ - Q_ASSERT(m_device); - return reinterpret_cast(m_device->device()); -} - -void QEglKmsHooks::screenInit() -{ - m_device->createScreens(); -} - -QSurfaceFormat QEglKmsHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const -{ - QSurfaceFormat format(inputFormat); - format.setRenderableType(QSurfaceFormat::OpenGLES); - format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - format.setRedBufferSize(8); - format.setGreenBufferSize(8); - format.setBlueBufferSize(8); - return format; -} - -EGLNativeWindowType QEglKmsHooks::createNativeWindow(QPlatformWindow *platformWindow, - const QSize &size, - const QSurfaceFormat &format) -{ - Q_UNUSED(size); - Q_UNUSED(format); - - QEglFSKmsScreen *screen = static_cast(platformWindow->screen()); - if (screen->surface()) { - qWarning("Only single window per screen supported!"); - return 0; - } - - return reinterpret_cast(screen->createSurface()); -} - -EGLNativeWindowType QEglKmsHooks::createNativeOffscreenWindow(const QSurfaceFormat &format) -{ - Q_UNUSED(format); - Q_ASSERT(m_device); - - qCDebug(qLcEglfsKmsDebug) << "Creating native off screen window"; - gbm_surface *surface = gbm_surface_create(m_device->device(), - 1, 1, - GBM_FORMAT_XRGB8888, - GBM_BO_USE_RENDERING); - - return reinterpret_cast(surface); -} - -void QEglKmsHooks::destroyNativeWindow(EGLNativeWindowType window) -{ - gbm_surface *surface = reinterpret_cast(window); - gbm_surface_destroy(surface); -} - -bool QEglKmsHooks::hasCapability(QPlatformIntegration::Capability cap) const -{ - switch (cap) { - case QPlatformIntegration::ThreadedPixmaps: - case QPlatformIntegration::OpenGL: - case QPlatformIntegration::ThreadedOpenGL: - return true; - default: - return false; - } -} - -QPlatformCursor *QEglKmsHooks::createCursor(QPlatformScreen *screen) const -{ - if (m_hwCursor) - return Q_NULLPTR; - else - return new QEGLPlatformCursor(screen); -} - -void QEglKmsHooks::waitForVSync(QPlatformSurface *surface) const -{ - QWindow *window = static_cast(surface->surface()); - QEglFSKmsScreen *screen = static_cast(window->screen()->handle()); - - screen->waitForFlip(); -} - -void QEglKmsHooks::presentBuffer(QPlatformSurface *surface) -{ - QWindow *window = static_cast(surface->surface()); - QEglFSKmsScreen *screen = static_cast(window->screen()->handle()); - - screen->flip(); -} - -bool QEglKmsHooks::supportsPBuffers() const -{ - return m_pbuffers; -} - -bool QEglKmsHooks::hwCursor() const -{ - return m_hwCursor; -} - -QMap QEglKmsHooks::outputSettings() const -{ - return m_outputSettings; -} - -void QEglKmsHooks::loadConfig() -{ - static QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); - if (json.isEmpty()) - return; - - qCDebug(qLcEglfsKmsDebug) << "Loading KMS setup from" << json; - - QFile file(QString::fromUtf8(json)); - if (!file.open(QFile::ReadOnly)) { - qCDebug(qLcEglfsKmsDebug) << "Could not open config file" - << json << "for reading"; - return; - } - - QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); - if (!doc.isObject()) { - qCDebug(qLcEglfsKmsDebug) << "Invalid config file" << json - << "- no top-level JSON object"; - return; - } - - QJsonObject object = doc.object(); - - m_hwCursor = object.value("hwcursor").toBool(m_hwCursor); - m_pbuffers = object.value("pbuffers").toBool(m_pbuffers); - m_devicePath = object.value("device").toString(); - - QJsonArray outputs = object.value("outputs").toArray(); - for (int i = 0; i < outputs.size(); i++) { - QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); - - if (outputSettings.contains("name")) { - QString name = outputSettings.value("name").toString(); - - if (m_outputSettings.contains(name)) { - qCDebug(qLcEglfsKmsDebug) << "Output" << name << "configured multiple times!"; - } - - m_outputSettings.insert(name, outputSettings); - } - } - - qCDebug(qLcEglfsKmsDebug) << "Configuration:\n" - << "\thwcursor:" << m_hwCursor << "\n" - << "\tpbuffers:" << m_pbuffers << "\n" - << "\toutputs:" << m_outputSettings; -} - -QEglFSKmsCursor::QEglFSKmsCursor(QEglFSKmsScreen *screen) - : m_screen(screen) - , m_cursorSize(64, 64) // 64x64 is the old standard size, we now try to query the real size below - , m_bo(Q_NULLPTR) - , m_cursorImage(0, 0, 0, 0, 0, 0) - , m_visible(true) -{ - uint64_t width, height; - if ((drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_WIDTH, &width) == 0) - && (drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_HEIGHT, &height) == 0)) { - m_cursorSize.setWidth(width); - m_cursorSize.setHeight(height); - } - - m_bo = gbm_bo_create(m_screen->device()->device(), m_cursorSize.width(), m_cursorSize.height(), - GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); - if (!m_bo) { - qWarning("Could not create buffer for cursor!"); - } else { - initCursorAtlas(); - } - - drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); -} - -QEglFSKmsCursor::~QEglFSKmsCursor() -{ - drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); - drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); - - gbm_bo_destroy(m_bo); - m_bo = Q_NULLPTR; -} - -void QEglFSKmsCursor::pointerEvent(const QMouseEvent &event) -{ - setPos(event.screenPos().toPoint()); -} - -#ifndef QT_NO_CURSOR -void QEglFSKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) -{ - Q_UNUSED(window); - - if (!m_visible) - return; - - const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; - if (newShape == Qt::BitmapCursor) { - m_cursorImage.set(windowCursor->pixmap().toImage(), - windowCursor->hotSpot().x(), - windowCursor->hotSpot().y()); - } else { - // Standard cursor, look up in atlas - const int width = m_cursorAtlas.cursorWidth; - const int height = m_cursorAtlas.cursorHeight; - const qreal ws = (qreal)m_cursorAtlas.cursorWidth / m_cursorAtlas.width; - const qreal hs = (qreal)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; - - QRect textureRect(ws * (newShape % m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.width, - hs * (newShape / m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.height, - width, - height); - QPoint hotSpot = m_cursorAtlas.hotSpots[newShape]; - m_cursorImage.set(m_cursorAtlas.image.copy(textureRect), - hotSpot.x(), - hotSpot.y()); - } - - if (m_cursorImage.image()->width() > m_cursorSize.width() || m_cursorImage.image()->height() > m_cursorSize.height()) - qWarning("Cursor larger than %dx%d, cursor will be clipped.", m_cursorSize.width(), m_cursorSize.height()); - - QImage cursorImage(m_cursorSize, QImage::Format_ARGB32); - cursorImage.fill(Qt::transparent); - - QPainter painter; - painter.begin(&cursorImage); - painter.drawImage(0, 0, *m_cursorImage.image()); - painter.end(); - - gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount()); - - uint32_t handle = gbm_bo_get_handle(m_bo).u32; - int status = drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, handle, - m_cursorSize.width(), m_cursorSize.height()); - if (status != 0) - qWarning("Could not set cursor: %d", status); -} -#endif // QT_NO_CURSOR - -QPoint QEglFSKmsCursor::pos() const -{ - return m_pos; -} - -void QEglFSKmsCursor::setPos(const QPoint &pos) -{ - QPoint adjustedPos = pos - m_cursorImage.hotspot(); - int ret = drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, adjustedPos.x(), adjustedPos.y()); - if (ret == 0) { - m_pos = pos; - } else { - qWarning("Failed to move cursor: %d", ret); - } -} - -void QEglFSKmsCursor::initCursorAtlas() -{ - static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); - if (json.isEmpty()) - json = ":/cursor.json"; - - qCDebug(qLcEglfsKmsDebug) << "Initializing cursor atlas from" << json; - - QFile file(QString::fromUtf8(json)); - if (!file.open(QFile::ReadOnly)) { - drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); - drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); - m_visible = false; - return; - } - - QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); - QJsonObject object = doc.object(); - - QString atlas = object.value(QLatin1String("image")).toString(); - Q_ASSERT(!atlas.isEmpty()); - - const int cursorsPerRow = object.value(QLatin1String("cursorsPerRow")).toDouble(); - Q_ASSERT(cursorsPerRow); - m_cursorAtlas.cursorsPerRow = cursorsPerRow; - - const QJsonArray hotSpots = object.value(QLatin1String("hotSpots")).toArray(); - Q_ASSERT(hotSpots.count() == Qt::LastCursor + 1); - for (int i = 0; i < hotSpots.count(); i++) { - QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble()); - m_cursorAtlas.hotSpots << hotSpot; - } - - QImage image = QImage(atlas).convertToFormat(QImage::Format_ARGB32); - m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow; - m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow) / cursorsPerRow); - m_cursorAtlas.width = image.width(); - m_cursorAtlas.height = image.height(); - m_cursorAtlas.image = image; -} - -void QEglFSKmsScreen::bufferDestroyedHandler(gbm_bo *bo, void *data) -{ - FrameBuffer *fb = static_cast(data); - - if (fb->fb) { - gbm_device *device = gbm_bo_get_device(bo); - drmModeRmFB(gbm_device_get_fd(device), fb->fb); - } - - delete fb; -} - -QEglFSKmsScreen::FrameBuffer *QEglFSKmsScreen::framebufferForBufferObject(gbm_bo *bo) -{ - { - FrameBuffer *fb = static_cast(gbm_bo_get_user_data(bo)); - if (fb) - return fb; - } - - uint32_t width = gbm_bo_get_width(bo); - uint32_t height = gbm_bo_get_height(bo); - uint32_t stride = gbm_bo_get_stride(bo); - uint32_t handle = gbm_bo_get_handle(bo).u32; - - QScopedPointer fb(new FrameBuffer); - - int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, - stride, handle, &fb->fb); - - if (ret) { - qWarning("Failed to create KMS FB!"); - return Q_NULLPTR; - } - - gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler); - return fb.take(); - -} - -QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) - : QEglFSScreen(eglGetDisplay(reinterpret_cast(device->device()))) - , m_device(device) - , m_gbm_surface(Q_NULLPTR) - , m_gbm_bo_current(Q_NULLPTR) - , m_gbm_bo_next(Q_NULLPTR) - , m_output(output) - , m_pos(position) - , m_cursor(Q_NULLPTR) -{ -} - -QEglFSKmsScreen::~QEglFSKmsScreen() -{ - restoreMode(); -} - -QRect QEglFSKmsScreen::geometry() const -{ - const int mode = m_output.mode; - return QRect(m_pos.x(), m_pos.y(), - m_output.modes[mode].hdisplay, - m_output.modes[mode].vdisplay); -} - -int QEglFSKmsScreen::depth() const -{ - return 32; -} - -QImage::Format QEglFSKmsScreen::format() const -{ - return QImage::Format_RGB32; -} - -QSizeF QEglFSKmsScreen::physicalSize() const -{ - return m_output.physical_size; -} - -QDpi QEglFSKmsScreen::logicalDpi() const -{ - QSizeF ps = physicalSize(); - QSize s = geometry().size(); - - if (ps.isValid() && s.isValid()) - return QDpi(25.4 * s.width() / ps.width(), - 25.4 * s.height() / ps.height()); - else - return QDpi(100, 100); -} - -Qt::ScreenOrientation QEglFSKmsScreen::nativeOrientation() const -{ - return Qt::PrimaryOrientation; -} - -Qt::ScreenOrientation QEglFSKmsScreen::orientation() const -{ - return Qt::PrimaryOrientation; -} - -QString QEglFSKmsScreen::name() const -{ - return m_output.name; -} - -QPlatformCursor *QEglFSKmsScreen::cursor() const -{ - if (kms_hooks.hwCursor()) { - if (m_cursor.isNull()) { - QEglFSKmsScreen *that = const_cast(this); - that->m_cursor.reset(new QEglFSKmsCursor(that)); - } - - return m_cursor.data(); - } else { - return QEglFSScreen::cursor(); - } -} - -gbm_surface *QEglFSKmsScreen::createSurface() -{ - if (!m_gbm_surface) { - qCDebug(qLcEglfsKmsDebug) << "Creating window for screen" << name(); - m_gbm_surface = gbm_surface_create(m_device->device(), - geometry().width(), - geometry().height(), - GBM_FORMAT_XRGB8888, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - } - return m_gbm_surface; -} - -void QEglFSKmsScreen::destroySurface() -{ - if (m_gbm_bo_current) { - gbm_bo_destroy(m_gbm_bo_current); - m_gbm_bo_current = Q_NULLPTR; - } - - if (m_gbm_bo_next) { - gbm_bo_destroy(m_gbm_bo_next); - m_gbm_bo_next = Q_NULLPTR; - } - - if (m_gbm_surface) { - gbm_surface_destroy(m_gbm_surface); - m_gbm_surface = Q_NULLPTR; - } -} - -void QEglFSKmsScreen::waitForFlip() -{ - // Don't lock the mutex unless we actually need to - if (!m_gbm_bo_next) - return; - - QMutexLocker lock(&m_waitForFlipMutex); - while (m_gbm_bo_next) - m_device->handleDrmEvent(); -} - -void QEglFSKmsScreen::flip() -{ - if (!m_gbm_surface) { - qWarning("Cannot sync before platform init!"); - return; - } - - m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); - if (!m_gbm_bo_next) { - qWarning("Could not lock GBM surface front buffer!"); - return; - } - - FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); - - if (!m_output.mode_set) { - int ret = drmModeSetCrtc(m_device->fd(), - m_output.crtc_id, - fb->fb, - 0, 0, - &m_output.connector_id, 1, - &m_output.modes[m_output.mode]); - - if (ret) - qErrnoWarning("Could not set DRM mode!"); - else - m_output.mode_set = true; - } - - int ret = drmModePageFlip(m_device->fd(), - m_output.crtc_id, - fb->fb, - DRM_MODE_PAGE_FLIP_EVENT, - this); - if (ret) { - qErrnoWarning("Could not queue DRM page flip!"); - gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); - m_gbm_bo_next = Q_NULLPTR; - } -} - -void QEglFSKmsScreen::flipFinished() -{ - if (m_gbm_bo_current) - gbm_surface_release_buffer(m_gbm_surface, - m_gbm_bo_current); - - m_gbm_bo_current = m_gbm_bo_next; - m_gbm_bo_next = Q_NULLPTR; -} - -void QEglFSKmsScreen::restoreMode() -{ - if (m_output.mode_set && m_output.saved_crtc) { - drmModeSetCrtc(m_device->fd(), - m_output.saved_crtc->crtc_id, - m_output.saved_crtc->buffer_id, - 0, 0, - &m_output.connector_id, 1, - &m_output.saved_crtc->mode); - - drmModeFreeCrtc(m_output.saved_crtc); - m_output.saved_crtc = Q_NULLPTR; - - m_output.mode_set = false; - } -} - -int QEglFSKmsDevice::crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector) -{ - for (int i = 0; i < connector->count_encoders; i++) { - drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoders[i]); - if (!encoder) { - qWarning("Failed to get encoder"); - continue; - } - - quint32 possibleCrtcs = encoder->possible_crtcs; - drmModeFreeEncoder(encoder); - - for (int j = 0; j < resources->count_crtcs; j++) { - bool isPossible = possibleCrtcs & (1 << j); - bool isAvailable = !(m_crtc_allocator & 1 << resources->crtcs[j]); - - if (isPossible && isAvailable) - return j; - } - } - - return -1; -} - -static const char * const connector_type_names[] = { - "None", - "VGA", - "DVI", - "DVI", - "DVI", - "Composite", - "TV", - "LVDS", - "CTV", - "DIN", - "DP", - "HDMI", - "HDMI", - "TV", - "eDP", -}; - -static QString nameForConnector(const drmModeConnectorPtr connector) -{ - QString connectorName = "UNKNOWN"; - - if (connector->connector_type < ARRAY_LENGTH(connector_type_names)) - connectorName = connector_type_names[connector->connector_type]; - - connectorName += QString::number(connector->connector_type_id); - - return connectorName; -} - -static bool parseModeline(const QString &s, drmModeModeInfoPtr mode) -{ - char hsync[16]; - char vsync[16]; - float fclock; - - mode->type = DRM_MODE_TYPE_USERDEF; - mode->hskew = 0; - mode->vscan = 0; - mode->vrefresh = 0; - mode->flags = 0; - - if (sscanf(qPrintable(s), "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s", - &fclock, - &mode->hdisplay, - &mode->hsync_start, - &mode->hsync_end, - &mode->htotal, - &mode->vdisplay, - &mode->vsync_start, - &mode->vsync_end, - &mode->vtotal, hsync, vsync) != 11) - return false; - - mode->clock = fclock * 1000; - - if (strcmp(hsync, "+hsync") == 0) - mode->flags |= DRM_MODE_FLAG_PHSYNC; - else if (strcmp(hsync, "-hsync") == 0) - mode->flags |= DRM_MODE_FLAG_NHSYNC; - else - return false; - - if (strcmp(vsync, "+vsync") == 0) - mode->flags |= DRM_MODE_FLAG_PVSYNC; - else if (strcmp(vsync, "-vsync") == 0) - mode->flags |= DRM_MODE_FLAG_NVSYNC; - else - return false; - - return true; -} - -QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos) -{ - const QString connectorName = nameForConnector(connector); - - const int crtc = crtcForConnector(resources, connector); - if (crtc < 0) { - qWarning() << "No usable crtc/encoder pair for connector" << connectorName; - return Q_NULLPTR; - } - - OutputConfiguration configuration; - QSize configurationSize; - drmModeModeInfo configurationModeline; - - const QString mode = kms_hooks.outputSettings().value(connectorName).value("mode", "preferred").toString().toLower(); - if (mode == "off") { - configuration = OutputConfigOff; - } else if (mode == "preferred") { - configuration = OutputConfigPreferred; - } else if (mode == "current") { - configuration = OutputConfigCurrent; - } else if (sscanf(qPrintable(mode), "%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) { - configuration = OutputConfigMode; - } else if (parseModeline(mode, &configurationModeline)) { - configuration = OutputConfigModeline; - } else { - qWarning("Invalid mode \"%s\" for output %s", qPrintable(mode), qPrintable(connectorName)); - configuration = OutputConfigPreferred; - } - - const uint32_t crtc_id = resources->crtcs[crtc]; - - if (configuration == OutputConfigOff) { - qCDebug(qLcEglfsKmsDebug) << "Turning off output" << connectorName; - drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, Q_NULLPTR); - return Q_NULLPTR; - } - - // Get the current mode on the current crtc - drmModeModeInfo crtc_mode; - memset(&crtc_mode, 0, sizeof crtc_mode); - if (drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->connector_id)) { - drmModeCrtcPtr crtc = drmModeGetCrtc(m_dri_fd, encoder->crtc_id); - drmModeFreeEncoder(encoder); - - if (!crtc) - return Q_NULLPTR; - - if (crtc->mode_valid) - crtc_mode = crtc->mode; - - drmModeFreeCrtc(crtc); - } - - QList modes; - qCDebug(qLcEglfsKmsDebug) << connectorName << "mode count:" << connector->count_modes; - for (int i = 0; i < connector->count_modes; i++) { - const drmModeModeInfo &mode = connector->modes[i]; - qCDebug(qLcEglfsKmsDebug) << "mode" << i << mode.hdisplay << "x" << mode.vdisplay - << "@" << mode.vrefresh << "hz"; - modes << connector->modes[i]; - } - - int preferred = -1; - int current = -1; - int configured = -1; - int best = -1; - - for (int i = modes.size() - 1; i >= 0; i--) { - const drmModeModeInfo &m = modes.at(i); - - if (configuration == OutputConfigMode && - m.hdisplay == configurationSize.width() && - m.vdisplay == configurationSize.height()) { - configured = i; - } - - if (!memcmp(&crtc_mode, &m, sizeof m)) - current = i; - - if (m.type & DRM_MODE_TYPE_PREFERRED) - preferred = i; - - best = i; - } - - if (configuration == OutputConfigModeline) { - modes << configurationModeline; - configured = modes.size() - 1; - } - - if (current < 0 && crtc_mode.clock != 0) { - modes << crtc_mode; - current = mode.size() - 1; - } - - if (configuration == OutputConfigCurrent) - configured = current; - - int selected_mode = -1; - - if (configured >= 0) - selected_mode = configured; - else if (preferred >= 0) - selected_mode = preferred; - else if (current >= 0) - selected_mode = current; - else if (best >= 0) - selected_mode = best; - - if (selected_mode < 0) { - qWarning() << "No modes available for output" << connectorName; - return Q_NULLPTR; - } else { - int width = modes[selected_mode].hdisplay; - int height = modes[selected_mode].vdisplay; - int refresh = modes[selected_mode].vrefresh; - qCDebug(qLcEglfsKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height - << "@" << refresh << "hz for output" << connectorName; - } - - QEglFSKmsOutput output = { - connectorName, - connector->connector_id, - crtc_id, - QSizeF(connector->mmWidth, connector->mmHeight), - selected_mode, - false, - drmModeGetCrtc(m_dri_fd, crtc_id), - modes - }; - - m_crtc_allocator |= (1 << output.crtc_id); - m_connector_allocator |= (1 << output.connector_id); - - return new QEglFSKmsScreen(this, output, pos); -} - -void QEglFSKmsDevice::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); - - QEglFSKmsScreen *screen = static_cast(user_data); - screen->flipFinished(); -} - -QEglFSKmsDevice::QEglFSKmsDevice(const QString &path) - : m_path(path) - , m_dri_fd(-1) - , m_gbm_device(Q_NULLPTR) - , m_crtc_allocator(0) - , m_connector_allocator(0) -{ -} - -bool QEglFSKmsDevice::open() -{ - Q_ASSERT(m_dri_fd == -1); - Q_ASSERT(m_gbm_device == Q_NULLPTR); - - qCDebug(qLcEglfsKmsDebug) << "Opening device" << m_path; - m_dri_fd = qt_safe_open(m_path.toLocal8Bit().constData(), O_RDWR | O_CLOEXEC); - if (m_dri_fd == -1) { - qErrnoWarning("Could not open DRM device %s", qPrintable(m_path)); - return false; - } - - qCDebug(qLcEglfsKmsDebug) << "Creating GBM device for file descriptor" << m_dri_fd - << "obtained from" << m_path; - m_gbm_device = gbm_create_device(m_dri_fd); - if (!m_gbm_device) { - qErrnoWarning("Could not create GBM device"); - qt_safe_close(m_dri_fd); - m_dri_fd = -1; - return false; - } - - return true; -} - -void QEglFSKmsDevice::close() -{ - if (m_gbm_device) { - gbm_device_destroy(m_gbm_device); - m_gbm_device = Q_NULLPTR; - } - - if (m_dri_fd != -1) { - qt_safe_close(m_dri_fd); - m_dri_fd = -1; - } -} - -void QEglFSKmsDevice::createScreens() -{ - drmModeResPtr resources = drmModeGetResources(m_dri_fd); - if (!resources) { - qWarning("drmModeGetResources failed"); - return; - } - - QPoint pos(0, 0); - QEglFSIntegration *integration = static_cast(QGuiApplicationPrivate::platformIntegration()); - - for (int i = 0; i < resources->count_connectors; i++) { - drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); - if (!connector) - continue; - - QEglFSKmsScreen *screen = screenForConnector(resources, connector, pos); - if (screen) { - integration->addScreen(screen); - pos.rx() += screen->geometry().width(); - } - - drmModeFreeConnector(connector); - } - - drmModeFreeResources(resources); -} - -gbm_device *QEglFSKmsDevice::device() const -{ - return m_gbm_device; -} - -int QEglFSKmsDevice::fd() const -{ - return m_dri_fd; -} - -void QEglFSKmsDevice::handleDrmEvent() -{ - drmEventContext drmEvent = { - DRM_EVENT_CONTEXT_VERSION, - Q_NULLPTR, // vblank handler - pageFlipHandler // page flip handler - }; - - drmHandleEvent(m_dri_fd, &drmEvent); -} - -#include "qeglfshooks_kms.moc" diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp deleted file mode 100644 index 1b042a0743..0000000000 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the qmake spec of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qeglfshooks.h" -#include "qeglfsintegration.h" -#include "qeglfsscreen.h" - -#include -#include -#include -#include - -#if defined(Q_OS_LINUX) -#include -#include -#include -#include -#endif - -#include - -QT_BEGIN_NAMESPACE - -// file descriptor for the frame buffer -// this is a global static to keep the QEglFSHooks interface as clean as possible -static int framebuffer = -1; - -QByteArray QEglFSHooks::fbDeviceName() const -{ - QByteArray fbDev = qgetenv("QT_QPA_EGLFS_FB"); - if (fbDev.isEmpty()) - fbDev = QByteArrayLiteral("/dev/fb0"); - - return fbDev; -} - -int QEglFSHooks::framebufferIndex() const -{ - int fbIndex = 0; -#ifndef QT_NO_REGULAREXPRESSION - QRegularExpression fbIndexRx(QLatin1String("fb(\\d+)")); - QRegularExpressionMatch match = fbIndexRx.match(fbDeviceName()); - if (match.hasMatch()) - fbIndex = match.captured(1).toInt(); - -#endif - return fbIndex; -} - -void QEglFSHooks::platformInit() -{ - QByteArray fbDev = fbDeviceName(); - - framebuffer = qt_safe_open(fbDev, O_RDONLY); - - if (framebuffer == -1) { - qWarning("EGLFS: Failed to open %s", qPrintable(fbDev)); - qFatal("EGLFS: Can't continue without a display"); - } -} - -void QEglFSHooks::platformDestroy() -{ - if (framebuffer != -1) - close(framebuffer); -} - -EGLNativeDisplayType QEglFSHooks::platformDisplay() const -{ - return EGL_DEFAULT_DISPLAY; -} - -void QEglFSHooks::screenInit() -{ - QEglFSIntegration *integration = static_cast(QGuiApplicationPrivate::platformIntegration()); - integration->addScreen(new QEglFSScreen(integration->display())); -} - -void QEglFSHooks::screenDestroy() -{ - while (!qApp->screens().isEmpty()) - delete qApp->screens().last()->handle(); -} - -QSizeF QEglFSHooks::physicalScreenSize() const -{ - return q_physicalScreenSizeFromFb(framebuffer, screenSize()); -} - -QSize QEglFSHooks::screenSize() const -{ - return q_screenSizeFromFb(framebuffer); -} - -QDpi QEglFSHooks::logicalDpi() const -{ - QSizeF ps = physicalScreenSize(); - QSize s = screenSize(); - - return QDpi(25.4 * s.width() / ps.width(), - 25.4 * s.height() / ps.height()); -} - -Qt::ScreenOrientation QEglFSHooks::nativeOrientation() const -{ - return Qt::PrimaryOrientation; -} - -Qt::ScreenOrientation QEglFSHooks::orientation() const -{ - return Qt::PrimaryOrientation; -} - -int QEglFSHooks::screenDepth() const -{ - return q_screenDepthFromFb(framebuffer); -} - -QImage::Format QEglFSHooks::screenFormat() const -{ - return screenDepth() == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; -} - -QSurfaceFormat QEglFSHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const -{ - QSurfaceFormat format = inputFormat; - - static const bool force888 = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCE888"); - if (force888) { - format.setRedBufferSize(8); - format.setGreenBufferSize(8); - format.setBlueBufferSize(8); - } - - return format; -} - -bool QEglFSHooks::filterConfig(EGLDisplay, EGLConfig) const -{ - return true; -} - -EGLNativeWindowType QEglFSHooks::createNativeWindow(QPlatformWindow *platformWindow, - const QSize &size, - const QSurfaceFormat &format) -{ - Q_UNUSED(platformWindow); - Q_UNUSED(size); - Q_UNUSED(format); - return 0; -} - -EGLNativeWindowType QEglFSHooks::createNativeOffscreenWindow(const QSurfaceFormat &format) -{ - Q_UNUSED(format); - return 0; -} - -void QEglFSHooks::destroyNativeWindow(EGLNativeWindowType window) -{ - Q_UNUSED(window); -} - -bool QEglFSHooks::hasCapability(QPlatformIntegration::Capability cap) const -{ - Q_UNUSED(cap); - return false; -} - -QPlatformCursor *QEglFSHooks::createCursor(QPlatformScreen *screen) const -{ - return new QEGLPlatformCursor(screen); -} - -void QEglFSHooks::waitForVSync(QPlatformSurface *surface) const -{ - Q_UNUSED(surface); - -#if defined(FBIO_WAITFORVSYNC) - static const bool forceSync = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCEVSYNC"); - if (forceSync && framebuffer != -1) { - int arg = 0; - if (ioctl(framebuffer, FBIO_WAITFORVSYNC, &arg) == -1) - qWarning("Could not wait for vsync."); - } -#endif -} - -void QEglFSHooks::presentBuffer(QPlatformSurface *surface) -{ - Q_UNUSED(surface); -} - -bool QEglFSHooks::supportsPBuffers() const -{ - return true; -} - -#ifndef EGLFS_PLATFORM_HOOKS -QEglFSHooks stubHooks; -#endif - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp b/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp deleted file mode 100644 index c026546ced..0000000000 --- a/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the qmake spec of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qeglfshooks.h" - -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QEglFSX11Hooks; - -class EventReader : public QThread -{ -public: - EventReader(QEglFSX11Hooks *hooks) - : m_hooks(hooks) { } - - void run(); - -private: - QEglFSX11Hooks *m_hooks; -}; - -namespace Atoms { - enum { - _NET_WM_NAME = 0, - UTF8_STRING, - WM_PROTOCOLS, - WM_DELETE_WINDOW, - _NET_WM_STATE, - _NET_WM_STATE_FULLSCREEN, - - N_ATOMS - }; -} - -class QEglFSX11Hooks : public QEglFSHooks -{ -public: - QEglFSX11Hooks() : m_connection(0), m_window(0), m_eventReader(0) {} - - virtual void platformInit(); - virtual void platformDestroy(); - virtual EGLNativeDisplayType platformDisplay() const; - virtual QSize screenSize() const; - virtual EGLNativeWindowType createNativeWindow(QPlatformWindow *window, - const QSize &size, - const QSurfaceFormat &format); - virtual void destroyNativeWindow(EGLNativeWindowType window); - virtual bool hasCapability(QPlatformIntegration::Capability cap) const; - - xcb_connection_t *connection() { return m_connection; } - const xcb_atom_t *atoms() const { return m_atoms; } - QPlatformWindow *platformWindow() { return m_platformWindow; } - -private: - void sendConnectionEvent(xcb_atom_t a); - - Display *m_display; - xcb_connection_t *m_connection; - xcb_atom_t m_atoms[Atoms::N_ATOMS]; - xcb_window_t m_window; - EventReader *m_eventReader; - xcb_window_t m_connectionEventListener; - QPlatformWindow *m_platformWindow; - mutable QSize m_screenSize; -}; - -QAtomicInt running; - -static Qt::MouseButtons translateMouseButtons(int s) -{ - Qt::MouseButtons ret = 0; - if (s & XCB_BUTTON_MASK_1) - ret |= Qt::LeftButton; - if (s & XCB_BUTTON_MASK_2) - ret |= Qt::MidButton; - if (s & XCB_BUTTON_MASK_3) - ret |= Qt::RightButton; - return ret; -} - -static Qt::MouseButton translateMouseButton(xcb_button_t s) -{ - switch (s) { - case 1: return Qt::LeftButton; - case 2: return Qt::MidButton; - case 3: return Qt::RightButton; - // Button values 4-7 were already handled as Wheel events, and won't occur here. - case 8: return Qt::BackButton; // Also known as Qt::ExtraButton1 - case 9: return Qt::ForwardButton; // Also known as Qt::ExtraButton2 - case 10: return Qt::ExtraButton3; - case 11: return Qt::ExtraButton4; - case 12: return Qt::ExtraButton5; - case 13: return Qt::ExtraButton6; - case 14: return Qt::ExtraButton7; - case 15: return Qt::ExtraButton8; - case 16: return Qt::ExtraButton9; - case 17: return Qt::ExtraButton10; - case 18: return Qt::ExtraButton11; - case 19: return Qt::ExtraButton12; - case 20: return Qt::ExtraButton13; - case 21: return Qt::ExtraButton14; - case 22: return Qt::ExtraButton15; - case 23: return Qt::ExtraButton16; - case 24: return Qt::ExtraButton17; - case 25: return Qt::ExtraButton18; - case 26: return Qt::ExtraButton19; - case 27: return Qt::ExtraButton20; - case 28: return Qt::ExtraButton21; - case 29: return Qt::ExtraButton22; - case 30: return Qt::ExtraButton23; - case 31: return Qt::ExtraButton24; - default: return Qt::NoButton; - } -} - -void EventReader::run() -{ - Qt::MouseButtons buttons; - - xcb_generic_event_t *event; - while (running.load() && (event = xcb_wait_for_event(m_hooks->connection()))) { - uint response_type = event->response_type & ~0x80; - switch (response_type) { - case XCB_BUTTON_PRESS: { - xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; - QPoint p(press->event_x, press->event_y); - buttons = (buttons & ~0x7) | translateMouseButtons(press->state); - buttons |= translateMouseButton(press->detail); - QWindowSystemInterface::handleMouseEvent(0, press->time, p, p, buttons); - break; - } - case XCB_BUTTON_RELEASE: { - xcb_button_release_event_t *release = (xcb_button_release_event_t *)event; - QPoint p(release->event_x, release->event_y); - buttons = (buttons & ~0x7) | translateMouseButtons(release->state); - buttons &= ~translateMouseButton(release->detail); - QWindowSystemInterface::handleMouseEvent(0, release->time, p, p, buttons); - break; - } - case XCB_MOTION_NOTIFY: { - xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; - QPoint p(motion->event_x, motion->event_y); - QWindowSystemInterface::handleMouseEvent(0, motion->time, p, p, buttons); - break; - } - case XCB_CLIENT_MESSAGE: { - xcb_client_message_event_t *client = (xcb_client_message_event_t *) event; - const xcb_atom_t *atoms = m_hooks->atoms(); - if (client->format == 32 - && client->type == atoms[Atoms::WM_PROTOCOLS] - && client->data.data32[0] == atoms[Atoms::WM_DELETE_WINDOW]) { - QWindow *window = m_hooks->platformWindow() ? m_hooks->platformWindow()->window() : 0; - if (window) - QWindowSystemInterface::handleCloseEvent(window); - } - break; - } - default: - break; - } - } -} - -void QEglFSX11Hooks::sendConnectionEvent(xcb_atom_t a) -{ - xcb_client_message_event_t event; - memset(&event, 0, sizeof(event)); - - event.response_type = XCB_CLIENT_MESSAGE; - event.format = 32; - event.sequence = 0; - event.window = m_connectionEventListener; - event.type = a; - - xcb_send_event(m_connection, false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); - xcb_flush(m_connection); -} - -void QEglFSX11Hooks::platformInit() -{ - m_display = XOpenDisplay(0); - if (!m_display) - qFatal("Could not open display"); - - XSetEventQueueOwner(m_display, XCBOwnsEventQueue); - m_connection = XGetXCBConnection(m_display); - - running.ref(); - - xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); - - m_connectionEventListener = xcb_generate_id(m_connection); - xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, - m_connectionEventListener, it.data->root, - 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, - it.data->root_visual, 0, 0); - - m_eventReader = new EventReader(this); - m_eventReader->start(); -} - -void QEglFSX11Hooks::platformDestroy() -{ - running.deref(); - - sendConnectionEvent(XCB_ATOM_NONE); - - m_eventReader->wait(); - delete m_eventReader; - m_eventReader = 0; - - XCloseDisplay(m_display); - m_display = 0; - m_connection = 0; -} - -EGLNativeDisplayType QEglFSX11Hooks::platformDisplay() const -{ - return m_display; -} - -QSize QEglFSX11Hooks::screenSize() const -{ - if (m_screenSize.isEmpty()) { - QList env = qgetenv("EGLFS_X11_SIZE").split('x'); - if (env.length() == 2) { - m_screenSize = QSize(env.at(0).toInt(), env.at(1).toInt()); - } else { - m_screenSize = QSize(640, 480); - qWarning("EGLFS_X11_SIZE not set, falling back to 640x480"); - } - } - return m_screenSize; -} - -EGLNativeWindowType QEglFSX11Hooks::createNativeWindow(QPlatformWindow *platformWindow, - const QSize &size, - const QSurfaceFormat &format) -{ - Q_UNUSED(format); - - m_platformWindow = platformWindow; - - xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); - m_window = xcb_generate_id(m_connection); - xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, it.data->root, - 0, 0, size.width(), size.height(), 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, it.data->root_visual, - 0, 0); - - xcb_map_window(m_connection, m_window); - - xcb_intern_atom_cookie_t cookies[Atoms::N_ATOMS]; - static const char *atomNames[Atoms::N_ATOMS] = { - "_NET_WM_NAME", - "UTF8_STRING", - "WM_PROTOCOLS", - "WM_DELETE_WINDOW", - "_NET_WM_STATE", - "_NET_WM_STATE_FULLSCREEN" - }; - - for (int i = 0; i < Atoms::N_ATOMS; ++i) { - cookies[i] = xcb_intern_atom(m_connection, false, strlen(atomNames[i]), atomNames[i]); - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(m_connection, cookies[i], 0); - m_atoms[i] = reply->atom; - free(reply); - } - - // Set window title - xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, - m_atoms[Atoms::_NET_WM_NAME], m_atoms[Atoms::UTF8_STRING], 8, 5, "EGLFS"); - - // Enable WM_DELETE_WINDOW - xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, - m_atoms[Atoms::WM_PROTOCOLS], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::WM_DELETE_WINDOW]); - - if (qEnvironmentVariableIntValue("EGLFS_X11_FULLSCREEN")) { - // Go fullscreen. The QScreen and QWindow size is controlled by EGLFS_X11_SIZE regardless, - // this is just the native window. - xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, - m_atoms[Atoms::_NET_WM_STATE], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::_NET_WM_STATE_FULLSCREEN]); - } - - xcb_flush(m_connection); - - return m_window; -} - -void QEglFSX11Hooks::destroyNativeWindow(EGLNativeWindowType window) -{ - xcb_destroy_window(m_connection, window); -} - -bool QEglFSX11Hooks::hasCapability(QPlatformIntegration::Capability cap) const -{ - Q_UNUSED(cap); - return false; -} - -static QEglFSX11Hooks eglFSX11Hooks; -QEglFSHooks *platformHooks = &eglFSX11Hooks; - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 125e4e1bf9..8cb23f2c09 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -71,7 +71,7 @@ QEglFSIntegration::QEglFSIntegration() bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const { // We assume that devices will have more and not less capabilities - if (QEglFSHooks::hooks() && QEglFSHooks::hooks()->hasCapability(cap)) + if (qt_egl_device_integration()->hasCapability(cap)) return true; return QEGLPlatformIntegration::hasCapability(cap); @@ -84,26 +84,29 @@ void QEglFSIntegration::addScreen(QPlatformScreen *screen) void QEglFSIntegration::initialize() { - QEglFSHooks::hooks()->platformInit(); + qt_egl_device_integration()->platformInit(); QEGLPlatformIntegration::initialize(); if (!mDisableInputHandlers) createInputHandlers(); - QEglFSHooks::hooks()->screenInit(); + if (qt_egl_device_integration()->usesDefaultScreen()) + addScreen(new QEglFSScreen(display())); + else + qt_egl_device_integration()->screenInit(); } void QEglFSIntegration::destroy() { - QEglFSHooks::hooks()->screenDestroy(); + qt_egl_device_integration()->screenDestroy(); QEGLPlatformIntegration::destroy(); - QEglFSHooks::hooks()->platformDestroy(); + qt_egl_device_integration()->platformDestroy(); } EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const { - return QEglFSHooks::hooks()->platformDisplay(); + return qt_egl_device_integration()->platformDisplay(); } QEGLPlatformWindow *QEglFSIntegration::createWindow(QWindow *window) const @@ -117,7 +120,7 @@ QEGLPlatformContext *QEglFSIntegration::createContext(const QSurfaceFormat &form QVariant *nativeHandle) const { QEglFSContext *ctx; - QSurfaceFormat adjustedFormat = QEglFSHooks::hooks()->surfaceFormatFor(format); + QSurfaceFormat adjustedFormat = qt_egl_device_integration()->surfaceFormatFor(format); if (!nativeHandle || nativeHandle->isNull()) { EGLConfig config = QEglFSIntegration::chooseConfig(display, adjustedFormat); ctx = new QEglFSContext(adjustedFormat, shareContext, display, &config, QVariant()); @@ -132,8 +135,8 @@ QPlatformOffscreenSurface *QEglFSIntegration::createOffscreenSurface(EGLDisplay const QSurfaceFormat &format, QOffscreenSurface *surface) const { - QSurfaceFormat fmt = QEglFSHooks::hooks()->surfaceFormatFor(format); - if (QEglFSHooks::hooks()->supportsPBuffers()) + QSurfaceFormat fmt = qt_egl_device_integration()->surfaceFormatFor(format); + if (qt_egl_device_integration()->supportsPBuffers()) return new QEGLPbuffer(display, fmt, surface); else return new QEglFSOffscreenWindow(display, fmt, surface); @@ -145,23 +148,15 @@ EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceForm { class Chooser : public QEglConfigChooser { public: - Chooser(EGLDisplay display, QEglFSHooks *hooks) - : QEglConfigChooser(display) - , m_hooks(hooks) - { - } - - protected: - bool filterConfig(EGLConfig config) const Q_DECL_OVERRIDE - { - return m_hooks->filterConfig(display(), config) && QEglConfigChooser::filterConfig(config); + Chooser(EGLDisplay display) + : QEglConfigChooser(display) { } + bool filterConfig(EGLConfig config) const Q_DECL_OVERRIDE { + return qt_egl_device_integration()->filterConfig(display(), config) + && QEglConfigChooser::filterConfig(config); } - - private: - QEglFSHooks *m_hooks; }; - Chooser chooser(display, QEglFSHooks::hooks()); + Chooser chooser(display); chooser.setSurfaceFormat(format); return chooser.chooseConfig(); } diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index 86038910ca..b75dc240bb 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -37,10 +37,11 @@ #include #include #include +#include "qeglfsglobal.h" QT_BEGIN_NAMESPACE -class QEglFSIntegration : public QEGLPlatformIntegration +class Q_EGLFS_EXPORT QEglFSIntegration : public QEGLPlatformIntegration { public: QEglFSIntegration(); diff --git a/src/plugins/platforms/eglfs/qeglfsmain.cpp b/src/plugins/platforms/eglfs/qeglfsmain.cpp new file mode 100644 index 0000000000..2c5e79534d --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfsmain.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qeglfsintegration.h" + +QT_BEGIN_NAMESPACE + +class QEglFSIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "eglfs.json") +public: + QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE; +}; + +QPlatformIntegration* QEglFSIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (!system.compare(QLatin1String("eglfs"), Qt::CaseInsensitive)) + return new QEglFSIntegration; + + return 0; +} + +QT_END_NAMESPACE + +#include "qeglfsmain.moc" diff --git a/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp index 0e8e3957d0..7bf53ef351 100644 --- a/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp @@ -63,7 +63,7 @@ QEglFSOffscreenWindow::QEglFSOffscreenWindow(EGLDisplay display, const QSurfaceF , m_surface(EGL_NO_SURFACE) , m_window(0) { - m_window = QEglFSHooks::hooks()->createNativeOffscreenWindow(format); + m_window = qt_egl_device_integration()->createNativeOffscreenWindow(format); if (!m_window) { qWarning("QEglFSOffscreenWindow: Failed to create native window"); return; @@ -79,7 +79,7 @@ QEglFSOffscreenWindow::~QEglFSOffscreenWindow() if (m_surface != EGL_NO_SURFACE) eglDestroySurface(m_display, m_surface); if (m_window) - QEglFSHooks::hooks()->destroyNativeWindow(m_window); + qt_egl_device_integration()->destroyNativeWindow(m_window); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h index 8bd4089c34..128ff56968 100644 --- a/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h +++ b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h @@ -44,10 +44,11 @@ #include #include +#include "qeglfsglobal.h" QT_BEGIN_NAMESPACE -class QEglFSOffscreenWindow : public QPlatformOffscreenSurface +class Q_EGLFS_EXPORT QEglFSOffscreenWindow : public QPlatformOffscreenSurface { public: QEglFSOffscreenWindow(EGLDisplay display, const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface); diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index a66cff165b..84ea1c6483 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -43,7 +43,7 @@ QEglFSScreen::QEglFSScreen(EGLDisplay dpy) m_surface(EGL_NO_SURFACE), m_cursor(0) { - m_cursor = QEglFSHooks::hooks()->createCursor(this); + m_cursor = qt_egl_device_integration()->createCursor(this); } QEglFSScreen::~QEglFSScreen() @@ -53,37 +53,37 @@ QEglFSScreen::~QEglFSScreen() QRect QEglFSScreen::geometry() const { - return QRect(QPoint(0, 0), QEglFSHooks::hooks()->screenSize()); + return QRect(QPoint(0, 0), qt_egl_device_integration()->screenSize()); } int QEglFSScreen::depth() const { - return QEglFSHooks::hooks()->screenDepth(); + return qt_egl_device_integration()->screenDepth(); } QImage::Format QEglFSScreen::format() const { - return QEglFSHooks::hooks()->screenFormat(); + return qt_egl_device_integration()->screenFormat(); } QSizeF QEglFSScreen::physicalSize() const { - return QEglFSHooks::hooks()->physicalScreenSize(); + return qt_egl_device_integration()->physicalScreenSize(); } QDpi QEglFSScreen::logicalDpi() const { - return QEglFSHooks::hooks()->logicalDpi(); + return qt_egl_device_integration()->logicalDpi(); } Qt::ScreenOrientation QEglFSScreen::nativeOrientation() const { - return QEglFSHooks::hooks()->nativeOrientation(); + return qt_egl_device_integration()->nativeOrientation(); } Qt::ScreenOrientation QEglFSScreen::orientation() const { - return QEglFSHooks::hooks()->orientation(); + return qt_egl_device_integration()->orientation(); } QPlatformCursor *QEglFSScreen::cursor() const diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index d109ad9d9b..59f55d33fb 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -34,10 +34,8 @@ #ifndef QEGLFSSCREEN_H #define QEGLFSSCREEN_H +#include "qeglfsglobal.h" #include - -#include - #include QT_BEGIN_NAMESPACE @@ -45,7 +43,7 @@ QT_BEGIN_NAMESPACE class QEglFSWindow; class QOpenGLContext; -class QEglFSScreen : public QEGLPlatformScreen +class Q_EGLFS_EXPORT QEglFSScreen : public QEGLPlatformScreen { public: QEglFSScreen(EGLDisplay display); diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index 0ab19e6065..d568d50e59 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -93,7 +93,7 @@ void QEglFSWindow::create() QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size())); EGLDisplay display = static_cast(screen)->display(); - QSurfaceFormat platformFormat = QEglFSHooks::hooks()->surfaceFormatFor(window()->requestedFormat()); + QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat()); m_config = QEglFSIntegration::chooseConfig(display, platformFormat); m_format = q_glFormatFromConfig(display, m_config, platformFormat); @@ -140,7 +140,7 @@ void QEglFSWindow::invalidateSurface() eglDestroySurface(display, m_surface); m_surface = EGL_NO_SURFACE; } - QEglFSHooks::hooks()->destroyNativeWindow(m_window); + qt_egl_device_integration()->destroyNativeWindow(m_window); m_window = 0; } @@ -148,7 +148,7 @@ void QEglFSWindow::resetSurface() { QEglFSScreen *nativeScreen = static_cast(screen()); EGLDisplay display = nativeScreen->display(); - m_window = QEglFSHooks::hooks()->createNativeWindow(this, nativeScreen->geometry().size(), m_format); + m_window = qt_egl_device_integration()->createNativeWindow(this, nativeScreen->geometry().size(), m_format); m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL); if (m_surface == EGL_NO_SURFACE) { EGLint error = eglGetError(); diff --git a/src/plugins/platforms/eglfs/qeglfswindow.h b/src/plugins/platforms/eglfs/qeglfswindow.h index 439338f756..a3e3722b5a 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.h +++ b/src/plugins/platforms/eglfs/qeglfswindow.h @@ -36,12 +36,12 @@ #include "qeglfsintegration.h" #include "qeglfsscreen.h" - +#include "qeglfsglobal.h" #include QT_BEGIN_NAMESPACE -class QEglFSWindow : public QEGLPlatformWindow +class Q_EGLFS_EXPORT QEglFSWindow : public QEGLPlatformWindow { public: QEglFSWindow(QWindow *w); -- cgit v1.2.3