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