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 <QtEglSupport/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(pathName: pluginPath) |
93 | + QLatin1Char(')'); |
94 | const QStringList::iterator end = list.end(); |
95 | for (QStringList::iterator it = list.begin(); it != end; ++it) |
96 | (*it).append(s: postFix); |
97 | } |
98 | } |
99 | #else |
100 | Q_UNUSED(pluginPath); |
101 | #endif |
102 | list.append(t: 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>(loader: directLoader(), key); |
114 | } |
115 | #else |
116 | Q_UNUSED(pluginPath); |
117 | #endif |
118 | if (!integration) |
119 | integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(loader: 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(varName: "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(str: fbDeviceName())); |
149 | QRegularExpressionMatch match; |
150 | if (fbinfo.isSymLink()) |
151 | match = fbIndexRx.match(subject: fbinfo.symLinkTarget()); |
152 | else |
153 | match = fbIndexRx.match(subject: fbinfo.fileName()); |
154 | if (match.hasMatch()) |
155 | fbIndex = match.captured(nth: 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(pathname: fbDev, O_RDONLY); |
166 | |
167 | if (Q_UNLIKELY(framebuffer == -1)) { |
168 | qWarning(msg: "EGLFS: Failed to open %s" , fbDev.constData()); |
169 | qFatal(msg: "EGLFS: Can't continue without a display" ); |
170 | } |
171 | |
172 | #ifdef FBIOBLANK |
173 | ioctl(fd: 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(fd: framebuffer); |
183 | #endif |
184 | } |
185 | |
186 | EGLNativeDisplayType QEglFSDeviceIntegration::platformDisplay() const |
187 | { |
188 | bool displayOk; |
189 | const int defaultDisplay = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_DEFAULT_DISPLAY" , ok: &displayOk); |
190 | return displayOk ? EGLNativeDisplayType(quintptr(defaultDisplay)) : EGL_DEFAULT_DISPLAY; |
191 | } |
192 | |
193 | EGLDisplay QEglFSDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) |
194 | { |
195 | return eglGetDisplay(display_id: 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(screen: app->screens().constLast()->handle()); |
213 | } |
214 | |
215 | QSizeF QEglFSDeviceIntegration::physicalScreenSize() const |
216 | { |
217 | return q_physicalScreenSizeFromFb(framebufferDevice: framebuffer, screenSize: screenSize()); |
218 | } |
219 | |
220 | QSize QEglFSDeviceIntegration::screenSize() const |
221 | { |
222 | return q_screenSizeFromFb(framebufferDevice: framebuffer); |
223 | } |
224 | |
225 | QDpi QEglFSDeviceIntegration::logicalDpi() const |
226 | { |
227 | const QSizeF ps = physicalScreenSize(); |
228 | const QSize s = screenSize(); |
229 | |
230 | if (!ps.isEmpty() && !s.isEmpty()) |
231 | return QDpi(25.4 * s.width() / ps.width(), |
232 | 25.4 * s.height() / ps.height()); |
233 | else |
234 | return QDpi(100, 100); |
235 | } |
236 | |
237 | qreal QEglFSDeviceIntegration::pixelDensity() const |
238 | { |
239 | return qMax(a: 1, b: qRound(d: logicalDpi().first / qreal(100))); |
240 | } |
241 | |
242 | Qt::ScreenOrientation QEglFSDeviceIntegration::nativeOrientation() const |
243 | { |
244 | return Qt::PrimaryOrientation; |
245 | } |
246 | |
247 | Qt::ScreenOrientation QEglFSDeviceIntegration::orientation() const |
248 | { |
249 | return Qt::PrimaryOrientation; |
250 | } |
251 | |
252 | int QEglFSDeviceIntegration::screenDepth() const |
253 | { |
254 | return q_screenDepthFromFb(framebufferDevice: framebuffer); |
255 | } |
256 | |
257 | QImage::Format QEglFSDeviceIntegration::screenFormat() const |
258 | { |
259 | return screenDepth() == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; |
260 | } |
261 | |
262 | qreal QEglFSDeviceIntegration::refreshRate() const |
263 | { |
264 | return q_refreshRateFromFb(framebufferDevice: framebuffer); |
265 | } |
266 | |
267 | EGLint QEglFSDeviceIntegration::surfaceType() const |
268 | { |
269 | return EGL_WINDOW_BIT; |
270 | } |
271 | |
272 | QSurfaceFormat QEglFSDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const |
273 | { |
274 | QSurfaceFormat format = inputFormat; |
275 | |
276 | static const bool force888 = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_FORCE888" ); |
277 | if (force888) { |
278 | format.setRedBufferSize(8); |
279 | format.setGreenBufferSize(8); |
280 | format.setBlueBufferSize(8); |
281 | } |
282 | |
283 | return format; |
284 | } |
285 | |
286 | bool QEglFSDeviceIntegration::filterConfig(EGLDisplay, EGLConfig) const |
287 | { |
288 | return true; |
289 | } |
290 | |
291 | QEglFSWindow *QEglFSDeviceIntegration::createWindow(QWindow *window) const |
292 | { |
293 | return new QEglFSWindow(window); |
294 | } |
295 | |
296 | EGLNativeWindowType QEglFSDeviceIntegration::createNativeWindow(QPlatformWindow *platformWindow, |
297 | const QSize &size, |
298 | const QSurfaceFormat &format) |
299 | { |
300 | Q_UNUSED(platformWindow); |
301 | Q_UNUSED(size); |
302 | Q_UNUSED(format); |
303 | return 0; |
304 | } |
305 | |
306 | EGLNativeWindowType QEglFSDeviceIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format) |
307 | { |
308 | Q_UNUSED(format); |
309 | return 0; |
310 | } |
311 | |
312 | void QEglFSDeviceIntegration::destroyNativeWindow(EGLNativeWindowType window) |
313 | { |
314 | Q_UNUSED(window); |
315 | } |
316 | |
317 | bool QEglFSDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) const |
318 | { |
319 | Q_UNUSED(cap); |
320 | return false; |
321 | } |
322 | |
323 | QPlatformCursor *QEglFSDeviceIntegration::createCursor(QPlatformScreen *screen) const |
324 | { |
325 | #ifndef QT_NO_OPENGL |
326 | return new QEglFSCursor(static_cast<QEglFSScreen *>(screen)); |
327 | #else |
328 | Q_UNUSED(screen); |
329 | return nullptr; |
330 | #endif |
331 | } |
332 | |
333 | void QEglFSDeviceIntegration::waitForVSync(QPlatformSurface *surface) const |
334 | { |
335 | Q_UNUSED(surface); |
336 | |
337 | #if defined(Q_OS_LINUX) && defined(FBIO_WAITFORVSYNC) |
338 | static const bool forceSync = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_FORCEVSYNC" ); |
339 | if (forceSync && framebuffer != -1) { |
340 | int arg = 0; |
341 | if (ioctl(fd: framebuffer, FBIO_WAITFORVSYNC, &arg) == -1) |
342 | qWarning(msg: "Could not wait for vsync." ); |
343 | } |
344 | #endif |
345 | } |
346 | |
347 | void QEglFSDeviceIntegration::presentBuffer(QPlatformSurface *surface) |
348 | { |
349 | Q_UNUSED(surface); |
350 | } |
351 | |
352 | bool QEglFSDeviceIntegration::supportsPBuffers() const |
353 | { |
354 | return true; |
355 | } |
356 | |
357 | bool QEglFSDeviceIntegration::supportsSurfacelessContexts() const |
358 | { |
359 | return true; |
360 | } |
361 | |
362 | QFunctionPointer QEglFSDeviceIntegration::platformFunction(const QByteArray &function) const |
363 | { |
364 | Q_UNUSED(function); |
365 | return nullptr; |
366 | } |
367 | |
368 | void *QEglFSDeviceIntegration::nativeResourceForIntegration(const QByteArray &name) |
369 | { |
370 | Q_UNUSED(name); |
371 | return nullptr; |
372 | } |
373 | |
374 | void *QEglFSDeviceIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) |
375 | { |
376 | Q_UNUSED(resource); |
377 | Q_UNUSED(screen); |
378 | return nullptr; |
379 | } |
380 | |
381 | void *QEglFSDeviceIntegration::wlDisplay() const |
382 | { |
383 | return nullptr; |
384 | } |
385 | |
386 | #if QT_CONFIG(vulkan) |
387 | QPlatformVulkanInstance *QEglFSDeviceIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) |
388 | { |
389 | Q_UNUSED(instance); |
390 | return nullptr; |
391 | } |
392 | #endif |
393 | |
394 | EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) |
395 | { |
396 | class Chooser : public QEglConfigChooser { |
397 | public: |
398 | Chooser(EGLDisplay display) |
399 | : QEglConfigChooser(display) { } |
400 | bool filterConfig(EGLConfig config) const override { |
401 | return qt_egl_device_integration()->filterConfig(display(), config) |
402 | && QEglConfigChooser::filterConfig(config); |
403 | } |
404 | }; |
405 | |
406 | Chooser chooser(display); |
407 | chooser.setSurfaceType(qt_egl_device_integration()->surfaceType()); |
408 | chooser.setSurfaceFormat(format); |
409 | return chooser.chooseConfig(); |
410 | } |
411 | |
412 | QT_END_NAMESPACE |
413 | |