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

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