eglfs: Split kms integration into multiple files
Now that it's a plugin, the restrictions for the compiled-in hooks do not apply. Make it maintainable and prepare for future development by splitting up properly. The actual functions are not touched at all in this patch. Change-Id: I4c666817afe15b31a63c1f9fc01413e9a2376a3b Reviewed-by: Louai Al-Khanji <louai.al-khanji@theqtcompany.com>bb10
parent
43807479bc
commit
f342bf2533
|
|
@ -19,8 +19,14 @@ CONFIG += egl
|
|||
QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF
|
||||
|
||||
SOURCES += $$PWD/qeglfskmsmain.cpp \
|
||||
$$PWD/qeglfskmsintegration.cpp
|
||||
$$PWD/qeglfskmsintegration.cpp \
|
||||
$$PWD/qeglfskmsdevice.cpp \
|
||||
$$PWD/qeglfskmsscreen.cpp \
|
||||
$$PWD/qeglfskmscursor.cpp
|
||||
|
||||
HEADERS += $$PWD/qeglfskmsintegration.h
|
||||
HEADERS += $$PWD/qeglfskmsintegration.h \
|
||||
$$PWD/qeglfskmsdevice.h \
|
||||
$$PWD/qeglfskmsscreen.h \
|
||||
$$PWD/qeglfskmscursor.h
|
||||
|
||||
OTHER_FILES += $$PWD/eglfs_kms.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,218 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 "qeglfskmscursor.h"
|
||||
#include "qeglfskmsscreen.h"
|
||||
#include "qeglfskmsdevice.h"
|
||||
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#ifndef DRM_CAP_CURSOR_WIDTH
|
||||
#define DRM_CAP_CURSOR_WIDTH 0x8
|
||||
#endif
|
||||
|
||||
#ifndef DRM_CAP_CURSOR_HEIGHT
|
||||
#define DRM_CAP_CURSOR_HEIGHT 0x9
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_CURSOR
|
||||
QCursor cursor(Qt::ArrowCursor);
|
||||
changeCursor(&cursor, 0);
|
||||
#endif
|
||||
setPos(QPoint(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;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 QEGLFSKMSCURSOR_H
|
||||
#define QEGLFSKMSCURSOR_H
|
||||
|
||||
#include <qpa/qplatformcursor.h>
|
||||
#include <QtCore/QList>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <gbm.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QEglFSKmsScreen;
|
||||
|
||||
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<QPoint> hotSpots;
|
||||
QImage image;
|
||||
} m_cursorAtlas;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 "qeglfskmsdevice.h"
|
||||
#include "qeglfskmsscreen.h"
|
||||
|
||||
#include "qeglfsintegration.h"
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/private/qcore_unix_p.h>
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
|
||||
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
|
||||
|
||||
enum OutputConfiguration {
|
||||
OutputConfigOff,
|
||||
OutputConfigPreferred,
|
||||
OutputConfigCurrent,
|
||||
OutputConfigMode,
|
||||
OutputConfigModeline
|
||||
};
|
||||
|
||||
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<drmModeModeInfo> 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<QEglFSKmsScreen *>(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<QEglFSIntegration *>(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
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 QEGLFSKMSDEVICE_H
|
||||
#define QEGLFSKMSDEVICE_H
|
||||
|
||||
#include "qeglfskmsintegration.h"
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QEglFSKmsScreen;
|
||||
|
||||
class QEglFSKmsDevice
|
||||
{
|
||||
public:
|
||||
QEglFSKmsDevice(QEglFSKmsIntegration *integration, const QString &path);
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
void createScreens();
|
||||
|
||||
gbm_device *device() const;
|
||||
int fd() const;
|
||||
|
||||
void handleDrmEvent();
|
||||
|
||||
private:
|
||||
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);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
|
@ -40,190 +40,30 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "qeglfskmsintegration.h"
|
||||
#include "qeglfsintegration.h"
|
||||
#include "qeglfsscreen.h"
|
||||
#include "qeglfskmsdevice.h"
|
||||
#include "qeglfskmsscreen.h"
|
||||
#include "qeglfskmscursor.h"
|
||||
|
||||
#include <QtPlatformSupport/private/qeglplatformcursor_p.h>
|
||||
#include <QtPlatformSupport/private/qdevicediscovery_p.h>
|
||||
#include <QtCore/private/qcore_unix_p.h>
|
||||
#include <QtCore/QScopedPointer>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtGui/qpa/qplatformwindow.h>
|
||||
#include <QtGui/qpa/qplatformcursor.h>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
#include <QtGui/QScreen>
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
|
||||
#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<drmModeModeInfo> 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<QEglFSKmsCursor> 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<QPoint> hotSpots;
|
||||
QImage image;
|
||||
} m_cursorAtlas;
|
||||
};
|
||||
|
||||
QEglFSKmsIntegration::QEglFSKmsIntegration()
|
||||
: m_device(Q_NULLPTR)
|
||||
, m_hwCursor(true)
|
||||
|
|
@ -425,735 +265,4 @@ void QEglFSKmsIntegration::loadConfig()
|
|||
<< "\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();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_CURSOR
|
||||
QCursor cursor(Qt::ArrowCursor);
|
||||
changeCursor(&cursor, 0);
|
||||
#endif
|
||||
setPos(QPoint(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<FrameBuffer *>(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<FrameBuffer *>(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<FrameBuffer> 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<EGLNativeDisplayType>(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<QEglFSKmsScreen *>(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<drmModeModeInfo> 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<QEglFSKmsScreen *>(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<QEglFSIntegration *>(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"
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@
|
|||
#define QEGLFSKMSINTEGRATION_H
|
||||
|
||||
#include "qeglfsdeviceintegration.h"
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,286 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 "qeglfskmsscreen.h"
|
||||
#include "qeglfskmsdevice.h"
|
||||
#include "qeglfskmscursor.h"
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
|
||||
|
||||
void QEglFSKmsScreen::bufferDestroyedHandler(gbm_bo *bo, void *data)
|
||||
{
|
||||
FrameBuffer *fb = static_cast<FrameBuffer *>(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<FrameBuffer *>(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<FrameBuffer> 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<EGLNativeDisplayType>(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<QEglFSKmsScreen *>(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;
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -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 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 QEGLFSKMSSCREEN_H
|
||||
#define QEGLFSKMSSCREEN_H
|
||||
|
||||
#include "qeglfskmsintegration.h"
|
||||
#include "qeglfsscreen.h"
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QEglFSKmsDevice;
|
||||
class QEglFSKmsCursor;
|
||||
|
||||
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<drmModeModeInfo> modes;
|
||||
};
|
||||
|
||||
class QEglFSKmsScreen : public QEglFSScreen
|
||||
{
|
||||
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();
|
||||
|
||||
private:
|
||||
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<QEglFSKmsCursor> 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;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue