1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtCore/qtextstream.h>
41#include <qpa/qwindowsysteminterface.h>
42#include <qpa/qplatformintegration.h>
43#include <private/qguiapplication_p.h>
44#include <private/qwindow_p.h>
45#ifndef QT_NO_OPENGL
46# include <QtGui/private/qopenglcontext_p.h>
47# include <QtGui/QOpenGLContext>
48# include <QtPlatformCompositorSupport/private/qopenglcompositorbackingstore_p.h>
49#endif
50#include <QtEglSupport/private/qeglconvenience_p.h>
51
52#include "qeglfswindow_p.h"
53#ifndef QT_NO_OPENGL
54# include "qeglfscursor_p.h"
55#endif
56#include "qeglfshooks_p.h"
57#include "qeglfsdeviceintegration_p.h"
58
59QT_BEGIN_NAMESPACE
60
61QEglFSWindow::QEglFSWindow(QWindow *w)
62 : QPlatformWindow(w),
63#ifndef QT_NO_OPENGL
64 m_backingStore(0),
65 m_rasterCompositingContext(0),
66#endif
67 m_raster(false),
68 m_winId(0),
69 m_surface(EGL_NO_SURFACE),
70 m_window(0),
71 m_flags(0)
72{
73}
74
75QEglFSWindow::~QEglFSWindow()
76{
77 destroy();
78}
79
80static WId newWId()
81{
82 static WId id = 0;
83
84 if (id == std::numeric_limits<WId>::max())
85 qWarning("QEGLPlatformWindow: Out of window IDs");
86
87 return ++id;
88}
89
90void QEglFSWindow::create()
91{
92 if (m_flags.testFlag(Created))
93 return;
94
95 m_winId = newWId();
96
97 // Save the original surface type before changing to OpenGLSurface.
98 m_raster = (window()->surfaceType() == QSurface::RasterSurface);
99 if (m_raster) // change to OpenGL, but not for RasterGLSurface
100 window()->setSurfaceType(QSurface::OpenGLSurface);
101
102 if (window()->type() == Qt::Desktop) {
103 QRect fullscreenRect(QPoint(), screen()->availableGeometry().size());
104 QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect);
105 return;
106 }
107
108 m_flags = Created;
109
110 if (window()->type() == Qt::Desktop)
111 return;
112
113 // Stop if there is already a window backed by a native window and surface. Additional
114 // raster windows will not have their own native window, surface and context. Instead,
115 // they will be composited onto the root window's surface.
116 QEglFSScreen *screen = this->screen();
117#ifndef QT_NO_OPENGL
118 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
119 if (screen->primarySurface() != EGL_NO_SURFACE) {
120 if (Q_UNLIKELY(!isRaster() || !compositor->targetWindow())) {
121#if !defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED)
122 // We can have either a single OpenGL window or multiple raster windows.
123 // Other combinations cannot work.
124 qFatal("EGLFS: OpenGL windows cannot be mixed with others.");
125#endif
126 return;
127 }
128 m_format = compositor->targetWindow()->format();
129 return;
130 }
131#endif // QT_NO_OPENGL
132
133 m_flags |= HasNativeWindow;
134 setGeometry(QRect()); // will become fullscreen
135
136 resetSurface();
137
138 if (Q_UNLIKELY(m_surface == EGL_NO_SURFACE)) {
139 EGLint error = eglGetError();
140 eglTerminate(screen->display());
141 qFatal("EGL Error : Could not create the egl surface: error = 0x%x\n", error);
142 }
143
144 screen->setPrimarySurface(m_surface);
145
146#ifndef QT_NO_OPENGL
147 if (isRaster()) {
148 m_rasterCompositingContext = new QOpenGLContext;
149 m_rasterCompositingContext->setShareContext(qt_gl_global_share_context());
150 m_rasterCompositingContext->setFormat(m_format);
151 m_rasterCompositingContext->setScreen(window()->screen());
152 if (Q_UNLIKELY(!m_rasterCompositingContext->create()))
153 qFatal("EGLFS: Failed to create compositing context");
154 compositor->setTarget(m_rasterCompositingContext, window(), screen->rawGeometry());
155 compositor->setRotation(qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION"));
156 // If there is a "root" window into which raster and QOpenGLWidget content is
157 // composited, all other contexts must share with its context.
158 if (!qt_gl_global_share_context()) {
159 qt_gl_set_global_share_context(m_rasterCompositingContext);
160 // What we set up here is in effect equivalent to the application setting
161 // AA_ShareOpenGLContexts. Set the attribute to be fully consistent.
162 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
163 }
164 }
165#endif // QT_NO_OPENGL
166}
167
168void QEglFSWindow::destroy()
169{
170#ifndef QT_NO_OPENGL
171 QOpenGLCompositor::instance()->removeWindow(this);
172#endif
173
174 QEglFSScreen *screen = this->screen();
175 if (m_flags.testFlag(HasNativeWindow)) {
176#ifndef QT_NO_OPENGL
177 QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor());
178 if (cursor)
179 cursor->resetResources();
180#endif
181 if (screen->primarySurface() == m_surface)
182 screen->setPrimarySurface(EGL_NO_SURFACE);
183
184 invalidateSurface();
185
186#ifndef QT_NO_OPENGL
187 QOpenGLCompositor::destroy();
188 delete m_rasterCompositingContext;
189#endif
190 }
191
192 m_flags = 0;
193}
194
195void QEglFSWindow::invalidateSurface()
196{
197 if (m_surface != EGL_NO_SURFACE) {
198 eglDestroySurface(screen()->display(), m_surface);
199 m_surface = EGL_NO_SURFACE;
200 }
201 qt_egl_device_integration()->destroyNativeWindow(m_window);
202 m_window = 0;
203}
204
205void QEglFSWindow::resetSurface()
206{
207 EGLDisplay display = screen()->display();
208 QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat());
209
210 m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
211 m_format = q_glFormatFromConfig(display, m_config, platformFormat);
212 const QSize surfaceSize = screen()->rawGeometry().size();
213 m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
214 m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL);
215}
216
217void QEglFSWindow::setVisible(bool visible)
218{
219#ifndef QT_NO_OPENGL
220 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
221 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
222 QWindow *wnd = window();
223
224 if (wnd->type() != Qt::Desktop) {
225 if (visible) {
226 compositor->addWindow(this);
227 } else {
228 compositor->removeWindow(this);
229 windows = compositor->windows();
230 if (windows.size())
231 windows.last()->sourceWindow()->requestActivate();
232 }
233 }
234#else
235 QWindow *wnd = window();
236#endif
237 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
238
239 if (visible)
240 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
241}
242
243void QEglFSWindow::setGeometry(const QRect &r)
244{
245 QRect rect = r;
246 if (m_flags.testFlag(HasNativeWindow))
247 rect = screen()->availableGeometry();
248
249 QPlatformWindow::setGeometry(rect);
250
251 QWindowSystemInterface::handleGeometryChange(window(), rect);
252
253 const QRect lastReportedGeometry = qt_window_private(window())->geometry;
254 if (rect != lastReportedGeometry)
255 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
256}
257
258QRect QEglFSWindow::geometry() const
259{
260 // For yet-to-become-fullscreen windows report the geometry covering the entire
261 // screen. This is particularly important for Quick where the root object may get
262 // sized to some geometry queried before calling create().
263 if (!m_flags.testFlag(Created) && screen()->primarySurface() == EGL_NO_SURFACE)
264 return screen()->availableGeometry();
265
266 return QPlatformWindow::geometry();
267}
268
269void QEglFSWindow::requestActivateWindow()
270{
271#ifndef QT_NO_OPENGL
272 if (window()->type() != Qt::Desktop)
273 QOpenGLCompositor::instance()->moveToTop(this);
274#endif
275 QWindow *wnd = window();
276 QWindowSystemInterface::handleWindowActivated(wnd);
277 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
278}
279
280void QEglFSWindow::raise()
281{
282 QWindow *wnd = window();
283 if (wnd->type() != Qt::Desktop) {
284#ifndef QT_NO_OPENGL
285 QOpenGLCompositor::instance()->moveToTop(this);
286#endif
287 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
288 }
289}
290
291void QEglFSWindow::lower()
292{
293#ifndef QT_NO_OPENGL
294 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
295 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
296 if (window()->type() != Qt::Desktop && windows.count() > 1) {
297 int idx = windows.indexOf(this);
298 if (idx > 0) {
299 compositor->changeWindowIndex(this, idx - 1);
300 QWindowSystemInterface::handleExposeEvent(windows.last()->sourceWindow(),
301 QRect(QPoint(0, 0), windows.last()->sourceWindow()->geometry().size()));
302 }
303 }
304#endif
305}
306
307EGLSurface QEglFSWindow::surface() const
308{
309 return m_surface != EGL_NO_SURFACE ? m_surface : screen()->primarySurface();
310}
311
312QSurfaceFormat QEglFSWindow::format() const
313{
314 return m_format;
315}
316
317EGLNativeWindowType QEglFSWindow::eglWindow() const
318{
319 return m_window;
320}
321
322QEglFSScreen *QEglFSWindow::screen() const
323{
324 return static_cast<QEglFSScreen *>(QPlatformWindow::screen());
325}
326
327bool QEglFSWindow::isRaster() const
328{
329 return m_raster || window()->surfaceType() == 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 0;
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())
361 qWarning("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