/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qkmsscreen.h" #include "qkmscursor.h" #include "qkmsdevice.h" #include "qkmscontext.h" #include #include #include QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.kms.screen") //Fallback mode (taken from Wayland DRM demo compositor) static drmModeModeInfo builtin_1024x768 = { 63500, //clock 1024, 1072, 1176, 1328, 0, 768, 771, 775, 798, 0, 59920, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, 0, "1024x768" }; QKmsScreen::QKmsScreen(QKmsDevice *device, const drmModeRes *resources, const drmModeConnector *connector) : m_device(device), m_current_bo(0), m_next_bo(0), m_connectorId(connector->connector_id), m_depth(32), m_format(QImage::Format_Invalid), m_eglWindowSurface(EGL_NO_SURFACE), m_modeSet(false) { m_cursor = new QKmsCursor(this); initializeScreenMode(resources, connector); } QKmsScreen::~QKmsScreen() { delete m_cursor; drmModeSetCrtc(m_device->fd(), m_oldCrtc->crtc_id, m_oldCrtc->buffer_id, m_oldCrtc->x, m_oldCrtc->y, &m_connectorId, 1, &m_oldCrtc->mode); drmModeFreeCrtc(m_oldCrtc); if (m_eglWindowSurface != EGL_NO_SURFACE) eglDestroySurface(m_device->eglDisplay(), m_eglWindowSurface); gbm_surface_destroy(m_gbmSurface); } QRect QKmsScreen::geometry() const { return m_geometry; } int QKmsScreen::depth() const { return m_depth; } QImage::Format QKmsScreen::format() const { return m_format; } QSizeF QKmsScreen::physicalSize() const { return m_physicalSize; } QPlatformCursor *QKmsScreen::cursor() const { return m_cursor; } void QKmsScreen::initializeScreenMode(const drmModeRes *resources, const drmModeConnector *connector) { //Determine optimal mode for screen drmModeModeInfo *mode = 0; for (int i = 0; i < connector->count_modes; ++i) { if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) { mode = &connector->modes[i]; break; } } if (!mode) { if (connector->count_modes > 0) mode = &connector->modes[0]; else mode = &builtin_1024x768; } drmModeEncoder *encoder = drmModeGetEncoder(m_device->fd(), connector->encoders[0]); if (encoder == 0) qFatal("No encoder for connector."); int i; for (i = 0; i < resources->count_crtcs; i++) { if (encoder->possible_crtcs & (1 << i)) break; } if (i == resources->count_crtcs) qFatal("No usable crtc for encoder."); m_oldCrtc = drmModeGetCrtc(m_device->fd(), encoder->crtc_id); m_crtcId = resources->crtcs[i]; m_mode = *mode; m_geometry = QRect(0, 0, m_mode.hdisplay, m_mode.vdisplay); qCDebug(lcQpaScreen) << "kms initialized with geometry" << m_geometry; m_depth = 32; m_format = QImage::Format_RGB32; m_physicalSize = QSizeF(connector->mmWidth, connector->mmHeight); m_gbmSurface = gbm_surface_create(m_device->gbmDevice(), m_mode.hdisplay, m_mode.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); qCDebug(lcQpaScreen) << "created gbm surface" << m_gbmSurface << m_mode.hdisplay << m_mode.vdisplay; //Cleanup drmModeFreeEncoder(encoder); } QSurfaceFormat QKmsScreen::tweakFormat(const QSurfaceFormat &format) { QSurfaceFormat fmt = format; fmt.setRedBufferSize(8); fmt.setGreenBufferSize(8); fmt.setBlueBufferSize(8); if (fmt.alphaBufferSize() != -1) fmt.setAlphaBufferSize(8); return fmt; } void QKmsScreen::initializeWithFormat(const QSurfaceFormat &format) { EGLDisplay display = m_device->eglDisplay(); EGLConfig config = q_configFromGLFormat(display, tweakFormat(format)); m_eglWindowSurface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)m_gbmSurface, NULL); qCDebug(lcQpaScreen) << "created window surface"; m_surfaceFormat = q_glFormatFromConfig(display, config); } void QKmsScreen::swapBuffers() { eglSwapBuffers(m_device->eglDisplay(), m_eglWindowSurface); m_next_bo = gbm_surface_lock_front_buffer(m_gbmSurface); if (!m_next_bo) qFatal("kms: Failed to lock front buffer"); performPageFlip(); } void QKmsScreen::performPageFlip() { if (!m_next_bo) return; uint32_t width = gbm_bo_get_width(m_next_bo); uint32_t height = gbm_bo_get_height(m_next_bo); uint32_t stride = gbm_bo_get_stride(m_next_bo); uint32_t handle = gbm_bo_get_handle(m_next_bo).u32; uint32_t fb_id; int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, stride, handle, &fb_id); if (ret) { qFatal("kms: Failed to create fb: fd %d, w %d, h %d, stride %d, handle %d, ret %d", m_device->fd(), width, height, stride, handle, ret); } if (!m_modeSet) { //Set the Mode of the screen. int ret = drmModeSetCrtc(m_device->fd(), m_crtcId, fb_id, 0, 0, &m_connectorId, 1, &m_mode); if (ret) qFatal("failed to set mode"); m_modeSet = true; // Initialize cursor static int hideCursor = qEnvironmentVariableIntValue("QT_QPA_KMS_HIDECURSOR"); if (!hideCursor) { QCursor cursor(Qt::ArrowCursor); m_cursor->changeCursor(&cursor, 0); } } int pageFlipStatus = drmModePageFlip(m_device->fd(), m_crtcId, fb_id, DRM_MODE_PAGE_FLIP_EVENT, this); if (pageFlipStatus) { qWarning("Pageflip status: %d", pageFlipStatus); gbm_surface_release_buffer(m_gbmSurface, m_next_bo); m_next_bo = 0; } } void QKmsScreen::handlePageFlipped() { if (m_current_bo) gbm_surface_release_buffer(m_gbmSurface, m_current_bo); m_current_bo = m_next_bo; m_next_bo = 0; } QKmsDevice * QKmsScreen::device() const { return m_device; } void QKmsScreen::waitForPageFlipComplete() { while (m_next_bo) { #if 0 //Check manually if there is something to be read on the device //as there are senarios where the signal is not received (starvation) fd_set fdSet; timeval timeValue; int returnValue; FD_ZERO(&fdSet); FD_SET(m_device->fd(), &fdSet); timeValue.tv_sec = 0; timeValue.tv_usec = 1000; returnValue = select(1, &fdSet, 0, 0, &timeValue); printf("select returns %d\n", returnValue); #endif m_device->handlePageFlipCompleted(); } } QT_END_NAMESPACE