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 topWindow = static_cast<QEglFSWindow *>(compositor->windows().last());
147 // Make fullscreen
148 topWindow->setGeometry(screen->rawGeometry());
149 topWindow->resetSurface();
150 screen->setPrimarySurface(topWindow->surface());
151 compositor->setTargetWindow(topWindow->sourceWindow(), screen->rawGeometry());
152 }
153 }
154#else
155 if (m_flags.testFlag(HasNativeWindow)) {
156 if (screen->primarySurface() == m_surface)
157 screen->setPrimarySurface(EGL_NO_SURFACE);
158
159 invalidateSurface();
160 }
161#endif
162
163 m_flags = { };
164}
165
166void QEglFSWindow::invalidateSurface()
167{
168 if (m_surface != EGL_NO_SURFACE) {
169 qCDebug(qLcEglDevDebug) << Q_FUNC_INFO << " about to destroy EGLSurface: " << m_surface;
170
171 bool ok = eglDestroySurface(screen()->display(), m_surface);
172
173 if (!ok) {
174 qCWarning(qLcEglDevDebug, "QEglFSWindow::invalidateSurface() eglDestroySurface failed!"
175 " Follow-up errors or memory leaks are possible."
176 " eglGetError(): %x", eglGetError());
177 }
178
179 if (eglGetCurrentSurface(EGL_READ) == m_surface ||
180 eglGetCurrentSurface(EGL_DRAW) == m_surface) {
181 bool ok = eglMakeCurrent(eglGetCurrentDisplay(), EGL_NO_DISPLAY, EGL_NO_DISPLAY, EGL_NO_CONTEXT);
182 qCDebug(qLcEglDevDebug) << Q_FUNC_INFO << " due to eglDestroySurface on *currently* bound surface"
183 << "we just called eglMakeCurrent(..,0,0,0)! It returned: " << ok;
184 }
185
186 if (screen()->primarySurface() == m_surface)
187 screen()->setPrimarySurface(EGL_NO_SURFACE);
188
189 m_surface = EGL_NO_SURFACE;
190 m_flags = m_flags & ~Created;
191 }
192 qt_egl_device_integration()->destroyNativeWindow(m_window);
193 m_window = 0;
194}
195
196void QEglFSWindow::resetSurface()
197{
198 EGLDisplay display = screen()->display();
199 QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat());
200
201 m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
202 m_format = q_glFormatFromConfig(display, m_config, platformFormat);
203 const QSize surfaceSize = screen()->rawGeometry().size();
204 m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
205 m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
206}
207
208void QEglFSWindow::setVisible(bool visible)
209{
210#ifndef QT_NO_OPENGL
211 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
212 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
213 QWindow *wnd = window();
214
215 if (visible) {
216 compositor->addWindow(this);
217 } else {
218 compositor->removeWindow(this);
219 windows = compositor->windows();
220 if (windows.size())
221 windows.last()->sourceWindow()->requestActivate();
222 }
223#else
224 QWindow *wnd = window();
225#endif
226 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
227
228 if (visible)
229 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
230}
231
232void QEglFSWindow::setGeometry(const QRect &r)
233{
234 QRect rect = r;
235 if (m_flags.testFlag(HasNativeWindow))
236 rect = screen()->availableGeometry();
237
238 QPlatformWindow::setGeometry(rect);
239
240 QWindowSystemInterface::handleGeometryChange(window(), rect);
241
242 const QRect lastReportedGeometry = qt_window_private(window())->geometry;
243 if (rect != lastReportedGeometry)
244 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
245}
246
247QRect QEglFSWindow::geometry() const
248{
249 // For yet-to-become-fullscreen windows report the geometry covering the entire
250 // screen. This is particularly important for Quick where the root object may get
251 // sized to some geometry queried before calling create().
252 if (!m_flags.testFlag(Created) && screen()->primarySurface() == EGL_NO_SURFACE)
253 return screen()->availableGeometry();
254
255 return QPlatformWindow::geometry();
256}
257
258void QEglFSWindow::requestActivateWindow()
259{
260#ifndef QT_NO_OPENGL
261 QOpenGLCompositor::instance()->moveToTop(this);
262#endif
263 QWindow *wnd = window();
264 QWindowSystemInterface::handleFocusWindowChanged(wnd, Qt::ActiveWindowFocusReason);
265 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
266}
267
268void QEglFSWindow::raise()
269{
270 QWindow *wnd = window();
271#ifndef QT_NO_OPENGL
272 QOpenGLCompositor::instance()->moveToTop(this);
273#endif
274 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
275}
276
277void QEglFSWindow::lower()
278{
279#ifndef QT_NO_OPENGL
280 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
281 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
282 if (windows.size() > 1) {
283 int idx = windows.indexOf(this);
284 if (idx > 0) {
285 compositor->changeWindowIndex(this, idx - 1);
286 QWindowSystemInterface::handleExposeEvent(windows.last()->sourceWindow(),
287 QRect(QPoint(0, 0), windows.last()->sourceWindow()->geometry().size()));
288 }
289 }
290#endif
291}
292
293EGLSurface QEglFSWindow::surface() const
294{
295 return m_surface != EGL_NO_SURFACE ? m_surface : screen()->primarySurface();
296}
297
298QSurfaceFormat QEglFSWindow::format() const
299{
300 return m_format;
301}
302
303EGLNativeWindowType QEglFSWindow::eglWindow() const
304{
305 return m_window;
306}
307
308QEglFSScreen *QEglFSWindow::screen() const
309{
310 return static_cast<QEglFSScreen *>(QPlatformWindow::screen());
311}
312
313bool QEglFSWindow::isRaster() const
314{
315 return window()->surfaceType() == QSurface::RasterSurface;
316}
317
318#ifndef QT_NO_OPENGL
319QWindow *QEglFSWindow::sourceWindow() const
320{
321 return window();
322}
323
324const QPlatformTextureList *QEglFSWindow::textures() const
325{
326 if (m_backingStore)
327 return m_backingStore->textures();
328
329 return nullptr;
330}
331
332void QEglFSWindow::endCompositing()
333{
334 if (m_backingStore)
335 m_backingStore->notifyComposited();
336}
337#endif
338
339WId QEglFSWindow::winId() const
340{
341 return m_winId;
342}
343
344void QEglFSWindow::setOpacity(qreal)
345{
346 if (!isRaster() && !backingStore())
347 qWarning("QEglFSWindow: Cannot set opacity for non-raster windows");
348
349 // Nothing to do here. The opacity is stored in the QWindow.
350}
351
352QT_END_NAMESPACE
Combined button and popup list for selecting options.
static WId newWId()