1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the plugins of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qeglfsdeviceintegration_p.h" |
41 | #include "qeglfsintegration_p.h" |
42 | #ifndef QT_NO_OPENGL |
43 | # include "qeglfscursor_p.h" |
44 | #endif |
45 | #include "qeglfswindow_p.h" |
46 | #include "qeglfsscreen_p.h" |
47 | #include "qeglfshooks_p.h" |
48 | |
49 | #include <QtGui/private/qeglconvenience_p.h> |
50 | #include <QGuiApplication> |
51 | #include <private/qguiapplication_p.h> |
52 | #include <QScreen> |
53 | #include <QDir> |
54 | #if QT_CONFIG(regularexpression) |
55 | # include <QFileInfo> |
56 | # include <QRegularExpression> |
57 | #endif |
58 | #include <QLoggingCategory> |
59 | |
60 | #if defined(Q_OS_LINUX) |
61 | #include <fcntl.h> |
62 | #include <unistd.h> |
63 | #include <linux/fb.h> |
64 | #include <sys/ioctl.h> |
65 | #endif |
66 | |
67 | #include <private/qfactoryloader_p.h> |
68 | #include <private/qcore_unix_p.h> |
69 | |
70 | QT_BEGIN_NAMESPACE |
71 | |
72 | Q_LOGGING_CATEGORY(qLcEglDevDebug, "qt.qpa.egldeviceintegration" ) |
73 | |
74 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, |
75 | (QEglFSDeviceIntegrationFactoryInterface_iid, QLatin1String("/egldeviceintegrations" ), Qt::CaseInsensitive)) |
76 | |
77 | #if QT_CONFIG(library) |
78 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, |
79 | (QEglFSDeviceIntegrationFactoryInterface_iid, QLatin1String("" ), Qt::CaseInsensitive)) |
80 | |
81 | #endif // QT_CONFIG(library) |
82 | |
83 | QStringList QEglFSDeviceIntegrationFactory::keys(const QString &pluginPath) |
84 | { |
85 | QStringList list; |
86 | #if QT_CONFIG(library) |
87 | if (!pluginPath.isEmpty()) { |
88 | QCoreApplication::addLibraryPath(pluginPath); |
89 | list = directLoader()->keyMap().values(); |
90 | if (!list.isEmpty()) { |
91 | const QString postFix = QLatin1String(" (from " ) |
92 | + QDir::toNativeSeparators(pluginPath) |
93 | + QLatin1Char(')'); |
94 | const QStringList::iterator end = list.end(); |
95 | for (QStringList::iterator it = list.begin(); it != end; ++it) |
96 | (*it).append(postFix); |
97 | } |
98 | } |
99 | #else |
100 | Q_UNUSED(pluginPath); |
101 | #endif |
102 | list.append(loader()->keyMap().values()); |
103 | qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys:" << list; |
104 | return list; |
105 | } |
106 | |
107 | QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key, const QString &pluginPath) |
108 | { |
109 | QEglFSDeviceIntegration *integration = nullptr; |
110 | #if QT_CONFIG(library) |
111 | if (!pluginPath.isEmpty()) { |
112 | QCoreApplication::addLibraryPath(pluginPath); |
113 | integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(directLoader(), key); |
114 | } |
115 | #else |
116 | Q_UNUSED(pluginPath); |
117 | #endif |
118 | if (!integration) |
119 | integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(loader(), key); |
120 | if (integration) |
121 | qCDebug(qLcEglDevDebug) << "Using EGL device integration" << key; |
122 | else |
123 | qCWarning(qLcEglDevDebug) << "Failed to load EGL device integration" << key; |
124 | |
125 | return integration; |
126 | } |
127 | |
128 | static int framebuffer = -1; |
129 | |
130 | QByteArray QEglFSDeviceIntegration::fbDeviceName() const |
131 | { |
132 | #ifdef Q_OS_LINUX |
133 | QByteArray fbDev = qgetenv("QT_QPA_EGLFS_FB" ); |
134 | if (fbDev.isEmpty()) |
135 | fbDev = QByteArrayLiteral("/dev/fb0" ); |
136 | |
137 | return fbDev; |
138 | #else |
139 | return QByteArray(); |
140 | #endif |
141 | } |
142 | |
143 | int QEglFSDeviceIntegration::framebufferIndex() const |
144 | { |
145 | int fbIndex = 0; |
146 | #if QT_CONFIG(regularexpression) |
147 | QRegularExpression fbIndexRx(QLatin1String("fb(\\d+)" )); |
148 | QFileInfo fbinfo(QString::fromLocal8Bit(fbDeviceName())); |
149 | QRegularExpressionMatch match; |
150 | if (fbinfo.isSymLink()) |
151 | match = fbIndexRx.match(fbinfo.symLinkTarget()); |
152 | else |
153 | match = fbIndexRx.match(fbinfo.fileName()); |
154 | if (match.hasMatch()) |
155 | fbIndex = match.captured(1).toInt(); |
156 | #endif |
157 | return fbIndex; |
158 | } |
159 | |
160 | void QEglFSDeviceIntegration::platformInit() |
161 | { |
162 | #ifdef Q_OS_LINUX |
163 | QByteArray fbDev = fbDeviceName(); |
164 | |
165 | framebuffer = qt_safe_open(fbDev, O_RDONLY); |
166 | |
167 | if (Q_UNLIKELY(framebuffer == -1)) { |
168 | qWarning("EGLFS: Failed to open %s" , fbDev.constData()); |
169 | qFatal("EGLFS: Can't continue without a display" ); |
170 | } |
171 | |
172 | #ifdef FBIOBLANK |
173 | ioctl(framebuffer, FBIOBLANK, VESA_NO_BLANKING); |
174 | #endif |
175 | #endif |
176 | } |
177 | |
178 | void QEglFSDeviceIntegration::platformDestroy() |
179 | { |
180 | #ifdef Q_OS_LINUX |
181 | if (framebuffer != -1) |
182 | close(framebuffer); |
183 | #endif |
184 | } |
185 | |
186 | EGLNativeDisplayType QEglFSDeviceIntegration::platformDisplay() const |
187 | { |
188 | bool displayOk; |
189 | const int defaultDisplay = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DEFAULT_DISPLAY" , &displayOk); |
190 | return displayOk ? EGLNativeDisplayType(quintptr(defaultDisplay)) : EGL_DEFAULT_DISPLAY; |
191 | } |
192 | |
193 | EGLDisplay QEglFSDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) |
194 | { |
195 | return eglGetDisplay(nativeDisplay); |
196 | } |
197 | |
198 | bool QEglFSDeviceIntegration::usesDefaultScreen() |
199 | { |
200 | return true; |
201 | } |
202 | |
203 | void QEglFSDeviceIntegration::screenInit() |
204 | { |
205 | // Nothing to do here. Called only when usesDefaultScreen is false. |
206 | } |
207 | |
208 | void QEglFSDeviceIntegration::screenDestroy() |
209 | { |
210 | QGuiApplication *app = qGuiApp; |
211 | while (!app->screens().isEmpty()) |
212 | QWindowSystemInterface::handleScreenRemoved(app->screens().constLast()->handle()); |
213 | } |
214 | |
215 | QSizeF QEglFSDeviceIntegration::physicalScreenSize() const |
216 | { |
217 | return q_physicalScreenSizeFromFb(framebuffer, screenSize()); |
218 | } |
219 | |
220 | QSize QEglFSDeviceIntegration::screenSize() const |
221 | { |
222 | return q_screenSizeFromFb(framebuffer); |
223 | } |
224 | |
225 | QDpi QEglFSDeviceIntegration::logicalDpi() const |
226 | { |
227 | return QDpi(100, 100); |
228 | } |
229 | |
230 | QDpi QEglFSDeviceIntegration::logicalBaseDpi() const |
231 | { |
232 | return QDpi(100, 100); |
233 | } |
234 | |
235 | Qt::ScreenOrientation QEglFSDeviceIntegration::nativeOrientation() const |
236 | { |
237 | return Qt::PrimaryOrientation; |
238 | } |
239 | |
240 | Qt::ScreenOrientation QEglFSDeviceIntegration::orientation() const |
241 | { |
242 | return Qt::PrimaryOrientation; |
243 | } |
244 | |
245 | int QEglFSDeviceIntegration::screenDepth() const |
246 | { |
247 | return q_screenDepthFromFb(framebuffer); |
248 | } |
249 | |
250 | QImage::Format QEglFSDeviceIntegration::screenFormat() const |
251 | { |
252 | return screenDepth() == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; |
253 | } |
254 | |
255 | qreal QEglFSDeviceIntegration::refreshRate() const |
256 | { |
257 | return q_refreshRateFromFb(framebuffer); |
258 | } |
259 | |
260 | EGLint QEglFSDeviceIntegration::surfaceType() const |
261 | { |
262 | return EGL_WINDOW_BIT; |
263 | } |
264 | |
265 | QSurfaceFormat QEglFSDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const |
266 | { |
267 | QSurfaceFormat format = inputFormat; |
268 | |
269 | static const bool force888 = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCE888" ); |
270 | if (force888) { |
271 | format.setRedBufferSize(8); |
272 | format.setGreenBufferSize(8); |
273 | format.setBlueBufferSize(8); |
274 | } |
275 | |
276 | return format; |
277 | } |
278 | |
279 | bool QEglFSDeviceIntegration::filterConfig(EGLDisplay, EGLConfig) const |
280 | { |
281 | return true; |
282 | } |
283 | |
284 | QEglFSWindow *QEglFSDeviceIntegration::createWindow(QWindow *window) const |
285 | { |
286 | return new QEglFSWindow(window); |
287 | } |
288 | |
289 | EGLNativeWindowType QEglFSDeviceIntegration::createNativeWindow(QPlatformWindow *platformWindow, |
290 | const QSize &size, |
291 | const QSurfaceFormat &format) |
292 | { |
293 | Q_UNUSED(platformWindow); |
294 | Q_UNUSED(size); |
295 | Q_UNUSED(format); |
296 | return 0; |
297 | } |
298 | |
299 | EGLNativeWindowType QEglFSDeviceIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format) |
300 | { |
301 | Q_UNUSED(format); |
302 | return 0; |
303 | } |
304 | |
305 | void QEglFSDeviceIntegration::destroyNativeWindow(EGLNativeWindowType window) |
306 | { |
307 | Q_UNUSED(window); |
308 | } |
309 | |
310 | bool QEglFSDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) const |
311 | { |
312 | Q_UNUSED(cap); |
313 | return false; |
314 | } |
315 | |
316 | QPlatformCursor *QEglFSDeviceIntegration::createCursor(QPlatformScreen *screen) const |
317 | { |
318 | #ifndef QT_NO_OPENGL |
319 | return new QEglFSCursor(static_cast<QEglFSScreen *>(screen)); |
320 | #else |
321 | Q_UNUSED(screen); |
322 | return nullptr; |
323 | #endif |
324 | } |
325 | |
326 | void QEglFSDeviceIntegration::waitForVSync(QPlatformSurface *surface) const |
327 | { |
328 | Q_UNUSED(surface); |
329 | |
330 | #if defined(Q_OS_LINUX) && defined(FBIO_WAITFORVSYNC) |
331 | static const bool forceSync = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCEVSYNC" ); |
332 | if (forceSync && framebuffer != -1) { |
333 | int arg = 0; |
334 | if (ioctl(framebuffer, FBIO_WAITFORVSYNC, &arg) == -1) |
335 | qWarning("Could not wait for vsync." ); |
336 | } |
337 | #endif |
338 | } |
339 | |
340 | void QEglFSDeviceIntegration::presentBuffer(QPlatformSurface *surface) |
341 | { |
342 | Q_UNUSED(surface); |
343 | } |
344 | |
345 | bool QEglFSDeviceIntegration::supportsPBuffers() const |
346 | { |
347 | return true; |
348 | } |
349 | |
350 | bool QEglFSDeviceIntegration::supportsSurfacelessContexts() const |
351 | { |
352 | return true; |
353 | } |
354 | |
355 | QFunctionPointer QEglFSDeviceIntegration::platformFunction(const QByteArray &function) const |
356 | { |
357 | Q_UNUSED(function); |
358 | return nullptr; |
359 | } |
360 | |
361 | void *QEglFSDeviceIntegration::nativeResourceForIntegration(const QByteArray &name) |
362 | { |
363 | Q_UNUSED(name); |
364 | return nullptr; |
365 | } |
366 | |
367 | void *QEglFSDeviceIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) |
368 | { |
369 | Q_UNUSED(resource); |
370 | Q_UNUSED(screen); |
371 | return nullptr; |
372 | } |
373 | |
374 | void *QEglFSDeviceIntegration::wlDisplay() const |
375 | { |
376 | return nullptr; |
377 | } |
378 | |
379 | #if QT_CONFIG(vulkan) |
380 | QPlatformVulkanInstance *QEglFSDeviceIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) |
381 | { |
382 | Q_UNUSED(instance); |
383 | return nullptr; |
384 | } |
385 | #endif |
386 | |
387 | EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) |
388 | { |
389 | class Chooser : public QEglConfigChooser { |
390 | public: |
391 | Chooser(EGLDisplay display) |
392 | : QEglConfigChooser(display) { } |
393 | bool filterConfig(EGLConfig config) const override { |
394 | return qt_egl_device_integration()->filterConfig(display(), config) |
395 | && QEglConfigChooser::filterConfig(config); |
396 | } |
397 | }; |
398 | |
399 | Chooser chooser(display); |
400 | chooser.setSurfaceType(qt_egl_device_integration()->surfaceType()); |
401 | chooser.setSurfaceFormat(format); |
402 | return chooser.chooseConfig(); |
403 | } |
404 | |
405 | QT_END_NAMESPACE |
406 | |