1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
4 ** Copyright (C) 2017 The Qt Company Ltd.
5 ** Copyright (C) 2016 Pelagicore AG
6 ** Contact: https://www.qt.io/licensing/
7 **
8 ** This file is part of the plugins of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Commercial License Usage
12 ** Licensees holding valid commercial Qt licenses may use this file in
13 ** accordance with the commercial license agreement provided with the
14 ** Software or, alternatively, in accordance with the terms contained in
15 ** a written agreement between you and The Qt Company. For licensing terms
16 ** and conditions see https://www.qt.io/terms-conditions. For further
17 ** information use the contact form at https://www.qt.io/contact-us.
18 **
19 ** GNU Lesser General Public License Usage
20 ** Alternatively, this file may be used under the terms of the GNU Lesser
21 ** General Public License version 3 as published by the Free Software
22 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
23 ** packaging of this file. Please review the following information to
24 ** ensure the GNU Lesser General Public License version 3 requirements
25 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26 **
27 ** GNU General Public License Usage
28 ** Alternatively, this file may be used under the terms of the GNU
29 ** General Public License version 2.0 or (at your option) the GNU General
30 ** Public license version 3 or any later version approved by the KDE Free
31 ** Qt Foundation. The licenses are as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33 ** included in the packaging of this file. Please review the following
34 ** information to ensure the GNU General Public License requirements will
35 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36 ** https://www.gnu.org/licenses/gpl-3.0.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qeglfskmsvsp2integration.h"
43 #include "qeglfskmsvsp2device.h"
44 #include "qeglfskmsvsp2screen.h"
45 #include "private/qeglfswindow_p.h"
46 
47 #include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
48 #include <QtEglSupport/private/qeglconvenience_p.h>
49 #include <QtCore/QLoggingCategory>
50 #include <QtCore/QJsonDocument>
51 #include <QtCore/QJsonObject>
52 #include <QtCore/QJsonArray>
53 #include <QtGui/qpa/qplatformwindow.h>
54 #include <QtGui/QScreen>
55 #include <QtPlatformHeaders/qeglfsfunctions.h>
56 
57 #include <xf86drm.h>
58 #include <xf86drmMode.h>
59 #include <gbm.h>
60 
61 QT_BEGIN_NAMESPACE
62 
QEglFSKmsVsp2Integration()63 QEglFSKmsVsp2Integration::QEglFSKmsVsp2Integration()
64 {
65     qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via Vsp2 integration created");
66 }
67 
68 #ifndef EGL_EXT_platform_base
69 typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
70 typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
71 #endif
72 
73 #ifndef EGL_PLATFORM_GBM_KHR
74 #define EGL_PLATFORM_GBM_KHR 0x31D7
75 #endif
76 
createDisplay(EGLNativeDisplayType nativeDisplay)77 EGLDisplay QEglFSKmsVsp2Integration::createDisplay(EGLNativeDisplayType nativeDisplay)
78 {
79     qCDebug(qLcEglfsKmsDebug, "Querying EGLDisplay");
80     EGLDisplay display;
81 
82     PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = nullptr;
83     const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
84     if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
85         getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
86             eglGetProcAddress("eglGetPlatformDisplayEXT"));
87     }
88 
89     if (getPlatformDisplay) {
90         display = getPlatformDisplay(EGL_PLATFORM_GBM_KHR, nativeDisplay, nullptr);
91     } else {
92         qCDebug(qLcEglfsKmsDebug, "No eglGetPlatformDisplay for GBM, falling back to eglGetDisplay");
93         display = eglGetDisplay(nativeDisplay);
94     }
95 
96     return display;
97 }
98 
createNativeOffscreenWindow(const QSurfaceFormat & format)99 EGLNativeWindowType QEglFSKmsVsp2Integration::createNativeOffscreenWindow(const QSurfaceFormat &format)
100 {
101     Q_UNUSED(format);
102     Q_ASSERT(device());
103 
104     gbm_surface *surface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
105                                               1, 1,
106                                               GBM_FORMAT_XRGB8888,
107                                               GBM_BO_USE_RENDERING);
108 
109     return reinterpret_cast<EGLNativeWindowType>(surface);
110 }
111 
destroyNativeWindow(EGLNativeWindowType window)112 void QEglFSKmsVsp2Integration::destroyNativeWindow(EGLNativeWindowType window)
113 {
114     gbm_surface *surface = reinterpret_cast<gbm_surface *>(window);
115     //TODO call screen destroysurface instead
116     gbm_surface_destroy(surface);
117 }
118 
presentBuffer(QPlatformSurface * surface)119 void QEglFSKmsVsp2Integration::presentBuffer(QPlatformSurface *surface)
120 {
121     QWindow *window = static_cast<QWindow *>(surface->surface());
122     auto *screen = static_cast<QEglFSKmsVsp2Screen *>(window->screen()->handle());
123     screen->flip();
124 }
125 
platformFunction(const QByteArray & function) const126 QFunctionPointer QEglFSKmsVsp2Integration::platformFunction(const QByteArray &function) const
127 {
128     if (function == QEglFSFunctions::vsp2AddLayerTypeIdentifier())
129         return QFunctionPointer(addLayerStatic);
130     if (function == QEglFSFunctions::vsp2RemoveLayerTypeIdentifier())
131         return QFunctionPointer(removeLayerStatic);
132     if (function == QEglFSFunctions::vsp2SetLayerBufferTypeIdentifier())
133         return QFunctionPointer(setLayerBufferStatic);
134     if (function == QEglFSFunctions::vsp2SetLayerPositionTypeIdentifier())
135         return QFunctionPointer(setLayerPositionStatic);
136     if (function == QEglFSFunctions::vsp2SetLayerAlphaTypeIdentifier())
137         return QFunctionPointer(setLayerAlphaStatic);
138     if (function == QEglFSFunctions::vsp2AddBlendListenerTypeIdentifier())
139         return QFunctionPointer(addBlendListenerStatic);
140 
141     return nullptr;
142 }
143 
createDevice()144 QKmsDevice *QEglFSKmsVsp2Integration::createDevice()
145 {
146     QString path = screenConfig()->devicePath();
147     if (!path.isEmpty()) {
148         qCDebug(qLcEglfsKmsDebug) << "VSP2: Using DRM device" << path << "specified in config file";
149     } else {
150         QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask);
151         const QStringList devices = d->scanConnectedDevices();
152         qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices;
153         d->deleteLater();
154 
155         if (Q_UNLIKELY(devices.isEmpty()))
156             qFatal("Could not find DRM device!");
157 
158         path = devices.first();
159         qCDebug(qLcEglfsKmsDebug) << "Using" << path;
160     }
161 
162     return new QEglFSKmsVsp2Device(screenConfig(), path);
163 }
164 
addLayerStatic(const QScreen * screen,int dmabufFd,const QSize & size,const QPoint & position,uint pixelFormat,uint bytesPerLine)165 int QEglFSKmsVsp2Integration::addLayerStatic(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint pixelFormat, uint bytesPerLine)
166 {
167     auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
168     return vsp2Screen->addLayer(dmabufFd, size, position, pixelFormat, bytesPerLine);
169 }
170 
removeLayerStatic(const QScreen * screen,int id)171 bool QEglFSKmsVsp2Integration::removeLayerStatic(const QScreen *screen, int id)
172 {
173     auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
174     return vsp2Screen->removeLayer(id);
175 }
176 
setLayerBufferStatic(const QScreen * screen,int id,int dmabufFd)177 void QEglFSKmsVsp2Integration::setLayerBufferStatic(const QScreen *screen, int id, int dmabufFd)
178 {
179     auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
180     vsp2Screen->setLayerBuffer(id, dmabufFd);
181 }
182 
setLayerPositionStatic(const QScreen * screen,int id,const QPoint & position)183 void QEglFSKmsVsp2Integration::setLayerPositionStatic(const QScreen *screen, int id, const QPoint &position)
184 {
185     auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
186     vsp2Screen->setLayerPosition(id, position);
187 }
188 
setLayerAlphaStatic(const QScreen * screen,int id,qreal alpha)189 void QEglFSKmsVsp2Integration::setLayerAlphaStatic(const QScreen *screen, int id, qreal alpha)
190 {
191     auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
192     vsp2Screen->setLayerAlpha(id, alpha);
193 }
194 
addBlendListenerStatic(const QScreen * screen,void (* callback)())195 void QEglFSKmsVsp2Integration::addBlendListenerStatic(const QScreen *screen, void(*callback)())
196 {
197     auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
198     vsp2Screen->addBlendListener(callback);
199 }
200 
201 class QEglFSKmsVsp2Window : public QEglFSWindow
202 {
203 public:
QEglFSKmsVsp2Window(QWindow * w,const QEglFSKmsVsp2Integration * integration)204     QEglFSKmsVsp2Window(QWindow *w, const QEglFSKmsVsp2Integration *integration)
205         : QEglFSWindow(w)
206         , m_integration(integration)
207     {}
208 
~QEglFSKmsVsp2Window()209     ~QEglFSKmsVsp2Window() { destroy(); }
210 
211     void resetSurface() override;
212     void invalidateSurface() override;
213     const QEglFSKmsVsp2Integration *m_integration;
214 };
215 
resetSurface()216 void QEglFSKmsVsp2Window::resetSurface()
217 {
218     auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen());
219     EGLDisplay display = vsp2Screen->display();
220     QSurfaceFormat platformFormat = m_integration->surfaceFormatFor(window()->requestedFormat());
221     m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
222     m_format = q_glFormatFromConfig(display, m_config, platformFormat);
223     // One fullscreen window per screen -> the native window is simply the gbm_surface the screen created.
224     m_window = reinterpret_cast<EGLNativeWindowType>(vsp2Screen->createSurface());
225 
226     PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurface = nullptr;
227     const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
228     if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
229         createPlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
230             eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
231     }
232 
233     if (createPlatformWindowSurface) {
234         m_surface = createPlatformWindowSurface(display, m_config, reinterpret_cast<void *>(m_window), nullptr);
235     } else {
236         qCDebug(qLcEglfsKmsDebug, "No eglCreatePlatformWindowSurface for GBM, falling back to eglCreateWindowSurface");
237         m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
238     }
239 }
240 
invalidateSurface()241 void QEglFSKmsVsp2Window::invalidateSurface()
242 {
243     auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen());
244     QEglFSWindow::invalidateSurface();
245     vsp2Screen->resetSurface();
246 }
247 
createWindow(QWindow * window) const248 QEglFSWindow *QEglFSKmsVsp2Integration::createWindow(QWindow *window) const
249 {
250     return new QEglFSKmsVsp2Window(window, this);
251 }
252 
253 QT_END_NAMESPACE
254