1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qeglfsdeviceintegration_p.h"
5#include "qeglfsintegration_p.h"
6#ifndef QT_NO_OPENGL
7# include "qeglfscursor_p.h"
8#endif
9#include "qeglfswindow_p.h"
10#include "qeglfsscreen_p.h"
11#include "qeglfshooks_p.h"
12
13#include <QtGui/private/qeglconvenience_p.h>
14#include <QGuiApplication>
15#include <private/qguiapplication_p.h>
16#include <QScreen>
17#include <QDir>
18#if QT_CONFIG(regularexpression)
19# include <QFileInfo>
20# include <QRegularExpression>
21#endif
22#include <QLoggingCategory>
23
24#if defined(Q_OS_LINUX)
25#include <fcntl.h>
26#include <unistd.h>
27#include <linux/fb.h>
28#include <sys/ioctl.h>
29#endif
30
31#include <private/qfactoryloader_p.h>
32#include <private/qcore_unix_p.h>
33
34QT_BEGIN_NAMESPACE
35
36using namespace Qt::StringLiterals;
37
38Q_LOGGING_CATEGORY(qLcEglDevDebug, "qt.qpa.egldeviceintegration")
39
40Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
41 (QEglFSDeviceIntegrationFactoryInterface_iid, "/egldeviceintegrations"_L1, Qt::CaseInsensitive))
42
43QStringList QEglFSDeviceIntegrationFactory::keys()
44{
45 QStringList list;
46 list.append(other: loader()->keyMap().values());
47 qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys:" << list;
48 return list;
49}
50
51QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key)
52{
53 QEglFSDeviceIntegration *integration = nullptr;
54 if (!integration)
55 integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(loader: loader(), key);
56 if (integration)
57 qCDebug(qLcEglDevDebug) << "Using EGL device integration" << key;
58 else
59 qCWarning(qLcEglDevDebug) << "Failed to load EGL device integration" << key;
60
61 return integration;
62}
63
64static int framebuffer = -1;
65
66QByteArray QEglFSDeviceIntegration::fbDeviceName() const
67{
68#ifdef Q_OS_LINUX
69 QByteArray fbDev = qgetenv(varName: "QT_QPA_EGLFS_FB");
70 if (fbDev.isEmpty())
71 fbDev = QByteArrayLiteral("/dev/fb0");
72
73 return fbDev;
74#else
75 return QByteArray();
76#endif
77}
78
79int QEglFSDeviceIntegration::framebufferIndex() const
80{
81 int fbIndex = 0;
82#if QT_CONFIG(regularexpression)
83 QRegularExpression fbIndexRx("fb(\\d+)"_L1);
84 QFileInfo fbinfo(QString::fromLocal8Bit(ba: fbDeviceName()));
85 QRegularExpressionMatch match;
86 if (fbinfo.isSymLink())
87 match = fbIndexRx.match(subject: fbinfo.symLinkTarget());
88 else
89 match = fbIndexRx.match(subject: fbinfo.fileName());
90 if (match.hasMatch())
91 fbIndex = match.captured(nth: 1).toInt();
92#endif
93 return fbIndex;
94}
95
96void QEglFSDeviceIntegration::platformInit()
97{
98#ifdef Q_OS_LINUX
99 QByteArray fbDev = fbDeviceName();
100
101 framebuffer = qt_safe_open(pathname: fbDev, O_RDONLY);
102
103 if (Q_UNLIKELY(framebuffer == -1)) {
104 qWarning(msg: "EGLFS: Failed to open %s", fbDev.constData());
105 qFatal(msg: "EGLFS: Can't continue without a display");
106 }
107
108#ifdef FBIOBLANK
109 ioctl(fd: framebuffer, FBIOBLANK, VESA_NO_BLANKING);
110#endif
111#endif
112}
113
114void QEglFSDeviceIntegration::platformDestroy()
115{
116#ifdef Q_OS_LINUX
117 if (framebuffer != -1)
118 close(fd: framebuffer);
119#endif
120}
121
122EGLNativeDisplayType QEglFSDeviceIntegration::platformDisplay() const
123{
124 bool displayOk;
125 const int defaultDisplay = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_DEFAULT_DISPLAY", ok: &displayOk);
126 return displayOk ? EGLNativeDisplayType(quintptr(defaultDisplay)) : EGL_DEFAULT_DISPLAY;
127}
128
129EGLDisplay QEglFSDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay)
130{
131 return eglGetDisplay(display_id: nativeDisplay);
132}
133
134bool QEglFSDeviceIntegration::usesDefaultScreen()
135{
136 return true;
137}
138
139void QEglFSDeviceIntegration::screenInit()
140{
141 // Nothing to do here. Called only when usesDefaultScreen is false.
142}
143
144void QEglFSDeviceIntegration::screenDestroy()
145{
146 QGuiApplication *app = qGuiApp;
147 while (!app->screens().isEmpty())
148 QWindowSystemInterface::handleScreenRemoved(screen: app->screens().constLast()->handle());
149}
150
151QSizeF QEglFSDeviceIntegration::physicalScreenSize() const
152{
153 return q_physicalScreenSizeFromFb(framebufferDevice: framebuffer, screenSize: screenSize());
154}
155
156QSize QEglFSDeviceIntegration::screenSize() const
157{
158 return q_screenSizeFromFb(framebufferDevice: framebuffer);
159}
160
161QDpi QEglFSDeviceIntegration::logicalDpi() const
162{
163 return QDpi(100, 100);
164}
165
166QDpi QEglFSDeviceIntegration::logicalBaseDpi() const
167{
168 return QDpi(100, 100);
169}
170
171Qt::ScreenOrientation QEglFSDeviceIntegration::nativeOrientation() const
172{
173 return Qt::PrimaryOrientation;
174}
175
176Qt::ScreenOrientation QEglFSDeviceIntegration::orientation() const
177{
178 return Qt::PrimaryOrientation;
179}
180
181int QEglFSDeviceIntegration::screenDepth() const
182{
183 return q_screenDepthFromFb(framebufferDevice: framebuffer);
184}
185
186QImage::Format QEglFSDeviceIntegration::screenFormat() const
187{
188 return screenDepth() == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
189}
190
191qreal QEglFSDeviceIntegration::refreshRate() const
192{
193 return q_refreshRateFromFb(framebufferDevice: framebuffer);
194}
195
196EGLint QEglFSDeviceIntegration::surfaceType() const
197{
198 return EGL_WINDOW_BIT;
199}
200
201QSurfaceFormat QEglFSDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const
202{
203 QSurfaceFormat format = inputFormat;
204
205 static const bool force888 = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_FORCE888");
206 if (force888) {
207 format.setRedBufferSize(8);
208 format.setGreenBufferSize(8);
209 format.setBlueBufferSize(8);
210 }
211
212 return format;
213}
214
215bool QEglFSDeviceIntegration::filterConfig(EGLDisplay, EGLConfig) const
216{
217 return true;
218}
219
220QEglFSWindow *QEglFSDeviceIntegration::createWindow(QWindow *window) const
221{
222 return new QEglFSWindow(window);
223}
224
225EGLNativeWindowType QEglFSDeviceIntegration::createNativeWindow(QPlatformWindow *platformWindow,
226 const QSize &size,
227 const QSurfaceFormat &format)
228{
229 Q_UNUSED(platformWindow);
230 Q_UNUSED(size);
231 Q_UNUSED(format);
232 return 0;
233}
234
235EGLNativeWindowType QEglFSDeviceIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format)
236{
237 Q_UNUSED(format);
238 return 0;
239}
240
241void QEglFSDeviceIntegration::destroyNativeWindow(EGLNativeWindowType window)
242{
243 Q_UNUSED(window);
244}
245
246bool QEglFSDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) const
247{
248 Q_UNUSED(cap);
249 return false;
250}
251
252QPlatformCursor *QEglFSDeviceIntegration::createCursor(QPlatformScreen *screen) const
253{
254#ifndef QT_NO_OPENGL
255 return new QEglFSCursor(static_cast<QEglFSScreen *>(screen));
256#else
257 Q_UNUSED(screen);
258 return nullptr;
259#endif
260}
261
262void QEglFSDeviceIntegration::waitForVSync(QPlatformSurface *surface) const
263{
264 Q_UNUSED(surface);
265
266#if defined(Q_OS_LINUX) && defined(FBIO_WAITFORVSYNC)
267 static const bool forceSync = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_FORCEVSYNC");
268 if (forceSync && framebuffer != -1) {
269 int arg = 0;
270 if (ioctl(fd: framebuffer, FBIO_WAITFORVSYNC, &arg) == -1)
271 qWarning(msg: "Could not wait for vsync.");
272 }
273#endif
274}
275
276void QEglFSDeviceIntegration::presentBuffer(QPlatformSurface *surface)
277{
278 Q_UNUSED(surface);
279}
280
281bool QEglFSDeviceIntegration::supportsPBuffers() const
282{
283 return true;
284}
285
286bool QEglFSDeviceIntegration::supportsSurfacelessContexts() const
287{
288 return true;
289}
290
291QFunctionPointer QEglFSDeviceIntegration::platformFunction(const QByteArray &function) const
292{
293 Q_UNUSED(function);
294 return nullptr;
295}
296
297void *QEglFSDeviceIntegration::nativeResourceForIntegration(const QByteArray &name)
298{
299 Q_UNUSED(name);
300 return nullptr;
301}
302
303void *QEglFSDeviceIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
304{
305 Q_UNUSED(resource);
306 Q_UNUSED(screen);
307 return nullptr;
308}
309
310void *QEglFSDeviceIntegration::wlDisplay() const
311{
312 return nullptr;
313}
314
315EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format)
316{
317 class Chooser : public QEglConfigChooser {
318 public:
319 Chooser(EGLDisplay display)
320 : QEglConfigChooser(display) { }
321 bool filterConfig(EGLConfig config) const override {
322 return qt_egl_device_integration()->filterConfig(display(), config)
323 && QEglConfigChooser::filterConfig(config);
324 }
325 };
326
327 Chooser chooser(display);
328 chooser.setSurfaceType(qt_egl_device_integration()->surfaceType());
329 chooser.setSurfaceFormat(format);
330 return chooser.chooseConfig();
331}
332
333QT_END_NAMESPACE
334
335#include "moc_qeglfsdeviceintegration_p.cpp"
336

source code of qtbase/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp