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