Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qeglfswindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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// Qt-Security score:significant reason:default
4
5#include <QtCore/qtextstream.h>
6#include <qpa/qwindowsysteminterface.h>
7#include <qpa/qplatformintegration.h>
8#include <private/qguiapplication_p.h>
9#include <private/qwindow_p.h>
10#ifndef QT_NO_OPENGL
11# include <QtGui/private/qopenglcontext_p.h>
12# include <QtGui/QOpenGLContext>
13# include <QtOpenGL/private/qopenglcompositorbackingstore_p.h>
14#endif
15#include <QtGui/private/qeglconvenience_p.h>
16
17#include "qeglfswindow_p.h"
18#ifndef QT_NO_OPENGL
19# include "qeglfscursor_p.h"
20#endif
21#include "qeglfshooks_p.h"
23
25
26QEglFSWindow::QEglFSWindow(QWindow *w)
27 : QPlatformWindow(w),
28#ifndef QT_NO_OPENGL
29 m_backingStore(nullptr),
30 m_rasterCompositingContext(nullptr),
31#endif
32 m_winId(0),
33 m_surface(EGL_NO_SURFACE),
34 m_window(0)
35{
36}
37
38QEglFSWindow::~QEglFSWindow()
39{
40 destroy();
41}
42
43static WId newWId()
44{
45 static WId id = 0;
46
47 if (id == std::numeric_limits<WId>::max())
48 qWarning("QEGLPlatformWindow: Out of window IDs");
49
50 return ++id;
51}
52
53void QEglFSWindow::create()
54{
55 if (m_flags.testFlag(Created))
56 return;
57
58 m_winId = newWId();
59 m_flags = Created;
60
61 // Stop if there is already a window backed by a native window and surface. Additional
62 // raster windows will not have their own native window, surface and context. Instead,
63 // they will be composited onto the root window's surface.
64 QEglFSScreen *screen = this->screen();
65#ifndef QT_NO_OPENGL
66 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
67 if (screen->primarySurface() != EGL_NO_SURFACE) {
68 if (Q_UNLIKELY(isRaster() != (compositor->targetWindow() != nullptr))) {
69# ifndef Q_OS_ANDROID
70 // We can have either a single OpenGL window or multiple raster windows.
71 // Other combinations cannot work.
72 qFatal("EGLFS: OpenGL windows cannot be mixed with others.");
73# endif
74 return;
75 }
76 m_format = compositor->targetWindow()->format();
77 return;
78 }
79#endif // QT_NO_OPENGL
80
81 m_flags |= HasNativeWindow;
82 setGeometry(QRect()); // will become fullscreen
83
84 resetSurface();
85
86 if (Q_UNLIKELY(m_surface == EGL_NO_SURFACE)) {
87 EGLint error = eglGetError();
88 eglTerminate(screen->display());
89 qFatal("EGL Error : Could not create the egl surface: error = 0x%x\n", error);
90 }
91
92 screen->setPrimarySurface(m_surface);
93
94#ifndef QT_NO_OPENGL
95 compositor->setTargetWindow(window(), screen->rawGeometry());
96 compositor->setRotation(qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION"));
97#endif
98}
99
100void QEglFSWindow::setBackingStore(QOpenGLCompositorBackingStore *backingStore)
101{
102#ifndef QT_NO_OPENGL
103 if (!m_rasterCompositingContext) {
104 m_rasterCompositingContext = new QOpenGLContext;
105 m_rasterCompositingContext->setShareContext(QOpenGLContext::globalShareContext());
106 m_rasterCompositingContext->setFormat(m_format);
107 m_rasterCompositingContext->setScreen(window()->screen());
108 if (Q_UNLIKELY(!m_rasterCompositingContext->create()))
109 qFatal("EGLFS: Failed to create compositing context");
110 // If there is a "root" window into which raster and QOpenGLWidget content is
111 // composited, all other contexts must share with its context.
112 if (!QOpenGLContext::globalShareContext())
113 qt_gl_set_global_share_context(m_rasterCompositingContext);
114 }
115 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
116 compositor->setTargetContext(m_rasterCompositingContext);
117#endif
118 m_backingStore = backingStore;
119}
120
121void QEglFSWindow::destroy()
122{
123 if (!m_flags.testFlag(Created))
124 return; // already destroyed
125
126 QEglFSScreen *screen = this->screen();
127#ifndef QT_NO_OPENGL
128 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
129 compositor->removeWindow(this);
130 if (compositor->targetWindow() == window()) {
131 QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor());
132 if (cursor)
133 cursor->resetResources();
134
135 if (screen->primarySurface() == m_surface)
136 screen->setPrimarySurface(EGL_NO_SURFACE);
137
138 invalidateSurface();
139
140 if (compositor->windows().isEmpty()) {
141 compositor->destroy();
142 if (QOpenGLContext::globalShareContext() == m_rasterCompositingContext)
143 qt_gl_set_global_share_context(nullptr);
144 delete m_rasterCompositingContext;
145 } else {
146 auto windows = compositor->windows();
147 auto topWindow = static_cast<QEglFSWindow *>(windows.last());
148 // Make fullscreen
149 topWindow->setGeometry(screen->rawGeometry());
150 topWindow->resetSurface();
151 screen->setPrimarySurface(topWindow->surface());
152 compositor->setTargetWindow(topWindow->sourceWindow(), screen->rawGeometry());
153 }
154 }
155#else
156 if (m_flags.testFlag(HasNativeWindow)) {
157 if (screen->primarySurface() == m_surface)
158 screen->setPrimarySurface(EGL_NO_SURFACE);
159
160 invalidateSurface();
161 }
162#endif
163
164 m_flags = { };
165}
166
167void QEglFSWindow::invalidateSurface()
168{
169 if (m_surface != EGL_NO_SURFACE) {
170 qCDebug(qLcEglDevDebug) << Q_FUNC_INFO << " about to destroy EGLSurface: " << m_surface;
171
172 bool ok = eglDestroySurface(screen()->display(), m_surface);
173
174 if (!ok) {
175 qCWarning(qLcEglDevDebug, "QEglFSWindow::invalidateSurface() eglDestroySurface failed!"
176 " Follow-up errors or memory leaks are possible."
177 " eglGetError(): %x", eglGetError());
178 }
179
180 if (eglGetCurrentSurface(EGL_READ) == m_surface ||
181 eglGetCurrentSurface(EGL_DRAW) == m_surface) {
182 bool ok = eglMakeCurrent(eglGetCurrentDisplay(), EGL_NO_DISPLAY, EGL_NO_DISPLAY, EGL_NO_CONTEXT);
183 qCDebug(qLcEglDevDebug) << Q_FUNC_INFO << " due to eglDestroySurface on *currently* bound surface"
184 << "we just called eglMakeCurrent(..,0,0,0)! It returned: " << ok;
185 }
186
187 if (screen()->primarySurface() == m_surface)
188 screen()->setPrimarySurface(EGL_NO_SURFACE);
189
190 m_surface = EGL_NO_SURFACE;
191 m_flags = m_flags & ~Created;
192 }
193 qt_egl_device_integration()->destroyNativeWindow(m_window);
194 m_window = 0;
195}
196
197void QEglFSWindow::resetSurface()
198{
199 EGLDisplay display = screen()->display();
200 QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat());
201
202 m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
203 m_format = q_glFormatFromConfig(display, m_config, platformFormat);
204 const QSize surfaceSize = screen()->rawGeometry().size();
205 m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
206 m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
207}
208
209void QEglFSWindow::setVisible(bool visible)
210{
211#ifndef QT_NO_OPENGL
212 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
213 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
214 QWindow *wnd = window();
215
216 if (visible) {
217 compositor->addWindow(this);
218 } else {
219 compositor->removeWindow(this);
220 windows = compositor->windows();
221 if (windows.size())
222 windows.last()->sourceWindow()->requestActivate();
223 }
224#else
225 QWindow *wnd = window();
226#endif
227 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
228
229 if (visible)
230 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
231}
232
233void QEglFSWindow::setGeometry(const QRect &r)
234{
235 QRect rect = r;
236 if (m_flags.testFlag(HasNativeWindow))
237 rect = screen()->availableGeometry();
238
239 QPlatformWindow::setGeometry(rect);
240
241 QWindowSystemInterface::handleGeometryChange(window(), rect);
242
243 const QRect lastReportedGeometry = qt_window_private(window())->geometry;
244 if (rect != lastReportedGeometry)
245 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
246}
247
248QRect QEglFSWindow::geometry() const
249{
250 // For yet-to-become-fullscreen windows report the geometry covering the entire
251 // screen. This is particularly important for Quick where the root object may get
252 // sized to some geometry queried before calling create().
253 if (!m_flags.testFlag(Created) && screen()->primarySurface() == EGL_NO_SURFACE)
254 return screen()->availableGeometry();
255
256 return QPlatformWindow::geometry();
257}
258
259void QEglFSWindow::requestActivateWindow()
260{
261#ifndef QT_NO_OPENGL
262 QOpenGLCompositor::instance()->moveToTop(this);
263#endif
264 QWindow *wnd = window();
265 QWindowSystemInterface::handleFocusWindowChanged(wnd, Qt::ActiveWindowFocusReason);
266 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
267}
268
269void QEglFSWindow::raise()
270{
271 QWindow *wnd = window();
272#ifndef QT_NO_OPENGL
273 QOpenGLCompositor::instance()->moveToTop(this);
274#endif
275 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
276}
277
278void QEglFSWindow::lower()
279{
280#ifndef QT_NO_OPENGL
281 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
282 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
283 if (windows.size() > 1) {
284 int idx = windows.indexOf(this);
285 if (idx > 0) {
286 compositor->changeWindowIndex(this, idx - 1);
287 QWindowSystemInterface::handleExposeEvent(windows.last()->sourceWindow(),
288 QRect(QPoint(0, 0), windows.last()->sourceWindow()->geometry().size()));
289 }
290 }
291#endif
292}
293
294EGLSurface QEglFSWindow::surface() const
295{
296 return m_surface != EGL_NO_SURFACE ? m_surface : screen()->primarySurface();
297}
298
299QSurfaceFormat QEglFSWindow::format() const
300{
301 return m_format;
302}
303
304EGLNativeWindowType QEglFSWindow::eglWindow() const
305{
306 return m_window;
307}
308
309QEglFSScreen *QEglFSWindow::screen() const
310{
311 return static_cast<QEglFSScreen *>(QPlatformWindow::screen());
312}
313
314bool QEglFSWindow::isRaster() const
315{
316 return window()->surfaceType() == QSurface::RasterSurface;
317}
318
319#ifndef QT_NO_OPENGL
320QWindow *QEglFSWindow::sourceWindow() const
321{
322 return window();
323}
324
325const QPlatformTextureList *QEglFSWindow::textures() const
326{
327 if (m_backingStore)
328 return m_backingStore->textures();
329
330 return nullptr;
331}
332
333void QEglFSWindow::endCompositing()
334{
335 if (m_backingStore)
336 m_backingStore->notifyComposited();
337}
338#endif
339
340WId QEglFSWindow::winId() const
341{
342 return m_winId;
343}
344
345void QEglFSWindow::setOpacity(qreal)
346{
347 if (!isRaster() && !backingStore())
348 qWarning("QEglFSWindow: Cannot set opacity for non-raster windows");
349
350 // Nothing to do here. The opacity is stored in the QWindow.
351}
352
353QT_END_NAMESPACE
Combined button and popup list for selecting options.
static WId newWId()