/**************************************************************************** ** ** Copyright (C) 2015 Pier Luigi Fiorini ** 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 "qeglfskmsscreen.h" #include "qeglfskmsdevice.h" #include "qeglfskmscursor.h" #include #include #include #include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) class QEglFSKmsInterruptHandler : public QObject { public: QEglFSKmsInterruptHandler(QEglFSKmsScreen *screen) : m_screen(screen) { m_vtHandler = static_cast(QGuiApplicationPrivate::platformIntegration())->vtHandler(); connect(m_vtHandler, &QFbVtHandler::interrupted, this, &QEglFSKmsInterruptHandler::restoreVideoMode); connect(m_vtHandler, &QFbVtHandler::suspendRequested, this, &QEglFSKmsInterruptHandler::handleSuspendRequest); } public slots: void restoreVideoMode() { m_screen->restoreMode(); } void handleSuspendRequest() { m_screen->restoreMode(); m_vtHandler->suspend(); } private: QFbVtHandler *m_vtHandler; QEglFSKmsScreen *m_screen; }; void QEglFSKmsScreen::bufferDestroyedHandler(gbm_bo *bo, void *data) { FrameBuffer *fb = static_cast(data); if (fb->fb) { gbm_device *device = gbm_bo_get_device(bo); drmModeRmFB(gbm_device_get_fd(device), fb->fb); } delete fb; } QEglFSKmsScreen::FrameBuffer *QEglFSKmsScreen::framebufferForBufferObject(gbm_bo *bo) { { FrameBuffer *fb = static_cast(gbm_bo_get_user_data(bo)); if (fb) return fb; } uint32_t width = gbm_bo_get_width(bo); uint32_t height = gbm_bo_get_height(bo); uint32_t stride = gbm_bo_get_stride(bo); uint32_t handle = gbm_bo_get_handle(bo).u32; QScopedPointer fb(new FrameBuffer); int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, stride, handle, &fb->fb); if (ret) { qWarning("Failed to create KMS FB!"); return Q_NULLPTR; } gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler); return fb.take(); } QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) : QEglFSScreen(eglGetDisplay(reinterpret_cast(device->device()))) , m_integration(integration) , m_device(device) , m_gbm_surface(Q_NULLPTR) , m_gbm_bo_current(Q_NULLPTR) , m_gbm_bo_next(Q_NULLPTR) , m_output(output) , m_pos(position) , m_cursor(Q_NULLPTR) , m_interruptHandler(new QEglFSKmsInterruptHandler(this)) { m_siblings << this; } QEglFSKmsScreen::~QEglFSKmsScreen() { restoreMode(); if (m_output.saved_crtc) { drmModeFreeCrtc(m_output.saved_crtc); m_output.saved_crtc = Q_NULLPTR; } delete m_interruptHandler; } 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_integration->separateScreens()) return m_device->globalCursor(); if (m_cursor.isNull()) { QEglFSKmsScreen *that = const_cast(this); that->m_cursor.reset(new QEglFSKmsCursor(that)); } return m_cursor.data(); } else { return QEglFSScreen::cursor(); } } gbm_surface *QEglFSKmsScreen::createSurface() { if (!m_gbm_surface) { qCDebug(qLcEglfsKmsDebug) << "Creating window for screen" << name(); m_gbm_surface = gbm_surface_create(m_device->device(), geometry().width(), geometry().height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); } return m_gbm_surface; } void QEglFSKmsScreen::destroySurface() { if (m_gbm_bo_current) { gbm_bo_destroy(m_gbm_bo_current); m_gbm_bo_current = Q_NULLPTR; } if (m_gbm_bo_next) { gbm_bo_destroy(m_gbm_bo_next); m_gbm_bo_next = Q_NULLPTR; } if (m_gbm_surface) { gbm_surface_destroy(m_gbm_surface); m_gbm_surface = Q_NULLPTR; } } void QEglFSKmsScreen::waitForFlip() { // Don't lock the mutex unless we actually need to if (!m_gbm_bo_next) return; QMutexLocker lock(&m_waitForFlipMutex); while (m_gbm_bo_next) m_device->handleDrmEvent(); } void QEglFSKmsScreen::flip() { if (!m_gbm_surface) { qWarning("Cannot sync before platform init!"); return; } m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); if (!m_gbm_bo_next) { qWarning("Could not lock GBM surface front buffer!"); return; } FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); if (!m_output.mode_set) { int ret = drmModeSetCrtc(m_device->fd(), m_output.crtc_id, fb->fb, 0, 0, &m_output.connector_id, 1, &m_output.modes[m_output.mode]); if (ret) qErrnoWarning("Could not set DRM mode!"); else m_output.mode_set = true; } int ret = drmModePageFlip(m_device->fd(), m_output.crtc_id, fb->fb, DRM_MODE_PAGE_FLIP_EVENT, this); if (ret) { qErrnoWarning("Could not queue DRM page flip!"); gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); m_gbm_bo_next = Q_NULLPTR; } } void QEglFSKmsScreen::flipFinished() { if (m_gbm_bo_current) gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_current); m_gbm_bo_current = m_gbm_bo_next; m_gbm_bo_next = Q_NULLPTR; } void QEglFSKmsScreen::restoreMode() { if (m_output.mode_set && m_output.saved_crtc) { drmModeSetCrtc(m_device->fd(), m_output.saved_crtc->crtc_id, m_output.saved_crtc->buffer_id, 0, 0, &m_output.connector_id, 1, &m_output.saved_crtc->mode); m_output.mode_set = false; } } qreal QEglFSKmsScreen::refreshRate() const { quint32 refresh = m_output.modes[m_output.mode].vrefresh; return refresh > 0 ? refresh : 60; } QT_END_NAMESPACE