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 <QtCore/qtextstream.h>
5#include <QtGui/private/qguiapplication_p.h>
6
7#include <qpa/qplatformwindow.h>
8#include <QtGui/QSurfaceFormat>
9#include <QtGui/QScreen>
10#ifndef QT_NO_OPENGL
11# include <QtGui/QOpenGLContext>
12# include <QtGui/QOffscreenSurface>
13#endif
14#include <QtGui/QWindow>
15#include <QtCore/QLoggingCategory>
16#include <qpa/qwindowsysteminterface.h>
17#include <qpa/qplatforminputcontextfactory_p.h>
18
19#include "qeglfsintegration_p.h"
20#include "qeglfswindow_p.h"
21#include "qeglfshooks_p.h"
22#ifndef QT_NO_OPENGL
23# include "qeglfscontext_p.h"
24# include "qeglfscursor_p.h"
25#endif
26#include "qeglfsoffscreenwindow_p.h"
27
28#include <QtGui/private/qeglconvenience_p.h>
29#ifndef QT_NO_OPENGL
30# include <QtGui/private/qeglplatformcontext_p.h>
31# include <QtGui/private/qeglpbuffer_p.h>
32#endif
33
34#include <QtGui/private/qgenericunixfontdatabase_p.h>
35#include <QtGui/private/qgenericunixtheme_p.h>
36#include <QtGui/private/qgenericunixeventdispatcher_p.h>
37#include <QtFbSupport/private/qfbvthandler_p.h>
38#ifndef QT_NO_OPENGL
39# include <QtOpenGL/private/qopenglcompositorbackingstore_p.h>
40#endif
41#include <qpa/qplatformservices.h>
42
43#if QT_CONFIG(libinput)
44#include <QtInputSupport/private/qlibinputhandler_p.h>
45#endif
46
47#if QT_CONFIG(evdev)
48#include <QtInputSupport/private/qevdevmousemanager_p.h>
49#include <QtInputSupport/private/qevdevkeyboardmanager_p.h>
50#include <QtInputSupport/private/qevdevtouchmanager_p.h>
51#elif QT_CONFIG(vxworksevdev)
52#include <QtInputSupport/private/qvxkeyboardmanager_p.h>
53#include <QtInputSupport/private/qvxmousemanager_p.h>
54#include <QtInputSupport/private/qvxtouchmanager_p.h>
55#endif
56
57#if QT_CONFIG(tslib)
58#include <QtInputSupport/private/qtslib_p.h>
59#endif
60
61#if QT_CONFIG(integrityhid)
62#include <QtInputSupport/qintegrityhidmanager.h>
63#endif
64
65static void initResources()
66{
67#ifndef QT_NO_CURSOR
68 Q_INIT_RESOURCE(cursor);
69#endif
70}
71
72QT_BEGIN_NAMESPACE
73
74using namespace Qt::StringLiterals;
75
76QEglFSIntegration::QEglFSIntegration()
77 : m_display(EGL_NO_DISPLAY),
78 m_inputContext(nullptr),
79 m_fontDb(new QGenericUnixFontDatabase),
80 m_disableInputHandlers(false)
81{
82 m_disableInputHandlers = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_DISABLE_INPUT");
83
84 initResources();
85}
86
87void QEglFSIntegration::initialize()
88{
89 qt_egl_device_integration()->platformInit();
90
91 m_display = qt_egl_device_integration()->createDisplay(nativeDisplay: nativeDisplay());
92 if (Q_UNLIKELY(m_display == EGL_NO_DISPLAY))
93 qFatal(msg: "Could not open egl display");
94
95 EGLint major, minor;
96 if (Q_UNLIKELY(!eglInitialize(m_display, &major, &minor)))
97 qFatal(msg: "Could not initialize egl display");
98
99 m_inputContext = QPlatformInputContextFactory::create();
100
101 m_vtHandler.reset(other: new QFbVtHandler);
102
103 if (qt_egl_device_integration()->usesDefaultScreen())
104 QWindowSystemInterface::handleScreenAdded(screen: new QEglFSScreen(display()));
105 else
106 qt_egl_device_integration()->screenInit();
107
108 // Input code may rely on the screens, so do it only after the screen init.
109 if (!m_disableInputHandlers)
110 createInputHandlers();
111}
112
113void QEglFSIntegration::destroy()
114{
115 const auto toplevels = qGuiApp->topLevelWindows();
116 for (QWindow *w : toplevels)
117 w->destroy();
118
119 qt_egl_device_integration()->screenDestroy();
120
121 if (m_display != EGL_NO_DISPLAY)
122 eglTerminate(dpy: m_display);
123
124 qt_egl_device_integration()->platformDestroy();
125}
126
127QAbstractEventDispatcher *QEglFSIntegration::createEventDispatcher() const
128{
129 return createUnixEventDispatcher();
130}
131
132QPlatformServices *QEglFSIntegration::services() const
133{
134 if (m_services.isNull())
135 m_services.reset(other: new QPlatformServices);
136
137 return m_services.data();
138}
139
140QPlatformFontDatabase *QEglFSIntegration::fontDatabase() const
141{
142 return m_fontDb.data();
143}
144
145QPlatformTheme *QEglFSIntegration::createPlatformTheme(const QString &name) const
146{
147 return QGenericUnixTheme::createUnixTheme(name);
148}
149
150QPlatformBackingStore *QEglFSIntegration::createPlatformBackingStore(QWindow *window) const
151{
152#ifndef QT_NO_OPENGL
153 QOpenGLCompositorBackingStore *bs = new QOpenGLCompositorBackingStore(window);
154 if (!window->handle())
155 window->create();
156 static_cast<QEglFSWindow *>(window->handle())->setBackingStore(bs);
157 return bs;
158#else
159 Q_UNUSED(window);
160 return nullptr;
161#endif
162}
163
164QPlatformWindow *QEglFSIntegration::createPlatformWindow(QWindow *window) const
165{
166 QWindowSystemInterface::flushWindowSystemEvents(flags: QEventLoop::ExcludeUserInputEvents);
167 QEglFSWindow *w = qt_egl_device_integration()->createWindow(window);
168 w->create();
169
170 const auto showWithoutActivating = window->property(name: "_q_showWithoutActivating");
171 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
172 return w;
173
174 // Activate only the window for the primary screen to make input work
175 if (window->type() != Qt::ToolTip && window->screen() == QGuiApplication::primaryScreen())
176 w->requestActivateWindow();
177
178 return w;
179}
180
181#ifndef QT_NO_OPENGL
182QPlatformOpenGLContext *QEglFSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
183{
184 EGLDisplay dpy = context->screen() ? static_cast<QEglFSScreen *>(context->screen()->handle())->display() : display();
185 QPlatformOpenGLContext *share = context->shareHandle();
186
187 QEglFSContext *ctx;
188 QSurfaceFormat adjustedFormat = qt_egl_device_integration()->surfaceFormatFor(inputFormat: context->format());
189 EGLConfig config = QEglFSDeviceIntegration::chooseConfig(display: dpy, format: adjustedFormat);
190 ctx = new QEglFSContext(adjustedFormat, share, dpy, &config);
191
192 return ctx;
193}
194
195QOpenGLContext *QEglFSIntegration::createOpenGLContext(EGLContext context, EGLDisplay contextDisplay, QOpenGLContext *shareContext) const
196{
197 return QEGLPlatformContext::createFrom<QEglFSContext>(context, contextDisplay, platformDisplay: display(), shareContext);
198}
199
200QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
201{
202 EGLDisplay dpy = surface->screen() ? static_cast<QEglFSScreen *>(surface->screen()->handle())->display() : display();
203 QSurfaceFormat fmt = qt_egl_device_integration()->surfaceFormatFor(inputFormat: surface->requestedFormat());
204 if (qt_egl_device_integration()->supportsPBuffers()) {
205 QEGLPlatformContext::Flags flags;
206 if (!qt_egl_device_integration()->supportsSurfacelessContexts())
207 flags |= QEGLPlatformContext::NoSurfaceless;
208 return new QEGLPbuffer(dpy, fmt, surface, flags);
209 } else {
210 return new QEglFSOffscreenWindow(dpy, fmt, surface);
211 }
212 // Never return null. Multiple QWindows are not supported by this plugin.
213}
214#endif // QT_NO_OPENGL
215
216bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const
217{
218 // We assume that devices will have more and not less capabilities
219 if (qt_egl_device_integration()->hasCapability(cap))
220 return true;
221
222 switch (cap) {
223 case ThreadedPixmaps: return true;
224#ifndef QT_NO_OPENGL
225 case OpenGL: return true;
226 case ThreadedOpenGL: return true;
227 case RasterGLSurface: return true;
228#else
229 case OpenGL: return false;
230 case ThreadedOpenGL: return false;
231 case RasterGLSurface: return false;
232#endif
233 case WindowManagement: return false;
234 case OpenGLOnRasterSurface: return true;
235 default: return QPlatformIntegration::hasCapability(cap);
236 }
237}
238
239QPlatformNativeInterface *QEglFSIntegration::nativeInterface() const
240{
241 return const_cast<QEglFSIntegration *>(this);
242}
243
244enum ResourceType {
245 EglDisplay,
246 EglWindow,
247 EglContext,
248 EglConfig,
249 NativeDisplay,
250 XlibDisplay,
251 WaylandDisplay,
252 EglSurface,
253 VkSurface
254};
255
256static int resourceType(const QByteArray &key)
257{
258 static const QByteArray names[] = { // match ResourceType
259 QByteArrayLiteral("egldisplay"),
260 QByteArrayLiteral("eglwindow"),
261 QByteArrayLiteral("eglcontext"),
262 QByteArrayLiteral("eglconfig"),
263 QByteArrayLiteral("nativedisplay"),
264 QByteArrayLiteral("display"),
265 QByteArrayLiteral("server_wl_display"),
266 QByteArrayLiteral("eglsurface"),
267 QByteArrayLiteral("vksurface")
268 };
269 const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
270 const QByteArray *result = std::find(first: names, last: end, val: key);
271 if (result == end)
272 result = std::find(first: names, last: end, val: key.toLower());
273 return int(result - names);
274}
275
276void *QEglFSIntegration::nativeResourceForIntegration(const QByteArray &resource)
277{
278 void *result = nullptr;
279
280 switch (resourceType(key: resource)) {
281 case EglDisplay:
282 result = display();
283 break;
284 case NativeDisplay:
285 result = reinterpret_cast<void*>(nativeDisplay());
286 break;
287 case WaylandDisplay:
288 result = qt_egl_device_integration()->wlDisplay();
289 break;
290 default:
291 result = qt_egl_device_integration()->nativeResourceForIntegration(name: resource);
292 break;
293 }
294
295 return result;
296}
297
298void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
299{
300 void *result = nullptr;
301
302 switch (resourceType(key: resource)) {
303 case XlibDisplay:
304 // Play nice when using the x11 hooks: Be compatible with xcb that allows querying
305 // the X Display pointer, which is nothing but our native display.
306 result = reinterpret_cast<void*>(nativeDisplay());
307 break;
308 default:
309 result = qt_egl_device_integration()->nativeResourceForScreen(resource, screen);
310 break;
311 }
312
313 return result;
314}
315
316void *QEglFSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
317{
318 void *result = nullptr;
319
320 switch (resourceType(key: resource)) {
321 case EglDisplay:
322 if (window && window->handle())
323 result = static_cast<QEglFSScreen *>(window->handle()->screen())->display();
324 else
325 result = display();
326 break;
327 case EglWindow:
328 if (window && window->handle())
329 result = reinterpret_cast<void*>(static_cast<QEglFSWindow *>(window->handle())->eglWindow());
330 break;
331 case EglSurface:
332 if (window && window->handle())
333 result = reinterpret_cast<void*>(static_cast<QEglFSWindow *>(window->handle())->surface());
334 break;
335 default:
336 break;
337 }
338
339 return result;
340}
341
342#ifndef QT_NO_OPENGL
343void *QEglFSIntegration::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context)
344{
345 void *result = nullptr;
346
347 switch (resourceType(key: resource)) {
348 case EglContext:
349 if (context->handle())
350 result = static_cast<QEglFSContext *>(context->handle())->eglContext();
351 break;
352 case EglConfig:
353 if (context->handle())
354 result = static_cast<QEglFSContext *>(context->handle())->eglConfig();
355 break;
356 case EglDisplay:
357 if (context->handle())
358 result = static_cast<QEglFSContext *>(context->handle())->eglDisplay();
359 break;
360 default:
361 break;
362 }
363
364 return result;
365}
366
367static void *eglContextForContext(QOpenGLContext *context)
368{
369 Q_ASSERT(context);
370
371 QEglFSContext *handle = static_cast<QEglFSContext *>(context->handle());
372 if (!handle)
373 return nullptr;
374
375 return handle->eglContext();
376}
377#endif
378
379QPlatformNativeInterface::NativeResourceForContextFunction QEglFSIntegration::nativeResourceFunctionForContext(const QByteArray &resource)
380{
381#ifndef QT_NO_OPENGL
382 if (resource.compare(a: "get_egl_context", cs: Qt::CaseInsensitive) == 0)
383 return NativeResourceForContextFunction(eglContextForContext);
384#else
385 Q_UNUSED(resource);
386#endif
387 return nullptr;
388}
389
390QFunctionPointer QEglFSIntegration::platformFunction(const QByteArray &function) const
391{
392 return qt_egl_device_integration()->platformFunction(function);
393}
394
395QVariant QEglFSIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
396{
397 if (hint == QPlatformIntegration::ShowIsFullScreen)
398 return true;
399
400 return QPlatformIntegration::styleHint(hint);
401}
402
403#if QT_CONFIG(evdev) || QT_CONFIG(vxworksevdev)
404void QEglFSIntegration::loadKeymap(const QString &filename)
405{
406 if (m_kbdMgr)
407 m_kbdMgr->loadKeymap(file: filename);
408 else
409 qWarning(msg: "QEglFSIntegration: Cannot load keymap, no keyboard handler found");
410}
411
412void QEglFSIntegration::switchLang()
413{
414 if (m_kbdMgr)
415 m_kbdMgr->switchLang();
416 else
417 qWarning(msg: "QEglFSIntegration: Cannot switch language, no keyboard handler found");
418}
419#endif
420
421void QEglFSIntegration::createInputHandlers()
422{
423#if QT_CONFIG(libinput)
424 if (!qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_NO_LIBINPUT")) {
425 new QLibInputHandler("libinput"_L1, QString());
426 return;
427 }
428#endif
429
430#if QT_CONFIG(tslib)
431 bool useTslib = qEnvironmentVariableIntValue("QT_QPA_EGLFS_TSLIB");
432 if (useTslib)
433 new QTsLibMouseHandler("TsLib"_L1, QString() /* spec */);
434#endif
435
436#if QT_CONFIG(evdev)
437 m_kbdMgr = new QEvdevKeyboardManager("EvdevKeyboard"_L1, QString() /* spec */, this);
438 new QEvdevMouseManager("EvdevMouse"_L1, QString() /* spec */, this);
439#if QT_CONFIG(tslib)
440 if (!useTslib)
441#endif
442 new QEvdevTouchManager("EvdevTouch"_L1, QString() /* spec */, this);
443#elif QT_CONFIG(vxworksevdev)
444 m_kbdMgr = new QVxKeyboardManager("VxKeyboard"_L1, QString() /* spec */, this);
445 new QVxMouseManager("VxMouse"_L1, QString() /* spec */, this);
446 new QVxTouchManager("VxTouch"_L1, QString() /* spec */, this);
447#endif
448
449#if QT_CONFIG(integrityhid)
450 new QIntegrityHIDManager("HID", "", this);
451#endif
452}
453
454EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const
455{
456 return qt_egl_device_integration()->platformDisplay();
457}
458
459QT_END_NAMESPACE
460

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