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 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | using namespace Qt::StringLiterals; |
37 | |
38 | Q_LOGGING_CATEGORY(qLcEglDevDebug, "qt.qpa.egldeviceintegration" ) |
39 | |
40 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, |
41 | (QEglFSDeviceIntegrationFactoryInterface_iid, "/egldeviceintegrations"_L1 , Qt::CaseInsensitive)) |
42 | |
43 | QStringList 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 | |
51 | QEglFSDeviceIntegration *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 | |
64 | static int framebuffer = -1; |
65 | |
66 | QByteArray 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 | |
79 | int 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 | |
96 | void 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 | |
114 | void QEglFSDeviceIntegration::platformDestroy() |
115 | { |
116 | #ifdef Q_OS_LINUX |
117 | if (framebuffer != -1) |
118 | close(fd: framebuffer); |
119 | #endif |
120 | } |
121 | |
122 | EGLNativeDisplayType 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 | |
129 | EGLDisplay QEglFSDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) |
130 | { |
131 | return eglGetDisplay(display_id: nativeDisplay); |
132 | } |
133 | |
134 | bool QEglFSDeviceIntegration::usesDefaultScreen() |
135 | { |
136 | return true; |
137 | } |
138 | |
139 | void QEglFSDeviceIntegration::screenInit() |
140 | { |
141 | // Nothing to do here. Called only when usesDefaultScreen is false. |
142 | } |
143 | |
144 | void QEglFSDeviceIntegration::screenDestroy() |
145 | { |
146 | QGuiApplication *app = qGuiApp; |
147 | while (!app->screens().isEmpty()) |
148 | QWindowSystemInterface::handleScreenRemoved(screen: app->screens().constLast()->handle()); |
149 | } |
150 | |
151 | QSizeF QEglFSDeviceIntegration::physicalScreenSize() const |
152 | { |
153 | return q_physicalScreenSizeFromFb(framebufferDevice: framebuffer, screenSize: screenSize()); |
154 | } |
155 | |
156 | QSize QEglFSDeviceIntegration::screenSize() const |
157 | { |
158 | return q_screenSizeFromFb(framebufferDevice: framebuffer); |
159 | } |
160 | |
161 | QDpi QEglFSDeviceIntegration::logicalDpi() const |
162 | { |
163 | return QDpi(100, 100); |
164 | } |
165 | |
166 | QDpi QEglFSDeviceIntegration::logicalBaseDpi() const |
167 | { |
168 | return QDpi(100, 100); |
169 | } |
170 | |
171 | Qt::ScreenOrientation QEglFSDeviceIntegration::nativeOrientation() const |
172 | { |
173 | return Qt::PrimaryOrientation; |
174 | } |
175 | |
176 | Qt::ScreenOrientation QEglFSDeviceIntegration::orientation() const |
177 | { |
178 | return Qt::PrimaryOrientation; |
179 | } |
180 | |
181 | int QEglFSDeviceIntegration::screenDepth() const |
182 | { |
183 | return q_screenDepthFromFb(framebufferDevice: framebuffer); |
184 | } |
185 | |
186 | QImage::Format QEglFSDeviceIntegration::screenFormat() const |
187 | { |
188 | return screenDepth() == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; |
189 | } |
190 | |
191 | qreal QEglFSDeviceIntegration::refreshRate() const |
192 | { |
193 | return q_refreshRateFromFb(framebufferDevice: framebuffer); |
194 | } |
195 | |
196 | EGLint QEglFSDeviceIntegration::surfaceType() const |
197 | { |
198 | return EGL_WINDOW_BIT; |
199 | } |
200 | |
201 | QSurfaceFormat 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 | |
215 | bool QEglFSDeviceIntegration::filterConfig(EGLDisplay, EGLConfig) const |
216 | { |
217 | return true; |
218 | } |
219 | |
220 | QEglFSWindow *QEglFSDeviceIntegration::createWindow(QWindow *window) const |
221 | { |
222 | return new QEglFSWindow(window); |
223 | } |
224 | |
225 | EGLNativeWindowType 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 | |
235 | EGLNativeWindowType QEglFSDeviceIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format) |
236 | { |
237 | Q_UNUSED(format); |
238 | return 0; |
239 | } |
240 | |
241 | void QEglFSDeviceIntegration::destroyNativeWindow(EGLNativeWindowType window) |
242 | { |
243 | Q_UNUSED(window); |
244 | } |
245 | |
246 | bool QEglFSDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) const |
247 | { |
248 | Q_UNUSED(cap); |
249 | return false; |
250 | } |
251 | |
252 | QPlatformCursor *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 | |
262 | void 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 | |
276 | void QEglFSDeviceIntegration::presentBuffer(QPlatformSurface *surface) |
277 | { |
278 | Q_UNUSED(surface); |
279 | } |
280 | |
281 | bool QEglFSDeviceIntegration::supportsPBuffers() const |
282 | { |
283 | return true; |
284 | } |
285 | |
286 | bool QEglFSDeviceIntegration::supportsSurfacelessContexts() const |
287 | { |
288 | return true; |
289 | } |
290 | |
291 | QFunctionPointer QEglFSDeviceIntegration::platformFunction(const QByteArray &function) const |
292 | { |
293 | Q_UNUSED(function); |
294 | return nullptr; |
295 | } |
296 | |
297 | void *QEglFSDeviceIntegration::nativeResourceForIntegration(const QByteArray &name) |
298 | { |
299 | Q_UNUSED(name); |
300 | return nullptr; |
301 | } |
302 | |
303 | void *QEglFSDeviceIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) |
304 | { |
305 | Q_UNUSED(resource); |
306 | Q_UNUSED(screen); |
307 | return nullptr; |
308 | } |
309 | |
310 | void *QEglFSDeviceIntegration::wlDisplay() const |
311 | { |
312 | return nullptr; |
313 | } |
314 | |
315 | EGLConfig 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 | |
333 | QT_END_NAMESPACE |
334 | |
335 | #include "moc_qeglfsdeviceintegration_p.cpp" |
336 | |