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

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