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 if (!m_flags.testFlag(Created))
171 return; // already destroyed
172
173#ifndef QT_NO_OPENGL
174 QOpenGLCompositor::instance()->removeWindow(this);
175#endif
176
177 QEglFSScreen *screen = this->screen();
178 if (m_flags.testFlag(HasNativeWindow)) {
179#ifndef QT_NO_OPENGL
180 QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor());
181 if (cursor)
182 cursor->resetResources();
183#endif
184 if (screen->primarySurface() == m_surface)
185 screen->setPrimarySurface(EGL_NO_SURFACE);
186
187 invalidateSurface();
188
189#ifndef QT_NO_OPENGL
190 QOpenGLCompositor::destroy();
191 delete m_rasterCompositingContext;
192#endif
193 }
194
195 m_flags = 0;
196}
197
198void QEglFSWindow::invalidateSurface()
199{
200 if (m_surface != EGL_NO_SURFACE) {
201 eglDestroySurface(screen()->display(), m_surface);
202 m_surface = EGL_NO_SURFACE;
203 }
204 qt_egl_device_integration()->destroyNativeWindow(m_window);
205 m_window = 0;
206}
207
208void QEglFSWindow::resetSurface()
209{
210 EGLDisplay display = screen()->display();
211 QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat());
212
213 m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
214 m_format = q_glFormatFromConfig(display, m_config, platformFormat);
215 const QSize surfaceSize = screen()->rawGeometry().size();
216 m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
217 m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL);
218}
219
220void QEglFSWindow::setVisible(bool visible)
221{
222#ifndef QT_NO_OPENGL
223 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
224 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
225 QWindow *wnd = window();
226
227 if (wnd->type() != Qt::Desktop) {
228 if (visible) {
229 compositor->addWindow(this);
230 } else {
231 compositor->removeWindow(this);
232 windows = compositor->windows();
233 if (windows.size())
234 windows.last()->sourceWindow()->requestActivate();
235 }
236 }
237#else
238 QWindow *wnd = window();
239#endif
240 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
241
242 if (visible)
243 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
244}
245
246void QEglFSWindow::setGeometry(const QRect &r)
247{
248 QRect rect = r;
249 if (m_flags.testFlag(HasNativeWindow))
250 rect = screen()->availableGeometry();
251
252 QPlatformWindow::setGeometry(rect);
253
254 QWindowSystemInterface::handleGeometryChange(window(), rect);
255
256 const QRect lastReportedGeometry = qt_window_private(window())->geometry;
257 if (rect != lastReportedGeometry)
258 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
259}
260
261QRect QEglFSWindow::geometry() const
262{
263 // For yet-to-become-fullscreen windows report the geometry covering the entire
264 // screen. This is particularly important for Quick where the root object may get
265 // sized to some geometry queried before calling create().
266 if (!m_flags.testFlag(Created) && screen()->primarySurface() == EGL_NO_SURFACE)
267 return screen()->availableGeometry();
268
269 return QPlatformWindow::geometry();
270}
271
272void QEglFSWindow::requestActivateWindow()
273{
274#ifndef QT_NO_OPENGL
275 if (window()->type() != Qt::Desktop)
276 QOpenGLCompositor::instance()->moveToTop(this);
277#endif
278 QWindow *wnd = window();
279 QWindowSystemInterface::handleWindowActivated(wnd);
280 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
281}
282
283void QEglFSWindow::raise()
284{
285 QWindow *wnd = window();
286 if (wnd->type() != Qt::Desktop) {
287#ifndef QT_NO_OPENGL
288 QOpenGLCompositor::instance()->moveToTop(this);
289#endif
290 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
291 }
292}
293
294void QEglFSWindow::lower()
295{
296#ifndef QT_NO_OPENGL
297 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
298 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
299 if (window()->type() != Qt::Desktop && windows.count() > 1) {
300 int idx = windows.indexOf(this);
301 if (idx > 0) {
302 compositor->changeWindowIndex(this, idx - 1);
303 QWindowSystemInterface::handleExposeEvent(windows.last()->sourceWindow(),
304 QRect(QPoint(0, 0), windows.last()->sourceWindow()->geometry().size()));
305 }
306 }
307#endif
308}
309
310EGLSurface QEglFSWindow::surface() const
311{
312 return m_surface != EGL_NO_SURFACE ? m_surface : screen()->primarySurface();
313}
314
315QSurfaceFormat QEglFSWindow::format() const
316{
317 return m_format;
318}
319
320EGLNativeWindowType QEglFSWindow::eglWindow() const
321{
322 return m_window;
323}
324
325QEglFSScreen *QEglFSWindow::screen() const
326{
327 return static_cast<QEglFSScreen *>(QPlatformWindow::screen());
328}
329
330bool QEglFSWindow::isRaster() const
331{
332 return m_raster || window()->surfaceType() == QSurface::RasterGLSurface;
333}
334
335#ifndef QT_NO_OPENGL
336QWindow *QEglFSWindow::sourceWindow() const
337{
338 return window();
339}
340
341const QPlatformTextureList *QEglFSWindow::textures() const
342{
343 if (m_backingStore)
344 return m_backingStore->textures();
345
346 return 0;
347}
348
349void QEglFSWindow::endCompositing()
350{
351 if (m_backingStore)
352 m_backingStore->notifyComposited();
353}
354#endif
355
356WId QEglFSWindow::winId() const
357{
358 return m_winId;
359}
360
361void QEglFSWindow::setOpacity(qreal)
362{
363 if (!isRaster())
364 qWarning("QEglFSWindow: Cannot set opacity for non-raster windows");
365
366 // Nothing to do here. The opacity is stored in the QWindow.
367}
368
369QT_END_NAMESPACE
370