diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/eglfs_device_lib.pro | 2 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfscontext.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfscursor.cpp | 401 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfscursor.h | 143 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfsintegration.cpp | 325 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfsintegration.h | 65 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfsscreen.cpp | 94 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfsscreen.h | 16 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfswindow.cpp | 79 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfswindow.h | 33 |
13 files changed, 1091 insertions, 87 deletions
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp index 45224ccb87..d1814fb85d 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp @@ -36,8 +36,8 @@ #include "qeglfskmsdevice.h" #include "qeglfskmsscreen.h" #include "qeglfskmscursor.h" +#include "qeglfscursor.h" -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qdevicediscovery_p.h> #include <QtCore/QLoggingCategory> #include <QtCore/QJsonDocument> @@ -176,7 +176,7 @@ QPlatformCursor *QEglFSKmsIntegration::createCursor(QPlatformScreen *screen) con if (m_hwCursor) return Q_NULLPTR; else - return new QEGLPlatformCursor(screen); + return new QEglFSCursor(screen); } void QEglFSKmsIntegration::waitForVSync(QPlatformSurface *surface) const diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp index 5e49c224a0..b3e3f06f6c 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp @@ -35,11 +35,11 @@ #include "qeglfskmsscreen.h" #include "qeglfskmsdevice.h" #include "qeglfskmscursor.h" +#include "qeglfsintegration.h" #include <QtCore/QLoggingCategory> #include <QtGui/private/qguiapplication_p.h> -#include <QtPlatformSupport/private/qeglplatformintegration_p.h> #include <QtPlatformSupport/private/qfbvthandler_p.h> QT_BEGIN_NAMESPACE @@ -50,7 +50,7 @@ class QEglFSKmsInterruptHandler : public QObject { public: QEglFSKmsInterruptHandler(QEglFSKmsScreen *screen) : m_screen(screen) { - m_vtHandler = static_cast<QEGLPlatformIntegration *>(QGuiApplicationPrivate::platformIntegration())->vtHandler(); + m_vtHandler = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->vtHandler(); connect(m_vtHandler, &QFbVtHandler::interrupted, this, &QEglFSKmsInterruptHandler::restoreVideoMode); connect(m_vtHandler, &QFbVtHandler::suspendRequested, this, &QEglFSKmsInterruptHandler::handleSuspendRequest); } diff --git a/src/plugins/platforms/eglfs/eglfs_device_lib.pro b/src/plugins/platforms/eglfs/eglfs_device_lib.pro index 729290706d..22a32663c7 100644 --- a/src/plugins/platforms/eglfs/eglfs_device_lib.pro +++ b/src/plugins/platforms/eglfs/eglfs_device_lib.pro @@ -26,6 +26,7 @@ DEFINES += QT_BUILD_EGL_DEVICE_LIB SOURCES += $$PWD/qeglfsintegration.cpp \ $$PWD/qeglfswindow.cpp \ $$PWD/qeglfsscreen.cpp \ + $$PWD/qeglfscursor.cpp \ $$PWD/qeglfshooks.cpp \ $$PWD/qeglfscontext.cpp \ $$PWD/qeglfsoffscreenwindow.cpp \ @@ -34,6 +35,7 @@ SOURCES += $$PWD/qeglfsintegration.cpp \ HEADERS += $$PWD/qeglfsintegration.h \ $$PWD/qeglfswindow.h \ $$PWD/qeglfsscreen.h \ + $$PWD/qeglfscursor.h \ $$PWD/qeglfshooks.h \ $$PWD/qeglfscontext.h \ $$PWD/qeglfsoffscreenwindow.h \ diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 9216b7a85d..e2223aba43 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -32,15 +32,13 @@ ****************************************************************************/ #include <QtGui/QSurface> -#include <QtDebug> - -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtPlatformSupport/private/qeglpbuffer_p.h> +#include "qeglfscontext.h" #include "qeglfswindow.h" #include "qeglfshooks.h" -#include "qeglfscontext.h" +#include "qeglfscursor.h" QT_BEGIN_NAMESPACE @@ -91,7 +89,7 @@ void QEglFSContext::swapBuffers(QPlatformSurface *surface) // draw the cursor if (surface->surface()->surfaceClass() == QSurface::Window) { QPlatformWindow *window = static_cast<QPlatformWindow *>(surface); - if (QEGLPlatformCursor *cursor = qobject_cast<QEGLPlatformCursor *>(window->screen()->cursor())) + if (QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(window->screen()->cursor())) cursor->paintOnScreen(); } diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp new file mode 100644 index 0000000000..d81aa2eaf2 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfscursor.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + +#include <qpa/qwindowsysteminterface.h> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLShaderProgram> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonObject> + +#include <QtGui/private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +QEglFSCursor::QEglFSCursor(QPlatformScreen *screen) + : m_visible(true), + m_screen(static_cast<QEglFSScreen *>(screen)), + m_program(0), + m_vertexCoordEntry(0), + m_textureCoordEntry(0), + m_textureEntry(0), + m_deviceListener(0), + m_updateRequested(false) +{ + QByteArray hideCursorVal = qgetenv("QT_QPA_EGLFS_HIDECURSOR"); + if (!hideCursorVal.isEmpty()) + m_visible = hideCursorVal.toInt() == 0; + if (!m_visible) + return; + + // Try to load the cursor atlas. If this fails, m_visible is set to false and + // paintOnScreen() and setCurrentCursor() become no-ops. + initCursorAtlas(); + + // initialize the cursor +#ifndef QT_NO_CURSOR + QCursor cursor(Qt::ArrowCursor); + setCurrentCursor(&cursor); +#endif + + m_deviceListener = new QEglFSCursorDeviceListener(this); + connect(QGuiApplicationPrivate::inputDeviceManager(), &QInputDeviceManager::deviceListChanged, + m_deviceListener, &QEglFSCursorDeviceListener::onDeviceListChanged); + updateMouseStatus(); +} + +QEglFSCursor::~QEglFSCursor() +{ + resetResources(); + delete m_deviceListener; +} + +void QEglFSCursor::updateMouseStatus() +{ + m_visible = m_deviceListener->hasMouse(); +} + +bool QEglFSCursorDeviceListener::hasMouse() const +{ + return QGuiApplicationPrivate::inputDeviceManager()->deviceCount(QInputDeviceManager::DeviceTypePointer) > 0; +} + +void QEglFSCursorDeviceListener::onDeviceListChanged(QInputDeviceManager::DeviceType type) +{ + if (type == QInputDeviceManager::DeviceTypePointer) + m_cursor->updateMouseStatus(); +} + +void QEglFSCursor::resetResources() +{ + if (QOpenGLContext::currentContext()) { + delete m_program; + glDeleteTextures(1, &m_cursor.customCursorTexture); + glDeleteTextures(1, &m_cursorAtlas.texture); + } + m_program = 0; + m_cursor.customCursorTexture = 0; + m_cursor.customCursorPending = !m_cursor.customCursorImage.isNull(); + m_cursorAtlas.texture = 0; +} + +void QEglFSCursor::createShaderPrograms() +{ + static const char *textureVertexProgram = + "attribute highp vec2 vertexCoordEntry;\n" + "attribute highp vec2 textureCoordEntry;\n" + "varying highp vec2 textureCoord;\n" + "void main() {\n" + " textureCoord = textureCoordEntry;\n" + " gl_Position = vec4(vertexCoordEntry, 1.0, 1.0);\n" + "}\n"; + + static const char *textureFragmentProgram = + "uniform sampler2D texture;\n" + "varying highp vec2 textureCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(texture, textureCoord).bgra;\n" + "}\n"; + + m_program = new QOpenGLShaderProgram; + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, textureVertexProgram); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, textureFragmentProgram); + m_program->link(); + + m_vertexCoordEntry = m_program->attributeLocation("vertexCoordEntry"); + m_textureCoordEntry = m_program->attributeLocation("textureCoordEntry"); + m_textureEntry = m_program->uniformLocation("texture"); +} + +void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image) +{ + if (!*texture) + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, image.width(), image.height(), 0 /* border */, + GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); +} + +void QEglFSCursor::initCursorAtlas() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); + if (json.isEmpty()) + json = ":/cursor.json"; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + 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_Premultiplied); + 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; +} + +#ifndef QT_NO_CURSOR +void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window) +{ + Q_UNUSED(window); + const QRect oldCursorRect = cursorRect(); + if (setCurrentCursor(cursor)) + update(oldCursorRect | cursorRect()); +} + +bool QEglFSCursor::setCurrentCursor(QCursor *cursor) +{ + if (!m_visible) + return false; + + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + if (m_cursor.shape == newShape && newShape != Qt::BitmapCursor) + return false; + + if (m_cursor.shape == Qt::BitmapCursor) { + m_cursor.customCursorImage = QImage(); + m_cursor.customCursorPending = false; + } + m_cursor.shape = newShape; + if (newShape != Qt::BitmapCursor) { // standard cursor + const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width, + hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; + m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow), + hs * (m_cursor.shape / m_cursorAtlas.cursorsPerRow), + ws, hs); + m_cursor.hotSpot = m_cursorAtlas.hotSpots[m_cursor.shape]; + m_cursor.texture = m_cursorAtlas.texture; + m_cursor.size = QSize(m_cursorAtlas.cursorWidth, m_cursorAtlas.cursorHeight); + } else { + QImage image = cursor->pixmap().toImage(); + m_cursor.textureRect = QRectF(0, 0, 1, 1); + m_cursor.hotSpot = cursor->hotSpot(); + m_cursor.texture = 0; // will get updated in the next render() + m_cursor.size = image.size(); + m_cursor.customCursorImage = image; + m_cursor.customCursorPending = true; + } + + return true; +} +#endif + +class CursorUpdateEvent : public QEvent +{ +public: + CursorUpdateEvent(const QPoint &pos, const QRegion &rgn) + : QEvent(QEvent::Type(QEvent::User + 1)), + m_pos(pos), + m_region(rgn) + { } + QPoint pos() const { return m_pos; } + QRegion region() const { return m_region; } + +private: + QPoint m_pos; + QRegion m_region; +}; + +bool QEglFSCursor::event(QEvent *e) +{ + if (e->type() == QEvent::User + 1) { + CursorUpdateEvent *ev = static_cast<CursorUpdateEvent *>(e); + m_updateRequested = false; + QWindowSystemInterface::handleExposeEvent(m_screen->topLevelAt(ev->pos()), ev->region()); + QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); + return true; + } + return QPlatformCursor::event(e); +} + +void QEglFSCursor::update(const QRegion &rgn) +{ + if (!m_updateRequested) { + // Must not flush the window system events directly from here since we are likely to + // be a called directly from QGuiApplication's processMouseEvents. Flushing events + // could cause reentering by dispatching more queued mouse events. + m_updateRequested = true; + QCoreApplication::postEvent(this, new CursorUpdateEvent(m_cursor.pos, rgn)); + } +} + +QRect QEglFSCursor::cursorRect() const +{ + return QRect(m_cursor.pos - m_cursor.hotSpot, m_cursor.size); +} + +QPoint QEglFSCursor::pos() const +{ + return m_cursor.pos; +} + +void QEglFSCursor::setPos(const QPoint &pos) +{ + QGuiApplicationPrivate::inputDeviceManager()->setCursorPos(pos); + const QRect oldCursorRect = cursorRect(); + m_cursor.pos = pos; + update(oldCursorRect | cursorRect()); + m_screen->handleCursorMove(m_cursor.pos); +} + +void QEglFSCursor::pointerEvent(const QMouseEvent &event) +{ + if (event.type() != QEvent::MouseMove) + return; + const QRect oldCursorRect = cursorRect(); + m_cursor.pos = event.screenPos().toPoint(); + update(oldCursorRect | cursorRect()); + m_screen->handleCursorMove(m_cursor.pos); +} + +void QEglFSCursor::paintOnScreen() +{ + if (!m_visible) + return; + + const QRectF cr = cursorRect(); + const QRect screenRect(m_screen->geometry()); + const GLfloat x1 = 2 * (cr.left() / screenRect.width()) - 1; + const GLfloat x2 = 2 * (cr.right() / screenRect.width()) - 1; + const GLfloat y1 = 1 - (cr.top() / screenRect.height()) * 2; + const GLfloat y2 = 1 - (cr.bottom() / screenRect.height()) * 2; + QRectF r(QPointF(x1, y1), QPointF(x2, y2)); + + draw(r); +} + +void QEglFSCursor::draw(const QRectF &r) +{ + if (!m_program) { + // one time initialization + initializeOpenGLFunctions(); + + createShaderPrograms(); + + if (!m_cursorAtlas.texture) { + createCursorTexture(&m_cursorAtlas.texture, m_cursorAtlas.image); + + if (m_cursor.shape != Qt::BitmapCursor) + m_cursor.texture = m_cursorAtlas.texture; + } + } + + if (m_cursor.shape == Qt::BitmapCursor && m_cursor.customCursorPending) { + // upload the custom cursor + createCursorTexture(&m_cursor.customCursorTexture, m_cursor.customCursorImage); + m_cursor.texture = m_cursor.customCursorTexture; + m_cursor.customCursorPending = false; + } + + Q_ASSERT(m_cursor.texture); + + m_program->bind(); + + const GLfloat x1 = r.left(); + const GLfloat x2 = r.right(); + const GLfloat y1 = r.top(); + const GLfloat y2 = r.bottom(); + const GLfloat cursorCoordinates[] = { + x1, y2, + x2, y2, + x1, y1, + x2, y1 + }; + + const GLfloat s1 = m_cursor.textureRect.left(); + const GLfloat s2 = m_cursor.textureRect.right(); + const GLfloat t1 = m_cursor.textureRect.top(); + const GLfloat t2 = m_cursor.textureRect.bottom(); + const GLfloat textureCoordinates[] = { + s1, t2, + s2, t2, + s1, t1, + s2, t1 + }; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_cursor.texture); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_program->enableAttributeArray(m_vertexCoordEntry); + m_program->enableAttributeArray(m_textureCoordEntry); + + m_program->setAttributeArray(m_vertexCoordEntry, cursorCoordinates, 2); + m_program->setAttributeArray(m_textureCoordEntry, textureCoordinates, 2); + + m_program->setUniformValue(m_textureEntry, 0); + + glDisable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); // disable depth testing to make sure cursor is always on top + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisable(GL_BLEND); + + glBindTexture(GL_TEXTURE_2D, 0); + m_program->disableAttributeArray(m_textureCoordEntry); + m_program->disableAttributeArray(m_vertexCoordEntry); + + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfscursor.h b/src/plugins/platforms/eglfs/qeglfscursor.h new file mode 100644 index 0000000000..048f276137 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfscursor.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSCURSOR_H +#define QEGLFSCURSOR_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 "qeglfsglobal.h" +#include <qpa/qplatformcursor.h> +#include <qpa/qplatformscreen.h> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/private/qinputdevicemanager_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; +class QEglFSCursor; +class QEglFSScreen; + +class QEglFSCursorDeviceListener : public QObject +{ + Q_OBJECT + +public: + QEglFSCursorDeviceListener(QEglFSCursor *cursor) : m_cursor(cursor) { } + bool hasMouse() const; + +public slots: + void onDeviceListChanged(QInputDeviceManager::DeviceType type); + +private: + QEglFSCursor *m_cursor; +}; + +class Q_EGLFS_EXPORT QEglFSCursor : public QPlatformCursor, protected QOpenGLFunctions +{ + Q_OBJECT +public: + QEglFSCursor(QPlatformScreen *screen); + ~QEglFSCursor(); + +#ifndef QT_NO_CURSOR + void changeCursor(QCursor *cursor, QWindow *widget) Q_DECL_OVERRIDE; +#endif + void pointerEvent(const QMouseEvent &event) Q_DECL_OVERRIDE; + QPoint pos() const Q_DECL_OVERRIDE; + void setPos(const QPoint &pos) Q_DECL_OVERRIDE; + + QRect cursorRect() const; + void paintOnScreen(); + void resetResources(); + + void updateMouseStatus(); + +private: + bool event(QEvent *e) Q_DECL_OVERRIDE; +#ifndef QT_NO_CURSOR + bool setCurrentCursor(QCursor *cursor); +#endif + void draw(const QRectF &rect); + void update(const QRegion ®ion); + void createShaderPrograms(); + void createCursorTexture(uint *texture, const QImage &image); + void initCursorAtlas(); + + // current cursor information + struct Cursor { + Cursor() : texture(0), shape(Qt::BlankCursor), customCursorTexture(0), customCursorPending(false) { } + uint texture; // a texture from 'image' or the atlas + Qt::CursorShape shape; + QRectF textureRect; // normalized rect inside texture + QSize size; // size of the cursor + QPoint hotSpot; + QImage customCursorImage; + QPoint pos; // current cursor position + uint customCursorTexture; + bool customCursorPending; + } m_cursor; + + // cursor atlas information + struct CursorAtlas { + CursorAtlas() : cursorsPerRow(0), texture(0), cursorWidth(0), cursorHeight(0) { } + int cursorsPerRow; + uint texture; + int width, height; // width and height of the atlas + int cursorWidth, cursorHeight; // width and height of cursors inside the atlas + QList<QPoint> hotSpots; + QImage image; // valid until it's uploaded + } m_cursorAtlas; + + bool m_visible; + QEglFSScreen *m_screen; + QOpenGLShaderProgram *m_program; + int m_vertexCoordEntry; + int m_textureCoordEntry; + int m_textureEntry; + QEglFSCursorDeviceListener *m_deviceListener; + bool m_updateRequested; +}; + +QT_END_NAMESPACE + +#endif // QEGLFSCURSOR_H diff --git a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp index 7771c981dd..d27c949c8d 100644 --- a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp @@ -33,8 +33,8 @@ #include "qeglfsdeviceintegration.h" #include "qeglfsintegration.h" +#include "qeglfscursor.h" #include <QtPlatformSupport/private/qeglconvenience_p.h> -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QGuiApplication> #include <private/qguiapplication_p.h> #include <QScreen> @@ -286,7 +286,7 @@ bool QEGLDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) QPlatformCursor *QEGLDeviceIntegration::createCursor(QPlatformScreen *screen) const { - return new QEGLPlatformCursor(screen); + return new QEglFSCursor(screen); } void QEGLDeviceIntegration::waitForVSync(QPlatformSurface *surface) const diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 5eb8485dc7..aec5a5e179 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -39,19 +39,41 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> #include <QtGui/QOffscreenSurface> -#include <qpa/qplatformcursor.h> +#include <QtGui/QWindow> +#include <qpa/qwindowsysteminterface.h> +#include <qpa/qplatforminputcontextfactory_p.h> #include "qeglfsintegration.h" #include "qeglfswindow.h" #include "qeglfshooks.h" #include "qeglfscontext.h" #include "qeglfsoffscreenwindow.h" +#include "qeglfscursor.h" #include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtPlatformSupport/private/qeglplatformcontext_p.h> #include <QtPlatformSupport/private/qeglpbuffer_p.h> + +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#include <QtPlatformSupport/private/qgenericunixservices_p.h> +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QtPlatformSupport/private/qfbvthandler_p.h> +#include <QtPlatformSupport/private/qopenglcompositorbackingstore_p.h> + #include <QtPlatformHeaders/QEGLNativeContext> +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) +#include <QtPlatformSupport/private/qevdevmousemanager_p.h> +#include <QtPlatformSupport/private/qevdevkeyboardmanager_p.h> +#include <QtPlatformSupport/private/qevdevtouchmanager_p.h> +#endif + +#if !defined(QT_NO_TSLIB) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) +#include <QtPlatformSupport/private/qtslib_p.h> +#endif + +#include <QtPlatformHeaders/qeglfsfunctions.h> + #include <EGL/egl.h> static void initResources() @@ -64,21 +86,18 @@ static void initResources() QT_BEGIN_NAMESPACE QEglFSIntegration::QEglFSIntegration() + : m_display(EGL_NO_DISPLAY), + m_inputContext(0), + m_fontDb(new QGenericUnixFontDatabase), + m_services(new QGenericUnixServices), + m_kbdMgr(0), + m_disableInputHandlers(false) { - mDisableInputHandlers = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DISABLE_INPUT"); + m_disableInputHandlers = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DISABLE_INPUT"); initResources(); } -bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const -{ - // We assume that devices will have more and not less capabilities - if (qt_egl_device_integration()->hasCapability(cap)) - return true; - - return QEGLPlatformIntegration::hasCapability(cap); -} - void QEglFSIntegration::addScreen(QPlatformScreen *screen) { screenAdded(screen); @@ -93,9 +112,19 @@ void QEglFSIntegration::initialize() { qt_egl_device_integration()->platformInit(); - QEGLPlatformIntegration::initialize(); + m_display = eglGetDisplay(nativeDisplay()); + if (m_display == EGL_NO_DISPLAY) + qFatal("Could not open egl display"); + + EGLint major, minor; + if (!eglInitialize(m_display, &major, &minor)) + qFatal("Could not initialize egl display"); + + m_inputContext = QPlatformInputContextFactory::create(); + + m_vtHandler.reset(new QFbVtHandler); - if (!mDisableInputHandlers) + if (!m_disableInputHandlers) createInputHandlers(); if (qt_egl_device_integration()->usesDefaultScreen()) @@ -108,52 +137,276 @@ void QEglFSIntegration::destroy() { foreach (QWindow *w, qGuiApp->topLevelWindows()) w->destroy(); + qt_egl_device_integration()->screenDestroy(); - if (display() != EGL_NO_DISPLAY) - eglTerminate(display()); + + if (m_display != EGL_NO_DISPLAY) + eglTerminate(m_display); + qt_egl_device_integration()->platformDestroy(); } -EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const +QAbstractEventDispatcher *QEglFSIntegration::createEventDispatcher() const { - return qt_egl_device_integration()->platformDisplay(); + return createUnixEventDispatcher(); +} + +QPlatformServices *QEglFSIntegration::services() const +{ + return m_services.data(); } -QEGLPlatformWindow *QEglFSIntegration::createWindow(QWindow *window) const +QPlatformFontDatabase *QEglFSIntegration::fontDatabase() const { - return new QEglFSWindow(window); + return m_fontDb.data(); } -QEGLPlatformContext *QEglFSIntegration::createContext(const QSurfaceFormat &format, - QPlatformOpenGLContext *shareContext, - EGLDisplay display, - QVariant *nativeHandle) const +QPlatformBackingStore *QEglFSIntegration::createPlatformBackingStore(QWindow *window) const { + QOpenGLCompositorBackingStore *bs = new QOpenGLCompositorBackingStore(window); + if (!window->handle()) + window->create(); + static_cast<QEglFSWindow *>(window->handle())->setBackingStore(bs); + return bs; +} + +QPlatformWindow *QEglFSIntegration::createPlatformWindow(QWindow *window) const +{ + QWindowSystemInterface::flushWindowSystemEvents(); + QEglFSWindow *w = new QEglFSWindow(window); + w->create(); + if (window->type() != Qt::ToolTip) + w->requestActivateWindow(); + return w; +} + +QPlatformOpenGLContext *QEglFSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + // If there is a "root" window into which raster and QOpenGLWidget content is + // composited, all other contexts must share with its context. + QOpenGLContext *compositingContext = QOpenGLCompositor::instance()->context(); + EGLDisplay dpy = context->screen() ? static_cast<QEglFSScreen *>(context->screen()->handle())->display() : display(); + QPlatformOpenGLContext *share = compositingContext ? compositingContext->handle() : context->shareHandle(); + QVariant nativeHandle = context->nativeHandle(); + QEglFSContext *ctx; - 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()); + QSurfaceFormat adjustedFormat = qt_egl_device_integration()->surfaceFormatFor(context->format()); + if (nativeHandle.isNull()) { + EGLConfig config = QEglFSIntegration::chooseConfig(dpy, adjustedFormat); + ctx = new QEglFSContext(adjustedFormat, share, dpy, &config, QVariant()); } else { - ctx = new QEglFSContext(adjustedFormat, shareContext, display, 0, *nativeHandle); + ctx = new QEglFSContext(adjustedFormat, share, dpy, 0, nativeHandle); } - *nativeHandle = QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(ctx->eglContext(), display)); + nativeHandle = QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(ctx->eglContext(), dpy)); + + context->setNativeHandle(nativeHandle); return ctx; } -QPlatformOffscreenSurface *QEglFSIntegration::createOffscreenSurface(EGLDisplay display, - const QSurfaceFormat &format, - QOffscreenSurface *surface) const +QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const { - QSurfaceFormat fmt = qt_egl_device_integration()->surfaceFormatFor(format); + EGLDisplay dpy = surface->screen() ? static_cast<QEglFSScreen *>(surface->screen()->handle())->display() : display(); + QSurfaceFormat fmt = qt_egl_device_integration()->surfaceFormatFor(surface->requestedFormat()); if (qt_egl_device_integration()->supportsPBuffers()) - return new QEGLPbuffer(display, fmt, surface); + return new QEGLPbuffer(dpy, fmt, surface); else - return new QEglFSOffscreenWindow(display, fmt, surface); - + return new QEglFSOffscreenWindow(dpy, fmt, surface); // Never return null. Multiple QWindows are not supported by this plugin. } +bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + // We assume that devices will have more and not less capabilities + if (qt_egl_device_integration()->hasCapability(cap)) + return true; + + switch (cap) { + case ThreadedPixmaps: return true; + case OpenGL: return true; + case ThreadedOpenGL: return true; + case WindowManagement: return false; + case RasterGLSurface: return true; + default: return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformNativeInterface *QEglFSIntegration::nativeInterface() const +{ + return const_cast<QEglFSIntegration *>(this); +} + +enum ResourceType { + EglDisplay, + EglWindow, + EglContext, + EglConfig, + NativeDisplay, + XlibDisplay +}; + +static int resourceType(const QByteArray &key) +{ + static const QByteArray names[] = { // match ResourceType + QByteArrayLiteral("egldisplay"), + QByteArrayLiteral("eglwindow"), + QByteArrayLiteral("eglcontext"), + QByteArrayLiteral("eglconfig"), + QByteArrayLiteral("nativedisplay"), + QByteArrayLiteral("display") + }; + const QByteArray *end = names + sizeof(names) / sizeof(names[0]); + const QByteArray *result = std::find(names, end, key); + if (result == end) + result = std::find(names, end, key.toLower()); + return int(result - names); +} + +void *QEglFSIntegration::nativeResourceForIntegration(const QByteArray &resource) +{ + void *result = 0; + + switch (resourceType(resource)) { + case EglDisplay: + result = display(); + break; + case NativeDisplay: + result = reinterpret_cast<void*>(nativeDisplay()); + break; + default: + break; + } + + return result; +} + +void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *) +{ + void *result = 0; + + switch (resourceType(resource)) { + case XlibDisplay: + // Play nice when using the x11 hooks: Be compatible with xcb that allows querying + // the X Display pointer, which is nothing but our native display. + result = reinterpret_cast<void*>(nativeDisplay()); + break; + default: + break; + } + + return result; +} + +void *QEglFSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + void *result = 0; + + switch (resourceType(resource)) { + case EglDisplay: + if (window && window->handle()) + result = static_cast<QEglFSScreen *>(window->handle()->screen())->display(); + else + result = display(); + break; + case EglWindow: + if (window && window->handle()) + result = reinterpret_cast<void*>(static_cast<QEglFSWindow *>(window->handle())->eglWindow()); + break; + default: + break; + } + + return result; +} + +void *QEglFSIntegration::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) +{ + void *result = 0; + + switch (resourceType(resource)) { + case EglContext: + if (context->handle()) + result = static_cast<QEglFSContext *>(context->handle())->eglContext(); + break; + case EglConfig: + if (context->handle()) + result = static_cast<QEglFSContext *>(context->handle())->eglConfig(); + break; + case EglDisplay: + if (context->handle()) + result = static_cast<QEglFSContext *>(context->handle())->eglDisplay(); + break; + default: + break; + } + + return result; +} + +static void *eglContextForContext(QOpenGLContext *context) +{ + Q_ASSERT(context); + + QEglFSContext *handle = static_cast<QEglFSContext *>(context->handle()); + if (!handle) + return 0; + + return handle->eglContext(); +} + +QPlatformNativeInterface::NativeResourceForContextFunction QEglFSIntegration::nativeResourceFunctionForContext(const QByteArray &resource) +{ + QByteArray lowerCaseResource = resource.toLower(); + if (lowerCaseResource == "get_egl_context") + return NativeResourceForContextFunction(eglContextForContext); + + return 0; +} + +QFunctionPointer QEglFSIntegration::platformFunction(const QByteArray &function) const +{ +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) + if (function == QEglFSFunctions::loadKeymapTypeIdentifier()) + return QFunctionPointer(loadKeymapStatic); +#else + Q_UNUSED(function) +#endif + + return 0; +} + +void QEglFSIntegration::loadKeymapStatic(const QString &filename) +{ +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) + QEglFSIntegration *self = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + if (self->m_kbdMgr) + self->m_kbdMgr->loadKeymap(filename); + else + qWarning("QEglFSIntegration: Cannot load keymap, no keyboard handler found"); +#else + Q_UNUSED(filename); +#endif +} + +void QEglFSIntegration::createInputHandlers() +{ +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) + m_kbdMgr = new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString() /* spec */, this); + new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString() /* spec */, this); +#ifndef QT_NO_TSLIB + const bool useTslib = qEnvironmentVariableIntValue("QT_QPA_EGLFS_TSLIB"); + if (useTslib) + new QTsLibMouseHandler(QLatin1String("TsLib"), QString() /* spec */); + else +#endif // QT_NO_TSLIB + new QEvdevTouchManager(QLatin1String("EvdevTouch"), QString() /* spec */, this); +#endif +} + +EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const +{ + return qt_egl_device_integration()->platformDisplay(); +} + EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) { class Chooser : public QEglConfigChooser { diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index 11b643d540..98c7ee9f78 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -34,41 +34,72 @@ #ifndef QEGLFSINTEGRATION_H #define QEGLFSINTEGRATION_H -#include <QtPlatformSupport/private/qeglplatformintegration_p.h> +#include <QtCore/QVariant> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformscreen.h> #include <EGL/egl.h> #include "qeglfsglobal.h" QT_BEGIN_NAMESPACE -class Q_EGLFS_EXPORT QEglFSIntegration : public QEGLPlatformIntegration +class QEglFSWindow; +class QEglFSContext; +class QFbVtHandler; +class QEvdevKeyboardManager; + +class Q_EGLFS_EXPORT QEglFSIntegration : public QPlatformIntegration, public QPlatformNativeInterface { public: QEglFSIntegration(); - void addScreen(QPlatformScreen *screen); - void removeScreen(QPlatformScreen *screen); - void initialize() Q_DECL_OVERRIDE; void destroy() Q_DECL_OVERRIDE; + EGLDisplay display() const { return m_display; } + + QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE; + QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE; + QPlatformServices *services() const Q_DECL_OVERRIDE; + QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE { return m_inputContext; } + + QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE; + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const Q_DECL_OVERRIDE; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); + QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE; -protected: - QEGLPlatformWindow *createWindow(QWindow *window) const Q_DECL_OVERRIDE; - QEGLPlatformContext *createContext(const QSurfaceFormat &format, - QPlatformOpenGLContext *shareContext, - EGLDisplay display, - QVariant *nativeHandle) const Q_DECL_OVERRIDE; - QPlatformOffscreenSurface *createOffscreenSurface(EGLDisplay display, - const QSurfaceFormat &format, - QOffscreenSurface *surface) const Q_DECL_OVERRIDE; - EGLNativeDisplayType nativeDisplay() const Q_DECL_OVERRIDE; + // QPlatformNativeInterface + void *nativeResourceForIntegration(const QByteArray &resource) Q_DECL_OVERRIDE; + void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) Q_DECL_OVERRIDE; + void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) Q_DECL_OVERRIDE; + void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) Q_DECL_OVERRIDE; + NativeResourceForContextFunction nativeResourceFunctionForContext(const QByteArray &resource) Q_DECL_OVERRIDE; + + QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE; + + QFbVtHandler *vtHandler() { return m_vtHandler.data(); } + + void addScreen(QPlatformScreen *screen); + void removeScreen(QPlatformScreen *screen); + + static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); private: - bool mDisableInputHandlers; + EGLNativeDisplayType nativeDisplay() const; + void createInputHandlers(); + static void loadKeymapStatic(const QString &filename); + + EGLDisplay m_display; + QPlatformInputContext *m_inputContext; + QScopedPointer<QPlatformFontDatabase> m_fontDb; + QScopedPointer<QPlatformServices> m_services; + QScopedPointer<QFbVtHandler> m_vtHandler; + QEvdevKeyboardManager *m_kbdMgr; + bool m_disableInputHandlers; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index 1b6e2307f8..22ec424451 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -32,7 +32,10 @@ ****************************************************************************/ #include <QtCore/qtextstream.h> -#include <QtGui/qpa/qplatformcursor.h> +#include <QtGui/qwindow.h> +#include <qpa/qwindowsysteminterface.h> +#include <qpa/qplatformcursor.h> +#include <QtPlatformSupport/private/qopenglcompositor_p.h> #include "qeglfsscreen.h" #include "qeglfswindow.h" @@ -41,7 +44,8 @@ QT_BEGIN_NAMESPACE QEglFSScreen::QEglFSScreen(EGLDisplay dpy) - : QEGLPlatformScreen(dpy), + : m_dpy(dpy), + m_pointerWindow(0), m_surface(EGL_NO_SURFACE), m_cursor(0) { @@ -51,6 +55,7 @@ QEglFSScreen::QEglFSScreen(EGLDisplay dpy) QEglFSScreen::~QEglFSScreen() { delete m_cursor; + QOpenGLCompositor::destroy(); } QRect QEglFSScreen::geometry() const @@ -103,4 +108,89 @@ void QEglFSScreen::setPrimarySurface(EGLSurface surface) m_surface = surface; } +void QEglFSScreen::handleCursorMove(const QPoint &pos) +{ + const QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); + const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); + + // Generate enter and leave events like a real windowing system would do. + if (windows.isEmpty()) + return; + + // First window is always fullscreen. + if (windows.count() == 1) { + QWindow *window = windows[0]->sourceWindow(); + if (m_pointerWindow != window) { + m_pointerWindow = window; + QWindowSystemInterface::handleEnterEvent(window, window->mapFromGlobal(pos), pos); + } + return; + } + + QWindow *enter = 0, *leave = 0; + for (int i = windows.count() - 1; i >= 0; --i) { + QWindow *window = windows[i]->sourceWindow(); + const QRect geom = window->geometry(); + if (geom.contains(pos)) { + if (m_pointerWindow != window) { + leave = m_pointerWindow; + m_pointerWindow = window; + enter = window; + } + break; + } + } + + if (enter && leave) + QWindowSystemInterface::handleEnterLeaveEvent(enter, leave, enter->mapFromGlobal(pos), pos); +} + +QPixmap QEglFSScreen::grabWindow(WId wid, int x, int y, int width, int height) const +{ + QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); + const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); + Q_ASSERT(!windows.isEmpty()); + + QImage img; + + if (static_cast<QEglFSWindow *>(windows.first()->sourceWindow()->handle())->isRaster()) { + // Request the compositor to render everything into an FBO and read it back. This + // is of course slow, but it's safe and reliable. It will not include the mouse + // cursor, which is a plus. + img = compositor->grab(); + } else { + // Just a single OpenGL window without compositing. Do not support this case for now. Doing + // glReadPixels is not an option since it would read from the back buffer which may have + // undefined content when calling right after a swapBuffers (unless preserved swap is + // available and enabled, but we have no support for that). + qWarning("grabWindow: Not supported for non-composited OpenGL content. Use QQuickWindow::grabWindow() instead."); + return QPixmap(); + } + + if (!wid) { + const QSize screenSize = geometry().size(); + if (width < 0) + width = screenSize.width() - x; + if (height < 0) + height = screenSize.height() - y; + return QPixmap::fromImage(img).copy(x, y, width, height); + } + + foreach (QOpenGLCompositorWindow *w, windows) { + const QWindow *window = w->sourceWindow(); + if (window->winId() == wid) { + const QRect geom = window->geometry(); + if (width < 0) + width = geom.width() - x; + if (height < 0) + height = geom.height() - y; + QRect rect(geom.topLeft() + QPoint(x, y), QSize(width, height)); + rect &= window->geometry(); + return QPixmap::fromImage(img).copy(rect); + } + } + + return QPixmap(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index 07b6ff63ef..dc291285ad 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -35,7 +35,6 @@ #define QEGLFSSCREEN_H #include "qeglfsglobal.h" -#include <QtPlatformSupport/private/qeglplatformscreen_p.h> #include <EGL/egl.h> QT_BEGIN_NAMESPACE @@ -43,7 +42,7 @@ QT_BEGIN_NAMESPACE class QEglFSWindow; class QOpenGLContext; -class Q_EGLFS_EXPORT QEglFSScreen : public QEGLPlatformScreen +class Q_EGLFS_EXPORT QEglFSScreen : public QPlatformScreen { public: QEglFSScreen(EGLDisplay display); @@ -62,16 +61,23 @@ public: qreal refreshRate() const Q_DECL_OVERRIDE; + QPixmap grabWindow(WId wid, int x, int y, int width, int height) const Q_DECL_OVERRIDE; + EGLSurface primarySurface() const { return m_surface; } -protected: - void setPrimarySurface(EGLSurface surface); + EGLDisplay display() const { return m_dpy; } + + void handleCursorMove(const QPoint &pos); private: - friend class QEglFSWindow; + void setPrimarySurface(EGLSurface surface); + EGLDisplay m_dpy; + QWindow *m_pointerWindow; EGLSurface m_surface; QPlatformCursor *m_cursor; + + friend class QEglFSWindow; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index c0d51c94a5..c3b9dd6ef0 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -37,21 +37,23 @@ #include <private/qguiapplication_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtGui/QOpenGLContext> -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qeglconvenience_p.h> +#include <QtPlatformSupport/private/qopenglcompositorbackingstore_p.h> #include "qeglfswindow.h" +#include "qeglfscursor.h" #include "qeglfshooks.h" -#include <QtDebug> - QT_BEGIN_NAMESPACE QEglFSWindow::QEglFSWindow(QWindow *w) - : QEGLPlatformWindow(w) - , m_surface(0) - , m_window(0) - , m_flags(0) + : QPlatformWindow(w), + m_backingStore(0), + m_raster(false), + m_winId(0), + m_surface(0), + m_window(0), + m_flags(0) { } @@ -60,12 +62,34 @@ QEglFSWindow::~QEglFSWindow() destroy(); } +static WId newWId() +{ + static WId id = 0; + + if (id == std::numeric_limits<WId>::max()) + qWarning("QEGLPlatformWindow: Out of window IDs"); + + return ++id; +} + void QEglFSWindow::create() { if (m_flags.testFlag(Created)) return; - QEGLPlatformWindow::create(); + m_winId = newWId(); + + // Save the original surface type before changing to OpenGLSurface. + m_raster = (window()->surfaceType() == QSurface::RasterSurface); + if (m_raster) // change to OpenGL, but not for RasterGLSurface + window()->setSurfaceType(QSurface::OpenGLSurface); + + if (window()->type() == Qt::Desktop) { + QRect fullscreenRect(QPoint(), screen()->availableGeometry().size()); + QPlatformWindow::setGeometry(fullscreenRect); + QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect); + return; + } m_flags = Created; @@ -120,7 +144,7 @@ void QEglFSWindow::destroy() { QEglFSScreen *screen = this->screen(); if (m_flags.testFlag(HasNativeWindow)) { - QEGLPlatformCursor *cursor = qobject_cast<QEGLPlatformCursor *>(screen->cursor()); + QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor()); if (cursor) cursor->resetResources(); @@ -265,4 +289,41 @@ QEglFSScreen *QEglFSWindow::screen() const return static_cast<QEglFSScreen *>(QPlatformWindow::screen()); } +bool QEglFSWindow::isRaster() const +{ + return m_raster || window()->surfaceType() == QSurface::RasterGLSurface; +} + +QWindow *QEglFSWindow::sourceWindow() const +{ + return window(); +} + +const QPlatformTextureList *QEglFSWindow::textures() const +{ + if (m_backingStore) + return m_backingStore->textures(); + + return 0; +} + +void QEglFSWindow::endCompositing() +{ + if (m_backingStore) + m_backingStore->notifyComposited(); +} + +WId QEglFSWindow::winId() const +{ + return m_winId; +} + +void QEglFSWindow::setOpacity(qreal) +{ + if (!isRaster()) + qWarning("QEglFSWindow: Cannot set opacity for non-raster windows"); + + // Nothing to do here. The opacity is stored in the QWindow. +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfswindow.h b/src/plugins/platforms/eglfs/qeglfswindow.h index f9d207c153..53b9e18dc1 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.h +++ b/src/plugins/platforms/eglfs/qeglfswindow.h @@ -37,17 +37,23 @@ #include "qeglfsintegration.h" #include "qeglfsscreen.h" #include "qeglfsglobal.h" -#include <QtPlatformSupport/private/qeglplatformwindow_p.h> + +#include <qpa/qplatformwindow.h> +#include <QtPlatformSupport/private/qopenglcompositor_p.h> +#include <EGL/egl.h> QT_BEGIN_NAMESPACE -class Q_EGLFS_EXPORT QEglFSWindow : public QEGLPlatformWindow +class QOpenGLCompositorBackingStore; +class QPlatformTextureList; + +class Q_EGLFS_EXPORT QEglFSWindow : public QPlatformWindow, public QOpenGLCompositorWindow { public: QEglFSWindow(QWindow *w); ~QEglFSWindow(); - void create() Q_DECL_OVERRIDE; + void create(); void destroy(); void setGeometry(const QRect &) Q_DECL_OVERRIDE; @@ -58,13 +64,15 @@ public: void lower() Q_DECL_OVERRIDE; void propagateSizeHints() Q_DECL_OVERRIDE { } - void setOpacity(qreal) Q_DECL_OVERRIDE { } void setMask(const QRegion &) Q_DECL_OVERRIDE { } bool setKeyboardGrabEnabled(bool) Q_DECL_OVERRIDE { return false; } bool setMouseGrabEnabled(bool) Q_DECL_OVERRIDE { return false; } + void setOpacity(qreal) Q_DECL_OVERRIDE; + WId winId() const Q_DECL_OVERRIDE; QSurfaceFormat format() const Q_DECL_OVERRIDE; - EGLNativeWindowType eglWindow() const Q_DECL_OVERRIDE; + + EGLNativeWindowType eglWindow() const; EGLSurface surface() const; QEglFSScreen *screen() const; @@ -73,11 +81,22 @@ public: virtual void invalidateSurface() Q_DECL_OVERRIDE; virtual void resetSurface(); -protected: + QOpenGLCompositorBackingStore *backingStore() { return m_backingStore; } + void setBackingStore(QOpenGLCompositorBackingStore *backingStore) { m_backingStore = backingStore; } + bool isRaster() const; + + QWindow *sourceWindow() const Q_DECL_OVERRIDE; + const QPlatformTextureList *textures() const Q_DECL_OVERRIDE; + void endCompositing() Q_DECL_OVERRIDE; + +private: + QOpenGLCompositorBackingStore *m_backingStore; + bool m_raster; + WId m_winId; + EGLSurface m_surface; EGLNativeWindowType m_window; -private: EGLConfig m_config; QSurfaceFormat m_format; |