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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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