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